摘要: 本文讲述了如何用Python对训练集测试集进行分割与交叉验证。html
在上一篇关于Python中的线性回归的文章以后,我想再写一篇关于训练测试分割和交叉验证的文章。在数据科学和数据分析领域中,这两个概念常常被用做防止或最小化过分拟合的工具。我会解释当使用统计模型时,一般将模型拟合在训练集上,以便对未被训练的数据进行预测。python
在统计学和机器学习领域中,咱们一般把数据分红两个子集:训练数据和测试数据,而且把模型拟合到训练数据上,以便对测试数据进行预测。当作到这一点时,可能会发生两种状况:模型的过分拟合或欠拟合。咱们不但愿出现这两种状况,由于这会影响模型的可预测性。咱们有可能会使用具备较低准确性或不经常使用的模型(这意味着你不能泛化对其它数据的预测)。web
什么是模型的过分拟合(Overfitting)和欠拟合(Underfitting)?dom
过分拟合意味着模型训练得“太好”了,而且与训练数据集过于接近了。这一般发生在模型过于复杂的状况下,模型在训练数据上很是的准确,但对于未训练数据或者新数据可能会很不许确。由于这种模型不是泛化的,意味着你能够泛化结果,而且不能对其它数据进行任何推断,这大概就是你要作的。基本上,当发生这种状况时,模型学习或描述训练数据中的“噪声”,而不是数据中变量之间的实际关系。这种噪声显然不是任何新数据集的一部分,不能应用于它。机器学习
与过分拟合相反,当模型欠拟合的时候,它意味着模型不适合训练数据,所以会错过数据中的趋势特色。这也意味着该模型不能被泛化到新的数据上。你可能猜到了,这一般是模型很是简单的结果。例如,当咱们将线性模型(如线性回归)拟合到非线性的数据时,也会发生这种状况。不言而喻,该模型对训练数据的预测能力差,而且还不能推广到其它的数据上。函数
实例工具
值得注意的是,欠拟合不像过分拟合那样广泛。然而,咱们但愿避免数据分析中的这两个问题。你可能会说,咱们正在试图找到模型的欠拟合与过分拟合的中间点。像你所看到的,训练测试分割和交叉验证有助于避免过分拟合超过欠拟合。学习
正如我以前所说的,咱们使用的数据一般被分红训练数据和测试数据。训练集包含已知的输出,而且模型在该数据上学习,以便之后将其泛化到其它数据上。咱们有测试数据集(或子集),为了测试模型在这个子集上的预测。测试
咱们将使用Scikit-Learn library,特别是其中的训练测试分割方法。咱们将从导入库开始:大数据
快速地看一下导入的库:
· Pandas —将数据文件做为Pandas数据帧加载,并对数据进行分析;
· 在Sklearn中,我导入了数据集模块,所以能够加载一个样本数据集和linear_model,所以能够运行线性回归;
· 在Sklearn的子库model_selection中,我导入了train_test_split,所以能够把它分红训练集和测试集;
· 在Matplotlib中,我导入了pyplot来绘制数据图表;
好了,一切都准备就绪,让咱们输入糖尿病数据集,将其转换成数据帧并定义列的名称:
如今咱们可使用train_test_split函数来进行分割。函数内的test_size=0.2代表了要测试的数据的百分比,一般是80/20或70/30左右。
# create training and testing vars X_train, X_test, y_train, y_test = train_test_split(df, y, test_size=0.2) print X_train.shape, y_train.shape print X_test.shape, y_test.shape (353, 10) (353,) (89, 10) (89,)
如今咱们将在训练数据上拟合模型:
# fit a model lm = linear_model.LinearRegression() model = lm.fit(X_train, y_train) predictions = lm.predict(X_test)
正如所看到的那样,咱们在训练数据上拟合模型并尝试预测测试数据。让咱们看一看都预测了什么:
predictions[0:5] array([ 205.68012533, 64.58785513, 175.12880278, 169.95993301, 128.92035866])
注:由于我在预测以后使用了[0:5],它只显示了前五个预测值。去掉[0:5]的限制就会使它输出咱们模型建立的全部预测值。
让咱们来绘制模型:
## The line / model plt.scatter(y_test, predictions) plt.xlabel(“True Values”) plt.ylabel(“Predictions”)
打印准确度得分:
print “Score:”, model.score(X_test, y_test) Score: 0.485829586737
总结:将数据分割成训练集和测试集,将回归模型拟合到训练数据,基于该数据作出预测,并在测试数据上测试预测结果。可是训练和测试的分离确实有其危险性,若是咱们所作的分割不是随机的呢?若是咱们数据的一个子集只包含来自某个州的人,或者具备必定收入水平但不包含其它收入水平的员工,或者只有妇女,或者只有某个年龄段的人,那该怎么办呢?这将致使过分拟合,即便咱们试图避免,这就是交叉验证要派上用场了。
在前一段中,我提到了训练测试分割方法中的注意事项。为了不这种状况,咱们能够执行交叉验证。它很是相似于训练测试分割,可是被应用于更多的子集。意思是,咱们将数据分割成k个子集,并训练第k-1个子集。咱们要作的是,为测试保留最后一个子集。
训练测试分割和交叉验证的可视化表示
有一组交叉验证方法,我来介绍其中的两个:第一个是K-Folds Cross Validation,第二个是Leave One Out Cross Validation (LOOCV)。
在K-Folds交叉验证中,咱们将数据分割成k个不一样的子集。咱们使用第k-1个子集来训练数据,并留下最后一个子集做为测试数据。而后,咱们对每一个子集模型计算平均值,接下来结束模型。以后,咱们对测试集进行测试。
K-Folds的可视化表示
这里有一个在Sklearn documentation上很是简单的K-Folds例子:
fromsklearn.model_selection import KFold # import KFold X = np.array([[1, 2], [3, 4], [1, 2], [3, 4]]) # create an array y = np.array([1, 2, 3, 4]) # Create another array kf = KFold(n_splits=2) # Define the split - into 2 folds kf.get_n_splits(X) # returns the number of splitting iterations in the cross-validator print(kf) KFold(n_splits=2, random_state=None, shuffle=False)
让咱们看看结果:
fortrain_index, test_index in kf.split(X): print(“TRAIN:”, train_index, “TEST:”, test_index) X_train, X_test = X[train_index], X[test_index] y_train, y_test = y[train_index], y[test_index] ('TRAIN:', array([2, 3]), 'TEST:', array([0, 1])) ('TRAIN:', array([0, 1]), 'TEST:', array([2, 3]))
正如看到的,函数将原始数据拆分红不一样的数据子集。这是个很是简单的例子,但我认为它把概念解释的至关好。
这是另外一种交叉验证的方法,弃一法交叉验证。能够在Sklearn website上查看。在这种交叉验证中,子集的数量等于咱们在数据集中观察到的数量。而后,咱们计算全部子集的平均数,并利用平均值创建模型。而后,对最后一个子集测试模型。由于咱们会获得大量的训练集(等于样本的数量),所以这种方法的计算成本也至关高,应该在小数据集上使用。若是数据集很大,最好使用其它的方法,好比kfold。
让咱们看看Sklearn上的另外一个例子:
fromsklearn.model_selectionimportLeaveOneOut X = np.array([[1, 2], [3, 4]]) y = np.array([1, 2]) loo = LeaveOneOut() loo.get_n_splits(X) fortrain_index, test_indexinloo.split(X): print("TRAIN:", train_index, "TEST:", test_index) X_train, X_test = X[train_index], X[test_index] y_train, y_test = y[train_index], y[test_index] print(X_train, X_test, y_train, y_test)
如下是输出:
('TRAIN:', array([1]), 'TEST:', array([0])) (array([[3, 4]]), array([[1, 2]]), array([2]), array([1])) ('TRAIN:', array([0]), 'TEST:', array([1])) (array([[1, 2]]), array([[3, 4]]), array([1]), array([2]))
那么,咱们应该使用什么方法呢?使用多少子集呢?拥有的子集越多,咱们将会因为误差而减小偏差,但会因为方差而增长偏差;计算成本也会上升,显然,拥有的子集越多,计算所需的时间就越长,也将须要更多的内存。若是利用数量较少的子集,咱们减小了因为方差而产生的偏差,可是因为误差引发的偏差会更大。它的计算成本也更低。所以,在大数据集中,一般建议k=3。在更小的数据集中,正如我以前提到的,最好使用弃一法交叉验证。
让咱们看看之前用过的一个例子,此次使用的是交叉验证。我将使用cross_val_predict函数来给每一个在测试切片中的数据点返回预测值。
# Necessary imports: from sklearn.cross_validation import cross_val_score, cross_val_predict from sklearn import metrics
以前,我给糖尿病数据集创建了训练测试分割,并拟合了一个模型。让咱们看看在交叉验证以后的得分是多少:
# Perform 6-fold cross validation scores = cross_val_score(model, df, y, cv=6) print “Cross-validated scores:”, scores Cross-validated scores: [ 0.4554861 0.46138572 0.40094084 0.55220736 0.43942775 0.56923406]
正如你所看到的,最后一个子集将原始模型的得分从0.485提升到0.569。这并非一个惊人的结果,但咱们获得了想要的。
如今,在进行交叉验证以后,让咱们绘制新的预测图:
# Make cross validated predictions predictions = cross_val_predict(model, df, y, cv=6) plt.scatter(y, predictions)
你能够看到这和原来的图有很大的不一样,是原来图的点数的六倍,由于我用的cv=6。
最后,让咱们检查模型的R²得分(R²是一个“表示与自变量分离的可预测的因变量中方差的比例的数量”)。能够看一下咱们的模型有多准确:
accuracy = metrics.r2_score(y, predictions) print “Cross-Predicted Accuracy:”, accuracy Cross-Predicted Accuracy: 0.490806583864