机器学习中级教程
在本教程中,您将学习如何使用交叉验证来更好地衡量模型性能。
正文
介绍
机器学习是一个迭代过程。
您将面临关于使用什么预测变量、使用什么类型的模型、为这些模型提供什么参数等方面的选择。到目前为止,您已经以数据驱动的方式通过使用验证集(或保持集)测量模型质量来做出这些选择。
但这种方法也有一些缺点。要看到这一点,假设您有一个包含5000行的数据集。通常,您将保留大约20%的数据作为验证数据集,或1000行。但这给确定模型分数留下了一些随机机会。也就是说,一个模型可能在一组1000行上表现良好,即使它在另一组1000行上不准确。
在极端情况下,您可以想象验证集中只有一行数据。如果你比较不同的模型,哪一个能在单个数据点上做出最好的预测将主要取决于运气!
一般来说,验证集越大,模型质量度量中的随机性(也称为“噪声”)就越小,也就越可靠。不幸的是,我们只能通过从训练数据中删除行来获得大的验证集,而较小的训练数据集意味着更糟糕的模型!
什么是交叉验证?
在交叉验证中,我们在不同的数据子集上运行建模过程,以获得模型质量的多个度量。
例如,我们可以首先将数据分成5个部分,每个部分占整个数据集的20%。在这种情况下,我们说我们已经将数据分成5个“折叠”。
然后,我们对每个折叠进行一个实验:
- 在
实验1
中,我们使用第一个折叠作为验证集(或保持集),其他所有内容作为训练数据。这为我们提供了一个基于20%保持集的模型质量度量。 - 在
实验2
中,我们从第二个折叠中拿出数据(并使用除第二个折叠外的所有东西来训练模型)。然后使用保持集对模型质量进行第二次估计。 - 我们重复这个过程,每折叠一次作为保持集。综上所述,在某一点上,100%的数据被用作保持数据,我们最终得到了基于数据集中所有行的模型质量度量(即使我们不同时使用所有行)。
什么时候应该使用交叉验证?
交叉验证为模型质量提供了更准确的度量,如果您正在做出大量建模决策,这一点尤其重要。然而,它可能需要更长的运行时间,因为它估计了多个模型(每个折叠一个)。
那么,考虑到这些权衡,您应该在什么时候使用每种方法?
- 对于小型数据集,如果额外的计算负担不是什么大问题,则应该运行交叉验证。
- 对于较大的数据集,单个验证集就足够了。您的代码将运行得更快,并且您可能有足够的数据,因此几乎不需要将其中的一些数据重新用于保持。
对于大数据集和小数据集的构成,没有简单的界限。但是,如果你的模型只需要几分钟或更少的时间就可以运行,那么切换到交叉验证可能是值得的。
或者,你可以进行交叉验证,看看每个实验的分数是否接近。如果每个实验产生相同的结果,一个单一的验证集可能就足够了。
举例
我们将使用与上一个教程中相同的数据。我们在X中加载输入数据,在y中加载输出数据。
import pandas as pd
# Read the data
data = pd.read_csv('../input/melbourne-housing-snapshot/melb_data.csv')
# Select subset of predictors
cols_to_use = ['Rooms', 'Distance', 'Landsize', 'BuildingArea', 'YearBuilt']
X = data[cols_to_use]
# Select target
y = data.Price
然后,我们定义了一个管道,它使用一个插补器来填充缺失值,并使用一个随机森林模型来进行预测。
虽然可以在没有管道的情况下进行交叉验证,但这相当困难!使用管道将使代码非常简单。
from sklearn.ensemble import RandomForestRegressor
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
my_pipeline = Pipeline(steps=[('preprocessor', SimpleImputer()),
('model', RandomForestRegressor(n_estimators=50,
random_state=0))
])
我们使用scikit-learn
中的cross_val_score()
函数获得交叉验证分数。我们使用cv
参数设置折叠的数量。
from sklearn.model_selection import cross_val_score
# Multiply by -1 since sklearn calculates *negative* MAE
scores = -1 * cross_val_score(my_pipeline, X, y,
cv=5,
scoring='neg_mean_absolute_error')
print("MAE scores:\n", scores)
MAE scores:
[301628.7893587 303164.4782723 287298.331666 236061.84754543
260383.45111427]
评分参数选择要报告的模型质量度量:在这种情况下,我们选择负平均绝对误差(MAE)。scikit-learn的文档显示了一个选项列表。
我们指定负MAE有点奇怪。Scikit-learn
有一个约定,其中定义了所有指标,因此高数字更好。在这里使用否定词可以让它们与传统保持一致,尽管否定词在其他地方几乎闻所未闻。
我们通常需要一个模型质量的单一度量来比较替代模型。所以我们取实验的平均值。
print("Average MAE score (across experiments):")
print(scores.mean())
Average MAE score (across experiments):
277707.3795913405
结论
使用交叉验证可以更好地衡量模型质量,还可以清理代码:请注意,我们不再需要跟踪单独的训练集和验证集。因此,特别是对于小型数据集,这是一个很好的改进!
轮到你了
在下一个练习中运用你的新技能!
练习部分
在本练习中,您将利用所学知识调整具有交叉验证的机器学习模型。
设置
下面的问题将为你的工作提供反馈。运行以下单元格设置反馈系统。
# Set up code checking
import os
if not os.path.exists("../input/train.csv"):
os.symlink("../input/home-data-for-ml-course/train.csv", "../input/train.csv")
os.symlink("../input/home-data-for-ml-course/test.csv", "../input/test.csv")
from learntools.core import binder
binder.bind(globals())
from learntools.ml_intermediate.ex5 import *
print("Setup Complete")
Setup Complete
您将使用来自 Housing Prices Competition for Kaggle Learn Users 案例.
。
在不做任何更改的情况下运行以下代码单元,以加载X_train、X_valid、y_train和y_valid中的训练集和验证集。测试集在X_test中加载。
import pandas as pd
from sklearn.model_selection import train_test_split
# Read the data
train_data = pd.read_csv('../input/train.csv', index_col='Id')
test_data = pd.read_csv('../input/test.csv', index_col='Id')
# Remove rows with missing target, separate target from predictors
train_data.dropna(axis=0, subset=['SalePrice'], inplace=True)
y = train_data.SalePrice
train_data.drop(['SalePrice'], axis=1, inplace=True)
# Select numeric columns only
numeric_cols = [cname for cname in train_data.columns if train_data[cname].dtype in ['int64', 'float64']]
X = train_data[numeric_cols].copy()
X_test = test_data[numeric_cols].copy()
使用下一个代码单元打印数据的前几行。
X_train.head()
到目前为止,您已经学会了如何使用scikit-learn
构建管道。例如,下面的管道将使用SimpleImputer()
替换数据中缺失的值,然后使用RandomForestRegressionor()
训练随机森林模型进行预测。我们使用n_estimators
参数设置随机森林模型中的树数,设置random_state
确保可重复性。
from sklearn.ensemble import RandomForestRegressor
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
my_pipeline = Pipeline(steps=[
('preprocessor', SimpleImputer()),
('model', RandomForestRegressor(n_estimators=50, random_state=0))
])
您还学习了如何在交叉验证中使用管道。下面的代码使用cross_val_score()
函数来获得平均绝对误差(MAE),它在五个不同的折叠中取平均值。回想一下,我们用cv
参数设置了折叠次数。
from sklearn.model_selection import cross_val_score
# Multiply by -1 since sklearn calculates *negative* MAE
scores = -1 * cross_val_score(my_pipeline, X, y,
cv=5,
scoring='neg_mean_absolute_error')
print("Average MAE score:", scores.mean())
Average MAE score: 18276.410356164386
第1步:编写一个有用的函数
在本练习中,您将使用交叉验证为机器学习模型选择参数。
首先编写一个函数get_score()
,该函数报告机器学习管道的平均(超过三次交叉验证)MAE,该管道使用:
X
和y
中的数据用于创建褶皱,SimpleImputer()
所有参数保留为默认值)以替换缺少的值,以及RandomForestRegressor()
(使用random_state=0
)来拟合随机森林模型。
在设置随机森林模型中的树数时,使用n_estimators
参数提供给get_score()。
def get_score(n_estimators):
"""Return the average MAE over 3 CV folds of random forest model.
Keyword argument:
n_estimators -- the number of trees in the forest
"""
# Replace this body with your own code
pass
# Check your answer
step_1.check()
第2步:测试不同的参数值
现在,您将使用在步骤1中定义的函数来评估与随机林中树数
的八个不同值相对应的模型性能:50、100、150、。。。,300, 350, 400.
将结果存储在Python 字典 results
中,其中results[i]
是get_score(i)
返回的平均MAE。
results = ____ # Your code here
# Check your answer
step_2.check()
注意,运行 以上代码 需要一些时间
使用下一个单元格可视化第2步的结果。运行代码,不做任何更改。
import matplotlib.pyplot as plt
%matplotlib inline
plt.plot(list(results.keys()), list(results.values()))
plt.show()
第3步:找到最佳参数值
考虑到结果,对于随机森林模型,哪个n_estimators
似乎最适合?使用你的答案来设置n_estimators
的最佳值。
n_estimators_best = ____
# Check your answer
step_3.check()
在本练习中,您探讨了在机器学习模型中选择适当参数的一种方法。
如果您想了解更多关于超参数优化的信息,建议您从网格搜索开始,这是一种确定机器学习模型参数最佳组合的简单方法。谢天谢地,scikit-learn
还包含一个内置函数GridSearchCV()
,它可以使网格搜索代码非常高效!
继续前进
继续学习梯度增强,这是一种强大的技术,可以在各种数据集上获得最优秀的结果。
答案
### step 1
def get_score(n_estimators):
my_pipeline = Pipeline(steps=[
('preprocessor', SimpleImputer()),
('model', RandomForestRegressor(n_estimators, random_state=0))
])
scores = -1 * cross_val_score(my_pipeline, X, y,
cv=3,
scoring='neg_mean_absolute_error')
return scores.mean()
# step 2
results = {}
for i in range(1,9):
results[50*i] = get_score(50*i)
# step 3
n_estimators_best = min(results, key=results.get)