Примечание
Перейти в конец, чтобы скачать полный код примера или запустить его в браузере с помощью JupyterLite или Binder.
Анализ сходимости моделей логистической регрессии с регуляризацией
У этого примера три цели:
- Показать, как зарегистрировать
ScoringMonitorна этапе логистической регрессии конвейера, вложенного вGridSearchCV. - Показать, как построить график значений метрик, собранных на каждой итерации каждого обучения модели логистической регрессии во время поиска по сетке, и проанализировать сходимость модели для каждой комбинации гиперпараметров.
- Показать, как мониторинг различных метрик качества помогает оценить качество модели и компромисс между уточнением и калибровкой.
# Authors: The scikit-learn developers # SPDX-License-Identifier: BSD-3-Clause
Подготовка
Сначала определим конвейер и поиск по сетке. Здесь мы регистрируем обратный вызов ScoringMonitor для модели логистической регрессии, чтобы отслеживать оценки на каждой итерации решателя L-BFGS.
Для самого поиска по сетке мы используем те же метрики качества и выбираем наилучшую комбинацию гиперпараметров по D² log-loss.
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from sklearn.callback import ProgressBar, ScoringMonitor
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import GridSearchCV
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
X, y = make_classification(
n_samples=1000, n_features=100, n_classes=10, n_informative=30, random_state=42
)
scoring_metrics = ["d2_log_loss_score", "accuracy", "average_precision"]
scoring_monitor = ScoringMonitor(scoring=scoring_metrics)
model = make_pipeline(
StandardScaler(),
LogisticRegression(solver="lbfgs", max_iter=1000).set_callbacks(scoring_monitor),
)
param_grid = {
"standardscaler__with_std": [True, False],
"logisticregression__C": np.geomspace(0.01, 100, 3),
}
grid_search = GridSearchCV(
model,
param_grid,
cv=5,
scoring=scoring_metrics,
n_jobs=2,
error_score="raise",
refit=scoring_metrics[0],
)
Выполним поиск по сетке с обратным вызовом индикатора выполнения, который автоматически распространяется. Чтобы получить более подробный вывод с индикаторами выполнения для конвейера, StandardScaler и логистической регрессии, задайте max_propagation_depth=3 в конструкторе ProgressBar.
grid_search.set_callbacks(ProgressBar()).fit(X, y)
GridSearchCV - fit ━━━ 100% 0:0…
GridSearchCV - search #0 ━━━ 100% 0:0…
GridSearchCV - candidate-split-evaluation | Pipeline - fit #0 ━━━ 100% 0:0…
GridSearchCV - candidate-split-evaluation | Pipeline - fit #1 ━━━ 100% 0:0…
GridSearchCV - candidate-split-evaluation | Pipeline - fit #2 ━━━ 100% 0:0…
GridSearchCV - candidate-split-evaluation | Pipeline - fit #3 ━━━ 100% 0:0…
GridSearchCV - candidate-split-evaluation | Pipeline - fit #4 ━━━ 100% 0:0…
GridSearchCV - candidate-split-evaluation | Pipeline - fit #5 ━━━ 100% 0:0…
GridSearchCV - candidate-split-evaluation | Pipeline - fit #6 ━━━ 100% 0:0…
GridSearchCV - candidate-split-evaluation | Pipeline - fit #7 ━━━ 100% 0:0…
GridSearchCV - candidate-split-evaluation | Pipeline - fit #8 ━━━ 100% 0:0…
GridSearchCV - candidate-split-evaluation | Pipeline - fit #9 ━━━ 100% 0:0…
GridSearchCV - candidate-split-evaluation | Pipeline - fit #10 ━━━ 100% 0:0…
GridSearchCV - candidate-split-evaluation | Pipeline - fit #11 ━━━ 100% 0:0…
GridSearchCV - candidate-split-evaluation | Pipeline - fit #12 ━━━ 100% 0:0…
GridSearchCV - candidate-split-evaluation | Pipeline - fit #13 ━━━ 100% 0:0…
GridSearchCV - candidate-split-evaluation | Pipeline - fit #14 ━━━ 100% 0:0…
GridSearchCV - candidate-split-evaluation | Pipeline - fit #15 ━━━ 100% 0:0…
GridSearchCV - candidate-split-evaluation | Pipeline - fit #16 ━━━ 100% 0:0…
GridSearchCV - candidate-split-evaluation | Pipeline - fit #17 ━━━ 100% 0:0…
GridSearchCV - candidate-split-evaluation | Pipeline - fit #18 ━━━ 100% 0:0…
GridSearchCV - candidate-split-evaluation | Pipeline - fit #19 ━━━ 100% 0:0…
GridSearchCV - candidate-split-evaluation | Pipeline - fit #20 ━━━ 100% 0:0…
GridSearchCV - candidate-split-evaluation | Pipeline - fit #21 ━━━ 100% 0:0…
GridSearchCV - candidate-split-evaluation | Pipeline - fit #22 ━━━ 100% 0:0…
GridSearchCV - candidate-split-evaluation | Pipeline - fit #23 ━━━ 100% 0:0…
GridSearchCV - candidate-split-evaluation | Pipeline - fit #24 ━━━ 100% 0:0…
GridSearchCV - candidate-split-evaluation | Pipeline - fit #25 ━━━ 100% 0:0…
GridSearchCV - candidate-split-evaluation | Pipeline - fit #26 ━━━ 100% 0:0…
GridSearchCV - candidate-split-evaluation | Pipeline - fit #27 ━━━ 100% 0:0…
GridSearchCV - candidate-split-evaluation | Pipeline - fit #28 ━━━ 100% 0:0…
GridSearchCV - candidate-split-evaluation | Pipeline - fit #29 ━━━ 100% 0:0…
GridSearchCV - refit-with-best-params | Pipeline - fit #1 ━━━ 100% 0:0…
Мы используем поиск по сетке с 3 значениями параметра регуляризации C и 2 значениями параметра стандартизации признаков, что даёт 6 комбинаций параметров.
Поскольку мы используем 5-блочную перекрёстную проверку (cv=5), для каждой комбинации параметров будет выполнено 5 обучений модели логистической регрессии, то есть всего 30 обучений в качестве подзадач «поиска» задачи обучения.
Кроме того, поиск по сетке выполняет итоговое повторное обучение на всём наборе данных с наилучшей комбинацией гиперпараметров, найденной в ходе поиска. Выше в выводе это показано как задача «refit-with-best-params».
Сводка результатов поиска по сетке
Рассмотрим результаты поиска по сетке.
cv_results = pd.DataFrame(grid_search.cv_results_) cv_results.sort_values(by="rank_test_d2_log_loss_score", ascending=True)
Мы видим, что лучшие модели используют регуляризацию (малые значения C). Стандартизация признаков, по-видимому, не имеет большого значения, но помогает сократить время обучения. У многих моделей показатели точности схожи, но значения D² log-loss и средней точности различаются. D² log-loss и средняя точность чувствительнее к качеству модели, чем точность, поскольку они оценивают всё распределение вероятностей предсказаний, а не только совпадение наиболее вероятного предсказанного класса с истинным классом.
Теперь уточним этот анализ, рассмотрев те же метрики, вычисленные на обучающей выборке на каждой итерации решателя L-BFGS и для каждой комбинации параметров. Обратите внимание, что это оценки на обучающей выборке, зафиксированные во время итераций L-BFGS, а не оценки на отложенных фолдах CV из cv_results_.
Эти значения хранятся в объекте обратного вызова scoring_monitor:
all_tasks_log = scoring_monitor.get_logs().data_as_pandas all_tasks_log
Добавим к этому журналу параметры-кандидаты и индекс фолда, чтобы построить графики оценок для каждой комбинации параметров на интересующем нас фолде CV.
candidate_params = pd.DataFrame(grid_search.cv_results_["params"]).add_prefix("param_")
n_splits = grid_search.n_splits_
lbfgs_log = all_tasks_log.query(
"estimator_name == 'LogisticRegression' and task_name == 'lbfgs-iter'"
).copy()
# Index 2 in ``task_id_path`` is the ``candidate-split-evaluation`` task id.
# Future versions of scikit-learn will provide a more convenient way to
# retrieve this task id.
lbfgs_log["eval_task_id"] = lbfgs_log["task_id_path"].map(lambda path: path[2])
lbfgs_log["candidate_idx"] = lbfgs_log["eval_task_id"] // n_splits
lbfgs_log["split_idx"] = lbfgs_log["eval_task_id"] % n_splits
lbfgs_log = lbfgs_log.query("split_idx == 0").join(candidate_params, on="candidate_idx")
Исключим финальное переобучение на полном наборе данных (parent_task_id_path начинается с (0, 1) вместо (0, 0) для моделей, обучаемых на фолдах кросс-валидации). При необходимости можно вызвать scoring_monitor.get_logs(include_lineage=True), чтобы получить имя задачи для задач-предков.
cv_lbfgs_log = lbfgs_log[
lbfgs_log["parent_task_id_path"].map(lambda path: path[1]) == 0
]
Зададим подписи для построения графиков и построим график для каждой метрики отдельно.
cv_lbfgs_log["param_label"] = cv_lbfgs_log.apply(
lambda row: (
f"with_std={row['param_standardscaler__with_std']}, "
f"C={row['param_logisticregression__C']:.2g}"
),
axis=1,
)
metrics = {
"d2_log_loss_score": "D² log-loss (train)",
"accuracy": "Accuracy (train)",
"average_precision": "Average precision (train)",
}
_, axes = plt.subplots(
len(metrics),
1,
figsize=(8, 2.5 * len(metrics)),
sharex=True,
constrained_layout=True,
)
for idx, (metric, ylabel) in enumerate(metrics.items()):
ax = axes[idx]
for param_label, group in cv_lbfgs_log.groupby("param_label", sort=False):
ax.plot(group["task_id"], group[metric], label=param_label)
ax.set_ylabel(ylabel)
if idx == 0:
ax.set_title("CV split 0")
ax.legend(title="Hyperparameters", fontsize="small")
_ = axes[-1].set_xlabel("L-BFGS iteration")

Анализ сходимости моделей логистической регрессии
Сходимость D² log-loss
Оценки D² log-loss, как правило, монотонно улучшаются для всех моделей. Это ожидаемо, поскольку модель логистической регрессии обучается минимизацией (регуляризованной) функции log-loss, вычисленной на обучающей выборке.
Колебания точности
Показатель точности растёт с увеличением числа итераций, хотя и с некоторыми локальными колебаниями. Это ожидаемо, поскольку точность — разрывная метрика, которую модель не оптимизирует напрямую. Вместо этого модель минимизирует функцию log-loss — гладкую аппроксимацию функции потерь zero-one (и поэтому связанную с точностью, но не оптимизируемую напрямую).
Регуляризация и масштабирование
Мы также видим, что модели с наименьшей регуляризацией (большие значения C) обычно достигают более высоких оценок D² log-loss, а модели, обученные на масштабированных признаках, сходятся за гораздо меньшее число итераций.
Кроме того, модели, обученные с сильной регуляризацией (меньшие значения C), сходятся к конечному значению D² log-loss, зависящему от силы регуляризации. Для моделей со слабой регуляризацией это не так: оптимальная сила регуляризации тесно связана с масштабированием признаков.
Средняя точность и log-loss: качество ранжирования и калибровка
Наконец, мы видим, что значение средней точности, измеренное на обучающей выборке, может быстро улучшаться на первых итерациях, а затем ухудшаться, несмотря на продолжающееся улучшение значения D² log-loss на тех же обучающих данных. Особенно заметно это для моделей, обученных со слабой регуляризацией и стандартизацией признаков. Это на первый взгляд парадоксальное поведение можно объяснить следующим образом. Прежде всего, напомним, что средняя точность — это метрика ранжирования, которая измеряет способность модели выдавать предсказанные вероятности, позволяющие расположить образцы заданного класса выше образцов других классов, но не учитывает калибровку предсказанных вероятностей. Иными словами, средняя точность оценивает лишь правильность взаимного упорядочивания предсказанных вероятностей и нечувствительна к преобразованию их абсолютных значений, сохраняющему ранжирование. Функция log-loss, напротив, является строго правильной scoring rule, которая учитывает как качество ранжирования модели, так и калибровку предсказанных вероятностей.
Таким образом, кривые средней точности моделей со слабой регуляризацией, обученных на масштабированных признаках, показывают, что первые итерации преимущественно улучшают качество ранжирования моделей, временно оставляя калибровку без внимания. На более поздних итерациях оценка log-loss продолжает улучшаться, но значения средней точности ухудшаются. Это позволяет предположить, что в ходе последних итераций модель логистической регрессии постепенно жертвует качеством ранжирования ради калибровки. Это явление изучалось в работе [1].
Было бы интересно проверить, происходит ли то же самое при оценке модели на валидационной выборке, чтобы реализовать досрочную остановку по средней точности и явно выбирать модель с высоким качеством ранжирования на валидационной выборке. На момент написания это пока невозможно. Планируется предоставить обратным вызовам доступ к валидационной выборке в будущей версии scikit-learn. Обратите внимание, что API обратных вызовов всё ещё является экспериментальным и может измениться без обычного цикла устаревания.
Ссылки
Общее время выполнения скрипта: (0 минут 27.101 секунды)
Связанные примеры
© 2007–2026 The scikit-learn developers
Licensed under the 3-clause BSD License.
https://scikit-learn.org/1.9/auto_examples/callbacks/plot_scoring_monitor.html