网站首页 > 技术文章 正文
应用监督机器学习模型的基本方法:
· 选择一类模型
· 选择模型的超参数
· 根据训练数据拟合模型
· 使用模型预测新数据的标签
前两部分——模型的选择和超参数的选择——可能是有效使用这些工具和技术的最重要部分。为了做出明智的选择,需要一种方法来验证模型和超参数是否适合数据。虽然听起来很简单,但要有效地做到这一点,必须避免一些陷阱。
· 关于模型验证
从原理上看模型验证很简单,在选择好模型和超参数后,可以通过将其应用于一些训练数据并将预测与已知值进行比较来估计其有效性。
在讨论如何使用holdout集和交叉验证来进行更健壮的模型评估之前,下面几节首先展示了模型验证的一种简单的方法,以及它失败的原因,
· 错误的模型验证
首先演示一下使用Iris数据进行验证的简单方法,将从加载数据开始:
from sklearn.neighbors import KNeighborsClassifier
model = KNeighborsClassifier(n_neighbors=1)
接着训练模型,用它预测已知数据所属的标签:
model.fit(X, y)
y_model = model.predict(X)
最后,计算出正确标记点的分数:
from sklearn.metrics import accuracy_score
accuracy_score(y, y_model)
1.0
我们看到准确率为1.0,这表明100%的点被我们的模型正确标注!但这真的是衡量预期的准确度吗?真的遇到了一个希望100%正确的模型吗?
正如你可能已经想到,答案是否定的。实际上,这种方法存在一个根本缺陷:它在相同的数据集上训练和评估模型。此外,最近邻模型是一个基于实例的评估器,它简单地存储训练数据,并通过将新数据与这些存储的点进行比较来预测标签:除了在人为的情况下,它每次都能获得100%的准确率!
· 正确的模型验证方法:Holdout 集合
那么能做些什么呢?使用所谓的holdout集合可以更好地了解模型的性能:也就是说,我们从模型的训练中保留一些数据子集,然后使用这个holdout集合检查模型的性能。这种拆分方式可以使用Scikit-Learn中的train_test_split工具来完成:
from sklearn.cross_validation import train_test_split
# split the data with 50% in each set
X1, X2, y1, y2 = train_test_split(X, y, random_state=0,
train_size=0.5)
# fit the model on one set of data
model.fit(X1, y1)
# evaluate the model on the second set of data
y2_model = model.predict(X2)
accuracy_score(y2, y2_model)
0.90666666666666662
我们在这里看到了一个更合理的结果:在这个holdout集上,最近邻分类器的精确度约为90%。holdout集类似于未知数据,因为模型之前没有"看到过"它。
· 通过交叉验证进行模型验证
使用holdout集进行模型验证的一个缺点是,我们在模型训练中丢失了一部分数据。在上面的例子中,有一半的数据集对模型的训练没有帮助!这不是最优的,而且可能会导致问题——尤其是在初始训练数据集很小的情况下。
解决这个问题的一种方法是使用交叉验证;也就是说,要进行一系列的匹配,其中数据的每个子集都用作训练集和验证集。
在这里,我们做了两个验证试验,轮流使用数据的每一半作为一个保持集。使用之前的分割数据,可以这样实现:
y2_model = model.fit(X1, y1).predict(X2)
y1_model = model.fit(X2, y2).predict(X1)
accuracy_score(y1, y1_model), accuracy_score(y2, y2_model)
(0.95999999999999996, 0.90666666666666662)
得出的结果是两个准确率得分,我们可以结合(例如,取平均值)来更好地衡量整体模型的性能。这种特殊形式的交叉验证是双重的交叉验证——也就是说,我们将数据分成两组,并将每组作为验证集使用。
我们可以扩展这个想法,使用更多的试验和数据中的更多折叠——例如,下面是一个五倍交叉验证的可视化描述:
在这里,我们将数据分成五组,然后依次使用每一组来评估模型与其他4/5数据的匹配程度。手工做的话,这将是相当乏味的,所以我们可以使用Scikit-Learn的cross_val_score例程很简洁的实现:
from sklearn.cross_validation import cross_val_score
cross_val_score(model, X, y, cv=5)
array([ 0.96666667, 0.96666667, 0.93333333, 0.93333333, 1.
在数据的不同子集之间重复验证可以让我们更好地了解算法的性能。
Scikit-Learn实现了一些在特定情况下有用的交叉验证方案;这些是通过cross_validation模块中的迭代器实现的。例如,我们可能希望进入一个极端的情况,在这种情况下,我们的折叠次数等于数据点的数量:也就是说,我们在每次试验中训练除了一个点以外的所有点。这种交叉验证的类型称为"leave-one-out "交叉验证,可以使用如下方式:
from sklearn.cross_validation import LeaveOneOut
scores = cross_val_score(model, X, y, cv=LeaveOneOut(len(X)))
scores
array([ 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 0., 1., 0., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 0., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 0., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 0., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 0., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1.])
因为我们有150个样本,所以忽略交叉验证就会得到150个试验的分数,这个分数表示成功(1.0)或失败(0.0)预测。取这些值的平均值可以估计错误率:
scores.mean()
0.95999999999999996
其他交叉验证方案也可以类似地使用。要了解Scikit-Learn中可用的内容,请使用IPython探索sklearn。或者查看Scikit-Learn的在线交叉验证文档。
· 选择最佳模型
现在已经了解了验证和交叉验证的基础知识,接下来将深入讨论模型选择和超参数选择。这些问题是机器学习实践中一些最重要的方面,我发现这些信息在机器学习入门教程中经常被忽略。
最重要的问题是:如果评估器表现不佳,我们应该如何进行?有几个可能的答案:
· 使用更复杂/更灵活的模型
· 使用不那么复杂/不那么灵活的模型
· 收集更多训练样本
· 收集更多数据,为每个示例添加特性
这个问题的答案通常是反直觉的。特别是,有时使用更复杂的模型会导致更糟糕的结果,并且添加更多的训练样本可能不会改善您的结果!确定哪些步骤可以改进模型的能力是区分成功的机器学习实践者和不成功的实践者的因素。
· 偏差-方差的权衡
从根本上说,"最佳模型"的问题是在偏差和方差之间找到平衡点。考虑下面的图,它显示了两个回归适合于相同的数据集:
很明显,这两个模型都不太拟合数据,但它们在不同方面都失败了。左边的模型试图通过数据找到一条直线拟合。因为数据本质上比直线更复杂,直线模型永远无法很好地描述这个数据集。这种模型称为欠拟合数据:也就是说,模型没有足够的灵活性来适当地考虑数据中的所有特征;另一种说法是模型有很高的偏差。
右边的模型试图通过数据拟合一个高阶多项式。这里模型拟合有足够的灵活性,可以完美地解释数据中的优良特性,但即使它非常准确地描述了训练数据,其精确的形式似乎更反映特定噪声数据而不是产生数据的任何过程的内在特性。这种模型叫做对数据的过拟合:也就是说,它具有太多的模型灵活性,以至于模型最终会考虑到随机错误以及底层数据分布;另一种说法是模型的方差很大。
从另一个角度来看,考虑一下如果我们使用这两个模型来预测一些新数据的y值会发生什么。在下面的图中,红色/较亮的点表示训练集中遗漏的数据:
这里的分数是R2分数,或者说是确定系数,用来衡量一个模型相对于目标值的简单平均值的表现有多好。R2=1表示完全匹配,R2=0表示模型并不比简单地取数据的平均值好,负值意味着模型更差。从这两个模型的得分来看,我们可以得出一个更普遍的结论:
· 对于高偏差模型,模型在验证集上的性能与训练集上的性能相似。
· 对于高方差模型,模型在验证集上的性能远远低于训练集上的性能。
如果我们假设我们有一些能力来调整模型的复杂性,那么我们期望训练分数和验证分数的表现如下图所示:
这里显示的图表通常被称为验证曲线,我们可以看到以下基本特征:
训练分数都高于验证分数。通常情况是这样的:模型更适合它所见过的数据,而不是它没有见过的数据。
对于非常低的模型复杂度(高偏差模型),训练数据欠拟合,这意味着模型对于训练数据和之前未见过的数据来说都是一个很差的预测器。
对于非常高的模型复杂度(高方差模型),训练数据是过拟合的,这意味着模型可以很好地预测训练数据,但是对于任何之前未见过的数据都是失败的。
对于某个中间值,验证曲线有一个最大值。这种复杂性水平表明了偏差和方差之间的适当平衡。
调整模型复杂性的方法因模型而异;当我们在后面的部分中深入讨论单个模型时,我们将看到每个模型如何允许这样的调优。
· 在Scikit-Learn中验证曲线
让我们来看一个使用交叉验证来计算一类模型的验证曲线的例子。这里我们将使用多项式回归模型:这是一个广义线性模型,其中多项式的阶数是一个可调参数。例如,1维多项式与数据的直线吻合;对于模型参数a和b:
y=ax+b
3阶多项式拟合数据的三次曲线; 模型参数a、b、c、d:
y=ax3+bx2+cx+d
我们可以把它推广到任意阶的多项式特征。在scikitlearn中,我们可以使用简单的线性回归和多项式预处理器来实现这个功能。我们将使用管道将这些操作串在一起(我们将在特征工程中更全面地讨论多项式特征和管道):
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
from sklearn.pipeline import make_pipeline
def PolynomialRegression(degree=2, **kwargs):
return make_pipeline(PolynomialFeatures(degree),
LinearRegression(**kwargs))
现在,让我们创建一些数据,以拟合我们的模型:
import numpy as np
def make_data(N, err=1.0, rseed=1):
# randomly sample the data
rng = np.random.RandomState(rseed)
X = rng.rand(N, 1) ** 2
y = 10 - 1. / (X.ravel() + 0.1)
if err > 0:
y += err * rng.randn(N)
return X, y
X, y = make_data(40)
我们现在可以将数据可视化,并通过几个阶数的多项式拟合:
%matplotlib inline
import matplotlib.pyplot as plt
import seaborn; seaborn.set() # plot formatting
X_test = np.linspace(-0.1, 1.1, 500)[:, None]
plt.scatter(X.ravel(), y, color='black')
axis = plt.axis()
for degree in [1, 3, 5]:
y_test = PolynomialRegression(degree).fit(X, y).predict(X_test)
plt.plot(X_test.ravel(), y_test, label='degree={0}'.format(degree))
plt.xlim(-0.1, 1.0)
plt.ylim(-2, 12)
plt.legend(loc='best');
在这种情况下,控制模型复杂度的旋钮是多项式的次数,它可以是任何非负整数。一个有用的问题是:在偏差(欠拟合)和方差(过拟合)之间,多项式的哪个阶数提供了合适的平衡?
我们可以通过可视化这个特定数据和模型的验证曲线来取得进展;这可以直接使用Scikit-Learn提供的validation_curve例程。给定一个模型、数据、参数名和要探索的范围,该函数将自动计算范围内的训练分数和验证分数:
from sklearn.learning_curve import validation_curve
degree = np.arange(0, 21)
train_score, val_score = validation_curve(PolynomialRegression(), X, y,
'polynomialfeatures__degree', degree, cv=7)
plt.plot(degree, np.median(train_score, 1), color='blue', label='training score')
plt.plot(degree, np.median(val_score, 1), color='red', label='validation score')
plt.legend(loc='best')
plt.ylim(0, 1)
plt.xlabel('degree')
plt.ylabel('score');
这恰恰显示了我们所期望的定性行为:训练分数处处高于验证分数;训练分数随模型复杂度的增加而单调增加;在模型过度拟合之前,验证分数达到最大值。
从验证曲线可以看出,对于一个三阶多项式,我们找到了偏置和方差之间的最优权衡;我们可以根据原始数据计算并显示如下图所示:
plt.scatter(X.ravel(), y)
lim = plt.axis()
y_test = PolynomialRegression(3).fit(X, y).predict(X_test)
plt.plot(X_test.ravel(), y_test);
plt.axis(lim);
注意,发现这个最优模型实际上并不需要我们计算训练分数,但是检查训练分数和验证分数之间的关系可以让我们对模型的性能有一个有益的了解。
· 学习曲线
模型复杂性的一个重要方面是,最优模型通常取决于训练数据的大小。例如,让我们生成一个新的数据集,5倍于现有点数:
X2, y2 = make_data(200)
plt.scatter(X2.ravel(), y2);
我们将复制前面的代码来绘制这个更大数据集的验证曲线;
degree = np.arange(21)
train_score2, val_score2 = validation_curve(PolynomialRegression(), X2, y2,
'polynomialfeatures__degree', degree, cv=7)
plt.plot(degree, np.median(train_score2, 1), color='blue', label='training score')
plt.plot(degree, np.median(val_score2, 1), color='red', label='validation score')
plt.plot(degree, np.median(train_score, 1), color='blue', alpha=0.3, linestyle='dashed')
plt.plot(degree, np.median(val_score, 1), color='red', alpha=0.3, linestyle='dashed')
plt.legend(loc='lower center')
plt.ylim(0, 1)
plt.xlabel('degree')
plt.ylabel('score');
实线表示新的结果,而较弱的虚线表示以前较小数据集的结果。从验证曲线可以清楚地看出,更大的数据集可以支持更复杂的模型:这里的峰值可能在6度左右,但即使是20度的模型也没有严重过度拟合数据——验证和训练分数仍然非常接近。
因此,我们看到验证曲线的行为有两个重要的输入:模型复杂性和训练点的数量。探索模型的行为作为训练点数量的函数是很有用的,我们可以使用越来越大的数据子集来适应我们的模型。与训练集大小相关的训练/验证分数图称为学习曲线。
我们从学习曲线中所期望的一般行为是:
· 一个给定复杂度的模型将会过拟合一个小数据集:这意味着训练分数将相对较高,而验证分数将相对较低。
· 给定复杂性的模型将欠拟合大型数据集:这意味着训练分数将会降低,但验证分数将会增加。
· 除了偶然的机会,一个模型永远不会给验证集比训练集更好的分数:这意味着曲线应该不断靠近,但不会交叉。
考虑到这些特性,我们期望一个学习曲线在质量上与下图所示类似:
学习曲线的显著特征是,随着训练样本数量的增加,它会收敛到一个特定的分数。特别是,一旦你有足够的点,一个特定的模型已经收敛,增加更多的训练数据将不会对你有帮助!在这种情况下,提高模型性能的唯一方法是使用另一个(通常更复杂)模型。
· scikitlearn中的学习曲线
Scikit-Learn提供了一个方便的实用工具,可以从你的模型中计算这种学习曲线;在这里,我们将用二阶多项式模型和九阶多项式计算原始数据集的学习曲线:
from sklearn.learning_curve import learning_curve
fig, ax = plt.subplots(1, 2, figsize=(16, 6))
fig.subplots_adjust(left=0.0625, right=0.95, wspace=0.1)
for i, degree in enumerate([2, 9]):
N, train_lc, val_lc = learning_curve(PolynomialRegression(degree),
X, y, cv=7,
train_sizes=np.linspace(0.3, 1, 25))
ax[i].plot(N, np.mean(train_lc, 1), color='blue', label='training score')
ax[i].plot(N, np.mean(val_lc, 1), color='red', label='validation score')
ax[i].hlines(np.mean([train_lc[-1], val_lc[-1]]), N[0], N[-1],
color='gray', linestyle='dashed')
ax[i].set_ylim(0, 1)
ax[i].set_xlim(N[0], N[-1])
ax[i].set_xlabel('training size')
ax[i].set_ylabel('score')
ax[i].set_title('degree = {0}'.format(degree), size=14)
ax[i].legend(loc='best')
这是一个有价值的诊断,因为它给了我们一个模型如何响应不断增加的训练数据的可视化描述。特别是当你的学习曲线已经收敛的时候。,当训练曲线和验证曲线已经接近时)增加更多的训练数据不会显著提高拟合度!这种情况在左边的面板中可以看到的带有2维模型的学习曲线。
增加收敛分数的唯一方法是使用不同的(通常更复杂的)模型。我们在右边的面板中看到了这一点:通过移动到一个更复杂的模型,我们增加了收敛的分数(用虚线表示),但是牺牲了更高的模型方差(用训练和验证分数的差异表示)。如果我们添加更多的数据点,更复杂模型的学习曲线最终会收敛。
为您特别选择的模型和数据集绘制学习曲线可以帮助您做出这种类型的决策,以改进您的分析。
· 验证的实践:网格搜索
前面的讨论旨在让您对偏差和方差之间的权衡,以及它对模型复杂性和训练集大小的依赖有一些直观的认识。在实践中,模型通常有不止一个旋钮需要调整,因此验证和学习曲线的图从直线变成多维曲面。在这些情况下,这样的可视化是困难的,我们宁愿简单地找到使验证分数最大化的特定模型。
Scikit-Learn在网格搜索模块中提供了自动工具来实现这一点。下面是一个使用网格搜索找到最优多项式模型的例子。我们将探索模型特征的三维网格;即多项式次数,标志告诉我们是否匹配截距,标志告诉我们是否标准化问题。这可以使用Scikit-Learn的GridSearchCV元估计器:
from sklearn.grid_search import GridSearchCV
param_grid = {'polynomialfeatures__degree': np.arange(21),
'linearregression__fit_intercept': [True, False],
'linearregression__normalize': [True, False]}
grid = GridSearchCV(PolynomialRegression(), param_grid, cv=7)
注意,与普通估计器一样,这还没有应用到任何数据中。调用fit()方法将在每个网格点匹配模型,并跟踪沿途的得分:
grid.fit(X, y);
现在这是拟合的,我们可以要求最好的参数如下:
grid.best_params_
{'linearregression__fit_intercept': False,
'linearregression__normalize': True,
'polynomialfeatures__degree': 4}
最后,如果我们愿意,我们可以使用最好的模型,并使用之前的代码显示拟合我们的数据:
model = grid.best_estimator_
plt.scatter(X.ravel(), y)
lim = plt.axis()
y_test = model.fit(X, y).predict(X_test)
plt.plot(X_test.ravel(), y_test, hold=True);
plt.axis(lim);
网格搜索提供了更多的选项,包括指定自定义评分函数、并行化计算、随机搜索等等。
· 总结
在本节中,我们已经开始探索模型验证和超参数优化的概念,重点从直观方面关注偏差-方差平衡,以及在将模型与数据进行匹配时如何发挥作用。特别地,我们发现在调优参数时使用验证集或交叉验证方法是至关重要的,以避免对更复杂/灵活的模型过度拟合。
在后面的部分中,我们将讨论特别有用的模型的细节,在整个部分中,我们将讨论这些模型可用的调优以及这些自由参数如何影响模型复杂性。当您继续阅读并了解这些机器学习方法时,请记住这一部分的教训。
原文出处:《Python Data Science Handbook》
猜你喜欢
- 2024-10-11 sklearn分类模型汇总 sklearn svm分类
- 2024-10-11 一款小众但实用的自动化特征选择工具:AutoFeatSelect
- 2024-10-11 基于决策树算法完成鸢尾花的分类并使用scikit-learn进行交叉验证
- 2024-10-11 「程序员」如何5分钟掌握,pandas数据累计与分组?
- 2024-10-11 Python 机器学习 特征降维 python tsne降维
- 2024-10-11 mooc机器学习第七天-分类支持向量机svm.svc
- 2024-10-11 模型选择之交叉验证(简单交叉验证、S折交叉验证和留一交叉验证)
- 2024-10-11 I2C七宗罪之第二罪 七宗罪二哥
- 2024-10-11 PCA 主成分分析 PCA主成分分析实现鸢尾花数据集分类-MATLAB代码
- 2024-10-11 Python实现机器学习算法——随机森林
你 发表评论:
欢迎- 最近发表
-
- 在 Spring Boot 项目中使用 activiti
- 开箱即用-activiti流程引擎(active 流程引擎)
- 在springBoot项目中整合使用activiti
- activiti中的网关是干什么的?(activiti包含网关)
- SpringBoot集成工作流Activiti(完整源码和配套文档)
- Activiti工作流介绍及使用(activiti工作流会签)
- SpringBoot集成工作流Activiti(实际项目演示)
- activiti工作流引擎(activiti工作流引擎怎么用)
- 工作流Activiti初体验及在数据库中生成的表
- Activiti工作流浅析(activiti6.0工作流引擎深度解析)
- 标签列表
-
- oraclesql优化 (66)
- 类的加载机制 (75)
- feignclient (62)
- 一致性hash算法 (71)
- dockfile (66)
- 锁机制 (57)
- javaresponse (60)
- 查看hive版本 (59)
- phpworkerman (57)
- spark算子 (58)
- vue双向绑定的原理 (68)
- springbootget请求 (58)
- docker网络三种模式 (67)
- spring控制反转 (71)
- data:image/jpeg (69)
- base64 (69)
- java分页 (64)
- kibanadocker (60)
- qabstracttablemodel (62)
- java生成pdf文件 (69)
- deletelater (62)
- com.aspose.words (58)
- android.mk (62)
- qopengl (73)
- epoch_millis (61)
本文暂时没有评论,来添加一个吧(●'◡'●)