学习了B站视频《随机森林及应用》,记录一下学习笔记啦,原视频链接:Python机器学习算法实践Ⅲ-随机森林及应用。
一、随机森林属于集成学习,所以首先了解集成学习。在集成学习中,主要分为Bagging算法和Boosting算法。
Bagging(套袋法):从原始样本集中使用Bootstraping方法(一种有放回的抽样方法)随机抽取n个训练样本,共进行k轮抽取,得到k个训练集,训练k个模型。对于分类问题,由投票表决产生分类结果(所有模型的重要性相同)。
Boosting(提升法):对训练集中的每个样本建立权值,表示对样本的关注度。当某个样本被误分类的概率很高时,需要加大该样本的权值。进行迭代的过程中,每一步迭代都是一个弱分类器,用某种策略将其组合作为最终模型。(如AdaBoost给每个弱分类器一个权值,将其线性组合作为最终分类器。误差越小的弱分类器,权值越大。)
Bagging和Boosting的主要区别:
Bagging | Boosting | |
---|---|---|
样本选择 | Bootstrap随机有放回抽样。 | 每一轮训练集不变,改变的是每一个样本的权重。 |
样本权重 | 每个样本权重相等。 | 根据错误率调整样本权重,错误率越大的样本权重越大。 |
预测函数 | 所有预测函数的权重相等。 | 误差越小的预测函数权重越大。 |
并行计算 | 各个预测函数可以并行生成。 | 各个预测函数必须按顺序迭代生成。 |
Bagging+决策树=随机森林,随机森林属于集成学习中的Bagging算法,不同的数据集可以生成不同的决策树,多棵树解决了决策树泛化能力弱的缺点。
同时,随机森林在bagging的基础上更进一步:
1、样本的随机:从样本集中用Bootstrap随机选取n个样本。
2、特征的随机:从所有属性中随机选取K个属性(K小于总属性个数),选择最佳分割属性作为节点建立CART决策树(也可以是其他类型分类器,如SVM、Logistics)。
3、重复以上两步m次,即建立了m棵CART决策树。
4、m个CART形成随机森林,通过投票表决决定数据属于哪一类(投票机制有一票否决制、少数服从多数、加权多数)。
生成树时没有用到的样本点所对应的类别可由生成树估计,与其真实类别比较即可得到袋外预测误差,无法获得验证集时,这是随机森林的一大优势。
二、以Kaggle上的比赛泰坦尼克号数据练习实践,使用随机森林对乘客遇难或者幸存进行预测分类。原始数据集各特征含义如下:
在正式应用算法进行预测分类前,首先需要初步了解数据,分析各特征之间的关系以及与预测目标特征的关系,方便后续数据缺失和异常值的处理,以及模型特征选取。本文只是初步实践随机森林,就不具体详述啦,参考内容:kaggle(一):随机森林与泰坦尼克,文章里有具体的数据分析及建模的整个过程叙述,总结块也有很多相关文章可以进一步了解。
查看数据概况如下,了解各特征类型,同时注意到部分特征存在缺失值,在输入模型前首先需要处理数据。
数据处理相关代码如下,将PassengerId、Name、Ticket、Cabin等特征删除,其中Name、Ticket、Cabin等特征在优化模型效果时,可以进一步处理作为特征输入。对于缺失的Age数据,根据相关性分析发现其与SibSp、Parch、Pclass这些特征相关性高,于是利用这三个特征将数据分成不同集合,用缺失数据所在集合的Age平均值填充,结果验证填充后Age数据分布与填充前基本一致,说明方法可行。Embarked特征缺失数据只有2个,直接用出现频率最高的值进行填充。Fare特征存在偏态分布情况,利用Log转换调整分布。离散型特征Embarked、Sex将特征值类别映射为数字表示。
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import learning_curve
from sklearn.model_selection import GridSearchCVtrain = pd.read_csv('train.csv') #读取训练数据集
#print(train.info()) #输出数据信息train.drop(['PassengerId', 'Name','Ticket', 'Cabin'],axis=1,inplace=True) #删除部分特征,inplace=True表示直接对原dataFrame进行操作#补全缺失的年龄数据,根据Age和SibSp、Parch、Pclass相关性高,利用这三个特征将数据分为不同的集合,用缺失数据所在集合的平均值进行填充
index = list(train[train['Age'].isnull()].index) #获得缺失年龄的样本索引
Age_mean = np.mean(train[train['Age'].notnull()]['Age']) #计算所有年龄数据的平均值
copy_data = train.copy() #复制数据集方便后边集合参照,并且可以直接在原数据上填充
for i in index:filling_age = np.mean(copy_data[(copy_data['Pclass'] == copy_data.iloc[i]['Pclass'])& (copy_data['SibSp'] == copy_data.iloc[i]['SibSp'])& (copy_data['Parch'] == copy_data.iloc[i]['Parch'])]['Age']) #与缺失样本相同SibSp、Parch、Pclass的集合的年龄平均值进行填充if not np.isnan(filling_age): #同一集合所有样本年龄缺失时,集合年龄均值为空,填充所有年龄数据的平均值train['Age'].iloc[i] = filling_ageelse:train['Age'].iloc[i] = Age_mean#使用频率最高的值来填充Embarked特征缺失值
most_fre = train[train['Embarked'].notnull()]['Embarked'].value_counts().index[0] #获得频率最高的值
train['Embarked'].fillna(most_fre, inplace=True) #填充缺失值
train['Embarked'] = train['Embarked'].map({'S': 0,'C': 1,'Q': 2}).astype(int) #将三个特征值映射为三个数字表示#Log转换调整Fare的分布
train['Fare'] = train['Fare'].map(lambda i: np.log(i) if i > 0 else 0)#对性别进行0、1编码
train['Sex'].replace('male', 0, inplace=True)
train['Sex'].replace('female', 1, inplace=True)
#print(train.info())
训练评估模型的相关代码如下:
#绘制学习曲线分析算法性能
def plot_learning_curve(estimator,title,X,y):plt.figure() #初始化图像plt.title(title) #设置标题plt.xlabel('Training examples') #设置坐标轴名称plt.ylabel('Score')train_sizes, train_scores, test_scores = learning_curve(estimator, X, y, cv=10, train_sizes=np.linspace(.1, 1.0, 5)) #调用learning_curve函数绘制学习曲线,cv使用10折交叉验证train_scores_mean = np.mean(train_scores, axis=1) #训练样本分数平均值train_scores_std = np.std(train_scores, axis=1) #训练样本分数标准差test_scores_mean = np.mean(test_scores, axis=1) #测试样本分数平均值test_scores_std = np.std(test_scores, axis=1) #测试样本分数标准差plt.grid() #设置网格plt.fill_between(train_sizes,train_scores_mean - train_scores_std,train_scores_mean + train_scores_std,alpha=0.1,color='g') #填充距离均值上下一个标准差的区域,alpha设置透明度plt.fill_between(train_sizes,test_scores_mean - test_scores_std,test_scores_mean + test_scores_std,alpha=0.1,color='r')plt.plot(train_sizes,train_scores_mean,'o-',color='g',label='training score') #绘制学习曲线plt.plot(train_sizes,test_scores_mean,'o-',color='r',label='testing score')plt.legend(loc='best') #设置图例return plty = train['Survived'] #分离数据特征集和结果集
X = train.drop(['Survived'], axis=1).values
classifier=RandomForestClassifier(oob_score=True) #初始化随机森林模型
classifier.fit(X,y) #对特征集进行分类预测
print(classifier.oob_score_) #袋外估计得分
g = plot_learning_curve(classifier, 'RFC', X, y) #绘制学习曲线
plt.show()
运行发现模型的袋外估计得分为0.807,效果表现良好。其学习曲线如下,训练误差接近0,相比之下测试误差较大,模型存在过拟合问题,需要调整参数进行改善。
利用网格搜索调参的相关代码如下,调整了n_estimators、max_depth、min_samples_leaf这三个影响较大的参数。
#调整n_estimators
param_test={'n_estimators':range(10,101,10)} #设置参数范围
gsearch = GridSearchCV(estimator=RandomForestClassifier(), param_grid=param_test, cv=5, scoring='accuracy') #利用网格搜索调参,cv设置交叉验证参数,用准确率作为评价标准
gsearch.fit(X,y)
print(gsearch.best_params_,gsearch.best_score_) #输出最佳结果的参数,以及参数对应的分数#调整max_depth
param_test={'max_depth':range(2,12,2)} #设置参数范围
gsearch = GridSearchCV(estimator=RandomForestClassifier(n_estimators=50), param_grid=param_test, cv=5, scoring='accuracy') #利用网格搜索调参,cv设置交叉验证参数,用准确率作为评价标准
gsearch.fit(X,y)
print(gsearch.best_params_,gsearch.best_score_) #输出最佳结果的参数,以及参数对应的分数#调整min_samples_leaf
param_test={'min_samples_leaf':range(2,8,1)} #设置参数范围
gsearch = GridSearchCV(estimator=RandomForestClassifier(n_estimators=50,max_depth=8), param_grid=param_test, cv=5, scoring='accuracy') #利用网格搜索调参,cv设置交叉验证参数,用准确率作为评价标准
gsearch.fit(X,y)
print(gsearch.best_params_,gsearch.best_score_) #输出最佳结果的参数,以及参数对应的分数
调参过程中各参数最佳结果如下:
{‘n_estimators’: 50} 0.8137216747222397
{‘max_depth’: 8} 0.8305567760969179
{‘min_samples_leaf’: 2} 0.8249576297784195
将模型参数设置为以上三个数值,重新训练模型,发现袋外估计得分为0.819,有一点点提升。此时学习曲线如下,可以看到过拟合得到一定程度缓解。
将调参后的模型对Kaggle上的test.csv进行预测,得分是0.7799,后来尝试处理了一开始删除的Name、Ticket、Cabin等特征,对特征进行选择再训练模型,经过多次尝试,Kaggle提交最高历史得分也就0.78229,还是需要努力。
(结语个人日记:前段时间给自己放了一周的假期,每天什么都不学,什么都不想,就只是从早到晚边看视频教程边钩织,新手成功钩出了一只小猫,自己看着倒是挺可爱的哈哈哈。用QQ的频率越来越低了,偶然间打开,算是告别了,过去感到孤独时的思想寄托的告别,小心翼翼隐藏自己但经常关注对方动态的告别,不知道是没有结果的双向关注还是只是自己一个人的独角戏的告别。四年的时间改变了很多,也改变了以后的轨迹,只希望自己可以更加勇敢。)