Pipelines de Dados (ETL/ELT para machine learning)
Pipelines de dados (data pipelines) para aprendizado de máquina (machine learning, ML) são os sistemas que ingerem (ingest), transformam (transform) e computam atributos (compute features) a partir de dados brutos (raw data) para que eles se tornem utilizáveis para treinamento (training), avaliação e inferência em produção (production inference). Diferentemente de pipelines analíticos (analytics pipelines) “clássicos”, pipelines de aprendizado de máquina precisam oferecer suporte a reprodutibilidade (reproducibility), correção ponto no tempo (point-in-time correctness), consistência entre treinamento e serviço em produção (training/serving consistency) e iteração rápida (rapid iteration) — enquanto operam em escala e sob restrições rigorosas de governança (governance).
Este artigo explica os fundamentos de ETL/ELT para cargas de trabalho (workloads) de aprendizado de máquina e como esses pipelines são implementados na prática.
O que “Pipelines de Dados” Significam em ML
Um pipeline típico de aprendizado de máquina move dados por estágios:
- Ingestão (ingestion): Coletar dados brutos de sistemas operacionais (bancos de dados, logs, APIs), provedores terceiros, sensores, ferramentas de rotulagem, etc.
- Transformação (transformation): Limpar, normalizar, fazer junções e estruturar dados em tabelas prontas para análise.
- Computação de atributos: Derivar entradas do modelo (model inputs) (atributos), frequentemente incluindo agregações, estatísticas em janela, representações vetoriais (embeddings) de texto/imagem e construção de “rótulos (label)”.
- Montagem do conjunto de dados (dataset assembly): Construir conjuntos de dados de treinamento/validação/teste com correção ponto no tempo e esquemas consistentes.
- Alinhamento com serving (serving alignment): Garantir que as mesmas definições de atributos (ou lógica equivalente) estejam disponíveis na inferência em produção.
Em aprendizado de máquina, “pipeline de dados” frequentemente inclui não só ETL/ELT, mas também pipelines de atributos (feature pipelines), versionamento de conjunto de dados (dataset versioning) e verificações de qualidade de dados (data quality checks). Muitas equipes tratam isso como parte de Operações de ML (MLOps).
ETL vs ELT (e Por Que ML Usa Ambos)
ETL (Extrair → Transformar → Carregar) (Extract → Transform → Load)
As transformações acontecem antes do carregamento no armazenamento/armazém final. Historicamente comum quando a computação fica fora do armazém de dados, ou quando é necessário sanitizar os dados antes que eles entrem em um repositório governado.
Prós
- Pode impor restrições de privacidade/segurança cedo
- Pode reduzir armazenamento ao carregar apenas dados curados
Contras
- Mais difícil iterar: transformações ficam “embutidas”
- Escalar transformações fora do armazém de dados pode ser complexo
ELT (Extrair → Carregar → Transformar) (Extract → Load → Transform)
Os dados brutos chegam primeiro (frequentemente em um lago de dados (data lake)/lago-armazém (lakehouse)), e as transformações rodam dentro da camada de computação (compute layer) do armazém/lago-armazém (motores SQL (SQL engines), Spark, etc.).
Prós
- Iteração rápida: manter histórico bruto, reprocessar conforme a lógica muda
- Ótimo encaixe para computação moderna em lago-armazém/armazém de dados
Contras
- Requer governança forte para evitar o “caos do dump bruto (raw dump chaos)”
- É preciso evitar que dados sensíveis fiquem amplamente acessíveis após o carregamento (veja Privacidade e Segurança de Dados)
Realidade prática: “ETLT”
Organizações de aprendizado de máquina comumente fazem:
- Extrair → Carregar bruto (zona de aterrissagem imutável (immutable landing zone))
- Transformar → Carregar curado
- Transformar → Materializar atributos / conjuntos de treinamento
Isso combina ELT (retenção do bruto) com camadas curadas com “portões” semelhantes ao ETL.
Requisitos Centrais para Pipelines de Dados de ML
Pipelines de aprendizado de máquina tendem a ser mais exigentes do que pipelines de BI (business intelligence):
- Reprodutibilidade: É necessário conseguir recriar o conjunto de dados de treinamento exato usado para uma versão do modelo (veja Versionamento de Dados).
- Correção ponto no tempo: Atributos devem ser computados usando apenas informação disponível antes do timestamp da predição (evita vazamento (leakage)).
- Consistência entre treinamento e serviço em produção: A inferência em produção deve computar atributos do mesmo jeito que o treinamento (ou usar os mesmos valores de atributos materializados).
- Trade-offs entre frescor e custo: Alguns atributos precisam de atualizações quase em tempo real; outros podem ser diários.
- Evolução de esquema (schema evolution): Dados mudam ao longo do tempo; pipelines precisam ser resilientes a adições/renomeações/mudanças de nulabilidade (nullability).
- Consciência de qualidade e deriva (drift): Dados de entrada quebram modelos; são necessários testes e monitoramento (veja Qualidade de Dados e Deriva).
- Governança, segurança, privacidade: Controle de acesso, tratamento de dados pessoais identificáveis (PII), regras de retenção (veja Governança de Dados, Segurança de Dados, Privacidade).
Padrões de Arquitetura Comuns
Arquitetura em Camadas (“Bronze/Prata/Ouro” ou Medallion)
Um padrão amplamente usado em sistemas de lago-armazém:
- Bronze: Dados brutos ingeridos (apenas anexação (append-only), transformação mínima)
- Prata: Dados limpos e conformados (sem duplicatas, tipados, unidos)
- Ouro: Tabelas prontas para negócio/ML (agregados, tabelas de atributos, conjuntos de dados rotulados)
Essa estrutura melhora a capacidade de depuração e dá suporte a reprocessamento.
Lote vs Fluxo (Streaming) (e Híbrido)
- Lote (batch): Jobs diários/horários para construir atributos e conjuntos de dados. Correção e backfills mais simples.
- Fluxo (streaming): Ingestão de baixa latência e atualizações de atributos (Kafka/Kinesis/PubSub + processadores de stream).
- Híbrido (hybrid): Fluxo para “estado mais recente” + recomputação em lote para correção e reprocessamentos históricos.
Muitas equipes de aprendizado de máquina usam fluxo para alimentar atributos em tempo real (real-time features) e lote para recomputar atributos canônicos.
Lago-armazém + Armazém de Dados
Uma configuração moderna comum:
- Armazenamento de objetos (object storage) + formato de tabela (table format) (Delta Lake / Apache Iceberg / Hudi) para histórico bruto/curado
- Mecanismo de armazém SQL para transformações e análises
- Armazenamento online ou cache separado para servir atributos com baixa latência
Ingestão: Colocando Dados no Sistema
Fontes típicas de ingestão
- Bancos de dados operacionais (operational databases): Postgres/MySQL, sistemas OLTP
- Logs de eventos (event logs): fluxo de cliques (clickstream), telemetria de app, barramentos de mensagens
- Arquivos: exportações CSV/Parquet/JSON, entregas de parceiros
- APIs: enriquecimento por terceiros, ferramentas de CRM
- Saídas de rotulagem: de fluxos de trabalho de rotulagem (veja Coleta de Dados e Rotulagem)
- Dados da web: pipelines de raspagem/rastreamento (scraping/crawling) (veja Dados da Web e Scraping)
- Multimodal: imagens/áudio/vídeo + metadados (veja Conjuntos de Dados Multimodais)
Decisões-chave de design de ingestão
1) Extrações completas vs CDC (Captura de Mudanças de Dados; Change Data Capture)
- Extrações completas: despejar tabelas inteiras periodicamente; mais fácil, porém caro e pode perder exclusões, a menos que seja modelado com cuidado.
- CDC: capturar inserções/atualizações/exclusões a partir de logs do banco; suporta sincronização quase em tempo real e reconstrução histórica.
CDC é comum em aprendizado de máquina quando você precisa de linhas do tempo e mudanças de estado precisas (por exemplo, evolução do perfil do usuário).
2) Logs de eventos apenas anexados vs atualizações com inserção (upserts)
- Apenas anexação é excelente para auditabilidade e reexecução (replay).
- Atualizações com inserção são convenientes para “estado mais recente”, mas podem esconder o histórico a menos que você preserve versões.
Um padrão forte é: anexar eventos brutos e, então, construir tabelas de “snapshot mais recente” como saídas derivadas.
3) Contratos de dados (data contracts) e esquemas
Para evitar que o pipeline quebre:
- Defina campos obrigatórios, tipos e semântica (um “contrato de dados”).
- Valide no momento da ingestão (rejeitar/quarentenar (quarantine) registros malformados).
- Planeje a evolução de esquema (mudanças aditivas são as mais fáceis; renomeações exigem coordenação).
4) Privacidade e minimização na ingestão
A ingestão é um ponto crítico para:
- Fazer hash/tokenizar identificadores
- Separar colunas de PII em tabelas restritas
- Aplicar políticas de retenção cedo
Isso impacta diretamente a usabilidade para aprendizado de máquina e a conformidade (veja Privacidade).
Transformação: Convertendo Dados Brutos em Tabelas Utilizáveis
Transformações tipicamente incluem:
- Limpeza: remover linhas corrompidas, normalizar codificações/fusos horários, tratar nulos
- Tipagem e parsing: JSON para colunas, string para timestamp
- Deduplicação: remover eventos duplicados usando IDs ou (timestamp, user_id, event_type)
- Resolução de entidades (entity resolution): mapear múltiplos identificadores para uma entidade canônica
- Junções (joins): combinar eventos com perfis de usuários, catálogos de produtos etc.
- Sessionização (sessionization): agrupar eventos em sessões usando janelas de inatividade
- Agregação (aggregation): contagens, taxas, janelas deslizantes (rolling windows)
- Construção de rótulos (label construction): construir variáveis-alvo (por exemplo, “comprou em até 7 dias”)
ELT com SQL + modelagem no estilo dbt (dbt-style modeling) (exemplo)
Um exemplo simplificado que constrói uma tabela de eventos limpos e um agregado diário.
-- silver.cleaned_events
select
cast(user_id as bigint) as user_id,
cast(event_time as timestamp) as event_time,
lower(event_type) as event_type,
cast(product_id as bigint) as product_id
from bronze.raw_events
where user_id is not null
and event_time is not null;
-- gold.user_daily_activity
select
user_id,
date_trunc('day', event_time) as day,
count(*) as events,
sum(case when event_type = 'purchase' then 1 else 0 end) as purchases
from silver.cleaned_events
group by 1, 2;
Na prática, você também adicionaria:
- verificações de unicidade em
(user_id, event_time, event_type, product_id)(ou event_id) - testes de nulabilidade
- verificações de frescor (a partição de hoje chegou?)
Processamento incremental (incremental processing)
Conjuntos de dados de aprendizado de máquina crescem rápido, então pipelines frequentemente usam estratégias incrementais:
- Particionar por data/event_time
- Processar apenas novas partições
- Usar mesclagem/atualização com inserção (merge/upsert) para dimensões lentamente mutáveis (padrões SCD2 (SCD2 patterns) para “histórico de atributos”)
O processamento incremental reduz custo e permite atualizações frequentes.
Pipelines de Computação de Atributos
Um atributo é um sinal de entrada usado por um modelo (por exemplo, “compras nos últimos 7 dias”). A computação de atributos é onde pipelines de aprendizado de máquina mais divergem de analytics padrão.
Atributos offline vs online
- Atributos offline: computados em lote e armazenados para treinamento (grande escala, menos sensíveis à latência).
- Atributos online: computados ou recuperados no momento da inferência com baixa latência (milissegundos).
Muitas equipes computam atributos offline e então:
- publicam em um armazenamento online, ou
- computam um subconjunto online (para sinais em tempo real como “atividade nos últimos 5 minutos”).
Correção ponto no tempo (evitando vazamento)
Uma regra central: ao construir uma linha de treino no tempo T, todo atributo deve ser computado usando apenas dados com timestamp ≤ T.
Exemplo de bug de vazamento:
- Você computa “compras nos últimos 7 dias” usando todos os eventos na partição, incluindo eventos depois do tempo do rótulo.
- Sua AUC (area under the curve) offline parece incrível; a produção falha.
Uma junção de atributos com correção ponto no tempo (point-in-time correct feature join) tipicamente usa:
- uma chave de entidade (entity key) (por exemplo, user_id)
- um timestamp de atributo (feature timestamp)
- um timestamp de rótulo (label timestamp)
- um filtro de tempo garantindo que dados do atributo não venham do futuro
Conceito de repositório de atributos (feature store) (opcional, mas comum)
Um repositório de atributos é um sistema para gerenciar definições de atributos, pipelines de computação e acesso para:
- treinamento (armazenamento offline (offline store))
- inferência (armazenamento online (online store))
- metadados (metadata) (linhagem (lineage), responsáveis, documentação)
Mesmo que você não adote um produto dedicado de repositório de atributos, frequentemente você implementa “tabelas de atributos + um registro” para impor definições consistentes.
Representações vetoriais e tipos modernos de atributos
Pipelines modernos de aprendizado de máquina frequentemente computam:
- Representações vetoriais de texto (para busca, recomendação, classificação)
- Representações vetoriais de imagem
- Representações vetoriais de usuário/item (representações aprendidas)
Esses atributos podem ser:
- computados em lote (por exemplo, atualização noturna de embeddings)
- atualizados online para novos itens/documentos
- armazenados em índices com suporte a vetores (vector-capable indexes) para recuperação em sistemas com recuperação aumentada (retrieval-augmented systems) (frequentemente parte de um pipeline de recuperação separado)
Orquestração: Agendamento, Dependências e Backfills
Pipelines de dados de aprendizado de máquina geralmente são DAGs (grafos acíclicos dirigidos; directed acyclic graphs) de tarefas:
- ingerir bruto → validar → limpar → agregar → computar atributos → construir conjuntos de treinamento → publicar artefatos (artifacts)
Ferramentas de orquestração (exemplos):
- Airflow, Dagster, Prefect (agendadores gerais de DAG)
- Argo Workflows / Kubeflow Pipelines (nativas do Kubernetes)
- Agendadores nativos do armazém (jobs dentro da plataforma)
O que a orquestração precisa lidar
- Retries e idempotência (idempotency): reexecutar uma tarefa não deve corromper dados
- Reprocessamentos históricos (backfills): recomputar partições históricas quando a lógica muda
- Acordos de nível de serviço (SLAs; service level agreements): garantir que conjuntos de treinamento estejam prontos até um horário de corte
- Rastreamento de dependências (dependency tracking): tarefas a jusante esperam por partições a montante
Esboço simples de orquestração (pseudo-Python)
@dag(schedule="0 3 * * *")
def daily_ml_features():
raw = ingest_events(ds)
validated = validate_schema(raw)
cleaned = transform_clean(validated)
features = compute_user_features(cleaned, as_of=ds)
publish_offline(features, partition=ds)
publish_online(features, partition=ds) # optional
daily_ml_features()
Em sistemas reais, cada etapa escreve em tabelas particionadas e registra metadados para linhagem e auditoria (auditing).
Construindo Conjuntos de Dados de Treinamento
Um conjunto de dados de treinamento é mais do que “uma tabela com atributos”:
- esquema consistente
- regras de amostragem determinísticas
- divisão adequada em treino/validação/teste (frequentemente baseada em tempo)
- definições e janelas de rótulo
- exclusões (por exemplo, remover usuários sem consentimento, remover tráfego anômalo)
Exemplo: construção de rótulo para predição de cancelamento (churn prediction)
Suponha que você queira prever se um usuário vai cancelar nos próximos 30 dias.
- Tempo de observação (observation time):
t0(o momento em que você “finge” fazer uma predição) - Janela do rótulo (label window): (t0, t0 + 30d]
- Rótulo: 1 se não houver atividade nessa janela, senão 0 (a definição varia por negócio)
Você deve garantir que os atributos em t0 usem apenas dados ≤ t0, enquanto o rótulo usa dados após t0.
Qualidade de Dados: Testes, Restrições e Monitoramento
Pipelines de aprendizado de máquina falham silenciosamente se você não testar os dados. Verificações comuns:
- Testes de esquema: colunas obrigatórias existem, tipos correspondem
- Testes de volume: contagens de linhas por partição dentro de limites esperados
- Testes de unicidade: chaves primárias únicas
- Testes de validade: faixas (idade ≥ 0), enums permitidos
- Testes de frescor: timestamp da última atualização não muito antigo
- Monitores de mudança de distribuição (distribution shift monitors): médias/quantis de atributos derivam (veja Qualidade de Dados e Deriva)
Frameworks e padrões de teste de dados comumente incluem:
- testes unitários (unit tests) para código de transformação
- suítes de expectativas (“expectations”) para tabelas
- alertas no nível do pipeline (pipeline-level alerts) (PagerDuty/Slack) em falhas ou anomalias
Versionamento, Linhagem e Documentação
Aprendizado de máquina reprodutível exige saber exatamente quais dados produziram um modelo:
- IDs de versão do conjunto de dados (dataset version IDs)
- versão do código (git SHA)
- definições e parâmetros de atributos (janelas, filtros)
- fontes e partições a montante
- definições de rótulo
Essa é a ponte entre pipelines de dados e governança de modelos:
- Versionamento de Dados cobre snapshots de conjunto de dados, linhagem e entradas reprodutíveis.
- Documentação de Conjuntos de Dados (Datasheets) cobre como documentar proveniência, usos pretendidos e limitações.
Um padrão prático é escrever um “manifesto de treinamento” (training manifest) imutável por execução de modelo:
{
"run_id": "2026-01-05T03:12Z",
"dataset": "gold.training_churn_v4",
"partitions": ["2025-10-01", "...", "2025-12-31"],
"feature_view_version": "fv_user_churn@1.3.0",
"label_definition": "inactive_next_30_days_v2",
"code_sha": "a1b2c3d4"
}
Segurança e Privacidade em Pipelines de ML
Como pipelines de aprendizado de máquina frequentemente combinam muitas fontes, eles são um local comum para sobreexposição acidental de dados sensíveis.
Práticas-chave:
- Acesso de menor privilégio (least privilege) a tabelas brutas/PII
- Segurança em nível de coluna (column-level security) e tokenização (tokenization) para identificadores
- Criptografia (encryption) em trânsito e em repouso
- Auditoria: quem acessou quais dados e quando
- Políticas de retenção: deletar ou agregar dados sensíveis antigos
- Consentimento e limitação de finalidade (purpose limitation): usar dados apenas para casos de uso de aprendizado de máquina permitidos
Veja Segurança de Dados e Privacidade para cobertura mais profunda.
Exemplo Prático de Ponta a Ponta: Atributos de Recomendação a partir de Clickstream
Assuma que você tem:
bronze.raw_events(user_id, item_id, event_type, event_time)- objetivo: computar atributos para um modelo de recomendação (recommender model)
Passo 1: Limpar eventos
- remover IDs nulos
- normalizar timestamps
- deduplicar por event_id, se disponível
Passo 2: Computar atributos em janela deslizante (offline)
Exemplos de atributos:
views_7d: número de visualizações de item nos últimos 7 diaspurchases_30d: compras nos últimos 30 diaslast_event_time: sinal de recência
Uma abordagem com consciência de ponto no tempo frequentemente é implementada com lógica de janela com chave por usuário e tempo do evento. Em mecanismos SQL que suportam isso, você pode computar agregações móveis; para junções estritamente ponto no tempo, muitas equipes materializam atributos por dia e então fazem join “as-of” em um limite diário.
Passo 3: Publicar
- Offline: escrever em
gold.user_features_dailyparticionado pords - Online (opcional): fazer upsert do mais recente por usuário em um armazenamento chave-valor (key-value store) para inferência
Passo 4: Montar o conjunto de treinamento
- Amostrar interações (user, item, t0) como exemplos de treinamento
- Fazer join de atributos do usuário “as-of”
t0 - Fazer join de atributos do item “as-of”
t0 - Computar rótulo (clicou/comprou nas próximas K horas/dias)
É aqui que a prevenção de vazamento e timestamps consistentes mais importam.
Boas Práticas Operacionais (O Que Torna Pipelines Sustentáveis)
Torne jobs idempotentes
Se um job roda novamente para 2025-12-01, ele deve produzir as mesmas saídas e não contar em dobro. Estratégias:
- escrever em um caminho temporário e então fazer uma troca atômica (atomically swap)
- sobrescrever partição
- fazer merge em uma chave primária
Trate reprocessamentos históricos como primeira classe
Mudanças de lógica são inevitáveis. Projete para:
- reprocessar intervalos históricos
- rastrear quais partições foram produzidas por qual versão de código
- minimizar o raio de impacto (blast radius) (recomputar apenas tabelas a jusante afetadas)
Prefira transformações declarativas (declarative transformations) quando possível
Modelos SQL (ou SQL-like) são mais fáceis de revisar e testar do que scripts ad hoc (ad-hoc scripts). Para lógica complexa, use Spark/Beam, mas mantenha interfaces estáveis.
Otimize para “depurabilidade” (debuggability)
- Manter bronze imutável
- Manter saídas prata intermediárias
- Armazenar quarentenas de erros (error quarantines) em nível de linha para registros ruins
Gerencie desbalanceamento e escala
- particione e clusterize por chaves de junção comuns
- pré-agregue quando apropriado
- cuidado com “chaves quentes (hot keys)” (por exemplo, um superusuário gerando a maioria dos eventos)
Alinhe definições offline e online
Se você computa um atributo de forma diferente online vs offline, corre risco de desalinhamento entre treinamento e serviço em produção (training-serving skew). Use:
- bibliotecas compartilhadas (shared libraries) para lógica de atributos, ou
- os mesmos valores materializados publicados em ambos os armazenamentos
Modos Comuns de Falha e Anti-Padrões
- Vazamento silencioso: usar informação futura na computação de atributos
- “Lago bruto” descontrolado: despejar tudo sem contratos, testes ou documentação
- Scripts pontuais de atributos (one-off feature scripts): atributos computados de forma diferente por modelo, impossível de manter
- Sem estratégia de backfill: qualquer correção vira uma correria (fire drill) de várias semanas
- Falta de responsáveis (ownership): sem responsáveis claros por conjuntos de dados/tabelas de atributos
- Ignorar exclusões/consentimento: violar requisitos de retenção ou privacidade
Como Isso Se Conecta ao Resto do Stack de Dados
Pipelines de dados ficam no centro do ciclo de vida de “dados para IA”:
- Dependem de boas práticas de coleta (Coleta de Dados e Rotulagem)
- Devem obedecer controles organizacionais (Governança de Dados)
- Exigem validação contínua (Qualidade de Dados e Deriva)
- Devem ser seguros e conscientes de privacidade (Segurança de Dados, Privacidade)
- Devem ser reprodutíveis e documentados (Versionamento de Dados, Documentação de Conjuntos de Dados (Datasheets))
Um pipeline de dados de aprendizado de máquina bem projetado é menos sobre uma ferramenta específica e mais sobre correção, reprodutibilidade e disciplina operacional. Ferramentas mudam; essas restrições não.