简介
本实验的目的是展示如何使用 scikit-learn 中的 LearningCurveDisplay 类来绘制学习曲线。学习曲线展示了在训练过程中增加更多样本的效果。我们将使用手写数字数据集(digits dataset)分析朴素贝叶斯分类器和具有径向基函数(RBF)核的支持向量机(SVM)分类器的学习曲线。此外,我们将通过查看这些预测模型的计算成本,而不仅仅是它们的统计准确性,来研究它们的可扩展性。
虚拟机使用提示
虚拟机启动完成后,点击左上角切换到 笔记本 标签页,以访问 Jupyter Notebook 进行练习。
有时,你可能需要等待几秒钟让 Jupyter Notebook 完成加载。由于 Jupyter Notebook 的限制,操作验证无法自动化。
如果你在学习过程中遇到问题,随时向 Labby 提问。课程结束后提供反馈,我们将立即为你解决问题。
加载数据集
from sklearn.datasets import load_digits
X, y = load_digits(return_X_y=True)
定义模型
from sklearn.naive_bayes import GaussianNB
from sklearn.svm import SVC
naive_bayes = GaussianNB()
svc = SVC(kernel="rbf", gamma=0.001)
绘制学习曲线
import matplotlib.pyplot as plt
import numpy as np
from sklearn.model_selection import LearningCurveDisplay, ShuffleSplit
fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(10, 6), sharey=True)
common_params = {
"X": X,
"y": y,
"train_sizes": np.linspace(0.1, 1.0, 5),
"cv": ShuffleSplit(n_splits=50, test_size=0.2, random_state=0),
"score_type": "both",
"n_jobs": 4,
"line_kw": {"marker": "o"},
"std_display_style": "fill_between",
"score_name": "Accuracy",
}
for ax_idx, estimator in enumerate([naive_bayes, svc]):
LearningCurveDisplay.from_estimator(estimator, **common_params, ax=ax[ax_idx])
handles, label = ax[ax_idx].get_legend_handles_labels()
ax[ax_idx].legend(handles[:2], ["Training Score", "Test Score"])
ax[ax_idx].set_title(f"Learning Curve for {estimator.__class__.__name__}")
分析学习曲线
## 解读学习曲线
我们可以分析朴素贝叶斯分类器的学习曲线。在更复杂的数据集中,其形状很常见:使用少量样本进行训练时,训练分数非常高,随着样本数量的增加而降低,而测试分数一开始非常低,然后在添加样本时增加。当所有样本都用于训练时,训练分数和测试分数会变得更符合实际情况。
我们看到了具有径向基函数(RBF)核的支持向量机(SVM)分类器的另一种典型学习曲线。无论训练集的大小如何,训练分数都保持较高。另一方面,测试分数随着训练数据集的大小而增加。实际上,它会一直增加到达到一个平稳期。观察到这样的平稳期表明,获取新数据来训练模型可能没有用,因为模型的泛化性能不会再提高了。
检查模型的可扩展性
from sklearn.model_selection import learning_curve
common_params = {
"X": X,
"y": y,
"train_sizes": np.linspace(0.1, 1.0, 5),
"cv": ShuffleSplit(n_splits=50, test_size=0.2, random_state=0),
"n_jobs": 4,
"return_times": True,
}
train_sizes, _, test_scores_nb, fit_times_nb, score_times_nb = learning_curve(
naive_bayes, **common_params
)
train_sizes, _, test_scores_svm, fit_times_svm, score_times_svm = learning_curve(
svc, **common_params
)
绘制模型的可扩展性
fig, ax = plt.subplots(nrows=2, ncols=2, figsize=(16, 12), sharex=True)
for ax_idx, (fit_times, score_times, estimator) in enumerate(
zip(
[fit_times_nb, fit_times_svm],
[score_times_nb, score_times_svm],
[naive_bayes, svc],
)
):
## 关于拟合时间的可扩展性
ax[0, ax_idx].plot(train_sizes, fit_times.mean(axis=1), "o-")
ax[0, ax_idx].fill_between(
train_sizes,
fit_times.mean(axis=1) - fit_times.std(axis=1),
fit_times.mean(axis=1) + fit_times.std(axis=1),
alpha=0.3,
)
ax[0, ax_idx].set_ylabel("拟合时间(秒)")
ax[0, ax_idx].set_title(
f"{estimator.__class__.__name__}分类器的可扩展性"
)
## 关于评分时间的可扩展性
ax[1, ax_idx].plot(train_sizes, score_times.mean(axis=1), "o-")
ax[1, ax_idx].fill_between(
train_sizes,
score_times.mean(axis=1) - score_times.std(axis=1),
score_times.mean(axis=1) + score_times.std(axis=1),
alpha=0.3,
)
ax[1, ax_idx].set_ylabel("评分时间(秒)")
ax[1, ax_idx].set_xlabel("训练样本数量")
检查增加训练时间与交叉验证分数之间的权衡
fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(16, 6))
for ax_idx, (fit_times, test_scores, estimator) in enumerate(
zip(
[fit_times_nb, fit_times_svm],
[test_scores_nb, test_scores_svm],
[naive_bayes, svc],
)
):
ax[ax_idx].plot(fit_times.mean(axis=1), test_scores.mean(axis=1), "o-")
ax[ax_idx].fill_between(
fit_times.mean(axis=1),
test_scores.mean(axis=1) - test_scores.std(axis=1),
test_scores.mean(axis=1) + test_scores.std(axis=1),
alpha=0.3,
)
ax[ax_idx].set_ylabel("准确率")
ax[ax_idx].set_xlabel("拟合时间(秒)")
ax[ax_idx].set_title(
f"{estimator.__class__.__name__}分类器的性能"
)
plt.show()
总结
在本实验中,我们展示了如何使用 scikit-learn 中的LearningCurveDisplay类来绘制学习曲线。我们使用手写数字数据集(digits dataset)分析了朴素贝叶斯分类器和具有径向基函数(RBF)核的支持向量机(SVM)分类器的学习曲线。此外,我们通过观察这些预测模型的计算成本,而不仅仅是它们的统计准确性,来研究它们的可扩展性。我们发现支持向量机和朴素贝叶斯分类器的可扩展性非常不同。支持向量机分类器在拟合和评分时的复杂度随着样本数量的增加而迅速增加。相比之下,朴素贝叶斯分类器在拟合和评分时的复杂度较低,扩展性更好。我们还检查了增加训练时间与交叉验证分数之间的权衡。