Machine Learning: Regressão Linear

Última atualização: 2026-08-26

A regressão linear é o "Hello World" do ML — simples, interpretável e surpreendentemente eficaz. É o ponto de partida para toda tarefa de regressão.

1. O que você vai aprender


2. A história real de um e-commerce

(1) O problema: não saber como dividir o orçamento de anúncios

O Bob tem um orçamento mensal de 200 mil USD para anúncios, que deve ser distribuído entre Google Ads, Facebook e e-mail marketing. Sua intuição diz "gaste mais, venda mais", mas no mês passado, 50 mil USD extras no Google Ads geraram apenas 30 mil USD de receita adicional — há retornos decrescentes, mas ele não faz ideia de onde está o ponto de virada.

(2) A solução com regressão linear

A regressão linear pode quantificar a contribuição de cada canal para a receita e encontrar a alocação ótima de orçamento.

PYTHON
from sklearn.linear_model import LinearRegression

model = LinearRegression()
model.fit(X_train, y_train)

# Cada coeficiente = contribuição marginal por 1k USD gasto em anúncios
for channel, coef in zip(channels, model.coef_):
    print(f"{channel}: +{coef:.2f}k USD de receita por 1k USD gasto")

(3) O resultado: melhoria de 35% no ROI

O Bob descobre que o Google Ads tem coeficiente de 0,8 (cada 1k USD gasto retorna 0,8k USD), enquanto o e-mail tem coeficiente de 2,5 (cada 1k USD gasto retorna 2,5k USD). Depois de realocar o orçamento, o ROI geral melhora 35%.


3. A matemática por trás da regressão linear

(1) Função hipótese e função de perda

A regressão linear assume que a saída é uma combinação linear das entradas: $\hat{y} = w_1x_1 + w_2x_2 + ... + b$

A função de perda usa MSE (Erro Quadrático Médio): $L = \frac{1}{n}\sum_{i=1}^{n}(y_i - \hat{y}_i)^2$

▶ Exemplo: Regressão linear do zero

PYTHON
import numpy as np

# Descida do gradiente para regressão linear
def linear_regression_gd(X, y, lr=0.01, epochs=1000):
    n_samples, n_features = X.shape
    w = np.zeros(n_features)
    b = 0.0

    for epoch in range(epochs):
        # Forward pass
        y_pred = X @ w + b

        # Calcular gradientes
        dw = (2 / n_samples) * (X.T @ (y_pred - y))
        db = (2 / n_samples) * np.sum(y_pred - y)

        # Atualizar parâmetros
        w -= lr * dw
        b -= lr * db

        if epoch % 200 == 0:
            loss = np.mean((y - y_pred) ** 2)
            print(f"Época {epoch}: MSE = {loss:.4f}")

    return w, b

# Testar com dados simples
rng = np.random.default_rng(42)
X = rng.uniform(0, 10, (100, 1))
y = 3 * X.squeeze() + 7 + rng.normal(0, 2, 100)

w, b = linear_regression_gd(X, y, lr=0.01, epochs=1000)
print(f"\nAprendido: w={w[0]:.2f}, b={b:.2f}")
print(f"Verdadeiro: w=3.00, b=7.00")

Saída:

TEXT 📖 Somente leitura
# Função definida com sucesso

(2) Visualização da descida do gradiente

100%
sequenceDiagram
    participant Init as Inicialização
    participant Fwd as Forward Pass
    participant Loss as Calcular Perda
    participant Grad as Calcular Gradiente
    participant Update as Atualizar Parâmetros

    Init->>Fwd: w=0, b=0
    loop Cada Época
        Fwd->>Loss: y_pred = Xw + b
        Loss->>Grad: MSE = mean((y - y_pred)²)
        Grad->>Update: dw, db = gradientes
        Update->>Fwd: w -= lr*dw, b -= lr*db
    end

4. Implementação com Scikit-learn

(1) LinearRegression vs SGDRegressor

▶ Exemplo: Comparando dois solvers

PYTHON
from sklearn.linear_model import LinearRegression, SGDRegressor
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
import numpy as np

rng = np.random.default_rng(42)
n = 500
X = rng.uniform(0, 100, (n, 3))
y = 50 + 0.8 * X[:, 0] + 1.2 * X[:, 1] - 0.5 * X[:, 2] + rng.normal(0, 5, n)

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# Método 1: Equação normal (forma fechada, exata)
lr_normal = LinearRegression()
lr_normal.fit(X_train, y_train)

# Método 2: SGD (iterativo, para grandes conjuntos de dados)
lr_sgd = Pipeline([
    ("scaler", StandardScaler()),
    ("sgd", SGDRegressor(max_iter=1000, learning_rate="constant", eta0=0.01, random_state=42)),
])
lr_sgd.fit(X_train, y_train)

print(f"R² da Equação Normal: {lr_normal.score(X_test, y_test):.4f}")
print(f"R² do SGD: {lr_sgd.score(X_test, y_test):.4f}")
print(f"\nCoeficientes normais: {lr_normal.coef_.round(2)}")
print(f"Coeficientes verdadeiros: [0.80, 1.20, -0.50]")

Saída:

TEXT 📖 Somente leitura
# Executado com sucesso
Dimensão LinearRegression (Equação Normal) SGDRegressor
Solver Solução analítica $(X^TX)^{-1}X^Ty$ Descida do gradiente iterativa
Complexidade O(n³) no número de features O(n) por passo
Tamanho de dados adequado Pequeno a médio (n < 100k) Grande (n > 100k)
Precisão Solução exata Solução aproximada
Requer normalização Não Sim

(2) Regularização: Ridge/Lasso/ElasticNet

▶ Exemplo: Comparação de regularização

PYTHON
from sklearn.linear_model import Ridge, Lasso, ElasticNet
from sklearn.model_selection import cross_val_score
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
import numpy as np

rng = np.random.default_rng(42)
n, p = 100, 20  # p > features relevantes (apenas 5 importam)
X = rng.standard_normal((n, p))
true_coefs = np.zeros(p)
true_coefs[:5] = [3, -2, 1.5, 0.8, -0.5]
y = X @ true_coefs + rng.normal(0, 1, n)

models = {
    "LinearRegression": LinearRegression(),
    "Ridge (alpha=1)": Ridge(alpha=1),
    "Lasso (alpha=0.1)": Lasso(alpha=0.1),
    "ElasticNet (alpha=0.1)": ElasticNet(alpha=0.1, l1_ratio=0.5),
}

for name, model in models.items():
    pipe = Pipeline([("scaler", StandardScaler()), ("model", model)])
    scores = cross_val_score(pipe, X, y, cv=5, scoring="r2")
    pipe.fit(X, y)
    nonzero = np.sum(np.abs(pipe.named_steps["model"].coef_) > 0.01)
    print(f"{name:25s}: R²={scores.mean():.3f}, Coefs não-zero={nonzero}/{p}")

Saída:

TEXT 📖 Somente leitura
# Executado com sucesso
Regularização Termo de penalidade Efeito Caso de uso
Ridge (L2) $\alpha \sum w_i^2$ Encolhe os coeficientes em direção a zero Muitas features relevantes
Lasso (L1) $\alpha \sum |w_i|$ Esparsa, leva alguns coeficientes a zero Seleção de features
ElasticNet L1 + L2 híbrido Esparsa + estável Grupos de features correlacionadas

5. Regressão múltipla e interpretação de features

▶ Exemplo: Análise de ROI de anúncios do Bob

PYTHON
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
import pandas as pd
import numpy as np

rng = np.random.default_rng(42)
n = 200
df = pd.DataFrame({
    "google_ads_k": rng.uniform(20, 80, n),
    "facebook_ads_k": rng.uniform(10, 50, n),
    "email_marketing_k": rng.uniform(5, 30, n),
    "traffic_k": rng.uniform(50, 300, n),
})
df["revenue_k"] = (
    100
    + 0.8 * df["google_ads_k"]
    + 1.2 * df["facebook_ads_k"]
    + 2.5 * df["email_marketing_k"]
    + 0.15 * df["traffic_k"]
    + rng.normal(0, 10, n)
)

X = df.drop(columns=["revenue_k"])
y = df["revenue_k"]

# Treinar com features padronizadas para comparação justa
pipe = Pipeline([("scaler", StandardScaler()), ("model", LinearRegression())])
pipe.fit(X, y)

# Interpretar coeficientes padronizados (ranking de importância)
coefs = pipe.named_steps["model"].coef_
importance = pd.DataFrame({
    "feature": X.columns,
    "std_coef": coefs,
    "abs_importance": np.abs(coefs),
}).sort_values("abs_importance", ascending=False)

print("Importância das Features (coeficientes padronizados):")
print(importance.to_string(index=False))

Saída:

TEXT 📖 Somente leitura
Importância das Features (coeficientes padronizados):

Conclusão-chave: A magnitude dos coeficientes padronizados reflete diretamente a importância das features. O coeficiente de 2,5 do e-mail é muito maior que os 0,8 do Google, o que significa que cada 1k USD investido em e-mail retorna 3x mais do que o mesmo investimento no Google Ads.


6. Diagnóstico de regressão

(1) Análise de resíduos

▶ Exemplo: Diagnóstico de regressão em 4 painéis

PYTHON
import matplotlib.pyplot as plt
import numpy as np
from sklearn.linear_model import LinearRegression
from scipy import stats

rng = np.random.default_rng(42)
X = rng.uniform(0, 100, 200).reshape(-1, 1)
y = 50 + 0.8 * X.squeeze() + rng.normal(0, 5, 200)

model = LinearRegression().fit(X, y)
y_pred = model.predict(X)
residuals = y - y_pred

fig, axes = plt.subplots(2, 2, figsize=(12, 10))

# (1) Resíduos vs Ajustados (verificar homocedasticidade)
axes[0, 0].scatter(y_pred, residuals, alpha=0.5, s=15)
axes[0, 0].axhline(0, color="red", linestyle="--")
axes[0, 0].set_xlabel("Valores Ajustados")
axes[0, 0].set_ylabel("Resíduos")
axes[0, 0].set_title("Resíduos vs Ajustados")

# (2) Gráfico Q-Q (verificar normalidade)
stats.probplot(residuals, plot=axes[0, 1])
axes[0, 1].set_title("Gráfico Q-Q")

# (3) Scale-Location (verificar variância)
axes[1, 0].scatter(y_pred, np.sqrt(np.abs(residuals / np.std(residuals))), alpha=0.5, s=15)
axes[1, 0].set_xlabel("Valores Ajustados")
axes[1, 0].set_ylabel("Scale-Location")
axes[1, 0].set_title("Scale-Location")

# (4) Real vs Previsto
axes[1, 1].scatter(y, y_pred, alpha=0.5, s=15)
axes[1, 1].plot([y.min(), y.max()], [y.min(), y.max()], "r--")
axes[1, 1].set_xlabel("Real")
axes[1, 1].set_ylabel("Previsto")
axes[1, 1].set_title("Real vs Previsto")

plt.tight_layout()
plt.savefig("regression_diagnostics.png", dpi=150)

Saída:

TEXT 📖 Somente leitura
# Executado com sucesso

(2) Multicolinearidade e VIF

▶ Exemplo: Detecção de VIF

PYTHON
from statsmodels.stats.outliers_influence import variance_inflation_factor
import pandas as pd
import numpy as np

# VIF > 10 indica multicolinearidade severa
rng = np.random.default_rng(42)
n = 100
df = pd.DataFrame({
    "ad_spend": rng.uniform(10, 100, n),
    "traffic": rng.normal(0, 1, n) * 50 + 500,
    "clicks": rng.normal(0, 1, n) * 100 + 1000,  # Altamente correlacionado com ad_spend
    "revenue": rng.uniform(50, 500, n),
})

features = ["ad_spend", "traffic", "clicks"]
for i, col in enumerate(features):
    vif = variance_inflation_factor(df[features].values, i)
    print(f"{col:12s}: VIF = {vif:.2f} {'⚠️ ALTO' if vif > 10 else '✅ OK'}")

Saída:

TEXT 📖 Somente leitura
# Executado com sucesso
Diagnóstico Método de verificação Critério normal Correção para violações
Linearidade Gráfico de resíduos vs ajustados Dispersão aleatória Adicionar termos polinomiais
Normalidade Gráfico Q-Q Aproximadamente em linha reta Transformação log
Homocedasticidade Gráfico Scale-Location Faixa horizontal Regressão ponderada
Multicolinearidade VIF VIF < 10 Remover ou mesclar features

❓ Perguntas Frequentes

P: A LinearRegression exige normalização das features? R: Pode treinar sem normalização, mas os coeficientes não serão comparáveis (unidades diferentes). Após a padronização, as magnitudes dos coeficientes refletem diretamente a importância das features. Ridge/Lasso/SGD exigem normalização.

P: Devo escolher Ridge ou Lasso? R: Muitas features que podem contribuir → Ridge. Necessidade de seleção automática de features → Lasso. Grupos de features altamente correlacionadas → ElasticNet. Na prática, teste os três e use validação cruzada para escolher o melhor.

P: Como escolho o parâmetro alpha? R: Use GridSearchCV ou RidgeCV/LassoCV para seleção automática. Um alpha maior significa regularização mais forte, empurrando os coeficientes para mais perto de zero. Normalmente, pesquise em escala logarítmica: [0.001, 0.01, 0.1, 1, 10, 100].

P: Como interpreto um coeficiente de regressão negativo? R: Um coeficiente negativo significa que o alvo diminui conforme a feature aumenta. Por exemplo, se "taxa de devolução" tem coeficiente -5, significa que a receita cai 5k USD para cada 1% de aumento na taxa de devolução.

P: E se o VIF > 10? R: Remova a feature com o VIF mais alto e recalcule. Alternativamente, use PCA para eliminar a colinearidade. Ou use regressão Ridge (a regularização L2 é naturalmente resistente à multicolinearidade).

P: E se os resíduos não forem normalmente distribuídos? R: Tente uma transformação log em y (log(y)). Se os resíduos mostrarem formato de funil (heterocedasticidade), use mínimos quadrados ponderados ou aplique uma transformação Box-Cox em y.


📖 Resumo


📝 Exercícios

  1. Básico (Dificuldade ⭐): Use a LinearRegression do sklearn para prever o conjunto de dados California Housing. Produza R² e todos os coeficientes das features. Dica: fetch_california_housing() + model.coef_.
  2. Intermediário (Dificuldade ⭐⭐): Compare Ridge (alpha=0.1/1/10/100) nos mesmos dados usando R² com validação cruzada e magnitudes dos coeficientes. Observe como a força da regularização afeta os coeficientes. Dica: use Pipeline(StandardScaler+Ridge) + GridSearchCV.
  3. Desafio (Dificuldade ⭐⭐⭐): Implemente um pipeline completo de diagnóstico de regressão — após o treino, plote os 4 painéis de diagnóstico, calcule o VIF, determine se as premissas da regressão linear se sustentam e proponha melhorias caso não se sustentem. Dica: consulte o código de diagnóstico na Seção 6.

← Anterior: Exercício Integrador — Projeto para Iniciantes | Próximo: Regressão Logística →

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%