W3docs

Machine Learning com Regressão Logística em Python

Aprenda como a regressão logística funciona, como treinar classificadores binários e multiclasse em Python com scikit-learn e ajustar a regularização.

A regressão logística é um algoritmo de classificação supervisionada que estima a probabilidade de uma amostra pertencer a uma determinada classe. Apesar da palavra "regressão" no nome, ela é usada para tarefas de classificação — prever um rótulo categórico como spam/não-spam, doença/saudável ou clique/sem-clique.

Este capítulo aborda:

  • Como a regressão logística funciona (a função sigmoide e o log-odds)
  • Construção de um classificador binário em Python com scikit-learn
  • Avaliação de um classificador além da acurácia bruta
  • Tratamento de problemas multiclasse
  • Regularização e quando ajustá-la
  • Escalonamento de features e por que isso importa
  • Quando usar regressão logística em vez de outros classificadores

Como a Regressão Logística Funciona

Da Regressão Linear a Probabilidades

A regressão linear prevê um valor contínuo. Se você tentasse usá-la para classificação, as previsões poderiam cair fora do intervalo [0, 1], tornando-as impossíveis de interpretar como probabilidades. A regressão logística resolve isso passando a combinação linear pela função sigmoide:

σ(z) = 1 / (1 + e^(-z))

Onde z = w₀ + w₁x₁ + w₂x₂ + … + wₙxₙ é a soma ponderada das features de entrada. A sigmoide comprime qualquer número real para o intervalo (0, 1), fornecendo uma estimativa de probabilidade válida.

Fronteira de Decisão

O modelo prevê a classe 1 quando a probabilidade excede um limiar (padrão 0,5) e a classe 0 caso contrário:

ŷ = 1  if σ(z) ≥ 0.5
ŷ = 0  if σ(z) < 0.5

O limiar σ(z) = 0.5 corresponde a z = 0, o que define a fronteira de decisão — um hiperplano no espaço de features que separa as duas classes.

Log-Odds (Logit)

Calcular o logaritmo da razão de chances mostra por que o modelo é linear nos parâmetros:

log(p / (1 - p)) = w₀ + w₁x₁ + … + wₙxₙ

Cada coeficiente wᵢ representa a mudança no log-odds para um aumento de uma unidade na feature xᵢ, mantendo as demais features constantes. Isso torna a regressão logística interpretável.

Como os Parâmetros São Aprendidos

Ao contrário da regressão linear, não existe solução de forma fechada. O modelo minimiza a função de custo log-loss (entropia cruzada) usando um otimizador iterativo (por padrão lbfgs no scikit-learn):

Loss = -1/m Σ [ yᵢ log(p̂ᵢ) + (1 - yᵢ) log(1 - p̂ᵢ) ]

Um log-loss menor significa que as probabilidades previstas estão melhor calibradas em relação aos rótulos verdadeiros.

Classificação Binária em Python

O exemplo a seguir usa o conjunto de dados Breast Cancer Wisconsin — 569 amostras, 30 features numéricas, alvo binário (maligno = 1, benigno = 0). Ele é distribuído com o scikit-learn, portanto nenhum arquivo externo é necessário.

from sklearn.datasets import load_breast_cancer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import accuracy_score, classification_report

# 1. Load data
data = load_breast_cancer()
X, y = data.data, data.target           # X: (569, 30)  y: 0=malignant, 1=benign

# 2. Split into train (80 %) and test (20 %)
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)

# 3. Scale features — logistic regression converges faster and more reliably
#    when features are on a similar scale
scaler = StandardScaler()
X_train_sc = scaler.fit_transform(X_train)
X_test_sc  = scaler.transform(X_test)

# 4. Train
clf = LogisticRegression(max_iter=1000, random_state=42)
clf.fit(X_train_sc, y_train)

# 5. Evaluate
y_pred = clf.predict(X_test_sc)
print(f"Accuracy: {accuracy_score(y_test, y_pred):.3f}")
print(classification_report(y_test, y_pred, target_names=data.target_names))

Saída esperada:

Accuracy: 0.974
              precision    recall  f1-score   support

   malignant       0.98      0.95      0.96        43
      benign       0.97      0.99      0.98        71

    accuracy                           0.97       114
   macro avg       0.97      0.97      0.97       114
weighted avg       0.97      0.97      0.97       114

O modelo atinge ~97% de acurácia nos dados de teste. Note que o LogisticRegression do scikit-learn adiciona regularização L2 por padrão (C=1.0), o que auxilia a generalização.

Por Que o Escalonamento de Features Importa

A regressão logística usa otimização baseada em gradiente. Sem escalonamento, uma feature com valores grandes (por exemplo, raio médio ~14) domina as atualizações do gradiente, retardando a convergência ou fazendo o solver falhar. O StandardScaler transforma cada feature para média zero e variância unitária.

from sklearn.linear_model import LogisticRegression
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler

data = load_breast_cancer()
X, y = data.data, data.target
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)

# Without scaling — needs more iterations, may warn about convergence
clf_raw = LogisticRegression(max_iter=200, random_state=42)
clf_raw.fit(X_train, y_train)
print(f"Unscaled accuracy : {clf_raw.score(X_test, y_test):.3f}")

# With scaling
scaler = StandardScaler()
clf_sc = LogisticRegression(max_iter=200, random_state=42)
clf_sc.fit(scaler.fit_transform(X_train), y_train)
print(f"Scaled accuracy   : {clf_sc.score(scaler.transform(X_test), y_test):.3f}")

Sempre ajuste o scaler somente no conjunto de treino e use o mesmo scaler ajustado para transformar tanto o treino quanto o teste — isso evita vazamento de dados.

Avaliando um Classificador

A acurácia isolada pode ser enganosa quando as classes estão desbalanceadas. Use uma matriz de confusão e as métricas derivadas dela.

from sklearn.datasets import load_breast_cancer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay
import matplotlib.pyplot as plt

data = load_breast_cancer()
X, y = data.data, data.target
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)
scaler = StandardScaler()
X_train_sc = scaler.fit_transform(X_train)
X_test_sc  = scaler.transform(X_test)

clf = LogisticRegression(max_iter=1000, random_state=42)
clf.fit(X_train_sc, y_train)
y_pred = clf.predict(X_test_sc)

cm = confusion_matrix(y_test, y_pred)
disp = ConfusionMatrixDisplay(cm, display_labels=data.target_names)
disp.plot(cmap="Blues")
plt.title("Logistic Regression — Breast Cancer")
plt.tight_layout()
plt.savefig("confusion_matrix.png", dpi=150)
plt.show()

Principais métricas derivadas da matriz de confusão:

MétricaFórmulaSignificado
PrecisãoTP / (TP + FP)De todos os positivos previstos, quantos são realmente positivos
Recall (Sensibilidade)TP / (TP + FN)De todos os positivos reais, quantos o modelo identificou
F1-score2 × (P × R) / (P + R)Média harmônica de precisão e recall
EspecificidadeTN / (TN + FP)De todos os negativos reais, quantos o modelo rejeitou corretamente

No diagnóstico médico, o recall (sensibilidade) costuma ser mais importante que a precisão — uma malignidade não detectada é pior do que um falso alarme.

Pontuações de Probabilidade e a Curva AUC-ROC

Em vez de uma previsão definitiva, você pode obter a probabilidade da classe positiva com predict_proba():

from sklearn.datasets import load_breast_cancer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import roc_auc_score

data = load_breast_cancer()
X, y = data.data, data.target
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)
scaler = StandardScaler()
clf = LogisticRegression(max_iter=1000, random_state=42)
clf.fit(scaler.fit_transform(X_train), y_train)

# Probability of the positive class (benign = 1)
y_proba = clf.predict_proba(scaler.transform(X_test))[:, 1]
print(f"AUC-ROC: {roc_auc_score(y_test, y_proba):.3f}")

Saída esperada:

AUC-ROC: 0.997

Um AUC próximo de 1,0 significa que o modelo classifica as amostras positivas acima das negativas quase perfeitamente. Consulte o capítulo Curva AUC-ROC para saber como plotar e interpretar a curva completa.

Classificação Multiclasse

Quando o alvo possui mais de duas classes, o scikit-learn estende a regressão logística automaticamente. A partir do scikit-learn 1.5, o solver lbfgs sempre utiliza a abordagem multinomial (softmax), que treina um único modelo com uma camada de saída softmax e minimiza a entropia cruzada sobre todas as classes em conjunto. Isso geralmente é mais preciso do que a antiga estratégia One-vs-Rest (OvR), que treinava um classificador binário separado por classe.

O conjunto de dados Iris possui três espécies de flores — um exemplo natural de multiclasse:

from sklearn.datasets import load_iris
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import accuracy_score

data = load_iris()
X, y = data.data, data.target

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)
scaler = StandardScaler()
X_train_sc = scaler.fit_transform(X_train)
X_test_sc  = scaler.transform(X_test)

# From scikit-learn 1.5+, multinomial softmax is the default for lbfgs
clf = LogisticRegression(solver="lbfgs", max_iter=1000, random_state=42)
clf.fit(X_train_sc, y_train)

y_pred = clf.predict(X_test_sc)
print(f"Accuracy: {accuracy_score(y_test, y_pred):.3f}")

# Class probabilities for the first three test samples
print("\nClass probabilities (first 3 samples):")
for proba in clf.predict_proba(X_test_sc)[:3]:
    print([f"{p:.3f}" for p in proba])

Saída esperada:

Accuracy: 1.000

Class probabilities (first 3 samples):
['0.011', '0.876', '0.113']
['0.964', '0.036', '0.000']
['0.000', '0.003', '0.997']

Regularização

A regularização penaliza coeficientes grandes para evitar overfitting. O scikit-learn oferece dois tipos por meio do parâmetro penalty:

ParâmetroTipoEfeito
penalty='l2' (padrão)RidgeEncolhe todos os coeficientes em direção a zero; mantém todas as features
penalty='l1'LassoLeva alguns coeficientes exatamente a zero; seleção implícita de features
penalty='elasticnet'MistoCombina L1 e L2; requer solver='saga'
penalty=NoneNenhumSem regularização; use somente se os dados forem grandes e limpos

A intensidade da regularização é controlada por C (o inverso da intensidade de regularização — um C menor significa regularização mais forte):

from sklearn.datasets import load_breast_cancer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
import numpy as np

data = load_breast_cancer()
X, y = data.data, data.target

results = {}
for C in [0.001, 0.01, 0.1, 1.0, 10.0, 100.0]:
    pipe = Pipeline([
        ("scaler", StandardScaler()),
        ("clf", LogisticRegression(C=C, max_iter=1000, random_state=42)),
    ])
    scores = cross_val_score(pipe, X, y, cv=5, scoring="accuracy")
    results[C] = scores.mean()
    print(f"C={C:7.3f}  CV accuracy: {scores.mean():.4f} ± {scores.std():.4f}")

Isso usa validação cruzada para encontrar o valor de C que melhor generaliza. Para uma busca sistemática sobre múltiplos hiperparâmetros, consulte Grid Search.

Usando um Pipeline

Um Pipeline encadeia o pré-processamento e o modelo em um único objeto. Isso evita vazamento acidental de dados e simplifica a validação cruzada e a implantação:

from sklearn.datasets import load_breast_cancer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.metrics import accuracy_score

data = load_breast_cancer()
X, y = data.data, data.target
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)

pipe = Pipeline([
    ("scaler", StandardScaler()),
    ("clf", LogisticRegression(C=1.0, max_iter=1000, random_state=42)),
])

pipe.fit(X_train, y_train)
y_pred = pipe.predict(X_test)
print(f"Accuracy: {accuracy_score(y_test, y_pred):.3f}")

# Predict probabilities on a new sample (raw, unscaled)
new_sample = X_test[:1]  # first test sample
print(f"Predicted class   : {pipe.predict(new_sample)[0]}")
print(f"Class probability : {pipe.predict_proba(new_sample)[0]}")

Saída esperada:

Accuracy: 0.974
Predicted class   : 1
Class probability : [0.11359025 0.88640975]

O pipeline lida com o escalonamento internamente — você chama predict() com os valores brutos das features.

Inspecionando os Coeficientes

Os coeficientes treinados revelam quais features empurram a previsão em direção a cada classe. Valores absolutos maiores significam influência mais forte:

from sklearn.datasets import load_breast_cancer
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler
import numpy as np

data = load_breast_cancer()
X, y = data.data, data.target

scaler = StandardScaler()
clf = LogisticRegression(max_iter=1000, random_state=42)
clf.fit(scaler.fit_transform(X), y)

# Sort by absolute coefficient value
coefs = clf.coef_[0]          # shape (n_features,) for binary classification
sorted_idx = np.argsort(np.abs(coefs))[::-1]

print(f"{'Feature':<35} {'Coefficient':>12}")
print("-" * 48)
for i in sorted_idx[:5]:
    print(f"{data.feature_names[i]:<35} {coefs[i]:>12.4f}")

Saída esperada (top 5 features por peso absoluto):

Feature                              Coefficient
------------------------------------------------
worst texture                            -1.3206
radius error                             -1.2893
worst radius                             -1.0266
area error                               -0.9989
worst area                               -0.9947

Coeficientes negativos (após escalonamento) empurram em direção à classe 0 (maligno); coeficientes positivos empurram em direção à classe 1 (benigno).

Vantagens e Limitações

Quando usar regressão logística

  • Você precisa de estimativas de probabilidade, não apenas de rótulos de classe.
  • A relação entre as features e o log-odds é aproximadamente linear.
  • Você precisa de um modelo interpretável — os coeficientes são significativos.
  • Como uma linha de base rápida antes de experimentar modelos mais complexos como Árvores de Decisão ou métodos de ensemble.
  • Os conjuntos de dados são grandes (a regressão logística escala bem com muitas amostras).

Limitações

LimitaçãoMitigação
Assume uma fronteira de decisão linearUse features polinomiais ou mude para Árvore de Decisão / K-Nearest Neighbors
Sensível à escala das featuresSempre aplique StandardScaler ou MinMaxScaler
Dificuldades com features altamente correlacionadasElimine ou regularize com L1 (penalty='l1')
Não indicada para interações de features muito complexasUse métodos de ensemble ou redes neurais

Regressão Logística vs. Classificadores Relacionados

AlgoritmoFronteira de decisãoEscalonamento necessárioSaída probabilística
Regressão LogísticaLinearSimSim (calibrada)
Árvore de DecisãoNão linear (alinhada aos eixos)NãoSim (menos calibrada)
K-Nearest NeighborsNão linear (baseada em instâncias)SimSim
Regressão LinearLinear (saída contínua)SimNão

Principais Conclusões

  • A regressão logística estima uma probabilidade usando a função sigmoide; a classe é atribuída pelo limiar dessa probabilidade.
  • Sempre escalone as features com StandardScaler antes do treino — isso acelera a convergência e melhora a acurácia.
  • Use um Pipeline para agrupar o escalonamento e o modelo; ele previne vazamento de dados e simplifica a implantação.
  • Avalie com precisão, recall, F1 e AUC-ROC em vez de apenas acurácia, especialmente com dados desbalanceados. Consulte os capítulos Matriz de Confusão e Curva AUC-ROC.
  • Controle o overfitting com o parâmetro C (menor = regularização mais forte); use validação cruzada ou grid search para ajustá-lo.
  • Para problemas multiclasse, use solver="lbfgs" (o padrão); o scikit-learn 1.5+ sempre usa softmax (multinomial), que lida bem com classes sobrepostas.
Was this page helpful?