W3docs

Машинное обучение с логистической регрессией в Python

Узнайте, как работает логистическая регрессия, как обучать бинарные и многоклассовые классификаторы в Python с scikit-learn и настраивать регуляризацию.

Логистическая регрессия — это алгоритм классификации с учителем, оценивающий вероятность принадлежности образца к определённому классу. Несмотря на слово «регрессия» в названии, он применяется для задач классификации — предсказания категориального метки, например: спам/не-спам, болезнь/здоров, клик/нет клика.

В этой главе рассматривается:

  • Как работает логистическая регрессия (сигмоидная функция и логарифм шансов)
  • Построение бинарного классификатора в Python с помощью scikit-learn
  • Оценка классификатора помимо простой точности
  • Обработка многоклассовых задач
  • Регуляризация и когда её корректировать
  • Масштабирование признаков и почему это важно
  • Когда использовать логистическую регрессию, а не другие классификаторы

Как работает логистическая регрессия

От линейной регрессии к вероятностям

Линейная регрессия предсказывает непрерывное значение. При использовании её для классификации предсказания могут выходить за пределы [0, 1], что делает их невозможными для интерпретации как вероятности. Логистическая регрессия решает эту проблему, пропуская линейную комбинацию через сигмоидную функцию:

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

Где z = w₀ + w₁x₁ + w₂x₂ + … + wₙxₙ — взвешенная сумма входных признаков. Сигмоид сжимает любое вещественное число в диапазон (0, 1), давая корректную оценку вероятности.

Граница принятия решений

Модель предсказывает класс 1, когда вероятность превышает порог (по умолчанию 0.5), и класс 0 в противном случае:

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

Порог σ(z) = 0.5 соответствует z = 0, что определяет границу принятия решений — гиперплоскость в пространстве признаков, разделяющую два класса.

Логарифм шансов (логит)

Взяв логарифм отношения шансов, можно показать, почему модель линейна по параметрам:

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

Каждый коэффициент wᵢ отражает изменение логарифма шансов при увеличении признака xᵢ на одну единицу при фиксированных остальных признаках. Это делает логистическую регрессию интерпретируемой.

Как обучаются параметры

В отличие от линейной регрессии, здесь нет аналитического решения. Модель минимизирует функцию потерь log-loss (перекрёстная энтропия) с помощью итерационного оптимизатора (по умолчанию lbfgs в scikit-learn):

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

Меньший log-loss означает, что предсказанные вероятности лучше соответствуют истинным меткам.

Бинарная классификация в Python

Следующий пример использует набор данных Breast Cancer Wisconsin — 569 образцов, 30 числовых признаков, бинарная цель (злокачественная = 1, доброкачественная = 0). Он поставляется с scikit-learn, поэтому внешние файлы не нужны.

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))

Ожидаемый вывод:

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

Модель достигает ~97% точности на отложенных данных. Обратите внимание, что LogisticRegression в scikit-learn по умолчанию добавляет L2-регуляризацию (C=1.0), что помогает обобщению.

Почему масштабирование признаков важно

Логистическая регрессия использует оптимизацию на основе градиента. Без масштабирования признак с большими значениями (например, средний радиус ~14) доминирует в обновлениях градиента, замедляя сходимость или вызывая сбой решателя. StandardScaler преобразует каждый признак к нулевому среднему и единичной дисперсии.

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}")

Всегда обучайте скейлер только на обучающей выборке, а затем используйте тот же обученный скейлер для преобразования обучающей и тестовой выборок — это предотвращает утечку данных.

Оценка классификатора

Только точность может вводить в заблуждение при несбалансированных классах. Используйте матрицу ошибок и метрики, производные от неё.

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()

Ключевые метрики из матрицы ошибок:

МетрикаФормулаЗначение
Точность (Precision)TP / (TP + FP)Из всех предсказанных положительных сколько действительно положительных
Полнота (Recall, Sensitivity)TP / (TP + FN)Из всех фактических положительных сколько поймала модель
F1-мера (F1-score)2 × (P × R) / (P + R)Гармоническое среднее точности и полноты
Специфичность (Specificity)TN / (TN + FP)Из всех фактических отрицательных сколько модель правильно отклонила

В медицинской диагностике полнота (чувствительность) зачастую важнее точности — пропущенное злокачественное образование хуже ложной тревоги.

Вероятностные оценки и кривая AUC-ROC

Вместо жёсткого предсказания можно получить вероятность положительного класса с помощью 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}")

Ожидаемый вывод:

AUC-ROC: 0.997

AUC, близкий к 1.0, означает, что модель почти идеально ранжирует положительные образцы выше отрицательных. Смотрите главу Кривая AUC-ROC для построения и интерпретации полной кривой.

Многоклассовая классификация

Когда целевая переменная имеет более двух классов, scikit-learn автоматически расширяет логистическую регрессию. Начиная с scikit-learn 1.5, решатель lbfgs всегда использует подход многочленной (softmax) регрессии, обучая единую модель с выходным слоем softmax и минимизируя перекрёстную энтропию по всем классам совместно. Это, как правило, точнее, чем старая стратегия One-vs-Rest (OvR), которая обучала отдельный бинарный классификатор для каждого класса.

Набор данных Iris содержит три вида цветов — естественный пример многоклассовой задачи:

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])

Ожидаемый вывод:

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']

Регуляризация

Регуляризация штрафует большие коэффициенты для предотвращения переобучения. scikit-learn предоставляет два типа через параметр penalty:

ПараметрТипЭффект
penalty='l2' (по умолчанию)RidgeСжимает все коэффициенты к нулю; сохраняет все признаки
penalty='l1'LassoОбнуляет некоторые коэффициенты; неявный отбор признаков
penalty='elasticnet'MixСочетает L1 и L2; требует solver='saga'
penalty=NoneNoneБез регуляризации; используйте только при больших и чистых данных

Сила регуляризации управляется параметром C (обратная величина силы регуляризации — меньшее C означает более сильную регуляризацию):

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}")

Здесь используется кросс-валидация для нахождения значения C, обеспечивающего наилучшее обобщение. Для систематического поиска по нескольким гиперпараметрам смотрите Grid Search.

Использование Pipeline

Pipeline объединяет предобработку и модель в единый объект. Это предотвращает случайную утечку данных и упрощает кросс-валидацию и развёртывание:

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]}")

Ожидаемый вывод:

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

Pipeline выполняет масштабирование внутри — вы вызываете predict() с исходными значениями признаков.

Анализ коэффициентов

Обученные коэффициенты показывают, какие признаки толкают предсказание к каждому классу. Большие абсолютные значения означают более сильное влияние:

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}")

Ожидаемый вывод (топ-5 признаков по абсолютному весу):

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

Отрицательные коэффициенты (после масштабирования) толкают к классу 0 (злокачественный); положительные — к классу 1 (доброкачественный).

Преимущества и ограничения

Когда использовать логистическую регрессию

  • Вам нужны оценки вероятности, а не только метки классов.
  • Связь между признаками и логарифмом шансов примерно линейна.
  • Вам нужна интерпретируемая модель — коэффициенты имеют смысл.
  • Как быстрый базовый вариант перед переходом к более сложным моделям, таким как Деревья решений или ансамблевые методы.
  • Наборы данных большие (логистическая регрессия хорошо масштабируется при большом количестве образцов).

Ограничения

ОграничениеСпособ устранения
Предполагает линейную границу принятия решенийИспользуйте полиномиальные признаки или перейдите к Decision Tree / K-Nearest Neighbors
Чувствительна к масштабу признаковВсегда применяйте StandardScaler или MinMaxScaler
Плохо работает с сильно коррелированными признакамиУдалите признаки или используйте L1-регуляризацию (penalty='l1')
Не подходит для сложных взаимодействий признаковИспользуйте ансамблевые методы или нейронные сети

Логистическая регрессия vs. связанные классификаторы

АлгоритмГраница решенийНужно масштабированиеВероятностный вывод
Логистическая регрессияЛинейнаяДаДа (калиброванный)
Decision TreeНелинейная (по осям)НетДа (менее калиброванный)
K-Nearest NeighborsНелинейная (на основе экземпляров)ДаДа
Linear RegressionЛинейная (непрерывный вывод)ДаНет

Ключевые выводы

  • Логистическая регрессия оценивает вероятность с помощью сигмоидной функции; класс присваивается путём сравнения этой вероятности с порогом.
  • Всегда масштабируйте признаки с помощью StandardScaler перед обучением — это ускоряет сходимость и улучшает точность.
  • Используйте Pipeline для объединения масштабирования и модели; это предотвращает утечку данных и упрощает развёртывание.
  • Оценивайте с помощью точности, полноты, F1 и AUC-ROC, а не только по точности, особенно при несбалансированных данных. Смотрите главы Матрица ошибок и Кривая AUC-ROC.
  • Управляйте переобучением с помощью параметра C (меньше = сильнее регуляризация); используйте кросс-валидацию или grid search для настройки.
  • Для многоклассовых задач используйте solver="lbfgs" (по умолчанию); scikit-learn 1.5+ всегда использует softmax (многочленный), который хорошо справляется с перекрывающимися классами.
Was this page helpful?