机器学习项目流程(四)选择并训练模型

选择并训练模型

至此,我们已明确了问题,并对数据进行了预处理。现在我们选择并训练一个机器学习模型。

 

在训练集上训练模型

这个过程相对来说较为简单,我们首先训练一个线性回归模型:

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

从均方误差的大小来看,这并不是一个很好的结果,说明模型存在欠拟合。在遇到欠拟合时,说明:

  1. 这些特征没有提供足够的信息给模型,导致无法学习到准确的预测模型
  2. 模型本身不适用这个数据集

一般我们可以采取以下方式解决欠拟合问题:

  1. 选择一个更强大更合适的模型
  2. 给训练集构造更好的属性特征
  3. 减少模型的限制(例如加了正则化导致欠拟合)

在这个场景下,模型没有使用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")

至此,我们已介绍了机器学习模型的训练,下面我们会继续介绍模型的调优。

 

posted @ 2020-02-15 13:41  ZacksTang  阅读(1313)  评论(0编辑  收藏  举报