机器学习项目流程(四)选择并训练模型
选择并训练模型
至此,我们已明确了问题,并对数据进行了预处理。现在我们选择并训练一个机器学习模型。
在训练集上训练模型
这个过程相对来说较为简单,我们首先训练一个线性回归模型:
from sklearn.linear_model import LinearRegression lin_reg = LinearRegression() lin_reg.fit(housing_prepared, housing_labels)
这样就已完成了一个线性回归模型的训练,非常简单。我们从训练集里抓几条数据验证一下:
sample_data = housing.iloc[:5] sample_labels = housing_labels.iloc[:5] sample_data_prepared = full_pipeline.transform(sample_data) lin_reg.predict(sample_data_prepared) >array([210644.60459286, 317768.80697211, 210956.43331178, 59218.98886849, 189747.55849879]) list(sample_labels) >[286600.0, 340600.0, 196900.0, 46300.0, 254500.0]
可以看到预测的精准度并不高,我们可以看一下这个模型的RMSE(均方误差)的大小:
from sklearn.metrics import mean_squared_error
housing_predictions = lin_reg.predict(housing_prepared) lin_mse = mean_squared_error(housing_labels, housing_predictions) lin_rmse = np.sqrt(lin_mse) >68628.19819848923
从均方误差的大小来看,这并不是一个很好的结果,说明模型存在欠拟合。在遇到欠拟合时,说明:
- 这些特征没有提供足够的信息给模型,导致无法学习到准确的预测模型
- 模型本身不适用这个数据集
一般我们可以采取以下方式解决欠拟合问题:
- 选择一个更强大更合适的模型
- 给训练集构造更好的属性特征
- 减少模型的限制(例如加了正则化导致欠拟合)
在这个场景下,模型没有使用L1/L2 正则,所以先排除第3点。对于第二点,我们可以尝试加更多的属性(例如population的对数),不过这里我们首先尝试第一个方法,选择另一个更复杂的模型试试。
下面我们试试决策树,决策树模型擅长发现复杂的非线性关系:
from sklearn.tree import DecisionTreeRegressor tree_reg = DecisionTreeRegressor() tree_reg.fit(housing_prepared, housing_labels) housing_predictions = tree_reg.predict(housing_prepared) tree_mse = mean_squared_error(housing_labels, housing_predictions) tree_rmse = np.sqrt(tree_mse) tree_rmse >0.0
可以看到此模型做出预测后,RMSE的结果是0.0。说明这个模型在训练集工作良好,但也有过拟合的可能。为了进一步判断是否过拟合,我们需要用测试集对它进行评估与验证。
使用交叉验证
其中一种评估决策树模型的方法是用train_test_split() 将训练集划分为一个更小的训练集以及一个验证集(validation set)。然后使用训练集训练模型,并使用验证集评估模型。
不过另一个更好的办法是使用K-折交叉验证(K-fold cross-validation),这个在sklearn 中也提供了。下面的代码会将训练集随机划分成10个互斥子集(每个子集称为一个fold,这里k=10,也是默认常用的k值),然后训练并评估模型10次,每次均取9个子集的并集作为训练数据,剩下的1个子集作为验证集。最终结果会包含10次validation 的分值:
from sklearn.model_selection import cross_val_score scores = cross_val_score(tree_reg, housing_prepared, housing_labels, scoring="neg_mean_squared_error", cv=10) tree_rmse_scores = np.sqrt(-scores)
tree_rmse_scores >array([67669.73816437, 67362.61970456, 70229.34328631, 68299.62160307, 71291.36213287, 75577.59634532, 72668.53981039, 70226.23940832, 77010.86953522, 69438.29976359])
这里我们在scoring 参数那里指定了一个neg_mean_squared_error,而不是我们之前提到的正均方误差。这是因为sklearn的交叉验证方法需要传入的是一个效应函数(utility function,值越大越好)而不是一个损失函数(cost function,值越小越好)。所以scoring function 这里指定的是均方误差的负数。
我们看一下结果:
def display_scores(scores): print("Scores:", scores) print("Mean:", scores.mean()) print("Standard deviattion:", scores.std()) display_scores(tree_rmse_scores) >Scores: [69462.00181424 67609.32626575 70422.57362602 69319.8112669 71825.7583015 75264.10640903 72395.84019687 71141.89336673 76759.56875965 68894.22449779]
Mean: 71309.51045044875 Standard deviattion: 2729.4780010832424
可以看到决策树现在的rmse 的结果并没有之前好了(之前在训练集上的 rmse 为0),并且结果比线性回归的结果还要差。通过交叉验证我们可以获得多次评估的平均值,并且!还能知道这个评估的精准度(也就是标准差)。随机数的rmse大约为 71309,一般是±2729(这个变动区间里)。
接下来我们计算一下线性回归的交叉验证结果:
lin_scores = cross_val_score(lin_reg, housing_prepared, housing_labels, scoring="neg_mean_squared_error", cv=10) lin_rmse_scores = np.sqrt(-lin_scores) display_scores(lin_rmse_scores) >Scores: [66782.73843989 66960.118071 70347.95244419 74739.57052552 68031.13388938 71193.84183426 64969.63056405 68281.61137997 71552.91566558 67665.10082067] Mean: 69052.46136345083 Standard deviattion: 2731.674001798344
可以看到线性回归的效果确实略好于决策树。而决策树在第一次训练集上的rsme 的值为0的现象,充分说明了决策树存在严重过拟合。
最后我们试试另一个模型:随机森林。随机森林的方法是:在多个随机子集上训练多个决策树,然后取它们预测值的平均值。根据多个模型而构造一个机器学习模型的方法称为集成学习,这是一个非常有效的手段,可以让机器学习算法更强大。下面是随机森林的表现:
from sklearn.ensemble import RandomForestRegressor forest_reg = RandomForestRegressor() forest_reg.fit(housing_prepared, housing_labels) forest_prediction = forest_reg.predict(housing_prepared) forest_rmse = np.sqrt(mean_squared_error(housing_labels, forest_prediction)) forest_rmse >22260.03021330574 forest_scores = cross_val_score(forest_reg, housing_prepared, housing_labels, scoring='neg_mean_squared_error', cv=10) forest_rmse_scores = np.sqrt(-forest_scores)
display_scores(forest_rmse_scores) >Scores: [52997.8760788 49958.86106325 52721.44943885 55406.53413082 51444.10657159 55939.86344122 50784.56562956 50979.4567054 56421.26248695 53300.44123253] Mean: 52995.44167789745 Standard deviattion: 2167.186371913895
可以看到效果明显好了很多,随机森林的效果看起来很不错。不过,需要注意的是:在训练集上的均方误差仍远小于验证集的均方误差,说明这个模型在训练集上仍存在过拟合的问题。在这个问题中,我们可以采用以下方式缓解过拟合:
- 简化模型,减少模型复杂度
- 添加限制,例如正则项
- 获取更多的训练数据
当然,在进一步调优随机森林模型之前,我们仍应该尝试一下其他机器学习算法(例如SVM+不同的kernel、神经网络等等),看看这些模型的表现结果(并不需要调参)。我们的目标是:找出几个(2到5个)可能效果还不错的模型。再进行下一步调优。
建议大家每次都要保存训练好的模型,这样之后可以很快的找到想要的模型。在保存时,需要确保保存了超参数、训练时的参数、以及交叉验证的所有结果、(可能)以及预测后的结果。这样我们在之后进行不同模型对比时,节省不少时间。我们可以通过 Python 的 pickle 模块保存 sk-learn 的模型。或是使用更高效的sklearn.extenals.joblib,它的性能更好(会序列化大型NumPy 数组):
from sklearn.externals import joblib joblib.dump(forest_reg, "my_model.pkl") #load model if necessary my_model_loaded = joblib.load("my_model.pkl")
至此,我们已介绍了机器学习模型的训练,下面我们会继续介绍模型的调优。