Machine Learning: Exercício Integrador
Última atualização: 2026-08-26
Você passou cinco lições aprendendo as ferramentas. Agora é hora de juntá-las e completar um projeto real de ML.
1. O que você vai aprender
- Fluxo de trabalho de ponta a ponta: Carregamento de Dados → EDA → Seleção de Features → Modelo de Linha de Base → Avaliação
- EDA na prática: análise de distribuição, correlação e tendência nos dados de e-commerce do SalesPredict
- Construção do modelo de linha de base: LinearRegression como referência para previsão de vendas
- Interpretação dos resultados: cálculo de R², MAE e RMSE — e o que significam para o negócio
- Retrospectiva do projeto: principais aprendizados da fase para iniciantes e direções de crescimento
2. A história real de um novato em ML
(1) O problema: conhecer todas as ferramentas, mas não saber como conectá-las
O Bob terminou de aprender NumPy, Pandas, Seaborn e Sklearn. Mas quando se deparou com o conjunto de dados real do SalesPredict, ele não fazia ideia de por onde começar — limpar primeiro ou visualizar primeiro? Qual modelo usar? Como saber se um modelo é bom? Ele conhecia as ferramentas, mas o fluxo de trabalho de um projeto era um mistério.
(2) A solução: um fluxo de trabalho de ponta a ponta
Todo projeto de ML segue um pipeline fixo: Carregar → Explorar → Limpar → Features → Linha de Base → Avaliar → Iterar. Coloque a linha de base em execução primeiro e otimize passo a passo.
# Template de fluxo de ML de ponta a ponta
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_absolute_error, r2_score
# Passo 1: Carregar e Explorar
df = pd.read_csv("sales_data.csv")
print(df.describe())
# Passo 2: Limpar e Preparar features
features = ["ad_spend", "traffic", "last_month_sales"]
target = "monthly_revenue"
df = df.dropna(subset=features + [target])
# Passo 3: Treinar linha de base
X_train, X_test, y_train, y_test = train_test_split(df[features], df[target], test_size=0.2)
model = LinearRegression().fit(X_train, y_train)
pred = model.predict(X_test)
# Passo 4: Avaliar
print(f"R²: {r2_score(y_test, pred):.3f}, MAE: {mean_absolute_error(y_test, pred):.0f} USD")
(3) O resultado: primeiro modelo de ponta a ponta em 30 minutos
Seguindo o fluxo padrão, o Bob conseguiu seu primeiro modelo de linha de base em apenas 30 minutos. O R² foi de apenas 0,72, mas com uma linha de base em vigor, cada melhoria subsequente tem um ponto de referência.
3. O fluxo de ML de ponta a ponta
(1) Pipeline completo do projeto
graph LR
A[1. Carregar Dados] --> B[2. EDA]
B --> C[3. Limpar Dados]
C --> D[4. Seleção de Features]
D --> E[5. Treinar Linha de Base]
E --> F[6. Avaliar]
F --> G{Bom o Suficiente?}
G -->|Não| H[7. Iterar<br/>Melhores Features / Modelo]
H --> D
G -->|Sim| I[8. Implantar]
▶ Exemplo: Gerando dados simulados do SalesPredict
import pandas as pd
import numpy as np
rng = np.random.default_rng(42)
n = 1000
df = pd.DataFrame({
"date": pd.date_range("2023-01-01", periods=n, freq="D"),
"ad_spend_k": rng.uniform(5, 100, n),
"traffic_k": rng.uniform(10, 500, n),
"category": rng.choice(["Electronics", "Clothing", "Food", "Books", "Home"], n),
"is_promotion": rng.choice([0, 1], n, p=[0.8, 0.2]),
"customer_count": rng.integers(50, 500, n),
})
# Fórmula de receita com relações realistas
df["revenue_k"] = (
20
+ 0.6 * df["ad_spend_k"]
+ 0.08 * df["traffic_k"]
+ 0.5 * df["customer_count"]
+ 50 * df["is_promotion"]
+ rng.normal(0, 15, n)
)
df["revenue_k"] = df["revenue_k"].clip(lower=0)
# Injetar alguns valores ausentes e outliers
missing_idx = rng.choice(n, size=50, replace=False)
df.loc[missing_idx[:25], "ad_spend_k"] = np.nan
df.loc[missing_idx[25:], "traffic_k"] = np.nan
outlier_idx = rng.choice(n, size=10, replace=False)
df.loc[outlier_idx, "revenue_k"] *= 5
print(f"Shape do conjunto de dados: {df.shape}")
print(f"Valores ausentes:\n{df.isnull().sum()}")
df.to_csv("salespredict_data.csv", index=False)
Saída:
# Executado com sucesso
4. Análise Exploratória de Dados (EDA)
(1) Visão geral dos dados
▶ Exemplo: EDA abrangente
import pandas as pd
import numpy as np
df = pd.read_csv("salespredict_data.csv", parse_dates=["date"])
# Visão geral básica
print("=" * 50)
print("VISÃO GERAL DOS DADOS")
print("=" * 50)
print(f"Shape: {df.shape}")
print(f"\nTipos:\n{df.dtypes}")
print(f"\nEstatísticas básicas:\n{df.describe().round(2)}")
print(f"\nValores ausentes:\n{df.isnull().sum()}")
print(f"\nDistribuição por categoria:\n{df['category'].value_counts()}")
Saída:
=
VISÃO GERAL DOS DADOS
=
(2) Análise de distribuição e tendência
▶ Exemplo: EDA visual
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
df = pd.read_csv("salespredict_data.csv", parse_dates=["date"])
sns.set_theme(style="whitegrid")
fig, axes = plt.subplots(2, 3, figsize=(18, 10))
# (1) Distribuição da receita
sns.histplot(df["revenue_k"], bins=40, kde=True, ax=axes[0, 0], color="#2196F3")
axes[0, 0].set_title("Distribuição da Receita (mil USD)")
# (2) Receita por categoria
sns.boxplot(data=df, x="category", y="revenue_k", ax=axes[0, 1], palette="Set2")
axes[0, 1].set_title("Receita por Categoria")
# (3) Efeito da promoção
sns.barplot(data=df, x="is_promotion", y="revenue_k", ax=axes[0, 2], palette="Pastel1")
axes[0, 2].set_title("Efeito da Promoção na Receita")
# (4) Dispersão gasto com anúncios vs receita
axes[1, 0].scatter(df["ad_spend_k"], df["revenue_k"], alpha=0.3, s=10)
axes[1, 0].set_xlabel("Gasto com Anúncios (mil USD)")
axes[1, 0].set_ylabel("Receita (mil USD)")
axes[1, 0].set_title("Gasto com Anúncios vs Receita")
# (5) Dispersão tráfego vs receita
axes[1, 1].scatter(df["traffic_k"], df["revenue_k"], alpha=0.3, s=10, color="#4CAF50")
axes[1, 1].set_xlabel("Tráfego (mil)")
axes[1, 1].set_ylabel("Receita (mil USD)")
axes[1, 1].set_title("Tráfego vs Receita")
# (6) Mapa de calor de correlação
num_cols = ["ad_spend_k", "traffic_k", "customer_count", "is_promotion", "revenue_k"]
corr = df[num_cols].corr()
sns.heatmap(corr, annot=True, fmt=".2f", cmap="RdBu_r", ax=axes[1, 2], vmin=-1, vmax=1)
axes[1, 2].set_title("Correlações entre Features")
plt.tight_layout()
plt.savefig("eda_overview.png", dpi=150)
Saída:
# Executado com sucesso
(3) Template de descobertas-chave da EDA
| Descoberta # | Descoberta | Impacto de negócio | Próxima ação |
|---|---|---|---|
| 1 | Dias de promoção têm receita média 50k USD maior | Promoções são altamente eficazes | Manter feature is_promotion |
| 2 | Correlação entre ad_spend e receita = 0,72 | Anúncios impulsionam vendas | Feature central |
| 3 | 10 outliers extremos de alto valor | Provavelmente erros de digitação | Precisa de limpeza |
| 4 | 5% dos dados têm valores ausentes | Afeta o treinamento do modelo | Imputar ou remover |
5. Limpeza de dados e preparação de features
▶ Exemplo: Pipeline de limpeza de dados
import pandas as pd
import numpy as np
df = pd.read_csv("salespredict_data.csv", parse_dates=["date"])
# Passo 1: Tratar valores ausentes
print(f"Antes da limpeza: {len(df)} linhas")
df = df.dropna(subset=["revenue_k"]) # Remover se o alvo está ausente
df["ad_spend_k"] = df["ad_spend_k"].fillna(df["ad_spend_k"].median())
df["traffic_k"] = df["traffic_k"].fillna(df["traffic_k"].median())
# Passo 2: Remover outliers usando IQR
def remove_outliers(df, col, factor=1.5):
q1, q3 = df[col].quantile([0.25, 0.75])
iqr = q3 - q1
return df[(df[col] >= q1 - factor * iqr) & (df[col] <= q3 + factor * iqr)]
df = remove_outliers(df, "revenue_k")
print(f"Após limpeza: {len(df)} linhas")
# Passo 3: Engenharia de features
df["month"] = df["date"].dt.month
df["day_of_week"] = df["date"].dt.dayofweek
df["is_weekend"] = (df["day_of_week"] >= 5).astype(int)
# Passo 4: Codificar categóricas
df_encoded = pd.get_dummies(df, columns=["category"], drop_first=True)
# Passo 5: Selecionar features
feature_cols = [c for c in df_encoded.columns if c not in ["date", "revenue_k"]]
X = df_encoded[feature_cols]
y = df_encoded["revenue_k"]
print(f"Features: {feature_cols}")
print(f"Shape de X: {X.shape}, shape de y: {y.shape}")
Saída:
# Funções definidas com sucesso
6. Construção e avaliação do modelo de linha de base
▶ Exemplo: Treinando uma linha de base com LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
import numpy as np
# Dividir dados
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# Pipeline de linha de base
baseline = Pipeline([
("scaler", StandardScaler()),
("model", LinearRegression()),
])
baseline.fit(X_train, y_train)
y_pred = baseline.predict(X_test)
# Avaliação abrangente
mae = mean_absolute_error(y_test, y_pred)
rmse = np.sqrt(mean_squared_error(y_test, y_pred))
r2 = r2_score(y_test, y_pred)
mape = np.mean(np.abs((y_test - y_pred) / y_test)) * 100
print("=" * 50)
print("AVALIAÇÃO DO MODELO DE LINHA DE BASE")
print("=" * 50)
print(f"MAE: {mae:.2f} mil USD")
print(f"RMSE: {rmse:.2f} mil USD")
print(f"R²: {r2:.4f}")
print(f"MAPE: {mape:.1f}%")
Saída:
=
AVALIAÇÃO DO MODELO DE LINHA DE BASE
=
▶ Exemplo: Visualizando os resultados da previsão
import matplotlib.pyplot as plt
import numpy as np
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# (1) Dispersão real vs previsto
axes[0].scatter(y_test, y_pred, alpha=0.5, s=20)
axes[0].plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], "r--", lw=2)
axes[0].set_xlabel("Receita Real (mil USD)")
axes[0].set_ylabel("Receita Prevista (mil USD)")
axes[0].set_title("Real vs Previsto")
# (2) Distribuição dos resíduos
residuals = y_test - y_pred
axes[1].hist(residuals, bins=30, edgecolor="white", color="#2196F3")
axes[1].axvline(x=0, color="red", linestyle="--")
axes[1].set_xlabel("Resíduo (mil USD)")
axes[1].set_title("Distribuição dos Resíduos")
plt.tight_layout()
plt.savefig("baseline_evaluation.png", dpi=150)
Saída:
# Executado com sucesso
(1) Interpretando métricas de negócio
| Métrica | Valor da linha de base | Significado de negócio | Meta |
|---|---|---|---|
| MAE | ~12k USD | Previsão erra em média $12k | < 10k |
| RMSE | ~15k USD | Erros grandes são mais penalizados | < 12k |
| R² | ~0,72 | Explica 72% da variância | > 0,85 |
| MAPE | ~15% | Erro percentual médio | < 10% |
❓ Perguntas Frequentes
P: Por que usar LinearRegression para a linha de base em vez de um modelo mais complexo? R: O objetivo de uma linha de base é estabelecer um piso de desempenho e entender a previsibilidade inerente dos dados. Modelos complexos podem sobreajustar e mascarar o verdadeiro sinal. Comece simples para definir um benchmark e depois melhore incrementalmente.
P: Devo olhar para MAE ou RMSE? R: Os dois. MAE é robusto a outliers e reflete o desempenho médio; RMSE penaliza erros grandes com mais peso e reflete cenários de pior caso. Se RMSE for muito maior que MAE, significa que há alguns erros individuais grandes.
P: Um MAPE de 15% é bom ou ruim? R: Depende do domínio. Para previsão de vendas em e-commerce, MAPE < 10% é nível de produção; 15% é nível de linha de base. Mas para séries temporais financeiras, MAPE < 2% é considerado bom. O essencial é vinculá-lo ao custo de negócio.
P: Quanto tempo devo gastar em EDA? R: Para um novo conjunto de dados, a EDA deve consumir 20–30% do tempo total do projeto. Não pule a EDA e vá direto para a modelagem — você pode perder features críticas ou problemas de dados.
P: Devo imputar valores ausentes com a mediana ou com a média? R: Use a mediana quando houver outliers (é robusta); use a média quando a distribuição for aproximadamente normal. Você também pode usar métodos mais avançados, como KNNImputer ou IterativeImputer.
P: Devo remover outliers? R: Distinga entre "anomalias verdadeiras" e "anomalias falsas". Erros de digitação → remova. Eventos extremos reais (como a Black Friday) → mantenha e sinalize com uma feature especial. Remover outliers cegamente perde informação.
📖 Resumo
- Pipeline padrão de projeto de ML: Carregar → EDA → Limpar → Features → Linha de Base → Avaliar → Iterar
- Objetivos centrais da EDA: entender distribuições, identificar anomalias, encontrar features fortemente correlacionadas e validar hipóteses de negócio
- Quatro passos de limpeza de dados: tratar valores ausentes → tratar outliers → converter tipos → codificar features
- Use LinearRegression + Pipeline para a linha de base, a fim de estabelecer um benchmark de desempenho
- Vincule métricas de avaliação ao significado de negócio: um MAPE de 15% significa um erro de $150k em uma previsão de um milhão de dólares
- Aprendizados da fase para iniciantes: cadeia de ferramentas conectada, fluxo de projeto estabelecido, linha de base construída — agora é iterar e melhorar
📝 Exercícios
- Básico (Dificuldade ⭐): Use seus próprios dados (ou um conjunto de dados embutido do sklearn, como o de habitação) para completar o pipeline completo: Carregar → EDA → Modelo de Linha de Base → Avaliação. Dica: consulte os templates de código das Seções 4–6.
- Intermediário (Dificuldade ⭐⭐): Sobre o modelo de linha de base, adicione 2 novas features (por exemplo, um termo de interação
ad_spend * is_promotion) e compare se R² e MAPE melhoram. Dica: usedf.assign()para adicionar novas colunas. - Desafio (Dificuldade ⭐⭐⭐): Implemente um framework simples de comparação de modelos — treine LinearRegression, DecisionTreeRegressor e RandomForestRegressor simultaneamente, e produza uma tabela de comparação de MAE/RMSE/R²/MAPE para cada modelo. Dica: itere sobre os modelos usando um dicionário e colete os resultados em um DataFrame.
← Anterior: Primeiros Passos com Scikit-learn | Próximo: Regressão Linear →