Escrito por: Felipe Almeida, com contribuições de Luiz Felix


Esta é a parte 2 de uma série de 3 partes sobre Feature Stores para ML em tempo real. Na parte 1 falamos sobre a Introdução e também sobre Prós e Contras. Na parte 3 concluímos com configurações de arquitetura do mundo real de ponta a ponta da Nubank.

Uma rápida recapitulação

Na parte 1, exploramos o que são os feature stores e o papel que desempenham nos sistemas de machine learning em tempo real. Discutimos como eles ajudam a mitigar problemas cruciais como o training-serving skew e por que, apesar dos custos de adoção, são um investimento valioso para organizações que operam ML em escala.

Nos últimos anos, usamos feature stores em uma ampla gama de aplicações de ML em tempo real na Nubank, incluindo análise de crédito, detecção de fraudes e atendimento ao cliente. Ao longo do caminho, vimos surgir padrões recorrentes, enfrentamos desafios inesperados e reunimos lições práticas que nos ajudaram a evitar armadilhas comuns enquanto maximizávamos o valor entregue pela plataforma.

Neste artigo, abordaremos algumas que consideramos especialmente importantes.

Check our job opportunies

Lição: Construir vs comprar é um espectro 

É comum nos referirmos ao trade-off de “construir vs comprar” ao discutir plataformas complexas como os feature stores. Algumas organizações podem querer construir tudo internamente, enquanto outras podem querer transferir parte da complexidade para feature stores mais “completos” como Chalk ou Feast.

Em nossa opinião, você não precisa escolher entre construir tudo ou comprar tudo. Em vez disso, você pode ter um feature store híbrido: você constrói algumas partes, usa ferramentas open-source em outras e delega a provedores de serviços terceirizados quando necessário. Isso é semelhante à abordagem que seguimos atualmente na Nubank.

Construir vs comprar não é uma escolha binária: você pode optar por “comprar” alguns componentes e construir outros você mesmo.

Figura 1 abaixo mostra uma visão de alto nível de um feature store em tempo real e alguns dos componentes que podem existir em cada camada. Cada um desses componentes pode ser “construído” ou “comprado”.

Figura 1: Um feature store em tempo real tem muitos componentes e muitos deles poderiam, em teoria, ser construídos internamente ou comprados como um produto “pronto para uso”. Curiosamente, muitos projetos open-source na verdade deram origem a empresas que fornecem versões gerenciadas deles (p. ex. Kafka/Confluent, Spark/Databricks, Cassandra/Datastax).

Na Nubank, usamos ferramentas open-source como Kubernetes, Kafka, Pinot, Spark e Flink para as partes centrais da arquitetura, onde nossos casos de uso não se distanciam demais daquilo para o qual as ferramentas foram pensadas. Também usamos serviços totalmente gerenciados dos nossos provedores de nuvem, enquanto outros componentes (como nossa infraestrutura de monitoramento) são construídos internamente. 

A chave para decidir quais partes são “construídas” vs “compradas” está no nível de controle que você tem e em onde os padrões de fluxo de trabalho da sua organização são diferentes demais dos casos de uso padrão para os quais as ferramentas foram pensadas: nesses casos, você pode não ter outra opção a não ser construir.

Lição: Os feature stores são produtos e devem ser gerenciados como tais 

Além de serem sistemas, os feature stores também são produtos. E produtos são entidades vivas que precisam ser gerenciadas, idealmente por um Product Manager (PM) dedicado. Tudo relacionado à gestão de produtos internos se aplica aqui. 

Muitas das práticas comumente associadas à gestão de produtos internos se aplicam diretamente aos feature stores, como:

  • Roadmap e priorização de tarefas: Quais tarefas e melhorias devem ser construídas primeiro? Quais solicitações estão fora do escopo da plataforma e por quê?
  • Canais de suporte: Como os usuários obterão ajuda quando encontrarem problemas ou tiverem dúvidas? Haverá um rodízio de engenheiros para fornecer suporte? Como a equipe acompanhará o tempo que leva para responder às perguntas?
  • Entrevistas/pesquisas com usuários: Como a equipe de FS avaliará o quão satisfeitos os usuários estão com o produto? 
  • Gestão de stakeholders: Como os stakeholders-chave se manterão informados sobre as melhorias do produto? Quem deve estar envolvido na definição do roadmap para que as prioridades da empresa sejam atendidas?

Observe que os feature stores diferem de outros produtos internos por naturalmente abrangerem múltiplas disciplinas. Resolver um problema de training-serving skew, por exemplo, frequentemente requer colaboração entre engenheiros de software, engenheiros de dados, cientistas de dados e especialistas de domínio. Como resultado, operar um feature store com sucesso envolve coordenar a expertise de várias áreas da organização.

Lição: Defina uma convenção de nomenclatura para as features

Estabeleça uma convenção de nomenclatura para as features no feature store. As convenções de nomenclatura têm muito pouca desvantagem (especialmente quando são leves o suficiente para acomodar muitos casos de uso).

Use uma convenção de nomenclatura para as features: isso ajuda humanos e ferramentas de IA a navegar pelo código.

As convenções de nomenclatura têm várias vantagens (assim como a padronização de modo geral):

  • Menor chance de erro humano: Nomes padronizados tornam tarefas como revisões de código, copiar e colar e edições em massa mais seguras e fáceis.
  • Menor carga cognitiva: Os usuários conseguem inferir o que uma feature representa sem precisar de documentação.
  • Maior descoberta de features: Os usuários são naturalmente expostos a outros itens do mesmo grupo ao explorar features.
  • Monitoramento e depuração mais simples: A maioria dos problemas de features (p. ex. drift, skew) afeta grupos inteiros de features, tornando os problemas mais fáceis de identificar quando essas relações estão visíveis.
  • Refatoração e organização mais fáceis: Os nomes dos grupos de features servem como namespaces naturais que podem ser reutilizados em pastas, dashboards e outros recursos de monitoramento.

Na verdade, não importa qual convenção de nomenclatura você escolha, desde que seja versátil e permita alguma personalização. Se as regras se tornarem restritivas demais, os usuários terão que dobrar e burlar a convenção de nomenclatura até que ela não sirva mais a nenhum propósito.

Alguns exemplos:

  • customer_cc_transaction_24h_count
  • customer_app_behavior_2h_logins
  • savings_account_5d_balance_daily_avg
  • savings_account__current_balance (o bloco TIME_FRAME é omitido, pois é o saldo da conta poupança em um determinado momento).

Algumas recomendações adicionais:

  • Evite usar m para representar meses, ou pode ser confundido com minutos. Se uma feature cobre os últimos 2 meses, prefira 60d.
  • Use nomes à prova de futuro que não dependam da estrutura da equipe, mas de conceitos de negócio. Os nomes das equipes refletem a estrutura organizacional e mudam com muita frequência, enquanto os conceitos de negócio tendem a permanecer estáveis ao longo do tempo.

Lição: Versionar features é difícil

Suponha que uma equipe precise de uma pequena variação de uma feature em tempo real existente no Feature Store. A equipe de FS não pode simplesmente mudar a implementação da feature, pois isso causaria training-serving skew para os modelos treinados na versão atual da feature. (Eles podem não querer retreinar seus modelos com a versão mais nova).

Portanto, é preciso criar uma feature completamente nova ou uma nova versão de uma feature existente. A maioria dos feature stores suporta o versionamento de features mas, na prática, isso significa que os clientes devem fornecer a versão da feature que desejam, além de seu nome.

No entanto, não é tão fácil saber quais mudanças são melhor descritas como uma nova versão de uma feature existente ou como uma feature completamente nova por si só. É preciso aplicar bom senso para decidir qual ação tomar em cada caso.

Por exemplo: você tem uma feature chamada customer_cc_purchases__5d__count. Intuitivamente, é o número de compras com cartão de crédito feitas por um determinado cliente nos últimos 5 dias. Agora vejamos alguns cenários do que pode acontecer em uma situação do mundo real, na Figura 2:

Figura 2: Exemplos de situações em que as equipes podem precisar criar uma nova versão de uma feature existente, criar uma totalmente diferente (dependendo da semântica da mudança) ou, em alguns casos, não fazer nada.

O desafio é que o versionamento pode se tornar rapidamente difícil de gerenciar. É fácil acabar com dezenas de versões de uma feature, o que significa um conjunto de features complexo e bagunçado (diferentes equipes têm diferentes cronogramas de retreinamento e sempre há modelos que ainda estão presos a versões anteriores de features e não podem ser retreinados no momento).

Uma possível saída desse “inferno de versionamento” é a equipe do feature store assumir para si a tarefa de migrar os modelos que usam versões antigas de features para que usem as novas. A situação é semelhante ao trabalho de atualizar aplicações para remover bibliotecas e dependências antigas e sem suporte em favor de versões mais novas. 

Os data contracts também podem ser usados para mitigar problemas de versionamento: os produtores de features definem as expectativas e os consumidores são avisados quando ocorrem mudanças de esquema e semânticas.

Lição: Use uma arquitetura de streaming quando puder e chamadas diretas quando precisar

Como discutido na Parte 1, há duas formas principais de recuperar features para inferência em tempo real. A primeira depende de chamadas diretas aos sistemas que detêm os dados de origem, enquanto a segunda depende de uma plataforma de streaming intermediária que recebe e processa eventos continuamente.

Essas abordagens podem ser resumidas como:

Features de “chamada direta”

As features são recuperadas por meio de chamadas HTTP aos microsserviços que detêm os bancos de dados transacionais “fonte da verdade”. Dependendo do caso de uso, isso pode incluir múltiplas chamadas e joins.

Features baseadas em streaming

Em vez disso, as features são recuperadas de um store intermediário: geralmente um banco de dados de curto prazo que escuta eventos de um barramento de eventos como o Kafka.

Como regra geral, recomendamos favorecer as features baseadas em streaming sempre que possível. Há várias razões para isso: 

  • Melhor escalabilidade: As features de streaming escalam melhor, porque a ingestão de dados é desacoplada do consumo de dados. Um aumento na taxa de consumo de eventos não exige que os produtores também escalem.
  • Melhor isolamento de falhas e menor raio de impacto em caso de quedas: As arquiteturas assíncronas fornecem um “isolamento de ambientes” natural que reduz as falhas em cascata. Se o produtor ou o consumidor cair, isso não causa uma falha na outra ponta.
  • Menor carga nos serviços transacionais: Em uma arquitetura de streaming, o feature store não consulta os serviços “fonte” para buscar informações. Esses serviços geralmente têm outras responsabilidades além de fornecer dados para cargas de trabalho de ML, então não se quer sobrecarregá-los a ponto de derrubá-los, o que poderia causar interrupções de negócio em fluxos não relacionados.

É verdade que usar features de streaming requer uma “plataforma” de streaming, que é a infraestrutura de mais baixo nível que permite que os serviços de origem transmitam eventos para um barramento de eventos assíncrono (geralmente o Apache Kafka). Essa é uma plataforma complexa e cara por si só, e é um pré-requisito para as features de streaming.

Lição: A recuperação de features por chamada direta e por streaming tem diferentes modos de falha

A recuperação de features falhará de tempos em tempos, seja usando estratégias de “chamada direta” ou baseadas em streaming. Alguns modos de falha são “explícitos” (ou seja, exceções e interrupções de fluxo), enquanto outros são “silenciosos”. Isso acontece porque eles apenas mudam a distribuição da feature, mas não disparam exceções explícitas. (As falhas silenciosas são muito perigosas porque você corre o risco de tomar decisões ruins por dias ou semanas antes que alguém detecte que há um problema.)

Cada estratégia falha de maneiras diferentes, e a natureza assíncrona da estratégia de streaming introduz alguns modos de falha diferentes aos quais você pode não estar acostumado. Você precisa entender como cada uma falha e quais são as implicações para o seu negócio. 

  • As features de “chamada direta” falham explicitamente se o microsserviço proprietário estiver indisponível por qualquer motivo: Isso inclui timeouts, quedas de serviço e até sobrecarga do serviço causada pela própria busca da feature.
  • As features de “chamada direta” falham silenciosamente se alguma lógica for alterada no serviço de origem: Muito frequentemente, os engenheiros que detêm os serviços não sabem que alguns endpoints são usados para “alimentar” modelos em tempo real. Às vezes, eles fazem mudanças para suportar algum caso de negócio e, sem perceber, mudam a distribuição dos dados solicitados, o que causa um problema de training-serving skew.
  • As features de streaming falham silenciosamente se os atrasos se tornarem grandes demais: Embora certo nível de atraso seja esperado nas features de streaming, às vezes esse atraso atinge níveis inaceitáveis devido a uma série de fatores estruturais e temporários. Isso significa que o modelo em tempo real consumirá informações desatualizadas, levando a training-serving skew e a uma degradação de desempenho.
  • As features de streaming falham silenciosamente se os eventos chegarem fora de ordem: Dependendo do barramento de eventos usado para processar as features ingeridas, alguns eventos podem chegar fora de ordem, o que causará training-serving skew e degradação de desempenho nos modelos.
  • As features de streaming falham silenciosamente se não houver paridade estrita entre a ingestão por batch e por streaming: Muito frequentemente, os engenheiros fazem mudanças na lógica de ingestão por batch mas esquecem de replicar as mudanças na lógica de ingestão por streaming. Nesses casos, os dados de streaming “sairão de sincronia” com os dados de batch, o que causará training-serving skew. Deve haver testes de integração para garantir que tudo o que é ingerido em batch também seja ingerido na camada de streaming.

Lição: O monitoramento do training-serving skew ainda é necessário

O training-serving skew refere-se a diferenças indesejadas entre como as features são geradas durante o tempo de treinamento e como são recuperadas no tempo de inferência ou serving

Na parte 1, argumentamos que os feature stores ajudam a mitigar esse problema, porque ambos os “caminhos” de dados (batch e tempo real) são definidos em um único lugar. Isso reduz a chance de tais desvios passarem despercebidos.

No entanto, os feature stores não eliminam o problema. Ainda é necessário monitorar o training-serving skew, pois ainda há muitas formas de os dados de batch e de tempo real saírem de sincronia mesmo quando ambos os caminhos são definidos dentro da plataforma do feature store. Dois exemplos: (1) Os serviços upstream podem mudar a semântica de um campo no caminho de tempo real sem atualizar o equivalente em batch. (2) Pode haver alguns atrasos sistemáticos no barramento de eventos, fazendo com que as features de streaming em tempo real sempre cheguem atrasadas em comparação com o instante em que deveriam ter estado disponíveis.

Mas como é, na prática, o monitoramento do training-serving skew?

É possível detectar o training-serving skew gerando programaticamente a lógica de extração de features do tempo de treinamento (sem o target) para aqueles exemplos que foram pontuados em tempo real no passado recente e, em seguida, comparando-os com os valores reais usados no tempo de inferência (obtidos dos logs de produção). Uma representação visual pode ser vista na Figura 3 abaixo:

Figura 3: Uma estratégia possível para monitorar o training-serving skew é extrair features usando a lógica de batch para instâncias pontuadas em tempo real e, em seguida, comparar os valores obtidos em cada “caminho” e ver se eles coincidem. Isso pode ser executado de forma ad-hoc ou como um job diário recorrente.

Uma ressalva importante se aplica aqui: Esta estratégia de monitoramento pressupõe que as equipes sejam capazes de gerar programaticamente conjuntos de dados de treinamento para os modelos, a serem usados como referência de comparação em relação aos logs em tempo real. Isso não apenas habilita o monitoramento do training-serving skew, mas é crucial para garantir a reprodutibilidade.

Lição: Projetos piloto são uma das formas mais eficazes de impulsionar a adoção

Ao introduzir um feature store em uma organização, pode ser que nem todas as equipes queiram usá-lo de imediato, pois podem perder alguma autonomia e controle sobre as features, perder poder relativo na organização, ter que despriorizar outros projetos, etc. Os fatores habituais que contribuem para a resistência aos esforços de plataformização.

O melhor argumento para adotar um feature store é uma visão clara dos benefícios. Mas se ainda houver resistência das equipes cliente, projetos piloto a cargo da equipe de FS são um bom caminho a seguir.

A adoção do feature store deve ser incentivada com cenouras, não com chicotes (p. ex. mandatos de cima para baixo). No fim das contas, os benefícios de usar um feature store (maior impacto, mais robustez, menor TTM, etc.) devem estar claros para incentivar a adoção. Figura 4 resume várias razões comuns pelas quais as equipes hesitam em adotar feature stores, junto com possíveis estratégias de mitigação.

Figura 4: Há muitas razões pelas quais as equipes resistem a adotar feature stores. A maioria delas pode ser mitigada se a equipe do Feature Store compartilhar parte da carga de integração (ou seja, projetos piloto) e se houver documentação clara e bons canais de suporte.

Na Figura 4, vemos que uma ótima estratégia geral de mitigação é a equipe de FS criar as primeiras integrações para as equipes cliente, como projetos piloto

Esses projetos reduzem enormemente o custo de adoção (do ponto de vista das equipes cliente) e é uma estratégia “win-win”: a equipe cliente recebe features construídas “de graça” e a equipe de FS tem um projeto piloto para submeter a plataforma a testes de estresse e servir como exemplo do qual outras equipes podem aprender.

Conclusão

Como afirmamos na parte 1, os feature stores exigem um investimento significativo em engenharia de plataforma, maturidade operacional e alinhamento organizacional. Mas para empresas que executam sistemas de ML em tempo real em escala, esse investimento desbloqueia benefícios substanciais de longo prazo.

Nossa experiência na Nubank mostrou que a adoção bem-sucedida de um feature store não se trata apenas de escolhas tecnológicas. Também depende de pensamento de produto, de práticas operacionais sólidas e de uma compreensão profunda de como os sistemas de ML do mundo real evoluem ao longo do tempo.

Ao longo do caminho, algumas lições se destacam de forma consistente:

  • É possível combinar vários tipos de componentes (open-source, de fornecedores, feitos em casa) para adaptar sua configuração às suas necessidades específicas. 
  • Os feature stores são produtos e devem ser gerenciados como tais.
  • Definir e fazer cumprir uma convenção de nomenclatura reduz a carga cognitiva e facilita o versionamento.
  • As features baseadas em streaming escalam melhor. Os feature stores também devem suportar a chamada direta para casos extremos.
  • Os fluxos de trabalho síncronos e assíncronos são muito diferentes e falham de maneiras diferentes. Entenda como cada um falha e como afetam o seu negócio.
  • Usar feature stores mitiga, mas nunca elimina completamente o training-serving skew. O monitoramento ainda é necessário.
  • Os projetos piloto são uma excelente tática geral para ajudar a comprovar o benefício de iniciativas do tipo plataforma. Os feature stores não são exceção.

Esta é a Parte 2 da nossa série sobre Feature Stores para ML em Tempo Real na Nubank. Na Parte 3, reuniremos esses conceitos e exploraremos arquiteturas do mundo real que usamos na prática.

Check our job opportunies