Por Que Essa Arquitetura é Importante

Construir um sistema de recomendação que funciona em escala não é só escolher o modelo certo — é sobre projetar um pipeline multistage que equilibre latência, precisão e complexidade operacional. Este post detalha um sistema de nível de produção implantado no Amazon EKS, cobrindo tudo desde preparação de dados até auto-scaling.

Desafios Principais Resolvidos

  • Cold-start: Usuários anônimos e novos itens recebem recomendações significativas através de feature masking durante o treinamento.
  • Latência: Cache em memória das features dos itens reduziu o tempo de lookup em 99,7%.
  • Atualização: Pipelines de fine-tuning diário atualizam modelos sem retreinar tudo.
  • Escala: Auto-scaling do Triton Inference Server no Kubernetes lida com cargas de requisições variáveis.

A arquitetura completa é open-source e está disponível no GitHub. Para contexto sobre tendências de front-end, veja este artigo sobre CSS em 2026: Alpha Function, Grid Lanes e o que aconteceu no CSS Day.

Visão Geral do Sistema

O sistema segue o padrão clássico retrieve-rank-rerank:

  1. Retrieval: Modelo Two-Tower + índice FAISS ANN para geração rápida de candidatos.
  2. Filtragem: Bloom filter remove itens já vistos.
  3. Ranking: Modelo DLRM pontua candidatos usando features de usuário, item e contexto.
  4. Reranking: Amostragem baseada em pontuação para diversidade final.

NVIDIA Triton Inference Server running on Amazon EKS with GPU nodes for recommender system Coding Session Visual

Componentes Chave e Padrões de Código

1. Tratamento de Cold-start com Feature Masking

Para tornar o modelo robusto a usuários desconhecidos, 5% das linhas de treino têm features de usuário e contexto substituídas por valores sentinela:

# Mascarar alguns usuários e features de contexto nos dados de treino com 5% de probabilidade
USUARIO_ANONIMO = -1
OOV_GENERO = -1
OOV_CATEGORIA_TOP = -1
OOV_DISPOSITIVO = -1

diretorio_mascarado = os.path.join(caminho_entrada, "treino_mascarado")
os.makedirs(diretorio_mascarado, exist_ok=True)

for i in range(dias_treino):
    dia = cudf.read_parquet(os.path.join(caminho_entrada, f"treino_dia_{i:02d}.parquet"))
    n = len(dia)
    
    mascara_usuario = cupy.random.random(n) < 0.05
    dia.loc[mascara_usuario, "user_id"] = USUARIO_ANONIMO
    dia.loc[mascara_usuario, "gender"] = OOV_GENERO
    dia.loc[mascara_usuario, "top_category"] = OOV_CATEGORIA_TOP
    
    mascara_dispositivo = cupy.random.random(n) < 0.05
    dia.loc[mascara_dispositivo, "device_type"] = OOV_DISPOSITIVO
    
    dia.to_parquet(os.path.join(diretorio_mascarado, f"treino_dia_{i:02d}.parquet"), index=False)
    del dia
    gc.collect()

Isso força o modelo a depender de features de contexto (tipo de dispositivo, hora) e embeddings OOV aprendidos — assim, até novos usuários recebem resultados personalizados.

2. Cache em Memória para Resolver Gargalo de Latência

A análise de perfil revelou que feast_item_lookup consumia 195 ms por requisição (52% da latência total). A solução foi substituir chamadas de rede por um cache NumPy local:

# Na inicialização, carregar todas as features dos itens uma vez na memória
class ConsultaItemFeast:
    def __init__(self, cliente_feast, ids_itens):
        self.cache = {}
        for id_item in ids_itens:
            features = cliente_feast.get_online_features(...)
            self.cache[id_item] = np.array(features)
    
    def consultar(self, ids_itens):
        # Lookup O(1) em memória em vez de chamada de rede
        return np.array([self.cache[i] for i in ids_itens])

Resultado: Redução de 99,7% na latência de lookup de features, melhoria de 54% na latência ponta a ponta e aumento de 310% na taxa de transferência com concorrência=4.

3. Ensemble do Triton Inference Server

O grafo de serviço é um DAG de 14 modelos orquestrados pelo Triton:

# Script de inicialização do Triton
set -e
DIRETORIO_MODELOS=${1:-"/model/triton_model_repository"}
echo "Iniciando Triton Inference Server"
echo "Diretório de modelos: $DIRETORIO_MODELOS"

tritonserver \
  --model-repository="$DIRETORIO_MODELOS" \
  --model-control-mode=explicit \
  --load-model=nvt_user_transform \
  --load-model=nvt_item_transform \
  --load-model=nvt_context_transform \
  --load-model=multimodal_embedding_lookup \
  --load-model=query_tower \
  --load-model=faiss_retrieval \
  --load-model=dlrm_ranking \
  --load-model=item_id_decoder \
  --load-model=feast_user_lookup \
  --load-model=feast_item_lookup \
  --load-model=filter_seen_items \
  --load-model=softmax_sampling \
  --load-model=context_preprocessor \
  --load-model=unroll_features \
  --load-model=ensemble_model

Esse carregamento explícito garante comportamento previsível de cold-start e gerenciamento limpo de versões.

Data pipeline diagram showing feature engineering with NVTabular and Kubeflow for recommender models

Limitações e Considerações Críticas

1. Contexto Apenas no Estágio de Ranking

Atualmente, o contexto da requisição (dispositivo, hora) é usado apenas pelo ranker, não pelo retriever. Isso significa que candidatos irrelevantes para um dado contexto podem ser filtrados antes do ranking. Correção: Adicionar features de contexto à query tower — já implementado em um branch separado.

2. Versionamento de Dados e Reprodutibilidade

O pipeline de treino não tem snapshots explícitos de dados. Embora um commit git fixe o código, o mesmo commit com novos dados de interação pode produzir resultados diferentes. Recomendado: Usar ferramentas como DVC ou LakeFS para versionamento de dados.

3. Sem Monitoramento de Qualidade Online

Latência e taxa de transferência são monitoradas, mas a qualidade da recomendação (ex.: CTR, engajamento) não é monitorada em produção. Deriva de performance pode passar despercebida. Próximo passo: Integrar um componente de monitoramento que dispare alertas quando métricas online se desviarem das linhas de base.

4. Lacuna de Modelagem de Sessão

A feature top_category atual é uma aproximação grosseira do interesse de curto prazo. Um encoder transformer baseado em sessão capturaria padrões comportamentais mais ricos.

Trabalhos Futuros

  • Retrieval sensível a contexto: Adicionar features de dispositivo/hora à query tower para melhor qualidade de candidatos.
  • Rastreamento de experimentos: Integrar MLflow ou Weights & Biases para comparar variantes de modelo.
  • Registro de modelos: Formalizar linhagem entre execuções de treino, dados e modelos implantados.
  • Avaliação online: Adicionar framework de teste A/B para monitorar qualidade das recomendações.

Kubernetes HPA and Karpenter autoscaling Triton Inference Server pods for production ML serving Programming Illustration

Conclusão

Esta arquitetura demonstra uma abordagem pronta para produção na construção de sistemas de recomendação que são:

  • Rápidos: Cache em memória e design multistage mantêm a latência controlada.
  • Atualizados: Fine-tuning diário se adapta a novos comportamentos sem retreinar tudo.
  • Escaláveis: Kubernetes HPA e Karpenter escalam nós GPU sob demanda.
  • Robustos: Tratamento de cold-start e Bloom filter previnem modos de falha comuns.

O código completo está disponível no repositório GitHub. Para mais sobre como plataformas de IA em larga escala lidam com otimização, veja esta análise da Plataforma Unificada de Agentes de IA da Meta.

Próximos Passos para Aprendizado

  • Aprofunde-se em modelos Two-Tower com a documentação do NVIDIA Merlin.
  • Explore Kubeflow Pipelines para automação de MLOps.
  • Experimente modelos baseados em sessão para modelagem de interesse de curto prazo.

Este artigo é baseado em um projeto open-source de nível de produção. Todo o código e configurações são compartilhados para fins educacionais.

Este conteúdo foi elaborado com o auxílio de ferramentas de IA, com base em fontes confiáveis, e revisado pela nossa equipe editorial antes da publicação. Não substitui o aconselhamento de um profissional especializado.