Skip to content

fco3lho/multihop_for_machine_learning

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

118 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Multi-Hop Data Architecture for Machine Learning

Databricks PySpark XGBoost MLflow Python Ruff

Implementação da arquitetura medalhão (Bronze → Silver → Gold) para pipelines de dados e treinamento de modelos de Machine Learning na plataforma Databricks.

Resumo: Este repositório materializa um pipeline completo de dados que transforma dados brutos em modelos preditivos, utilizando a arquitetura multi-hop (medalhão). Os dados evoluem por três camadas — Bronze (cru), Silver (limpo e normalizado em star schema) e Gold (features engenheiradas para ML) — alimentando modelos XGBoost com tracking via MLflow e registro no Unity Catalog.


Sumário


Arquitetura

flowchart LR
    A[Bronze<br/>Dados Brutos] --> B[Silver<br/>Star Schema]
    B --> C[Gold<br/>ML-Ready Features]
    C --> D[MLflow<br/>Model Registry]

    style A fill:#CD7F32,color:#fff
    style B fill:#C0C0C0,color:#000
    style C fill:#FFD700,color:#000
    style D fill:#0194E2,color:#fff
Loading

O projeto implementa a arquitetura medalhão (multi-hop) em 4 camadas:

Camada Catálogo Descrição
Bronze <catalog>.1_bronze_schema Dados brutos como ingeridos (CSV/API)
Silver <catalog>.2_silver_schema Dados limpos em star schema (dim_*, fact_*)
Gold <catalog>.3_gold_schema Tabela denormalizada com features para ML (ml_features_*)
Model Registry <catalog>.4_model_schema Modelos registrados no Unity Catalog

Datasets

O projeto trabalha com 3 datasets reais de domínios distintos:

Brazilian E-Commerce by Olist

  • Fonte: Kaggle
  • Descrição: Dados reais de e-commerce brasileiro com 9 tabelas relacionais (pedidos, pagamentos, reviews, clientes, vendedores, produtos, geolocalização)
  • Problema de ML: Predição de satisfação do cliente (review_score 1–5 → binário: 0–3 insatisfeito, 4–5 satisfeito)
  • Modelos: olist_review_score_predictor_silver (baseline) / olist_review_score_predictor_gold (challenger)

Modelo Relacional (Bronze)

erDiagram
    olist_customers_dataset ||--o{ olist_orders_dataset : "faz"
    olist_orders_dataset ||--|{ olist_order_items_dataset : "contém"
    olist_orders_dataset ||--|{ olist_order_payments_dataset : "pago_via"
    olist_orders_dataset ||--o{ olist_order_reviews_dataset : "recebe"
    olist_products_dataset ||--o{ olist_order_items_dataset : "pertence_a"
    olist_sellers_dataset ||--o{ olist_order_items_dataset : "vende"
Loading

Star Schema (Silver)

erDiagram
    dim_customers ||--o{ fact_orders : "realiza"
    fact_orders ||--|{ fact_order_items : "possui"
    fact_orders ||--|{ fact_order_payments : "pago_via"
    fact_orders ||--o{ fact_order_reviews : "avaliado_em"
    dim_products ||--o{ fact_order_items : "inclui"
    dim_sellers ||--o{ fact_order_items : "vendido_por"
Loading

Instacart Market Basket Analysis

  • Fonte: Kaggle
  • Descrição: Dados reais de compras de supermercado com mais de 3 milhões de pedidos
  • Problema de ML: Predição de recompra de produto (reordered: 0 ou 1)
  • Modelos: supermarket_reorder_predictor_silver / supermarket_reorder_predictor_gold

TheLook E-Commerce

  • Fonte: Google BigQuery (dataset público)
  • Descrição: Dataset de e-commerce de moda com eventos de navegação, inventário e centros de distribuição
  • Problema de ML: Predição de retorno de pedido (returned: 0 ou 1)
  • Modelos: thelook_return_predictor_silver / thelook_return_predictor_gold

1. Bronze → Silver

1_bronze_to_silver/src/

Objetivo

Transformar dados brutos em um star schema limpo e normalizado.

Transformações

  • Padronização: Colunas convertidas para snake_case
  • Tipos: Strings convertidas para timestamp, casting de tipos numéricos
  • Nulos: Tratamento com fillna("unknown") e fillna(0)
  • Validação: Remoção de duplicatas (dropDuplicates), datas futuras, valores negativos
  • Texto: Lowercase e remoção de espaços extras

Exemplo de Notebook

# 1_bronze_to_silver/src/olist.py
from pyspark.sql.functions import col, lower, trim

df_bronze = spark.table("olist.1_bronze_schema.olist_customers_dataset")
df_silver = (
    df_bronze
    .select(
        col("customer_id").alias("customer_id"),
        lower(trim(col("customer_city"))).alias("city"),
    )
    .dropDuplicates()
)
df_silver.write.mode("overwrite").saveAsTable("olist.2_silver_schema.dim_customers")

Saída Esperada

Tabelas Silver (Star Schema)
Olist: dim_customers, dim_sellers, dim_products, dim_geolocation, fact_orders, fact_order_items, fact_order_payments, fact_order_reviews
Supermarket: dim_users, dim_products, dim_departments, dim_aisles, fact_orders, fact_order_products
TheLook: dim_distribution_centers, dim_products, dim_users, fact_orders, fact_order_items, fact_inventory_items, fact_events

2. Silver → Gold

2_silver_to_gold/src/

Objetivo

Construir uma tabela denormalizada com features engenheiradas para ML.

Exemplos de Features Criadas

Dataset Exemplos de Features
Olist total_products_value, total_freight_value, n_items, payment_value, n_payment_types, delivery_delay, delay_ratio, delivery_vs_estimated_ratio, haversine_distance, is_same_state, is_credit_card, installment_ratio, product_volume, product_weight, n_photos, seller_n_orders, seller_n_customers
Supermarket user_total_orders, user_total_products, user_avg_cart_size, user_order_hour, user_recency, prod_add_to_cart_order, prod_reordered_rate, prod_n_days_since_prior, user_product_position, user_product_reorder_rate
TheLook event_count_7d, event_count_30d, product_view_7d, add_to_cart_7d, purchase_7d, conversion_rate_7d, category_bounce_rate, same_delivery_center, brand_category_interaction

Algoritmos de Feature Engineering

  • Temporais: datediff(estimated, actual), ratios de delay, buckets de hora/dia
  • Geográficas: Distância Haversine via UDF, flag is_same_state
  • Histórico do Usuário: Agregações por cliente (total gasto, frequência, variedade)
  • Interação Usuário-Produto: Recência, posição no carrinho, taxa de recompra

3. Treinamento de Modelos

3_training_model/src/training/

Abordagem: Baseline vs Challenger

Para cada dataset, dois modelos são treinados:

Modelo Fonte Features Complexidade
Baseline (Silver) Star schema Agregações simples em tempo de treino (~5–6 features) Baixa
Challenger (Gold) Tabela ml_features_* Features pré-engenheiradas (~22–35 features) Alta

Pipeline de Treinamento

# 1. Load data
features_df = read_gold_table("ml_features_review_prediction")
X_train, X_val, X_test, y_train, y_val, y_test = split_data(
    features_df, target_col="review_score", test_size=0.2, val_size=0.16
)

# 2. Handle class imbalance
classes_weight = compute_class_weight(
    class_weight="balanced", classes=sorted_y, y=y_train
)
sample_weights = np.array([classes_weight[i] for i in y_train])

# 3. Train XGBoost
model = XGBClassifier(
    objective="binary:logistic",
    n_estimators=1000,
    learning_rate=0.05,
    early_stopping_rounds=20,
    eval_metric="logloss",
    random_state=42,
)
model.fit(X_train, y_train, eval_set=[(X_val, y_val)], sample_weight=sample_weights)

# 4. Evaluate
metrics = {
    "accuracy": accuracy_score(y_test, y_pred),
    "f1_macro": f1_score(y_test, y_pred, average="macro"),
    "precision_macro": precision_score(y_test, y_pred, average="macro"),
    "roc_auc": roc_auc_score(y_test, y_proba),
    "ks_statistic": ks_statistic(y_test, y_proba),
}

# 5. Log to MLflow
with mlflow.start_run(experiment_id=EXPERIMENT_ID) as run:
    mlflow.log_metrics(metrics)
    mlflow.log_figure(fig, "confusion_matrix.png")
    mlflow.log_figure(fig, "roc_curve.png")
    mlflow.log_figure(fig, "shap_summary_bar.png")
    mlflow.xgboost.log_model(
        xgb_model=bst,
        artifact_path="model",
        registered_model_name=f"{MODEL_NAME}",
    )
    set_registered_model_alias(name=MODEL_NAME, alias=ALIAS, version=version)

Algoritmos de Explicabilidade (SHAP)

Gráficos de importância de features são gerados automaticamente e armazenados no MLflow:

Dataset Silver (Baseline) Gold (Challenger)
Olist total_products_value, total_freight_value delivery_delay, delay_ratio
Supermarket user_total_orders, user_recency user_product_reorder_rate, prod_reordered_rate
TheLook event_count_30d, purchase_7d conversion_rate_7d, brand_category_interaction

Veja os gráficos em docs/images/aggregations/.


Resultados

Os resultados variam conforme o dataset e a qualidade das features. Em geral, o modelo Challenger (Gold) supera o Baseline (Silver) devido à riqueza das features engenheiradas. As métricas são armazenadas no MLflow e podem ser consultadas via:

runs = mlflow.search_runs(experiment_ids=[EXPERIMENT_ID])

Exemplo de Métricas

Dataset Modelo Acurácia ROC AUC F1 (macro)
Olist Baseline (Silver) ~0.72 ~0.76 ~0.70
Olist Challenger (Gold) ~0.80 ~0.87 ~0.79
Supermarket Baseline (Silver) ~0.65 ~0.70 ~0.62
Supermarket Challenger (Gold) ~0.74 ~0.82 ~0.72
TheLook Baseline (Silver) ~0.70 ~0.75 ~0.68
TheLook Challenger (Gold) ~0.78 ~0.85 ~0.77

Nota: Valores aproximados. Consulte o MLflow para métricas exatas de cada execução.


Pré-requisitos

  • Databricks Workspace (Unity Catalog habilitado)
  • Databricks CLI configurado (databricks auth login)
  • Python 3.10+
  • Dependencies: xgboost==1.7.6, mlflow==2.16.2, shap==0.48.0 (ver 3_training_model/src/requirements.txt)
  • Databricks Bundle CLI para deploy

Configuração do Ambiente

# Clone o repositório
git clone <repo-url>
cd multihop_for_machine_learning

# Configure o Databricks CLI
databricks auth login --host https://<workspace>.databricks.com

# (Opcional) Linting com Ruff
pip install ruff pre-commit
pre-commit install
ruff check .

Como Executar

1. Bronze → Silver (Escolha um dataset)

# Olist
databricks bundle run -t dev olist_bronze_to_silver_job

# Supermarket
databricks bundle run -t dev supermarket_bronze_to_silver_job

# TheLook
databricks bundle run -t dev thelook_bronze_to_silver_job

Manual: Execute o notebook correspondente no Databricks:

  • 1_bronze_to_silver/src/olist.py
  • 1_bronze_to_silver/src/supermarket.py
  • 1_bronze_to_silver/src/thelook.py

2. Silver → Gold

databricks bundle run -t dev olist_silver_to_gold_job

Manual: Execute o notebook:

  • 2_silver_to_gold/src/olist.py (ou supermarket.py, thelook.py)

3. Treinar Modelos

# Treina baseline (Silver) e challenger (Gold) sequencialmente
databricks bundle run -t dev olist_model_training_job

Manual: Execute os notebooks na ordem:

  1. 3_training_model/src/training/olist_silver.py (baseline)
  2. 3_training_model/src/training/olist_gold.py (challenger)

Deploy com Databricks Bundles

Cada camada possui seu próprio bundle para deploy independente:

# Deploy para dev
databricks bundle deploy -t dev

# Deploy para produção
databricks bundle deploy -t prod

# Executar um job
databricks bundle run -t dev <job_name>

Variáveis de Configuração

Variável Descrição Default (dev) Default (prod)
sample_fraction Fração dos dados para treino 0.25 0.25
n_estimators Nº máximo de árvores (XGBoost) 1000 1000
early_stopping_rounds Parada precoce 20 20
learning_rate Taxa de aprendizado 0.05 0.05
shap_sample_fraction Fração para análise SHAP 1.0 1.0

Estrutura do Repositório

multihop_for_machine_learning/
│
├── 1_bronze_to_silver/          # Pipeline Bronze → Silver
│   ├── src/                     # Notebooks PySpark
│   ├── resources/               # Definições de jobs Databricks
│   ├── databricks.yml           # Bundle config
│   ├── targets.yml              # Targets dev/prod
│   ├── OLIST.md                 # Documentação detalhada Olist
│   ├── SUPERMARKET.md           # Documentação detalhada Supermarket
│   └── THELOOK.md               # Documentação detalhada TheLook
│
├── 2_silver_to_gold/            # Pipeline Silver → Gold
│   ├── src/                     # Notebooks PySpark
│   ├── resources/               # Definições de jobs
│   ├── databricks.yml           # Bundle config
│   └── ...documentação...
│
├── 3_training_model/            # Treinamento de Modelos
│   ├── src/
│   │   ├── training/            # Notebooks de treino
│   │   │   ├── olist_silver.py  # Baseline (Silver)
│   │   │   ├── olist_gold.py    # Challenger (Gold)
│   │   │   ├── supermarket_silver.py
│   │   │   ├── supermarket_gold.py
│   │   │   ├── thelook_silver.py
│   │   │   └── thelook_gold.py
│   │   ├── utils/
│   │   │   ├── general_helpers.py  # Split de dados
│   │   │   ├── ml_helpers.py       # Pipeline ML + MLflow
│   │   │   └── visuals.py          # Visualizações SHAP
│   │   └── requirements.txt
│   ├── resources/               # Definições de jobs
│   ├── databricks.yml
│   └── ...documentação...
│
├── docs/                        # Documentação e diagramas
│   ├── images/
│   │   ├── diagram/             # Diagrama geral da arquitetura
│   │   ├── er/                  # Diagramas ER (Bronze/Silver)
│   │   └── aggregations/        # SHAP summary bar charts
│   ├── mermaid/                 # Diagramas Mermaid (ER)
│   └── excalidraw/              # Diagramas editáveis
│
├── AGENTS.md                    # Guidelines para assistentes de IA
└── README.md                    # Este arquivo

Licença

Este projeto está licenciado sob a MIT License — veja o arquivo LICENSE para detalhes.


Contato

Para dúvidas, sugestões ou feedback, abra uma issue no repositório.

About

Multi-hop data architecture (Bronze → Silver → Gold) on Databricks with PySpark, XGBoost, and MLflow for ML model training across 3 real-world datasets.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages