在机器学习领域,学习曲线是一种可视化工具,用于展示随着训练样本数量的增加,模型训练得分和测试得分的变化情况。本文将通过学习曲线,对朴素贝叶斯分类器和支持向量机(SVM)分类器进行性能分析,并进一步探讨这些预测模型的可扩展性,即它们的计算成本,而不仅仅是统计准确性。
学习曲线的绘制涉及到模型在不同训练样本数量下的得分计算。这里,使用手写数字数据集(digits dataset)来计算朴素贝叶斯分类器和带有RBF核的SVM分类器的学习曲线。通过观察训练得分和测试得分,可以了解模型随着样本数量增加的性能变化。
from sklearn.datasets import load_digits
from sklearn.naive_bayes import GaussianNB
from sklearn.svm import SVC
X, y = load_digits(return_X_y=True)
naive_bayes = GaussianNB()
svc = SVC(kernel="rbf", gamma=0.001)
为了估计得分的不确定性,使用交叉验证方法。下面的代码展示了如何使用matplotlib和numpy库来绘制学习曲线。
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分类器,其学习曲线表现出另一种典型情况。训练得分无论训练集的大小如何,都保持在高水平。另一方面,测试得分随着训练数据集的大小而增加。实际上,它增加到某一点,达到一个平台期。观察到这样的平台期表明,获取新数据来训练模型可能没有用了,因为模型的泛化性能不会再增加。
除了学习曲线,还可以查看预测模型在训练和评分时间方面的可扩展性。LearningCurveDisplay类不提供此类信息,需要使用learning_curve函数并手动制作图表。
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)
通过绘制训练时间和评分时间的图表,可以看到SVM和朴素贝叶斯分类器的可扩展性非常不同。SVM分类器在训练和评分时间上的复杂性随着样本数量的增加而迅速增加。实际上,已知这种分类器的拟合时间复杂度与样本数量的平方以上,这使得它很难扩展到拥有超过几万个样本的数据集。相比之下,朴素贝叶斯分类器的复杂性较低,拟合和评分时间的扩展性更好。
接下来,可以检查增加训练时间和交叉验证得分之间的权衡。在这些图表中,可以寻找交叉验证得分不再增加的拐点,而训练时间仅增加。
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("Accuracy")
ax[ax_idx].set_xlabel("Fit time (s)")
ax[ax_idx].set_title(f"Performance of the {estimator.__class__.__name__} classifier")
plt.show()