zoukankan      html  css  js  c++  java
  • 机器学习项目流程(五)模型调优

    模型微调

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

    网格搜索

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

    不过在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、天池等竞赛平台练练手。

  • 相关阅读:
    解决UITableView中Cell重用机制导致内容出错的方法总结
    Hdu 1052 Tian Ji -- The Horse Racing
    Hdu 1009 FatMouse' Trade
    hdu 2037 今年暑假不AC
    hdu 1559 最大子矩阵
    hdu 1004 Let the Balloon Rise
    Hdu 1214 圆桌会议
    Hdu 1081 To The Max
    Hdu 2845 Beans
    Hdu 2955 Robberies 0/1背包
  • 原文地址:https://www.cnblogs.com/zackstang/p/12313789.html
Copyright © 2011-2022 走看看