误差模型:过拟合,交叉验证,偏差-方差权衡
作者Natasha Latysheva;Charles Ravarani
发表于cambridgecoding介绍
在本文中或许你会掌握机器学习中最核心的概念:偏差-方差权衡.其主要想法是,你想创建尽可能预測准确而且仍能适用于新数据的模型(这是泛化).危急的是,你能够轻松的在你制定的数据中创建过度拟合本地噪音的模型,这样的模型是没用的,而且导致弱泛化能力,由于噪声是随机的,故而在每一个数据集中是不同的.从本质上讲,你希望创建仅捕获数据集中实用成份的模型.还有一方面,泛化能力很好可是对于产生良好预測过于僵化的模型是还有一个极端(这称之为欠拟合).
我们使用k-近邻算法讨论并展示这些概念,k-近邻带有一个简单的參数k,能够用不同的參数清楚的展示欠拟合。过拟合以及泛化能力的思想.同一时候,平衡欠拟合和过拟合之间的相关概念称为偏差-方差权衡.这里有一个表格概括了不管是过拟合或者欠拟合模型中一些不同但相同![](http://i.imgur.com/W1ZBL9s.jpg)
产生数据
让我们从建立人工数据集開始.你能够轻松的使用sklearn.datasets中的make_classification()函数做到这一点.详细来说,你会生成相对简单的二元分类问题.为了让它更有趣一点,让我们的数据呈现月牙型并加入一些随机噪声.这应该能让其更真实并提高分类观測的难度.
“`
Creating the dataset
e.g. make_moons generates crescent-shaped data
Check out make_classification, which generates linearly-separable data
from sklearn.datasets import make_moons
X, y = make_moons( n_samples=500, # the number of observations random_state=1, noise=0.3 )Take a peek
print(X[:10,])
print(y[:10])“`
[[ 0.50316464 0.11135559]
[ 1.06597837 -0.63035547] [ 0.95663377 0.58199637] [ 0.33961202 0.40713937] [ 2.17952333 -0.08488181] [ 2.00520942 0.7817976 ] [ 0.12531776 -0.14925731] [ 1.06990641 0.36447753] [-0.76391099 -0.6136396 ] [ 0.55678871 0.8810501 ]] [1 1 0 0 1 1 1 0 0 0]你刚生成的数据集例如以下图所看到的:
“`
import matplotlib.pyplot as plt from matplotlib.colors import ListedColorma %matplotlib inline # for the plots to appear inline in jupyter notebooks
Plot the first feature against the other, color by class
plt.scatter(X[y == 1, 0], X[y == 1, 1], color=”#EE3D34”, marker=”x”)
plt.scatter(X[y == 0, 0], X[y == 0, 1], color=”#4458A7”, marker=”o”)“`
![](http://i.imgur.com/f3FP1Xu.jpg)
“`
from sklearn.cross_validation import train_test_split
Split into training and test sets
XTrain, XTest, yTrain, yTest = train_test_split(X, y, random_state=1, test_size=0.5)
“`
使用K近邻(KNN)分类器预測数据集类别.第二章提供了关于KNN理论很好介绍.我是ISLR书的脑残粉.你相同能够看看之前文章 .
介绍KNN中的超參数K
KNN算法的工作原理是,对新数据点利用K近邻信息分配类别标签.仅仅注重于和它最类似数据点的类,并分配这个新数据点到这些近邻中最常见的类.当使用KNN,你须要设定希望算法使用的K值.
假设K很高(k=99),模型在对未知数据点类别做决策是会考虑大量近邻.这意味着模型是相当受限的,由于它分类实例时,考虑了大量信息.换句话说,一个大的k值导致相当”刚性”的模型行为. 相反,假设k很低(k=1,或k=2),在做分类决策时仅仅考虑少量近邻,这是很灵活而且很复杂的模型,它能完美拟合数据的精确形式.因此模型预測更依赖于数据的局部趋势(关键的是,包括噪声). 让我们看一看k=99与k=1时KNN算法分类数据的情况.绿色的线是训练数据的决策边界(算法中的阈值决定一个数据点是否属于蓝或红类).![](http://i.imgur.com/iUFQFPw.jpg)
在新数据上表现怎样?
眼下你仅仅能看到训练数据,可是量化训练误差没多大用处.对模型概括刚学习的训练集性能有多好,你不感兴趣.让我们看看在測试集表现怎样,由于这会对模型好坏给你一个更直观的印象.试着使用不同的K值:from sklearn.neighbors import KNeighborsClassifierfrom sklearn import metrics knn99 = KNeighborsClassifier(n_neighbors = 99)knn99.fit(XTrain, yTrain)yPredK99 = knn99.predict(XTest)print "Overall Error of k=99 Model:", 1 - round(metrics.accuracy_score(yTest, yPredK99), 2)knn1 = KNeighborsClassifier(n_neighbors = 1)knn1.fit(XTrain, yTrain)yPredK1 = knn1.predict(XTest)print "Overall Error of k=1 Model:", 1 - round(metrics.accuracy_score(yTest, yPredK1), 2)
Overall Error of k=99 Model: 0.15
Overall Error of k=1 Model: 0.15 实际上,看起来这些模型对測试集表现的大约相同出色.以下是通过训练集学习到的决策边界应用于測试集.看是否能找出两个模型错误的预測.![](http://i.imgur.com/IbDmgAf.jpg)
knn50 = KNeighborsClassifier(n_neighbors = 50)knn50.fit(XTrain, yTrain)yPredK50 = knn50.predict(XTest)print "Overall Error of k=50 Model:", 1 - round(metrics.accuracy_score(yTest, yPredK50), 2)
Overall Error of k=50 Model: 0.11
看起来好了点.让我们检查k=50时模型的决策边界.![](http://i.imgur.com/u7gpBbc.jpg)
偏差-方差权衡:结论意见
希望你如今对模型的欠拟合和过拟合有良好的理解.看如今是否理解本文开头的全部术语.基本上,发现过拟合和欠拟合之间正确的平衡关系相当于偏差-方差权衡.
总的来说,当你对一个数据集训练机器学习算法,关注模型在一个独立数据模型的表现怎样.对于训练集做好分类是不够的.本质上来讲,仅仅关心构建可泛化的模型–对于训练集获得100%的准确率并不令人印象深刻,仅仅是过拟合的指标.过拟合是紧密拟合模型,而且调优噪声而不是信号的情况. 更清楚的讲,你不是建模数据集中的趋势.而是尝试建模真实世界过程,引导我们研究数据.你恰好使用的详细数据集仅仅是基础事实的一小部分实例,当中包括噪声和自身的特点. 下列汇总图片展示在训练集和測试集上欠拟合(高偏差,低方差),正确拟合,以及过拟合(低偏差,高方差)模型怎样表现:![](http://i.imgur.com/hLSW8aJ.jpg)
使用K折交叉验证评估模型性能
输入K折交叉验证,这是仅使用训练集衡量模型性能的一个方便技术.该步骤例如以下:你随机划分训练集为k等份;然后,我们在k-1/k的训练集上训练数据;对剩下的一部分评估性能.这给你一些模型性能的指标(如,总体精度).接下来训练在不同的k-1/k训练集训练算法,并在剩下的1部分评估.你反复这个过程k次,得到k个不同的模型性能度量,利用这些值的平均值得到总体性能的度量.继续样例,10折交叉验证背后例如以下:
![](http://i.imgur.com/B5vmvIg.jpg)
比較训练误差,交叉验证误差和測试误差
那么,什么k是最佳的?对训练数据构建模形式尝试不同K值,看对训练集本身和測试集预測类别的结果模型怎样.最后看K折交叉验证怎样支出最好的K.
注:实践中,当扫描这样的參数,使用训练集測试模型是以个糟糕的主意.相同的方式,你不能使用測试集多次浏览一个參数(每一个參数值一次).接下来,你是用这些计算仅仅是作为样例.实践中,仅仅有K折交叉验证是一种安全的方法!import numpy as npfrom sklearn.cross_validation import train_test_split, cross_val_scoreknn = KNeighborsClassifier()# the range of number of neighbors you want to testn_neighbors = np.arange(1, 141, 2)# here you store the models for each dataset usedtrain_scores = list()test_scores = list()cv_scores = list()# loop through possible n_neighbors and try them outfor n in n_neighbors:knn.n_neighbors = nknn.fit(XTrain, yTrain)train_scores.append(1 - metrics.accuracy_score(yTrain, knn.predict(XTrain))) # this will over-estimate the accuracytest_scores.append(1 - metrics.accuracy_score(yTest, knn.predict(XTest)))cv_scores.append(1 - cross_val_score(knn, XTrain, yTrain, cv = 10).mean()) # you take the mean of the CV scores
那么最优的k是多少?当多个相同的预測误差,你随便挑一个最小的作为k值.
# what do these different datasets think is the best value of k?
print( 'The best values of k are: n' '{} according to the Training Setn' '{} according to the Test Set andn' '{} according to Cross-Validation'.format( min(n_neighbors[train_scores == min(train_scores)]), min(n_neighbors[test_scores == min(test_scores)]), min(n_neighbors[cv_scores == min(cv_scores)]) ) )
最优K是:
1 according to the Training Set 23 according to the Test Set and 11 according to Cross-Validation不仅仅是收集最优的k,还须要对一系列測试的K看看预測误差.
# let's plot the error you get with different values of kplt.figure(figsize=(10,7.5))plt.plot(n_neighbors, train_scores, c="black", label="Training Set")plt.plot(n_neighbors, test_scores, c="black", linestyle="--", label="Test Set")plt.plot(n_neighbors, cv_scores, c="green", label="Cross-Validation")plt.xlabel('Number of K Nearest Neighbors')plt.ylabel('Classification Error')plt.gca().invert_xaxis()plt.legend(loc = "lower left")plt.show()
让我们谈谈训练集的分类错误.你考虑少量近邻,训练集会得到低的预測误差.这是有道理的,由于在做新的分类是,逼近每一个点仅仅考虑它本身的情况.測试误差遵循类似的轨迹,可是在某个点后由于过拟合而增长.这样的现象表明,构建的训练集模型拟合在指定測试集样本上建模效果不好.
在该图中能够看到,尤其是对于k的低值。採用k折交叉验证突出參数空间的区域(即k的很低的值)。这是很easy出现过拟合的。虽然交叉验证和測试集的评估导致一些不同的最优解。它们都是相当不错的。而且大致正确。你也能够看到。交叉验证是測试误差的合理预计。
这样的类型的情节是好的,以获得确定參数怎样影响模型表现的良好感觉。并帮助建立数据集的直觉来学习。
代码展示
这是生成以上全部图片,训练測试不同kNN算法的代码.代码是scikit-learn样例改编的代码,主要处理决策边界的计算并让图片好看.
包括机器学习中拆分数据集。算法拟合以及測试的部分。def detect_plot_dimension(X, h=0.02, b=0.05):x_min, x_max = X[:, 0].min() - b, X[:, 0].max() + by_min, y_max = X[:, 1].min() - b, X[:, 1].max() + bxx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))dimension = xx, yyreturn dimensiondef detect_decision_boundary(dimension, model):xx, yy = dimension # unpack the dimensionsboundary = model.predict(np.c_[xx.ravel(), yy.ravel()])boundary = boundary.reshape(xx.shape) # Put the result into a color plotreturn boundarydef plot_decision_boundary(panel, dimension, boundary, colors=['#DADDED', '#FBD8D8']):xx, yy = dimension # unpack the dimensionspanel.contourf(xx, yy, boundary, cmap=ListedColormap(colors), alpha=1)panel.contour(xx, yy, boundary, colors="g", alpha=1, linewidths=0.5) # the decision boundary in greendef plot_dataset(panel, X, y, colors=["#EE3D34", "#4458A7"], markers=["x", "o"]):panel.scatter(X[y == 1, 0], X[y == 1, 1], color=colors[0], marker=markers[0])panel.scatter(X[y == 0, 0], X[y == 0, 1], color=colors[1], marker=markers[1])def calculate_prediction_error(model, X, y):yPred = model.predict(X)score = 1 - round(metrics.accuracy_score(y, yPred), 2)return scoredef plot_prediction_error(panel, dimension, score, b=.3):xx, yy = dimension # unpack the dimensionspanel.text(xx.max() - b, yy.min() + b, ('%.2f' % score).lstrip('0'), size=15, horizontalalignment='right')def explore_fitting_boundaries(model, n_neighbors, datasets, width):# determine the height of the plot given the aspect ration of each panel should be equalheight = float(width)/len(n_neighbors) * len(datasets.keys())nrows = len(datasets.keys())ncols = len(n_neighbors)# set up the plotfigure, axes = plt.subplots(nrows,ncols,figsize=(width, height),sharex=True,sharey=True)dimension = detect_plot_dimension(X, h=0.02) # the dimension each subplot based on the data# Plotting the dataset and decision boundariesi = 0for n in n_neighbors:model.n_neighbors = nmodel.fit(datasets["Training Set"][0], datasets["Training Set"][1])boundary = detect_decision_boundary(dimension, model)j = 0for d in datasets.keys():try:panel = axes[j, i]except (TypeError, IndexError):if (nrows * ncols) == 1:panel = axeselif nrows == 1: # if you only have one datasetpanel = axes[i]elif ncols == 1: # if you only try one number of neighborspanel = axes[j]plot_decision_boundary(panel, dimension, boundary) # plot the decision boundaryplot_dataset(panel, X=datasets[d][0], y=datasets[d][1]) # plot the observationsscore = calculate_prediction_error(model, X=datasets[d][0], y=datasets[d][1])plot_prediction_error(panel, dimension, score, b=0.2) # plot the score# make compacted layoutpanel.set_frame_on(False)panel.set_xticks([])panel.set_yticks([])# format the axis labelsif i == 0:panel.set_ylabel(d)if j == 0:panel.set_title('k={}'.format(n))j += 1i += 1plt.subplots_adjust(hspace=0, wspace=0) # make compacted layout
然后,你能够这样执行代码:
# specify the model and settingsmodel = KNeighborsClassifier()n_neighbors = [200, 99, 50, 23, 11, 1]datasets = {"Training Set": [XTrain, yTrain],"Test Set": [XTest, yTest]}width = 20# explore_fitting_boundaries(model, n_neighbors, datasets, width)explore_fitting_boundaries(model=model, n_neighbors=n_neighbors, datasets=datasets, width=width)
结论
偏差-方差权衡出如今机器学习的不同领域.全部算法都能够觉得具有一定弹性,而且不仅仅是KNN.发现描写叙述良好数据模式而且能够泛化新数据,这样灵活的最佳点的目标适用于基本上全部算法.