[机器学习+sklearn库包]随机森林模型训练(分类实战)

1,563 阅读9分钟

image.png

1/前言

集成学习 ensemble learning
集成学习,包括bagging 和boosting

bagging集成学习思想, 各个决策树之间互相独立
boosting集成学习思想,各个决策树之间有依赖。

随机森林可以应用在分类和回归问题上。
到底是解决分类问题还是回归问题,这取决于随机森林中的每棵cart树是分类树还是回归树。

<1>如果cart树是分类树,那么采用的划分原则就是gini指数。

1)什么是gini指数?
  从样本集合中随机选择2条样本数据,计算它们不属于同一类别的概率。
  此概率越小,说明这2个样本属于同一类别的概率就越大,则划分的数据集合越纯,划分得越好。
  gini指数越小越好。
  
2)cart分类树对于连续型特征和离散型特征处理的改进?
  对于连续型特征的处理问题,其思想和C4.5是相同的,都是将连续的特征离散化。
  唯一的区别在于在选择划分点时的度量方式不同,C4.5使用的是信息增益比,则CART分类树使用的是基尼系数。
  具体的思路如下,比如某个连续特征A有m个值value,从小到大排列为a1,a2,…,am,
  则CART算法取相邻两样本值的中位数,一共取得m-1个划分点,
  对于这m-1个点,分别计算以该点作为二元分类点时的基尼系数。
  选择基尼系数最小的点作为该连续特征的二元离散分类点。
  比如取到的基尼系数最小的点为at,则小于at的值划分到一个子节点,大于at的值划分到另一个子节点。
  这样我们就做到了连续特征的离散化。
  要注意的是,与离散特征不同的是,如果当前节点为连续型特征,则该特征后面还可以参与子节点的产生选择过程(也就是说该连续型特征可能会被再次用到)
  
  对于CART分类树离散值的处理问题,采用的思路是不停的二分离散特征。

<2>如果cart树是回归树,采用的原则是最小均方差mse(mean square error)。

即对于任意特征A,对应其任意划分点s两边划分成的数据集D1和D2,
求出使D1和D2各自集合的均方差最小,同时D1和D2的均方差之和最小所对应的特征和特征值划分点。
cart树的回归预测是根据叶子结点的均值,因此随机森林的预测是所有树的预测值的平均值。

2/随机森林分类实战

  from sklearn.ensemble import RandomForestClassifier  # 集成学习,随机森林,分类
  
  from sklearn.model_selection import GridSearchCV     # 网格搜索+k折交叉验证
  from sklearn.model_selection import cross_val_score  # 交叉验证,k折交叉验证,不能选择参数
  from sklearn.model_selection import train_test_split # 训练集和测试集划分
  
  # 数据集
  from sklearn.datasets import load_breast_cancer   
  from sklearn.datasets import load_iris  
  
  from sklearn.preprocessing import StandardScaler  # 数据标准化
  from sklearn.preprocessing import MinMaxScaler    # 数据归一化,把数据映射到【0,1】之间

  from sklearn.metrics import make_scorer, fbeta_score # 自定义模型的评估函数

  # pickle 是 Python 标准库中的一个模块,
  # 用于序列化和反序列化 Python 对象。可以将训练好的随机森林模型保存为一个二进制文件。
  import pickle 
 
  import pandas as pd
  import numpy as np

  # 假如,我们有一个dataframe对象
  
  # 特征列
  x = final_data_df.drop(['ride_id', "scene_name",
                          'driver_user_id', 'passenger_user_id', 
                        
                          'driver_reg_time','passenger_reg_time',
                          
                          'create_time', 'passenger_plan_start_time',
                          'reply_time','cancel_time',
                        
                          'driver_setout_time', 'driver_arrival_startpoi_time',
                        
                          'target'], axis=1)  

  x = x.astype(int)
  x = x.values

  # 标签列
  y = final_data_df['target']
  y = y.astype(int)
  y = y.values

  random_forest = RandomForestClassifier(  random_state=90,
                                         class_weight="balanced"
                                        )

  # 模型选择(及,超参数组合的选择)
  # 不同的超参数的组合,对应不同的模型
  param_grid_dict = {'n_estimators':np.arange(1, 100, 10),
                       'max_depth': np.arange(1, 20, 2),
                       'min_samples_split': np.arange(2, 22, 2),
                       'criterion':['gini', 'entropy']
                 }


  # 网格搜索+交叉验证
  model = GridSearchCV(estimator=random_forest, 
                         param_grid= param_grid_dict,
                         scoring="precision",  # 准确率,精确率,召回率,roc_auc, f1, f2, f0.5
                         cv=10,
                         n_jobs=-1,
                         verbose=2)

  # 模型训练
  model.fit(x,y)
    
  # 最好的超参数组合,最好的得分,最好的模型
  print(model.best_params_)
  print(model.best_score_)
  print(model.best_estimator_) 

  # 最佳模型存储
  with open('random_forest_model.pkl', 'wb') as f:
       pickle.dump(model.best_estimator_, f)
  
  # 定义f2评估函数
  f2_scorer = make_scorer(fbeta_score, beta=2)  
  
  # 自定义回调函数,打印每一组参数的结果 
  # 入参是训练好的模型
  def print_results(  grid_search  ):  
    print("Best: %f using %s" % (grid_search.best_score_, grid_search.best_params_))  
    
    # 在【训练集】,代表的泛化能力和稳定性,means越到越好,std越小越好,越稳定
    train_means = grid_search.cv_results_['mean_train_score'] # 平均得分
    train_stds = grid_search.cv_results_['std_train_score']   # 标准差,稳定性
    
    # 在【测试集】
    test_means = grid_search.cv_results_['mean_test_score']   # 平均得分
    test_stds = grid_search.cv_results_['std_test_score']     # 标准差,稳定性

    params = grid_search.cv_results_['params']  

    for a,b,c,d,param in zip(train_means, train_stds, test_means, test_stds, params): 
        print("%f (%f)   %f (%f), with: %r" % (a, b, c,d, param))  

 # 打印每一次的结果(每一组超参数的组合)
 print_results(model)
            

random_state参数

随机森林分类器中的 random_state 参数是一个随机种子,它的作用是控制随机状态,进而决定模型构建的随机性。

当 random_state 取某一个固定值时,使用相同的训练集构建随机森林得到的结果将会是一模一样的,对测试集的预测结果也会相同。
如果不设置 random_state 参数,函数会自动选择一种随机模式,那么每次运行代码得到的结果可能会有所不同。
固定 random_state 后,每次构建的模型、生成的数据集以及每次的拆分结果都是相同的,这有助于结果的重现和比较。

RandomForestClassifier()分类模型中的所有的参数(超参数)

n_estimators: Any = 100

criterion:Any = "gini"

max_depth: Any = None,

min_samples_split: Any = 2

min_samples_leaf: Any =1

min_weight_fraction_leaf: Any = 0

max_features: Any ="sqrt"

max_leaf_nodes: Any = None,

min_impurity_decrease: Any = 0.0

bootstrap:Any = True,

oob_score: Any = False

random_state: Any = None

verbose: Any =0

warm_start:Any = False

class_weight: Any = None

①n_estimators: Any = 100,森林中树的数量,这个值越大越稳定,默认为100。

②criterion:Any = "gini",默认是基尼系数,gini指的是表示样本被分错的概率,gini参数越小,表示集合越集中,这里还可以是entropy,也就是信息熵,信息熵表示与相关性程度,熵越高,偏离的就越多,也就是离散程度大,相关性差,相反,熵越低,也就与越其接近,相关性搞,我们这里选用的是基尼系数。

③max_depth: Any = None,决定了最大深度,指的是树的层数,默认值是None,所以我们看最后的代码将max_depth的值修改了以后,所生成的图像的层数是不一样的。

④min_samples_split: Any = 2,内部节点再划分所需最小样本数,可选参数,默认是2,这个值限制了子树继续划分的条件,他表示的是最小的样本数,如果样本数小于min_samples_split,则会停止划分;如果这个参数我们输入的是一个浮点数的话,采取向上取整的原则。理论上这个值一般不用变动,只有当样本特别大的时候,建议向上调整这个值。(感觉和max_depth差不多,都是用来划分树的长度啥的)。

⑤min_samples_leaf: Any =1,这个参数与上面的参数很相似,上面的那个参数是针对于节点而言的,而这个是针对于节点后的子节点,停止划分的规则应该是一样的。

⑥min_weight_fraction_leaf: Any = 0,叶子节点最小的样本权重和,参数默认是0。如果小于这个值,则会和兄弟节点一起被剪枝。因此,一般当有很大偏差时,会引入这个参数,减小偏差。(防止有大的偏差用的)。

⑦max_features: Any ="sqrt",子集特征的个数,这个值越小,森林里的树就越不一样,默认是用sqrt来取值,auto,log2均可取值,sqrt和auto是取其开方数,而log2是取对数。

⑧max_leaf_nodes: Any = None,最大叶子节点数,参数默认是None。通过限制最大叶子节点数,可以防止过拟合。如果加了限制,算法会建立在最大叶子节点数内最优的决策树。特征少,不用管,特征多,用这个,通过交叉验证得到(我也不知道交叉验证是啥,可能是一种高深的计算吧)。

⑨min_impurity_decrease: Any = 0.0,节点划分最小不纯度,参数默认是1e-7。这个值限制了决策树的增长,如果某节点的不纯度(基尼系数,信息熵)小于这个值,则该节点不再生成子节点,即为叶子节点。(应该可以理解为criterion的下限)。

⑩bootstrap:Any = True,如果bootstrap=True,则使用替换方法绘制样本,也就是替换着抽取样本,而如果bookstrap=False,则不替换抽取样本,这个默认为True(我认为非必要就不要改这个值,这就是一种抽取样本的方式)。

⑪oob_score: Any = False,判断是否使用误差,这个值默认是False,(可能当误差比较大的时候,这个值就要编程True,一般应该是用不到的)。

⑫random_state: Any = None,默认值是None,若是使用None则是使用随机数生成器,但一般是建议设置一个整数进去(没有设置随机数,则随机出来的数随时间而变化,而设置了随机数,随机出来的数不随时间的变化而变化)。

⑬verbose: Any =0,用于判断是否生成日志信息,默认为0,也就是不生成日志信息。

⑭warm_start:Any = False,这个参数是判断是否基于上一次的结果进行运算,默认为False(当想不断提高精度的时候应该会用到这个值)。

⑮class_weight: Any = None,类别权重,参数默认是None,也可以字典、字典列表、balanced。主要是为了防止训练集某些类别的样本过多,导致训练的决策树过于偏向这些类别。类别的权重可以通过{class_label:weight}这样的格式给出,这里可以自己指定各个样本的权重,或者用balanced,使用balanced算法会自己计算权重,样本量少的类别所对应的样本权重会高。当然,如果样本类别分布没有明显的偏倚,直接不用管这个参数。(样本过多才用,过少直接不管)。