机器学习项目流程(五)模型调优

模型微调

假设我们现在已经有了几个效果还不错的模型,接下来我们要对它们进行调优。下面我们介绍几种调优的方法。

网格搜索

第一个方法是通过手动调整超参数,直到发现一组使模型表现良好的超参数。这个是一个很耗时的工作,手动调整的话,可能没有这么多时间探索这些超参数组合。

不过在sk-learn 中提供了GridSearchCV,它可以为我们自动进行超参数组合。我们需要做的是传入以下信息即可:

  • 需要测试的超参数
  • 超参数对应的值

然后它会自动使用交叉验证,来评估在这些超参数的组合下,模型的优劣。下面是一个对随机森林使用GridSearchCV的例子:

from sklearn.model_selection import GridSearchCV

param_grid = [
    {'n_estimators':[3, 10, 30], 'max_features':[2, 4, 6, 8]},
    {'bootstrap':[False], 'n_estimators':[3, 10], 'max_features':[2, 3, 4]},
]

forest_reg = RandomForestRegressor()
grid_search = GridSearchCV(forest_reg, param_grid, cv=5,

                           scoring='neg_mean_squared_error',

                           return_train_score=True)

grid_search.fit(housing_prepared, housing_labels)

这里n_estimators 、max_features 、以及bootstrap都是随机森林算法里的超参数,分别对应的是:

  • n_estimators:随机森林里多少棵决策树(默认为10)
  • max_features:在寻找最优化分时,需要考虑的特征数(默认为auto)
  • bootstrap:在构造树时是否使用bootstrap样本。如果为False,则构造每个树时都会使用整个数据集(默认为True)

如果最开始完全不知道一个超参数应该取什么值,一个简单的办法是:尝试连续的10 的幂,例如10,100,1000…(或者如果需要更微调的模型的话,可以试试稍小的值,如上方样例代码中的n_estimators超参数)。

代码中的param_grid 告诉了sk-learn,首先要评估3×4=12 组 n_estimators 与 max_features 超参数的组合(它们对应的值已经在第一个字典中给出)。然后尝试所有2×3=6 的超参数组合(在第二个字典中给出),但是这次的bootstrap 超参数被设置为False。

所以最终网格搜索会探索12+6=18种提供的超参数组合,并且训练每个模型5次(因为我们使用了5-折交叉验证)。换句话说,接下来会产生18×5=90 轮的训练!这可能会消耗很长的时间。但是在训练完成后,我们可以得到一组最好的超参数组合,例如:

grid_search.best_params_
>{'max_features': 8, 'n_estimators': 30}

看到这个结果,我们需要注意的是:8和30 是我们提供的两个超参数中的最大值。所以我们应尝试提供更高的值,可能模型的表现会更好。

我们也可以根据训练结果直接获取最优的estimator:

grid_search.best_estimator_
>RandomForestRegressor(bootstrap
=True, criterion='mse', max_depth=None, max_features=8, max_leaf_nodes=None, min_impurity_decrease=0.0, min_impurity_split=None, min_samples_leaf=1, min_samples_split=2, min_weight_fraction_leaf=0.0, n_estimators=30, n_jobs=None, oob_score=False, random_state=None, verbose=0, warm_start=False)

这里提示一下大家,如果GridSearchCV是以refit=True(默认为True)的方式初始化,则当它使用cross-validation发现了最好的estimator后,它会在整个训练集上再训练一次。这是一个非常好的功能,因为一般在输入更多训练数据后,更有可能提高模型的表现。

最后是获取评估的分数:

cvres = grid_search.cv_results_
for mean_score, params in zip(cvres['mean_test_score'], cvres['params']):   print(np.sqrt(-mean_score), params)
>64205.20202839318 {'max_features': 2, 'n_estimators': 3} 55675.09098994038 {'max_features': 2, 'n_estimators': 10} 52933.01529490763 {'max_features': 2, 'n_estimators': 30} … 49920.566152671745 {'max_features': 8, 'n_estimators': 30}

在这个例子中,在max_features=8,n_estimators=30 时,获得了最优模型,此时RMSE值为49920,相对于我们调优前的结果,有了一点提升。

当然,我们也可以将一些数据准备的步骤视为超参数,进而做出调整。例如,可以使用grid search自动评估是否要新添加一个参数(例如,使用add_bedrooms_per_room的新属性)。类似,也可以用于评估是否要处理一些缺失值、异常值等。

随机搜索

在处理较少的超参数组合时,grid search 的方法比较适用。但是如果超参数的搜索空间非常大时,我们会更倾向于使用RandomizedSearchCV。这个类的使用方法与GridSearchCV类似,不同的是:它会在每轮训练时,随机为每个超参数选择一个值,产生随机的组合。这个方法有以下好处:

  • 假设我们让随机搜索执行1000轮,则这个方法会为每个超参数探索1000个不同的值
  • 可以通过设置轮数,直接控制使用的计算量,用于控制计算成本

集成方法

另一个调优模型的方法是:尝试结合多个表现最好的模型。这种组合(或者集成)一般都会比单个模型的表现更好(正如随机森林,由决策树构成,比决策树的表现更好)。特别是各个单个模型均存在一些不同的异常时,集成方法尤为合适。

 

分析最佳模型

在调整完模型后,我们可以继续尝试分析训练后的模型。通过分析训练后表现良好的模型,经常可以洞察到问题更深层的关系。例如,RandomForestRegressor可以显示出:在作出一个正确的预测时,每个属性的相对重要程度:

feature_importances = grid_search.best_estimator_.feature_importances_

feature_importances >array([
6.91594346e-02, 6.38718614e-02, 4.35677133e-02, 1.54026809e-02, 1.48535430e-02, 1.46107328e-02, 1.40532945e-02, 3.66338153e-01, 5.82647833e-02, 1.12715044e-01, 5.65692605e-02, 3.29670235e-03, 1.61525425e-01, 4.72821187e-05, 2.36051938e-03, 3.36357014e-03])

我们打印一下这些相关性对应的属性名:

extra_attribs = ['room_per_hold', 'pop_per_hhold', 'bedrooms_per_room']
cat_encoder = full_pipeline.named_transformers_['cat']
cat_one_hot_attribs = list(cat_encoder.categories_[0])
attributes = num_attributes + extra_attribs + cat_one_hot_attribs
sorted(zip(feature_importances, attributes), reverse=True)
>[(0.36633815257549174, 'median_income'),
  (0.16152542478456855, 'INLAND'),
  (0.1127150444353515, 'pop_per_hhold'),
  (0.0691594346242182, 'longitude'),
  (0.06387186143215258, 'latitude'),
  (0.0582647832914909, 'room_per_hold'),
  (0.05656926046061604, 'bedrooms_per_room'),
  (0.04356771328321281, 'housing_median_age'),
  (0.015402680866247265, 'total_rooms'),
  (0.01485354302500722, 'total_bedrooms'),
  (0.01461073275219702, 'population'),
  (0.014053294473556873, 'households'),
  (0.003363570144929577, 'NEAR OCEAN'),
  (0.003296702353221601, '<1H OCEAN'),
  (0.0023605193789983515, 'NEAR BAY'),
  (4.7282118739907256e-05, 'ISLAND')]

通过这个信息,我们可以看到有些特征对于预测结果并不重要(例如在ocean_proximity 这个离散属性中,基本都不重要),所以可以尝试直接丢弃这个属性(或是其中的一个,例如ISLAND)。

我们也需要查看训练后的这个系统中的特定报错,并理解为什么会报这个错,然后尝试修复这个问题(例如添加额外的特征、清除异常值等)。

在测试集上评估

在模型微调完成后,我们最终得到了一个表现良好的模型(或是说系统)。最后,在测试集上评估最终模型。这个过程也比较简单,仅需要将测试集中的数据通过模型进行预测,然后对比预测值与测试集中的label值即可。首先执行full_pipeline 转换数据(调用的是transform() 方法,而不是fit_transform(),fit会直接开始训练测试集),然后直接评估即可:

final_model = grid_search.best_estimator_

X_test = strat_test_set.drop("median_house_value", axis=1)
y_test = strat_test_set['median_house_value'].copy()

X_test_prepared = full_pipeline.transform(X_test)

final_predictions = final_model.predict(X_test_prepared)
final_mse = mean_squared_error(y_test, final_predictions)
final_rmse = np.sqrt(final_mse)
final_rmse
>48002.10224600167

当然,这个均方误差不足以让我们判断模型的精准度。为了衡量模型的精准度,我们可以使用 scipy.stats.t.interval() 为泛化误差计算一个 95% 置信区间值:

from scipy import stats

confidence = 0.95
squared_errors = (final_predictions - y_test) ** 2
np.sqrt(stats.t.interval(confidence, len(squared_errors) - 1,
                         loc=squared_errors.mean(),
                         scale=stats.sem(squared_errors)))

>array([
45977.59459228, 49944.61367935])

如果之前做了较多的超参数调整,则在模型在测试集上的表现会稍差于验证集上的表现(因为模型是基于验证集进行的调优,所以可能并不会在位置数据集上仍表现良好)。在这个例子中,没有遇到这个问题,但是当遇到这个问题时,大家最好是抵制住不断调整超参数的诱惑。因为我们的最终目标是让模型在测试集上表现良好。

现在我们模型已训练好,在系统上线之前,我们可能需要展示这个解决方案、将所有事情记录在案、做一些可视化的图、以及一些结论(例如,与预测房价最相关的属性是 median_income)

上线、监控以及维护

在系统准备上线到生产环境前,我们还需要将生产环境中的输入数据集成到这个系统中,并编写测试。同时我们也需要编写监控脚本,用于定期监控系统的性能,并在发生异常时发送警报。在监控性能时,不仅仅是要监控宕机,还需要监控系统的预测性能(精准度)。因为随着系统的不断运行,输入的数据可能会稍有变化,继而影响到系统的预测性能。除非我们定期使用新数据对模型进行训练。

我们还需要确保系统输入数据的质量,有时候输入数据的质量会大大影响模型的精准度。特别是在在线学习(online learning)系统中,监控输入数据的质量尤为重要。

最后,为了模型的稳定性,一般建议定期使用新数据训练模型。如果你的系统是一个在线学习(online learning)系统,则还需要确保定期保存它的状态(state)的快照副本,在必要时可以roll back 到一个前一个正常状态(state)。

至此,机器学习项目流程已结束,之后可以尝试kaggle、天池等竞赛平台练练手。

原文地址:https://www.cnblogs.com/zackstang/p/12313789.html