当前位置:   article > 正文

kaggle(一):随机森林与泰坦尼克_泰坦尼克随机森林

泰坦尼克随机森林

引言

随着2018年飞逝而过,2019悄然到来,我并没有什么太大的起伏去迎接新年,从上一篇博文截止到今天,好像是有接近一礼拜没有更文了,主要是最近正在参加一个建模比赛,导致原计划这两天要写的年终总结一推再推,另外还顺便报了个集训营,那么年终总结。。。So,不如来更新一篇kaggle小项目吧,趁现在还有时间,建模那边已经快到结尾了,年终总结等博客真正搭好的时候作为第一篇好了。虽然最近也并不是很想处理博客,wordpress看花了眼。。。

泰坦尼克号数据介绍与分析

数据介绍

作为当前机器学习最出名的几个入门kaggle项目,背景就无需多做介绍了。本次泰坦尼克号实验总结了实验楼以及相应的几个博客案例,主要侧重于对随机森林的描述,然后加上自己的一些理解,文底表明文章出处,数据出处如下:

https://www.kaggle.com/c/titanic/data

当下载好数据之后,因为数据集并不大,我便直接用excel打开然后观察数据特征以及背景了,数据标签含义如下:

特征label解释说明
PassengerId整型变量,标识乘客的ID,递增变量,对预测无帮助
Survived整型变量,标识该乘客是否幸存。0表示遇难,1表示幸存。将其转换为factor变量比较方便处理
Pclass整型变量,标识乘客的社会-经济状态,1代表Upper,2代表Middle,3代表Lower
Name字符型变量,除包含姓和名以外,还包含Mr. Mrs. Dr.这样的具有西方文化特点的信息
Sex字符型变量,标识乘客性别,适合转换为factor类型变量
Age整型变量,标识乘客年龄,有缺失值
SibSp整型变量,代表兄弟姐妹及配偶的个数。其中Sib代表Sibling也即兄弟姐妹,Sp代表Spouse也即配偶
Parch整型变量,代表父母或子女的个数。其中Par代表Parent也即父母,Ch代表Child也即子女
Ticket字符型变量,代表乘客的船票号
Fare数值型,代表乘客的船票价
Cabin字符型,代表乘客所在的舱位,有缺失值
Embarked字符型,代表乘客登船口岸,适合转换为factor型变量

从这里,我们大致了解了关于这份数据的存在形式,于是我们便可以进行python的使用与分析了,在此之前,导入基本我们需要使用的第三方库与数据:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline

train = pd.read_csv(r"C:\Users\xuzhenggen\Desktop\files\train.csv")
test = pd.read_csv(r"C:\Users\xuzhenggen\Desktop\files\test.csv")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

数据初步分析

当我们导入数据后,往往并不是直接做数据建模,还有太多的数据处理方面的问题等着我们,只有对数据的认识越深,那么后面的特征工程的构建也就越轻松。那么问题就来了,这份数据到底应该怎么处理呢?

我们首先可以看一下在python中数据的表现形式是否和之前在excel中的形式一致,出于运行单元速度以及效率来考虑,考虑用head,代码如下:

train.head()  # head参数默认是5个
test.head()
  • 1
  • 2
-PassengerIdSurvivedPclassNameSexAgeSibSpParchTicketFareCabinEmbarked
0103Braund, Mr. OwenHarrismale22.010A/5 211717.2500NaNS
-PassengerIdPclassPclassNameSexAgeSibSpParchTicketFareCabinEmbarked
089233Kelly, Mr. Jamesmale34.5003309117.8292NaNQ

由于篇幅考虑,显示一行。我们可以看到大体和我们想的一样,那么我们再来检查一下变量类型:

train.info()
  • 1

在这里插入图片描述

test.info()
  • 1

在这里插入图片描述
看到了数据的变量类型,和上述表格中预想的一致,那么我们便可以针对每个特征的数据结构给它们归一归类了,这也同样有益于我们加深理解数据,除了使后面的计算更方便,更能帮助我们选择合适的图形做可视化,我们可以进一步进行如下分析:

  1. 离散型特征: 离散型可以理解为一系列相似的样本,具有同样的数值或者名词,在特定范围有规律性的表示,在这个问题中,离散型的变量有:Survived,Sex 和 Embarked。基于序列的有:Pclass
  2. 连续型特征: 连续型可以说是和离散型相反,呈现的是表面上看起来没有什么规律,没有特定范围,随着样本变化而变化,在这个问题中,连续型的数值特征有:Age,Fare。离散型数值有:SibSp,Parch
  3. 混合型特征: 在样本中,既不是明确的英文单词,也不是单纯的数值或字母,而是将其混合起来,形成了一种特定的代号,这种往往我们都是需要注意用正则或者用一些我们习惯的方式去处理与修正它。在这个问题中,Ticket是混合了数值型以及字母数值型的数据类型,Cabin是字母数值型数据
  4. 缺失、异常型特征: 数据样本有缺失值和异常值,这是很常见的问题,这个问题也是后面需要详细说明的。在这个问题中,Ticket是混合了数值型以及字母数值型的数据类型,Cabin是字母数值型数据

对于连续型和离散型特征具体区别,可以看如下链接:

统计学:离散型和连续型随机变量的概率分布

然后我们就可以查看训练数据的具体分布情况:

#通过使用 percentile=[.1, .2, .3, .4, .5, .6, .7, .8, .9, .99] 来查看分布
train.describe(percentiles=[.1, .2, .3, .4, .5, .6, .7, .8, .9, .99])
  • 1
  • 2

在这里插入图片描述
我们可以从上表知道:

  1. 死亡率在60%~70%之间,因为Survice这一项从0变成了1
  2. 船上的父母以及子女数算比较少的,80%左右的人都算是单身或者说和女伴一起,这或许也是后来能让妇女和儿童先逃的一个因素,还是挺佩服他们的
  3. 船上贫富差距大,可以从Fare一项看出,虽然从Ticket一起看,会发现有团体票一说,但还是有差距的。假如Rose和Jack能获救,不知道可不可能打破世俗,当然,如果那样就不会有凄美爱情,男女主角的名字也会更换了。

大概可以很明确的就如上几点,可能有人还会说Age数量为啥与其它的少?这个问题就是我们如下需要分析的了:

缺失、异常值处理

我们可以统计一下缺失值到底有多少:

data.isnull().sum()  #查看 null 值,查看非空使用 notnull()
  • 1

在这里插入图片描述
总共有 891 个游客的数据,177 个 Age 缺失,687 个 Cabin 缺失,2 个 Embarked 缺失。在后面我们需要用不同的方法补充这些数据。

然后,我们查看特征类别分布是否平衡。类别平衡指分类样例不同类别的训练样例数目差别不大。当差别很大时,为类别不平衡。当类别不平衡的时候,例如正反比为 9:1,学习器将所有样本判别为正例的正确率都能达到 0.9。这时候,我们就需要使用 “再缩放”、“欠采样”、“过采样”、“阈值移动” 等方法。我们可以对Survice做图验证:

sns.countplot(x='Survived',data=train) #对不同值的 'Survived' 进行计数并绘图
  • 1

在这里插入图片描述
这个结果说明样本比例还算均衡,也同样应征了上表中说明的幸存者在30%~40%之间,而这组数据本身就少,差距变化不大,我们可以认为是属于类别平衡问题。

接下来,删除无用的特征 PassengerId, Name。

train.drop(['PassengerId','Name'],axis=1,inplace=True) #删除 train['PassengerId','Name'] 两列数据,axis=1 表示删除列,axis=0 表示删除行,inplace=True 原位删除
train.columns
"""
Index(['Survived', 'Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Ticket', 'Fare',
       'Cabin', 'Embarked'],
      dtype='object')
"""
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

名字在这里对我来讲并不是很想分析,看了一篇大佬对名字的分析,从分离出他们之间的姓氏,以及名字长度,去推测他们的社会地位,然后再看看是否与存活相关,虽然最后得出来的结果是有点相关性,但我感觉关联并不是很大,太复杂了,一般我是直接删除,删除不了,降维也会把它降掉。

g=sns.heatmap(train[['Survived','SibSp','Parch','Age','Fare','Pclass']].corr(),cmap='RdYlGn',annot=True) #corr() 计算相关系数,cmap 选择 color map,annot=True 显示相关系数
  • 1

在这里插入图片描述
这里我们可以做一个相关系数矩阵图,从理论上来讲,相关系数越大,那么相关系数就越强,这里Fare的颜色较深,相关性较大,其它次之。

如何通俗易懂地解释「协方差」与「相关系数」的概念?


缺失值处理:

缺失值有很多种处理方式,从直接剔除,到取平均值、中位数、众数,到线性回归,拉格朗日插值、三次样条插值、极大似然估计、KNN等等,这些都需要视具体情况具体分析,想起上午刚打完的一个建模比赛,通过spss用了KNN和上下四个数取平均,spss自从集成了python环境还是挺好用的,有时间我也可以写一些自己的理解。

上述列举了这么多的方法,那么它们都是什么情况下使用的呢?

  1. 缺失值过大,比如说已经超过了正常值的1/2,这种就不需要考虑怎么样填补了,留着这个特征反而是加大误差,可以选择剔除
  2. 缺失值小于1/2的,但出现了连续型缺失,也可以认为是一大段一大段的,这种如果在前面的话,可以不用去考虑,直接作为NaN构成新样本加入样本中,如果是在中间或者后面,根据缺失量,可以考虑用均值或者是线性回归、灰度预测等抢救一下
  3. 缺失值远小于1/2,并且是非连续的,这里就可以用一些复杂的插值,或者说用前后数的平均,众数都能填补,并且填补完可能会有一些意想不到的效果。

当然以上仅仅是一些我的理解,还是那句话,具体情况具体分析,另外本题没有采取什么缺失值的技巧,运用的是平均值方式去补充,我们首先观察年龄对存活率的变化情况:

Age0=train[(train['Survived']==0)&(train['Age'].notnull())]['Age'] #死亡乘客的 Age 数据
Age1=train[(train['Survived']==1)&(train['Age'].notnull())]['Age'] #生存乘客的 Age 数据
g=sns.kdeplot(Age0,legend=True,shade=True,color='r',label='NotSurvived') #死亡乘客年龄概率分布图, shade=True 设置阴影
g=sns.kdeplot(Age1,legend=True,shade=True,color='b',label='Survived') #生存乘客概率分布图
  • 1
  • 2
  • 3
  • 4

在这里插入图片描述

在前面,我们根据 heatmap构造的相关系数图中,Age 和 SibSp, Parch,Pclass 相关性高,我们再用箱型图直观感受下,以图形 Sex ~ Age,Pclass ~ Age 为例。

g=sns.factorplot(x='Sex',y='Age',data=train,kind='box') 
g=sns.factorplot(x='Pclass',y='Age',data=train,kind='box')
  • 1
  • 2

在这里插入图片描述
上面两图说明男性和女性的年龄分布(指箱型图中的五条线,从上到下依次是最大值、四分位数、中位数、四分位数、最小值)基本一致,而购买不同等级票的人的年龄分布是不同的。所以,我们根据票的等级将数据分为不同的集合,再用缺失数据所在集合的平均值来进行填充,并检查填充后 Age ~ Survived 是否受到影响。

index = list(train[train['Age'].isnull()].index) #Age 缺失样例的 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'])
    if not np.isnan(filling_age): # filling_age 非空为真
        train['Age'].iloc[i] = filling_age #填充 null 值
    else: # filling_age 空为真
        train['Age'].iloc[i] = Age_mean
g = sns.kdeplot(Age0, legend=True, shade=True, color='r', label='NotSurvived')
g = sns.kdeplot(Age1, legend=True, shade=True, color='b', label='Survived')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

在这里插入图片描述
结果是基本与原数据一致,那么初步来看,是可以这样做的。


然后我们可以来处理关于cabin这个缺失项,这个不同于age,cabin可以看做是离散的缺失值,我们可以分析一下这个样本:

train[train['Cabin'].notnull()]['Cabin'].head(10)
"""
1             C85
3            C123
6             E46
10             G6
11           C103
21            D56
23             A6
27    C23 C25 C27
31            B78
52            D33
Name: Cabin, dtype: object
"""
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

然后我们可以将缺失值取做新变量名,比如说“U”:

# fillna() 填充 null 值
train['Cabin'].fillna('U',inplace=True) 

# 使用 lambda 表达式定义匿名函数对 i 执行 list(i)[0]。map() 指对指定序列 data ['Cabin'] 进行映射,对每个元素执行 lambda 
train['Cabin']=train['Cabin'].map(lambda i: list(i)[0]) 

# kind='bar' 绘制条形图,ci=False 不绘制概率曲线,order 设置横坐标次序
g = sns.factorplot(x='Cabin',y='Survived',data=train,ci=False,kind='bar',order=['A','B','C','D','E','F','G','U']) 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

在这里插入图片描述
这样看上去就比较合理了。

另外关于Embarked特征主要有三个值,分别为S,Q,C,对应了三个登船港口。在训练集里,这个有2个缺失值,我们会使用频率最高的值来填充这个缺失值,填补为S,做法和上面的cabin类似。


异常值处理:

对于异常值的处理,方法也是多样的,可以设置一个初始阈值,超过那么就算做是异常,这也并没有什么问题,还有就是分布问题,同样会产生异常。在测绘里面,我记得有一种异常处理方式叫做sigma原则,即 3 σ 3\sigma 3σ原则, σ \sigma σ为:
σ = ∑ i = 1 n ( x i − x ˉ ) 2 n − 1 \sigma = \sqrt{\frac{\sum_{i=1}^{n}(x_{i}-\bar{x})^{2}}{n-1}} σ=n1i=1n(xixˉ)2
缺失值为:
∣ x i − x ˉ ∣ > 3 σ |x_{i}-\bar{x}|> 3\sigma xixˉ>3σ

而另一种就是关于数据分布问题了,比如说这里的Fare,我们可以看到它的分布情况:

g=sns.kdeplot(train[train['Survived']==0]['Fare'],shade='True',label='NotSurvived',color='r') # 死亡乘客 'Fare' 分布
g=sns.kdeplot(train[train['Survived']==1]['Fare'],shade='True',label='Survived',color='b') # 生存乘客 'Fare' 分布
  • 1
  • 2

在这里插入图片描述
可以看到Fare存在明显的集中情况,但并不是标准正态分布,可以看做是偏态分布,或者说叫长尾分布,这里可以举一个很形象的例子:世界上人口这么多,但真正能站在巅峰的也就那么百分之几,而大部分人都是集中在一个很狭小的范围内波动起伏。
(需要翻墙)
Skewed Distribution and Log Transformation

Fare 属于右偏态分布,Python 提供了计算数据偏态系数的函数 skew(), 计算值越大,数据偏态越明显。使用 Log Transformation 后,我们看到计算值从 4.79 降到 0.44。

train['Fare']=train['Fare'].map(lambda i:np.log(i) if i>0 else 0) # 匿名函数为对非零数据进行 Log Transformation,否则保持零值
g=sns.distplot(train['Fare'])
print('Skew Coefficient:%.2f' %(train['Fare'].skew())) # skew() 计算偏态系数
  • 1
  • 2
  • 3

在这里插入图片描述

对于性别,我们可以对其重新进行01编码,因为只有男性和女性:

train['Sex'].replace('male',0,inplace=True)#inplace=True 原位替换
train['Sex'].replace('female',1,inplace=True)
  • 1
  • 2

然后我们可以从上面的箱型图观察,其实还是有异常值的,可以考虑按照sigma原则排除,不过这种我只在spss里试过,这里还是按照箱型图的定义来,针对于 [‘Age’, ‘Parch’, ‘SibSp’, ‘Fare’] :

from collections import Counter

def outlier_detect(n, df, features):#定义函数 outlier_detect 探测离群点,输入变量 n, df, features,返回 outlier
    outlier_index = []
    for feature in features:
        Q1 = np.percentile(df[feature], 25)#计算上四分位数(1/4)
        Q3 = np.percentile(df[feature], 75)#计算下四分位数(3/4)
        IQR = Q3 - Q1
        outlier_span = 1.5 * IQR
        col = ((train[train[feature] > Q3 + outlier_span]) |
               (train[train[feature] < Q1 - outlier_span])).index
        outlier_index.extend(col)
        print('%s: %f (Q3+1.5*IQR) , %f (Q1-1.5*QIR) )' %
              (feature, Q3 + outlier_span, Q1 - outlier_span))
    outlier_index = Counter(outlier_index)#计数
    outlier = list(i for i, j in outlier_index.items() if j >= n)
    print('number of outliers: %d' % len(outlier))
    print(df[['Age', 'Parch', 'SibSp', 'Fare']].loc[outlier])
    return outlier

outlier = outlier_detect(3, train, ['Age', 'Parch', 'SibSp', 'Fare'])#调用函数 outlier_detect
"""
异常值总共有四个
Age: 59.500000 (Q3+1.5*IQR) , -0.500000 (Q1-1.5*QIR) )
Parch: 0.000000 (Q3+1.5*IQR) , 0.000000 (Q1-1.5*QIR) )
SibSp: 2.500000 (Q3+1.5*IQR) , -1.500000 (Q1-1.5*QIR) )
Fare: 1.994305 (Q3+1.5*IQR) , -0.033916 (Q1-1.5*QIR) )
number of outliers: 0
Empty DataFrame
Columns: [Age, Parch, SibSp, Fare]
Index: []
"""
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

然后删除上述四个值就行啦。

Ticket 特征值中的一串数字编号对我们没有意义,下面代码中,我们用正则表达式过滤掉这串数字,并使用 pandas的get_dummies 函数进行数值化(以 Ticket 特征值 作为新的特征,0,1 作为新的特征值),类似于onehot编码性质的方法。

OneHot编码知识点

Ticket=[]
import re
r=re.compile(r'\w*')#正则表达式,查找所有单词字符[a-z/A-Z/0-9]
for i in train['Ticket']:
    sp=i.split(' ')#拆分空格前后字符串,返回列表
    if len(sp)==1:
       Ticket.append('U')#对于只有一串数字的 Ticket,Ticket 增加字符 'U'
    else:
       t=r.findall(sp[0])#查找所有单词字符,忽略符号,返回列表
       Ticket.append(''.join(t))#将 t 中所有字符串合并
train['Ticket']=Ticket
train=pd.get_dummies(train,columns=['Ticket'],prefix='T')#get_dummies:如果DataFrame的某一列中含有k个不同的值,则可以派生出一个k列矩阵或DataFrame(其值全为1和0)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

这里一定需要注意的是,Ticket样本可以直接删除,也可以这样分隔,但一定不能不处理,否则后面的模型构建时会有错误,比如这个:

Python: Cannot Convert String to Float

上面是kaggle上的一个讨论,可能中间还会有一些其它小错误,但基本都和数据清洗处理不当有关,比如说上面的错误就是因为ticket样本依然包括空格,但一般用get_dummies基本没有。那么到此为止,数据清洗的工作就做完啦。

模型构建与评价

关于随机森林的构建,如果运用sklearn包的话,其实很简单,重点其实是需要搞清楚评估方法,比如说交叉验证:

交叉验证(简单交叉验证、k折交叉验证、留一法)

留出法和 k 折交叉验证法需要划分一部分样本作为测试集,就引入由于训练样本规模不同而产生的偏差。留一法改善了这一问题,但计算复杂度高。自助法也改善了这一个问题,但改变了数据集分布,同样会引入偏差,该方法适合数据集较小的情况。所以,留出法和 k 折交叉验证法是最常用的。这里选择 k 折交叉验证法进行模型评估。

Python sklearn.model_selection 提供了 Stratified k-fold。参考 Stratified k-fold

我推荐使用 sklearn cross_val_score。这个函数输入我们选择的算法、数据集 D,k 的值,输出训练精度(误差是错误率,精度是正确率)。对于分类问题,默认采用 stratified k-fold 方法。参考 sklearn cross_val_score

下面我们用 10 折交叉验证法(k=10)对两种常用的集成学习算法 AdaBoost 以及 Random Forest 进行评估。最后我们看到 Random Forest 比 Adaboost 效果更好。

from sklearn.ensemble import AdaBoostClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score

y = train['Survived']
X = train.drop(['Survived'], axis=1).values

classifiers = [AdaBoostClassifier(
    random_state=2), RandomForestClassifier(random_state=2)]
for clf in classifiers:
    score = cross_val_score(clf, X, y, cv=10, scoring='accuracy')#cv=10:10 折交叉验证法,scoring='accuracy':返回测试精度
    print([np.mean(score)])#显示测试精度平均值
"""
[0.7353336738168198]
[0.8047636477130858]
"""
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

过拟合是学习器性能过好,把样本的一些特性当做了数据的一般性质,从而导致训练误差低但泛化误差高。学习曲线是判断过拟合的一种方式,同时可以判断学习器的表现。学习曲线包括训练误差(或精度)随样例数目的变化曲线与测试误差(或精度)随样例数目的变化曲线。

下面将以训练样例数目为横坐标,训练精度和测试精度为纵坐标绘制学习曲线,并分析 Random Forest 算法的性能。

from sklearn.model_selection import learning_curve
import matplotlib.pyplot as plt

# 定义函数 plot_learning_curve 绘制学习曲线。train_sizes 初始化为 array([ 0.1  ,  0.325,  0.55 ,  0.775,  1\.   ]),cv 初始化为 10,以后调用函数时不再输入这两个变量

def plot_learning_curve(estimator, title, X, y, cv=10,
                        train_sizes=np.linspace(.1, 1.0, 5)):
    plt.figure()
    plt.title(title) # 设置图的 title
    plt.xlabel('Training examples') # 横坐标
    plt.ylabel('Score') # 纵坐标
    train_sizes, train_scores, test_scores = learning_curve(estimator, X, y, cv=cv,
                                                            train_sizes=train_sizes) 
    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') # 设置颜色
    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='traning score') # 绘制训练精度曲线
    plt.plot(train_sizes, test_scores_mean, 'o-', color='r',
             label='testing score') # 绘制测试精度曲线
    plt.legend(loc='best')
    return plt

g = plot_learning_curve(RandomForestClassifier(), 'RFC', X, y) # 调用函数 plot_learning_curve 绘制随机森林学习器学习曲线
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

在这里插入图片描述

Random Forest 的学习曲线我们得到了,训练误差始终接近 0,而测试误差始终偏高,说明存在过拟合的问题。这个问题的产生是因为 Random Forest 算法使用决策树作为基学习器,而决策树的一些特性将造成较严重的过拟合。所以我们接下来需要调整随机森林参数,改善这种问题。随机森林参数说明:

参数说明特点
n_estimators基学习器数目(默认值 10)基本趋势是值越大精度越高 ,直到达到一个上限
criterion选择算法 gini 或者 entropy (默认 gini)视具体情况定
max_features2.2.3 节中子集的大小,即 k 值(默认 sqrt(n_features))
max_depth决策树深度过小基学习器欠拟合,过大基学习器过拟合。粗调节
max_leaf_nodes最大叶节点数(默认无限制)粗调节
min_samples_split分裂时最小样本数,默认 2细调节, 越小模型越复杂
min_samples_leaf叶节点最小样本数,默认 2细调节,越小模型越复杂
bootstrap是否采用自助法进行样本抽样(默认使用)决定基学习器样本是否一致
out-of-bag estimate包外估计:是否选用包外样本(即bootstrap采样剩下的36.8%的样本)作为验证集,对训练结果进行验证默认不采用。
n_jobsinteger, optional (default=1) 并行使用的进程数默认1个,如果设置为-1,该值为总核数。
random_state随机状态默认由np.numpy生成
verboseint, optional (default=0)显输出的一些参数,默认不输出。

随机森林分类器调参

根据反馈来的结果,我们便可以根据如上的参数进行参数调节,解决遇到的过拟合问题。

def para_tune(para,X,y):
    clf = RandomForestClassifier(n_estimators=para)  # n_estimators 设置为parameter
    score = np.mean(cross_val_score(clf,X,y,scoring="accuracy"))
    return score

def accurate_curve(para_range,X,y,title):
    score = []
    for para in para_range:
        score.append(para_tune(para,X,y))
    plt.figure()
    plt.title(title)
    plt.xlabel("Paramters")
    plt.ylabel("Score")
    plt.grid()
    plt.plot(para_range,score,"o-")
    return plt
g = accurate_curve([2,5,80,100,150],X,y,"n_estimator tuning")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

在这里插入图片描述

我们利用交叉验证的方式得到最优参数 n_estimators=80,同理:

def para_tune(para, X, y):
    clf = RandomForestClassifier(n_estimators=300, max_depth=para)
    score = np.mean(cross_val_score(clf, X, y, scoring='accuracy'))
    return score

def accurate_curve(para_range, X, y, title):
    score = []
    for para in para_range:
        score.append(para_tune(para, X, y))
    plt.figure()
    plt.title(title)
    plt.xlabel('Paramters')
    plt.ylabel('Score')
    plt.grid()
    plt.plot(para_range, score, 'o-')
    return plt

g = accurate_curve([2, 10, 20, 30, 40], X, y, 'max_depth tuning')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

在这里插入图片描述
得到最优max_depth=10

from sklearn.model_selection import GridSearchCV

def plot_learning_curve(estimator, title, X, y, cv=10,
                        train_sizes=np.linspace(.1, 1.0, 5)):
    plt.figure()
    plt.title(title) # 设置图的 title
    plt.xlabel('Training examples') # 横坐标
    plt.ylabel('Score') # 纵坐标
    train_sizes, train_scores, test_scores = learning_curve(estimator, X, y, cv=cv,
                                                            train_sizes=train_sizes) 
    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') # 设置颜色
    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='traning score') # 绘制训练精度曲线
    plt.plot(train_sizes, test_scores_mean, 'o-', color='r',
             label='testing score') # 绘制测试精度曲线
    plt.legend(loc='best')
    return plt

clf = RandomForestClassifier()
para_grid = {'max_depth': [10], 'n_estimators': [80], 'max_features': [1, 5, 10], 'criterion': ['gini', 'entropy'],
             'min_samples_split': [2, 5, 10], 'min_samples_leaf': [1, 5, 10]}#对以上参数进行网格搜索
gs = GridSearchCV(clf, param_grid=para_grid, cv=3, scoring='accuracy')
gs.fit(X, y)
gs_best = gs.best_estimator_ #选择出最优的学习器
gs.best_score_ #最优学习器的精度

g = plot_learning_curve(gs_best, 'RFC', X, y)#调用实验2中定义的 plot_learning_curve 绘制学习曲线
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

在这里插入图片描述
我们发现,相比于上面,有效地缓解了过拟合。

总结

没啥太大总结的,思路大概就这么多,前面写得挺顺的,后面调bug调了很久,因为有些代码并不是我写的,我从中午从零开始写,到晚上10点基本完成了雏形,数据之前就有关注过,毕竟最出名的题目之一,然后有些代码确实时间不多,还有就是有些博文已经总结得非常好了,有些大佬写得连我都看不懂,这里贴出一些好的链接。

[1].机器学习系列(3)_逻辑回归应用之Kaggle泰坦尼克之灾
[2].Kaggle_Titanic生存预测 – 详细流程吐血梳理
[3].Kaggle泰坦尼克-Python(建模完整流程,小白学习用)
[4].Kaggle入门:泰坦尼克号乘客生死预测
[5].kaggle 泰坦尼克号生存预测——六种算法模型实现与比较
[6].分分钟带你杀入Kaggle Top 1%

第一篇是韩小阳写的,实力不用多说,内容很全面,语言很幽默,第二篇偏向于可视化,作图技巧叹为观止,第三篇对整个赛题的理解都非常清晰,还有整体的框架确实适合于小白,第四、五篇都还好,很全面代码量也多,第六篇偏向于科普。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/237081
推荐阅读
相关标签
  

闽ICP备14008679号