随机梯度下降的早停法

Machine LearningMachine LearningBeginner
立即练习

This tutorial is from open-source community. Access the source code

💡 本教程由 AI 辅助翻译自英文原版。如需查看原文,您可以 切换至英文原版

简介

随机梯度下降(Stochastic Gradient Descent)是一种常用的优化技术,用于最小化损失函数。该技术以随机方式逐步执行梯度下降,即在每次迭代中随机选择样本。这种方法效率很高,尤其适用于拟合线性模型。然而,每次迭代并不能保证收敛,损失函数也不一定在每次迭代时都会减小。在这种情况下,监测损失函数的收敛情况可能会很困难。在本实验中,我们将探索早停策略,这是一种用于监测验证分数收敛情况的方法。我们将使用 scikit-learn 库中的 SGDClassifier 模型和 MNIST 数据集来说明如何使用早停来达到与未使用早停构建的模型几乎相同的准确率,并显著减少训练时间。

虚拟机使用提示

虚拟机启动完成后,点击左上角切换到 笔记本 标签页,以访问 Jupyter Notebook 进行练习。

有时,你可能需要等待几秒钟让 Jupyter Notebook 完成加载。由于 Jupyter Notebook 的限制,操作的验证无法自动化。

如果你在学习过程中遇到问题,随时向 Labby 提问。课程结束后提供反馈,我们会及时为你解决问题。


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL sklearn(("Sklearn")) -.-> sklearn/UtilitiesandDatasetsGroup(["Utilities and Datasets"]) ml(("Machine Learning")) -.-> ml/FrameworkandSoftwareGroup(["Framework and Software"]) sklearn(("Sklearn")) -.-> sklearn/ModelSelectionandEvaluationGroup(["Model Selection and Evaluation"]) sklearn/ModelSelectionandEvaluationGroup -.-> sklearn/model_selection("Model Selection") sklearn/UtilitiesandDatasetsGroup -.-> sklearn/utils("Utilities") sklearn/UtilitiesandDatasetsGroup -.-> sklearn/datasets("Datasets") sklearn/UtilitiesandDatasetsGroup -.-> sklearn/exceptions("Exceptions and Warnings") ml/FrameworkandSoftwareGroup -.-> ml/sklearn("scikit-learn") subgraph Lab Skills sklearn/model_selection -.-> lab-49287{{"随机梯度下降的早停法"}} sklearn/utils -.-> lab-49287{{"随机梯度下降的早停法"}} sklearn/datasets -.-> lab-49287{{"随机梯度下降的早停法"}} sklearn/exceptions -.-> lab-49287{{"随机梯度下降的早停法"}} ml/sklearn -.-> lab-49287{{"随机梯度下降的早停法"}} end

加载必要的库和 MNIST 数据集

第一步是加载必要的库和数据集。我们将使用 pandasnumpymatplotlibscikit-learn 库。我们还将使用 scikit-learn 中的 fetch_openml 函数来加载 MNIST 数据集。

import time
import sys
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn import linear_model
from sklearn.datasets import fetch_openml
from sklearn.model_selection import train_test_split
from sklearn.utils._testing import ignore_warnings
from sklearn.exceptions import ConvergenceWarning
from sklearn.utils import shuffle

## 加载 MNIST 数据集
def load_mnist(n_samples=None, class_0="0", class_1="8"):
    """加载 MNIST,选择两个类别,打乱顺序并仅返回 n_samples 个样本。"""
    ## 从 http://openml.org/d/554 加载数据
    mnist = fetch_openml("mnist_784", version=1, as_frame=False, parser="pandas")

    ## 仅选取两个类别进行二分类
    mask = np.logical_or(mnist.target == class_0, mnist.target == class_1)

    X, y = shuffle(mnist.data[mask], mnist.target[mask], random_state=42)
    if n_samples is not None:
        X, y = X[:n_samples], y[:n_samples]
    return X, y

X, y = load_mnist(n_samples=10000)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.5, random_state=0)

定义估计器和早停策略

下一步是定义估计器和早停策略。我们将使用 scikit-learn 中的 SGDClassifier 模型。我们将定义三种不同的停止标准:无停止标准、训练损失和验证分数。我们将使用 fit_and_score 函数在训练集上拟合估计器,并在两个数据集上对其进行评分。

@ignore_warnings(category=ConvergenceWarning)
def fit_and_score(estimator, max_iter, X_train, X_test, y_train, y_test):
    """在训练集上拟合估计器,并在两个数据集上对其进行评分"""
    estimator.set_params(max_iter=max_iter)
    estimator.set_params(random_state=0)

    start = time.time()
    estimator.fit(X_train, y_train)

    fit_time = time.time() - start
    n_iter = estimator.n_iter_
    train_score = estimator.score(X_train, y_train)
    test_score = estimator.score(X_test, y_test)

    return fit_time, n_iter, train_score, test_score

## 定义要比较的估计器
estimator_dict = {
    "无停止标准": linear_model.SGDClassifier(n_iter_no_change=3),
    "训练损失": linear_model.SGDClassifier(
        early_stopping=False, n_iter_no_change=3, tol=0.1
    ),
    "验证分数": linear_model.SGDClassifier(
        early_stopping=True, n_iter_no_change=3, tol=0.0001, validation_fraction=0.2
    ),
}

训练和评估估计器

下一步是使用每种停止标准来训练和评估估计器。我们将使用一个循环来遍历每个估计器和停止标准,并且使用另一个循环来遍历不同的最大迭代次数。然后,我们会将结果存储在一个 pandas 数据框中以便于绘图。

results = []
for estimator_name, estimator in estimator_dict.items():
    print(estimator_name + ": ", end="")
    for max_iter in range(1, 50):
        print(".", end="")
        sys.stdout.flush()

        fit_time, n_iter, train_score, test_score = fit_and_score(
            estimator, max_iter, X_train, X_test, y_train, y_test
        )

        results.append(
            (estimator_name, max_iter, fit_time, n_iter, train_score, test_score)
        )
    print("")

## 将结果转换为 pandas 数据框以便于绘图
columns = [
    "停止标准",
    "max_iter",
    "拟合时间(秒)",
    "n_iter_",
    "训练分数",
    "测试分数",
]
results_df = pd.DataFrame(results, columns=columns)

绘制结果

最后一步是绘制结果。我们将使用两个子图来绘制训练分数和测试分数,以及迭代次数和拟合时间。对于每个估计器和停止标准,我们将使用不同的线条样式。

## 定义要绘制的内容
lines = "停止标准"
x 轴 = "max_iter"
样式 = ["-.", "--", "-"]

## 第一个图:训练分数和测试分数
fig, axes = plt.subplots(nrows=1, ncols=2, sharey=True, figsize=(12, 4))
for ax, y_axis in zip(axes, ["训练分数", "测试分数"]):
    for style, (标准, group_df) in zip(样式, results_df.groupby(lines)):
        group_df.plot(x=x 轴, y=y_axis, label=标准, ax=ax, style=style)
    ax.set_title(y_axis)
    ax.legend(title=lines)
fig.tight_layout()

## 第二个图:n_iter 和拟合时间
fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(12, 4))
for ax, y_axis in zip(axes, ["n_iter_", "拟合时间(秒)"]):
    for style, (标准, group_df) in zip(样式, results_df.groupby(lines)):
        group_df.plot(x=x 轴, y=y_axis, label=标准, ax=ax, style=style)
    ax.set_title(y_axis)
    ax.legend(title=lines)
fig.tight_layout()

plt.show()

需注意,这里代码中的中文部分是为了符合翻译要求对变量名进行的翻译,实际代码中变量名应为英文。

总结

在本实验中,我们探讨了在使用随机梯度下降(Stochastic Gradient Descent)来最小化损失函数时,用于根据验证分数监测收敛情况的早停策略。我们使用了 scikit-learn 中的 SGDClassifier 模型和 MNIST 数据集来说明与未使用早停构建的模型相比,早停如何能够实现几乎相同的准确率,并显著减少训练时间。我们定义了三种不同的停止标准:无停止标准、训练损失和验证分数,并使用一个循环来使用每种停止标准训练和评估估计器。然后,我们针对每个估计器和停止标准使用不同的线条样式绘制了结果。