机器学习系列5:进阶算法-随机森林

机器学习中,随机森林是一种组合方法,由许多的决策树组成,因为这些决策树的形成采用了随机的方法,因此也叫做随机决策树。随机森林中的树之间是没有关联的。

当测试数据进入随机森林时,其实就是让每一颗决策树进行分类,最后取所有决策树中分类结果最多的那类为最终的结果。因此随机森林是一个包含多个决策树的分类器,并且其输出的类别是由个别树输出的类别的众数而定。 随机森林由决策树组成,决策树实际上是将空间用超平面进行划分的一种方法,每次分割的时候,都将当前的空间一分为二,使得每一个叶子节点都是在空间中的一个不相交的区域,在进行决策的时候,会根据输入样本每一维feature的值,计算信息增益,或者Gini值,一步一步往下分裂,最后使得样本落入N个区域中的一个(假设有N个叶子节点,对于二元分类,N=2)。

随机森林可以既可以处理属性为离散值的量,比如ID3算法,也可以处理属性为连续值的量,比如C4.5算法。

    随机森林的优点

1.适合做多分类问题;当存在分类不平衡的情况时,随机森林能够提供平衡数据集误差的有效方法(通过属性评估?); 2.训练和预测速度快; 3.对训练数据的容错能力,是一种有效估计missing值的方法,当数据集中有大比例的数据缺失时仍然可以保持精度不变; 4.能够有效地处理大的数据集; 5.它能够处理很高维度(feature很多)的数据,并且不用做特征选择 6.能够在分类的过程中可以生成一个泛化误差的内部无偏估计(OOB error可以作为泛化误差的一个估计); 7.能够检测到特征之间的相互影响以及重要性程度(通过feature_importances_方法); 8.不容易出现过度拟合;实现简单容易并行化(通过n_jobs)。 9.模型的上述性能可以被扩展运用到未标记的数据集中,用于引导无监督聚类、数据透视和异常检测;

    随机森林的缺点:

1.对于有不同级别的属性的数据,级别划分较多的属性会对随机森林产生更大的影响,所以随机森林在这种数据上产出的属性权值是不可信的; 2.单棵决策树的预测效果很差:由于随机选择属性,使得单棵决策树的预测效果很差。 3.随机森林在解决回归问题时并没有像它在分类中表现的那么好,这是因为它并不能给出一个连续型的输出。当进行回归时,随机森林不能够作出超越训练集数据范围的预测,这可能导致在对某些还有特定噪声的数据进行建模时出现过度拟合。 4.对于许多统计建模者来说,随机森林给人的感觉像是一个黑盒子,你几乎无法控制模型内部的运行,只能在不同的参数和随机种子之间进行尝试。

随机森林的定义


在机器学习中,随机森林是一个包含多个决策树的分类器, 并且其输出的类别是由个别树输出的类别的众数而定。 Leo Breiman和Adele Cutler发展出推论出随机森林的算法。 而 “Random Forests” 是他们的商标。 这个术语是1995年由贝尔实验室的Tin Kam Ho所提出的随机决策森林(random decision forests)而来的。这个方法则是结合 Breimans 的 “Bootstrap aggregating” 想法和 Ho 的”random subspace method”以建造决策树的集合。
随机森林是一种统计学习理论,其随机有两个方面:首先在训练的每一轮中,都是对原始样本集有放回的抽取固定数目的样本点,形成k 个互不相同的样本集。第二个点是:对于每一个决策树的建立是从总的属性中随机抽取一定量的属性作为分裂属性集,这样对于k个树分类器均是不相同的。由随机生成的k个决策树组成了随机森林。


在scikit-learn中,RF的分类类是RandomForestClassifier,回归类是RandomForestRegressor。当然RF的变种Extra Trees也有, 分类类ExtraTreesClassifier,回归类ExtraTreesRegressor。由于RF和Extra Trees的区别较小,调参方法基本相同,本文只关注于RF的调参。
和GBDT的调参类似,RF需要调参的参数也包括两部分,第一部分是Bagging框架的参数,第二部分是CART决策树的参数。下面我们就对这些参数做一个介绍。
随机森林主要有那种算法,一种是随机森林的分类算法,另外一个自然就是回归了。下面我们列出了两个类的不同。

class sklearn.ensemble.RandomForestClassifier(n_estimators=10, criterion='gini', max_depth=None, min_samples_split=2, min_samples_leaf=1, min_weight_fraction_leaf=0.0, max_features='auto', max_leaf_nodes=None, min_impurity_split=1e-07, bootstrap=True, oob_score=False, n_jobs=1, random_state=None, verbose=0, warm_start=False, class_weight=None)


class sklearn.ensemble.RandomForestRegressor(n_estimators=10, criterion='mse', max_depth=None, min_samples_split=2, min_samples_leaf=1, min_weight_fraction_leaf=0.0, max_features='auto', max_leaf_nodes=None, min_impurity_split=1e-07, bootstrap=True, oob_score=False, n_jobs=1, random_state=None, verbose=0, warm_start=False)


上面已经介绍,随机森林是一个包含多个决策树的分类器,所以它的参数我们分两部分介绍:1、决策树的参数;2、随机森林特有的参数。
sklearn中决策树的参数:
1,criterion: ”gini” or “entropy”(default=”gini”)是计算属性的gini(基尼不纯度)还是entropy(信息增益),来选择最合适的节点。
2,splitter: ”best” or “random”(default=”best”)随机选择属性还是选择不纯度最大的属性,建议用默认。
3,max_features: 选择最适属性时划分的特征不能超过此值。当为整数时,即最大特征数;如果是小数,训练集特征数百分比;如果是“auto”,那么max_features = sqrt(n_features)。如果“sqrt”,则max_features = sqrt(n_features)(与“auto”相同)。如果“log2”,则max_features = log2(n_features)。如果没有,那么max_features = n_features。
4,max_depth: (default=None)设置树的最大深度,默认为None,这样建树时,会使每一个叶节点只有一个类别,或是达到min_samples_split。
5,min_samples_split:根据属性划分节点时,每个划分最少的样本数。
6,min_samples_leaf:叶子节点最少的样本数。
7,max_leaf_nodes: (default=None)叶子树的最大样本数。
8,min_weight_fraction_leaf: (default=0) 叶子节点所需要的最小权值
9,verbose:(default=0) 是否显示任务进程

随机森林特有的参数:
1,n_estimators=10:决策树的个数,越多越好,但是性能就会越差,至少100左右(具体数字忘记从哪里来的了)可以达到可接受的性能和误差率。
2,bootstrap=True:是否有放回的采样。
3,oob_score=False:oob(out of band,带外)数据,即:在某次决策树训练中没有被bootstrap选中的数据。多单个模型的参数训练,我们知道可以用cross validation(cv)来进行,但是特别消耗时间,而且对于随机森林这种情况也没有大的必要,所以就用这个数据对决策树模型进行验证,算是一个简单的交叉验证。性能消耗小,但是效果不错。
4,n_jobs=1:并行job个数。这个在ensemble算法中非常重要,尤其是bagging(而非boosting,因为boosting的每次迭代之间有影响,所以很难进行并行化),因为可以并行从而提高性能。1=不并行;n:n个并行;-1:CPU有多少core,就启动多少job。
5,warm_start=False:热启动,决定是否使用上次调用该类的结果然后增加新的。
6,class_weight=None:各个label的权重。


对比上面两个类,唯一的不同就在于最后一个参数class_weight,给出各个标签的权重。
</font size=3>


进行预测可以有几种形式:
1,predict_proba(x):给出带有概率值的结果。每个点在所有label的概率和为1.
2,predict(x):直接给出预测结果。内部还是调用的predict_proba(),根据概率的结果看哪个类型的预测值最高就是哪个类型。
3,predict_log_proba(x):和predict_proba基本上一样,只是把结果给做了log()处理。
</font size=3>

       通过总结模型调参常见的问题,我们可以把模型的参数分为4类:目标类、性能类、效率类和附加类。下表详细地展示了4个模型参数的意义</font size=3>

Markdown

分类问题

In [1]:
from sklearn.tree import DecisionTreeRegressor  
from sklearn.ensemble import RandomForestRegressor  
import numpy as np  
   
from sklearn.datasets import load_iris  
iris=load_iris()  
#print iris#iris的4个属性是:萼片宽度 萼片长度 花瓣宽度 花瓣长度 标签是花的种类:setosa versicolour virginica  
print(iris['target'].shape)  
rf=RandomForestRegressor()#这里使用了默认的参数设置  
rf.fit(iris.data[:150],iris.target[:150])#进行模型的训练  
#    
#随机挑选两个预测不相同的样本  
instance=iris.data[[100,109]]  
print(instance)  
print('instance 0 prediction;',rf.predict(instance[0]))
print('instance 1 prediction;',rf.predict(instance[1]))  
print(iris.target[100],iris.target[109])
(150,)
[[ 6.3  3.3  6.   2.5]
 [ 7.2  3.6  6.1  2.5]]
instance 0 prediction; [ 2.]
instance 1 prediction; [ 2.]
2 2
/srv/env/lib64/python3.4/site-packages/sklearn/utils/validation.py:395: DeprecationWarning: Passing 1d arrays as data is deprecated in 0.17 and will raise ValueError in 0.19. Reshape your data either using X.reshape(-1, 1) if your data has a single feature or X.reshape(1, -1) if it contains a single sample.
  DeprecationWarning)
/srv/env/lib64/python3.4/site-packages/sklearn/utils/validation.py:395: DeprecationWarning: Passing 1d arrays as data is deprecated in 0.17 and will raise ValueError in 0.19. Reshape your data either using X.reshape(-1, 1) if your data has a single feature or X.reshape(1, -1) if it contains a single sample.
  DeprecationWarning)

可以对比看到我们的分类是完全正确

回归问题


我们对比了随机森林和多输出的回归模型,比较两个模型的效果。
</font size=3>

In [2]:
import numpy as np
import matplotlib.pyplot as plt
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import train_test_split
from sklearn.multioutput import MultiOutputRegressor


# Create a random dataset
rng = np.random.RandomState(1)
X = np.sort(200 * rng.rand(600, 1) - 100, axis=0)
y = np.array([np.pi * np.sin(X).ravel(), np.pi * np.cos(X).ravel()]).T
y += (0.5 - rng.rand(*y.shape))

X_train, X_test, y_train, y_test = train_test_split(X, y,
                                                    train_size=400,
                                                    random_state=4)

max_depth = 30
regr_multirf = MultiOutputRegressor(RandomForestRegressor(max_depth=max_depth,
                                                          random_state=0))
regr_multirf.fit(X_train, y_train)

regr_rf = RandomForestRegressor(max_depth=max_depth, random_state=2)
regr_rf.fit(X_train, y_train)

# Predict on new data
y_multirf = regr_multirf.predict(X_test)
y_rf = regr_rf.predict(X_test)

# Plot the results
plt.figure()
s = 50
a = 0.4
plt.scatter(y_test[:, 0], y_test[:, 1],
            c="navy", s=s, marker="s", alpha=a, label="Data")
plt.scatter(y_multirf[:, 0], y_multirf[:, 1],
            c="cornflowerblue", s=s, alpha=a,
            label="Multi RF score=%.2f" % regr_multirf.score(X_test, y_test))
plt.scatter(y_rf[:, 0], y_rf[:, 1],
            c="c", s=s, marker="^", alpha=a,
            label="RF score=%.2f" % regr_rf.score(X_test, y_test))
plt.xlim([-6, 6])
plt.ylim([-6, 6])
plt.xlabel("target 1")
plt.ylabel("target 2")
plt.title("Comparing random forests and the multi-output meta estimator")
plt.legend()
plt.show()

泰坦尼克号


泰坦尼克号的沉没是历史上最著名的沉船时间之一。 1912年4月15日,泰坦尼克号在首航期间,与冰山相撞后沉没,在2224名乘客和船员中造成1502人死亡。 这场悲剧震撼了国际社会,并为船舶制定了更好的安全规定。造成这种生命损失的原因之一是乘客和船员没有足够的救生艇。 虽然有幸遇难下沉,但一些群体比女性,儿童和上层阶级更有可能生存下去。
现在我们拿到了这些数据,并对这些数据进行分析,以探讨个体生命生存的可能性。
</font size=3>


数据已分为两组,即“训练集”和“测试集”。 对于训练集,我们为每个乘客提供了标签(是否生还)。
对于测试集中的每个乘客,我们将根据得到的特征判断其是否生还(0人死亡,1人幸存)。
</font size=3>

In [1]:
import pandas as pd
import numpy as np
import pylab as plt

# Set the global default size of matplotlib figures
plt.rc('figure', figsize=(10, 5))

# Size of matplotlib figures that contain subplots
fizsize_with_subplots = (10, 10)

# Size of matplotlib histogram bins
bin_size = 10
In [2]:
# 导入数据
df_train = pd.read_csv('train.csv')
df_train.head()
Out[2]:
PassengerIdSurvivedPclassNameSexAgeSibSpParchTicketFareCabinEmbarked
0103Braund, Mr. Owen Harrismale22.010A/5 211717.2500NaNS
1211Cumings, Mrs. John Bradley (Florence Briggs Th…female38.010PC 1759971.2833C85C
2313Heikkinen, Miss. Lainafemale26.000STON/O2. 31012827.9250NaNS
3411Futrelle, Mrs. Jacques Heath (Lily May Peel)female35.01011380353.1000C123S
4503Allen, Mr. William Henrymale35.0003734508.0500NaNS
In [3]:
# 统计性描述
df_train.describe()
Out[3]:
PassengerIdSurvivedPclassAgeSibSpParchFare
count891.000000891.000000891.000000714.000000891.000000891.000000891.000000
mean446.0000000.3838382.30864229.6991180.5230080.38159432.204208
std257.3538420.4865920.83607114.5264971.1027430.80605749.693429
min1.0000000.0000001.0000000.4200000.0000000.0000000.000000
25%223.5000000.0000002.00000020.1250000.0000000.0000007.910400
50%446.0000000.0000003.00000028.0000000.0000000.00000014.454200
75%668.5000001.0000003.00000038.0000001.0000000.00000031.000000
max891.0000001.0000003.00000080.0000008.0000006.000000512.329200


上面现在我们对数据集内容有一个总体的看法,观察一下具体的每个特征。 如果特征单独就具备很好的区分度这是一个不错的事情。
</font size=3>

In [4]:
fig = plt.figure(figsize=fizsize_with_subplots) 
fig_dims = (3, 2)

# 生死的区别
plt.subplot2grid(fig_dims, (0, 0))
df_train['Survived'].value_counts().plot(kind='bar', 
                                         title='Death and Survival Counts')

# 乘客类型
plt.subplot2grid(fig_dims, (0, 1))
df_train['Pclass'].value_counts().plot(kind='bar', 
                                       title='Passenger Class Counts')

# 性别
plt.subplot2grid(fig_dims, (1, 0))
df_train['Sex'].value_counts().plot(kind='bar', 
                                    title='Gender Counts')
plt.xticks(rotation=0)

# 位置
plt.subplot2grid(fig_dims, (1, 1))
df_train['Embarked'].value_counts().plot(kind='bar', 
                                         title='Ports of Embarkation Counts')

# 年龄段
plt.subplot2grid(fig_dims, (2, 0))
df_train['Age'].hist()
plt.title('Age Histogram')
Out[4]:
<matplotlib.text.Text at 0x7fd3ae47ffd0>

乘客类型

In [5]:
# 我们将观察各个类型程度的存活率
pclass_xt = pd.crosstab(df_train['Pclass'], df_train['Survived'])
pclass_xt_pct = pclass_xt.div(pclass_xt.sum(1).astype(float), axis=0)

pclass_xt_pct.plot(kind='bar', 
                   stacked=True, 
                   title='Survival Rate by Passenger Classes')
plt.xlabel('Passenger Class')
plt.ylabel('Survival Rate')
Out[5]:
<matplotlib.text.Text at 0x7fd3ae43afd0>

性别

In [6]:
sexes = sorted(df_train['Sex'].unique())
genders_mapping = dict(zip(sexes, range(0, len(sexes) + 1)))
genders_mapping
Out[6]:
{'female': 0, 'male': 1}
In [7]:
df_train['Sex_Val'] = df_train['Sex'].map(genders_mapping).astype(int)
df_train.head()
Out[7]:
PassengerIdSurvivedPclassNameSexAgeSibSpParchTicketFareCabinEmbarkedSex_Val
0103Braund, Mr. Owen Harrismale22.010A/5 211717.2500NaNS1
1211Cumings, Mrs. John Bradley (Florence Briggs Th…female38.010PC 1759971.2833C85C0
2313Heikkinen, Miss. Lainafemale26.000STON/O2. 31012827.9250NaNS0
3411Futrelle, Mrs. Jacques Heath (Lily May Peel)female35.01011380353.1000C123S0
4503Allen, Mr. William Henrymale35.0003734508.0500NaNS1
In [8]:
sex_val_xt = pd.crosstab(df_train['Sex_Val'], df_train['Survived'])
sex_val_xt_pct = sex_val_xt.div(sex_val_xt.sum(1).astype(float), axis=0)
sex_val_xt_pct.plot(kind='bar', stacked=True, title='Survival Rate by Gender')
Out[8]:
<matplotlib.axes._subplots.AxesSubplot at 0x7fd3ae689588>


大多数女性幸存下来,而大多数男性没有幸免。接下来,我们将通过查看Sex和Pclass来确定我们是否可以获得关于生存率的任何突破。
</font size=3>

In [9]:
passenger_classes = sorted(df_train['Pclass'].unique())

for p_class in passenger_classes:
    print('M: ', p_class, len(df_train[(df_train['Sex'] == 'male') & (df_train['Pclass'] == p_class)]))
    print('F: ', p_class, len(df_train[(df_train['Sex'] == 'female') & (df_train['Pclass'] == p_class)]))
M:  1 122
F:  1 94
M:  2 108
F:  2 76
M:  3 347
F:  3 144
In [10]:
# 性别不同情况下类别不同乘客的生存率
females_df = df_train[df_train['Sex'] == 'female']
females_xt = pd.crosstab(females_df['Pclass'], df_train['Survived'])
females_xt_pct = females_xt.div(females_xt.sum(1).astype(float), axis=0)
females_xt_pct.plot(kind='bar', 
                    stacked=True, 
                    title='Female Survival Rate by Passenger Class')
plt.xlabel('Passenger Class')
plt.ylabel('Survival Rate')

# Plot survival rate by Pclass
males_df = df_train[df_train['Sex'] == 'male']
males_xt = pd.crosstab(males_df['Pclass'], df_train['Survived'])
males_xt_pct = males_xt.div(males_xt.sum(1).astype(float), axis=0)
males_xt_pct.plot(kind='bar', 
                  stacked=True, 
                  title='Male Survival Rate by Passenger Class')
plt.xlabel('Passenger Class')
plt.ylabel('Survival Rate')
Out[10]:
<matplotlib.text.Text at 0x7fd3ac1dbe10>

登船数量

In [11]:
df_train[df_train['Embarked'].isnull()]
Out[11]:
PassengerIdSurvivedPclassNameSexAgeSibSpParchTicketFareCabinEmbarkedSex_Val
616211Icard, Miss. Ameliefemale38.00011357280.0B28NaN0
82983011Stone, Mrs. George Nelson (Martha Evelyn)female62.00011357280.0B28NaN0
In [12]:
embarked_locs_mapping = {np.nan: 0, 'C': 1, 'Q': 2, 'S': 3}
df_train['Embarked_Val'] = df_train['Embarked'] \
                               .map(embarked_locs_mapping) \
                               .astype(int)
df_train.head()
Out[12]:
PassengerIdSurvivedPclassNameSexAgeSibSpParchTicketFareCabinEmbarkedSex_ValEmbarked_Val
0103Braund, Mr. Owen Harrismale22.010A/5 211717.2500NaNS13
1211Cumings, Mrs. John Bradley (Florence Briggs Th…female38.010PC 1759971.2833C85C01
2313Heikkinen, Miss. Lainafemale26.000STON/O2. 31012827.9250NaNS03
3411Futrelle, Mrs. Jacques Heath (Lily May Peel)female35.01011380353.1000C123S03
4503Allen, Mr. William Henrymale35.0003734508.0500NaNS13
In [13]:
df_train['Embarked_Val'].hist(bins=4, range=(0, 3))
plt.title('Port of Embarkation Histogram')
plt.xlabel('Port of Embarkation')
plt.ylabel('Count')
plt.show()
In [14]:
if len(df_train[df_train['Embarked'].isnull()] > 0):
    df_train.replace({'Embarked_Val' : 
                   { embarked_locs_mapping[np.nan] : embarked_locs_mapping['S'] 
                   }
               }, 
               inplace=True)
In [15]:
embarked_locs = sorted(df_train['Embarked_Val'].unique())
embarked_locs
Out[15]:
[1, 2, 3]
In [16]:
embarked_val_xt = pd.crosstab(df_train['Embarked_Val'], df_train['Survived'])
embarked_val_xt_pct = \
    embarked_val_xt.div(embarked_val_xt.sum(1).astype(float), axis=0)
embarked_val_xt_pct.plot(kind='bar', stacked=True)
plt.title('Survival Rate by Port of Embarkation')
plt.xlabel('Port of Embarkation')
plt.ylabel('Survival Rate')
Out[16]:
<matplotlib.text.Text at 0x7fd3ac13dcc0>

看来那些开始位置“C”的人是最高的生存率。 我们会再多挖一些,看看为什么会出现这种情况。 下面我们绘制一个图表,以确定每个港口的性别和乘客班级构成:

In [17]:
fig = plt.figure(figsize=fizsize_with_subplots) 

rows = 2
cols = 3
col_names = ('Sex_Val', 'Pclass')

for portIdx in embarked_locs:
    for colIdx in range(0, len(col_names)):
        plt.subplot2grid((rows, cols), (colIdx, portIdx - 1))
        df_train[df_train['Embarked_Val'] == portIdx][col_names[colIdx]] \
            .value_counts().plot(kind='bar')
In [18]:
df_train = pd.concat([df_train, pd.get_dummies(df_train['Embarked_Val'], prefix='Embarked_Val')], axis=1)

年纪

In [19]:
df_train[df_train['Age'].isnull()][['Sex', 'Pclass', 'Age']].head()
Out[19]:
SexPclassAge
5male3NaN
17male2NaN
19female3NaN
26male3NaN
28female3NaN
In [20]:
df_train['AgeFill'] = df_train['Age']
df_train['AgeFill'] = df_train['AgeFill'] \
                        .groupby([df_train['Sex_Val'], df_train['Pclass']]) \
                        .apply(lambda x: x.fillna(x.median()))
In [21]:
len(df_train[df_train['AgeFill'].isnull()])
Out[21]:
0
In [22]:
fig, axes = plt.subplots(2, 1, figsize=fizsize_with_subplots)

# Histogram of AgeFill segmented by Survived
df1 = df_train[df_train['Survived'] == 0]['Age']
df2 = df_train[df_train['Survived'] == 1]['Age']
max_age = max(df_train['AgeFill'])
axes[0].hist([df1, df2], 
             bins=int(max_age / bin_size), 
             range=(1, max_age), 
             stacked=True)
axes[0].legend(('Died', 'Survived'), loc='best')
axes[0].set_title('Survivors by Age Groups Histogram')
axes[0].set_xlabel('Age')
axes[0].set_ylabel('Count')

# Scatter plot Survived and AgeFill
axes[1].scatter(df_train['Survived'], df_train['AgeFill'])
axes[1].set_title('Survivors by Age Plot')
axes[1].set_xlabel('Survived')
axes[1].set_ylabel('Age')
/srv/env/lib64/python3.4/site-packages/numpy/lib/function_base.py:747: RuntimeWarning: invalid value encountered in greater_equal
  keep = (tmp_a >= mn)
/srv/env/lib64/python3.4/site-packages/numpy/lib/function_base.py:748: RuntimeWarning: invalid value encountered in less_equal
  keep &= (tmp_a <= mx)
Out[22]:
<matplotlib.text.Text at 0x7fd3a46dc390>
In [23]:
for pclass in passenger_classes:
    df_train.AgeFill[df_train.Pclass == pclass].plot(kind='kde')
plt.title('Age Density Plot by Passenger Class')
plt.xlabel('Age')
plt.legend(('1st Class', '2nd Class', '3rd Class'), loc='best')
Out[23]:
<matplotlib.legend.Legend at 0x7fd3ac0205f8>

通过概率密度图,我们看到第一类乘客一般比二等乘客大,而二级乘客则大于三级乘客。 我们确定一等乘客的生存率要高于二级乘客,而乘客的生存率又高于三级乘客。

In [24]:
# Set up a grid of plots
fig = plt.figure(figsize=fizsize_with_subplots) 
fig_dims = (3, 1)

# Plot the AgeFill histogram for Survivors
plt.subplot2grid(fig_dims, (0, 0))
survived_df = df_train[df_train['Survived'] == 1]
survived_df['AgeFill'].hist(bins=int(max_age / bin_size), range=(1, max_age))

# Plot the AgeFill histogram for Females
plt.subplot2grid(fig_dims, (1, 0))
females_df = df_train[(df_train['Sex_Val'] == 0) & (df_train['Survived'] == 1)]
females_df['AgeFill'].hist(bins=int(max_age / bin_size), range=(1, max_age))

# Plot the AgeFill histogram for first class passengers
plt.subplot2grid(fig_dims, (2, 0))
class1_df = df_train[(df_train['Pclass'] == 1) & (df_train['Survived'] == 1)]
class1_df['AgeFill'].hist(bins=int(max_age / bin_size), range=(1, max_age))
Out[24]:
<matplotlib.axes._subplots.AxesSubplot at 0x7fd3a47ae7b8>


在第一张图中,我们看到大多数幸存者都是从20岁到30岁的年龄段,可以被以下两个图解释。 第二张图表显示,大多数女性都在20多岁。 第三张图表显示,大多数一流的乘客都在30多岁。
</font size=3>

家庭人口数量

In [25]:
df_train['FamilySize'] = df_train['SibSp'] + df_train['Parch']
df_train.head()
Out[25]:
PassengerIdSurvivedPclassNameSexAgeSibSpParchTicketFareCabinEmbarkedSex_ValEmbarked_ValEmbarked_Val_1Embarked_Val_2Embarked_Val_3AgeFillFamilySize
0103Braund, Mr. Owen Harrismale22.010A/5 211717.2500NaNS1300122.01
1211Cumings, Mrs. John Bradley (Florence Briggs Th…female38.010PC 1759971.2833C85C0110038.01
2313Heikkinen, Miss. Lainafemale26.000STON/O2. 31012827.9250NaNS0300126.00
3411Futrelle, Mrs. Jacques Heath (Lily May Peel)female35.01011380353.1000C123S0300135.01
4503Allen, Mr. William Henrymale35.0003734508.0500NaNS1300135.00
In [26]:
df_train['FamilySize'].hist()
plt.title('Family Size Histogram')
Out[26]:
<matplotlib.text.Text at 0x7fd3ac097080>
In [27]:
family_sizes = sorted(df_train['FamilySize'].unique())
family_size_max = max(family_sizes)

df1 = df_train[df_train['Survived'] == 0]['FamilySize']
df2 = df_train[df_train['Survived'] == 1]['FamilySize']
plt.hist([df1, df2], 
         bins=family_size_max + 1, 
         range=(0, family_size_max), 
         stacked=True)
plt.legend(('Died', 'Survived'), loc='best')
plt.title('Survivors by Family Size')
Out[27]:
<matplotlib.text.Text at 0x7fd39821bbe0>


基于直方图,家庭人口数量对生存的影响并不明显。
</font size=3>

随机森林

In [28]:
df_train.dtypes[df_train.dtypes.map(lambda x: x == 'object')]
df_train = df_train.drop(['Name', 'Sex', 'Ticket', 'Cabin', 'Embarked'], 
                         axis=1)
df_train = df_train.drop(['Age', 'SibSp', 'Parch', 'PassengerId', 'Embarked_Val'], axis=1)
train_data = df_train.values
In [29]:
def clean_data(df, drop_passenger_id):
    
    # Get the unique values of Sex
    sexes = sorted(df['Sex'].unique())
    
    # Generate a mapping of Sex from a string to a number representation    
    genders_mapping = dict(zip(sexes, range(0, len(sexes) + 1)))

    # Transform Sex from a string to a number representation
    df['Sex_Val'] = df['Sex'].map(genders_mapping).astype(int)
    
    # Get the unique values of Embarked
    embarked_locs = sorted(df['Embarked'].unique())

    # Generate a mapping of Embarked from a string to a number representation        
    embarked_locs_mapping = dict(zip(embarked_locs, 
                                     range(0, len(embarked_locs) + 1)))
    
    # Transform Embarked from a string to dummy variables
    df = pd.concat([df, pd.get_dummies(df['Embarked'], prefix='Embarked_Val')], axis=1)
    
    # Fill in missing values of Embarked
    # Since the vast majority of passengers embarked in 'S': 3, 
    # we assign the missing values in Embarked to 'S':
    if len(df[df['Embarked'].isnull()] > 0):
        df.replace({'Embarked_Val' : 
                       { embarked_locs_mapping[nan] : embarked_locs_mapping['S'] 
                       }
                   }, 
                   inplace=True)
    
    # Fill in missing values of Fare with the average Fare
    if len(df[df['Fare'].isnull()] > 0):
        avg_fare = df['Fare'].mean()
        df.replace({ None: avg_fare }, inplace=True)
    
    # To keep Age in tact, make a copy of it called AgeFill 
    # that we will use to fill in the missing ages:
    df['AgeFill'] = df['Age']

    # Determine the Age typical for each passenger class by Sex_Val.  
    # We'll use the median instead of the mean because the Age 
    # histogram seems to be right skewed.
    df['AgeFill'] = df['AgeFill'] \
                        .groupby([df['Sex_Val'], df['Pclass']]) \
                        .apply(lambda x: x.fillna(x.median()))
            
    # Define a new feature FamilySize that is the sum of 
    # Parch (number of parents or children on board) and 
    # SibSp (number of siblings or spouses):
    df['FamilySize'] = df['SibSp'] + df['Parch']
    
    # Drop the columns we won't use:
    df = df.drop(['Name', 'Sex', 'Ticket', 'Cabin', 'Embarked'], axis=1)
    
    # Drop the Age column since we will be using the AgeFill column instead.
    # Drop the SibSp and Parch columns since we will be using FamilySize.
    # Drop the PassengerId column since it won't be used as a feature.
    df = df.drop(['Age', 'SibSp', 'Parch'], axis=1)
    
    if drop_passenger_id:
        df = df.drop(['PassengerId'], axis=1)
    
    return df
In [30]:
from sklearn.ensemble import RandomForestClassifier

clf = RandomForestClassifier(n_estimators=100)
In [31]:
# 构建模型的特征
train_features = train_data[:, 1:]

# '目标标签
train_target = train_data[:, 0]

# 训练模型
clf = clf.fit(train_features, train_target)
score = clf.score(train_features, train_target)
"Mean accuracy of Random Forest: {0}".format(score)
Out[31]:
'Mean accuracy of Random Forest: 0.9809203142536476'
In [33]:
# 检验模型
df_test = pd.read_csv('test.csv')
df_test.head()
Out[33]:
PassengerIdPclassNameSexAgeSibSpParchTicketFareCabinEmbarked
08923Kelly, Mr. Jamesmale34.5003309117.8292NaNQ
18933Wilkes, Mrs. James (Ellen Needs)female47.0103632727.0000NaNS
28942Myles, Mr. Thomas Francismale62.0002402769.6875NaNQ
38953Wirz, Mr. Albertmale27.0003151548.6625NaNS
48963Hirvonen, Mrs. Alexander (Helga E Lindqvist)female22.011310129812.2875NaNS
In [34]:
df_test = clean_data(df_test, drop_passenger_id=False)
test_data = df_test.values
In [35]:
test_x = test_data[:, 1:]

# 预测
test_y = clf.predict(test_x)
In [36]:
df_test['Survived'] = test_y
df_test[['PassengerId', 'Survived']] \
    .to_csv('results-rf.csv', index=False)

评估模型的准确性

In [37]:
from sklearn import metrics
from sklearn.cross_validation import train_test_split

# 分为8-2两部分
train_x, test_x, train_y, test_y = train_test_split(train_features, 
                                                    train_target, 
                                                    test_size=0.20, 
                                                    random_state=0)
print (train_features.shape, train_target.shape)
print (train_x.shape, train_y.shape)
print (test_x.shape, test_y.shape)
(891, 8) (891,)
(712, 8) (712,)
(179, 8) (179,)
/srv/env/lib64/python3.4/site-packages/sklearn/cross_validation.py:44: DeprecationWarning: This module was deprecated in version 0.18 in favor of the model_selection module into which all the refactored classes and functions are moved. Also note that the interface of the new CV iterators are different from that of this module. This module will be removed in 0.20.
  "This module will be removed in 0.20.", DeprecationWarning)
In [38]:
clf = clf.fit(train_x, train_y)
predict_y = clf.predict(test_x)

from sklearn.metrics import accuracy_score
print ("Accuracy = %.2f" % (accuracy_score(test_y, predict_y)))
Accuracy = 0.84
In [41]:
model_score = clf.score(test_x, test_y)
print ("Model Score %.2f \n" % (model_score))

confusion_matrix = metrics.confusion_matrix(test_y, predict_y)
print ("Confusion Matrix ", confusion_matrix)

print ("          Predicted")
print ("         |  0  |  1  |")
print ("         |-----|-----|")
print ("       0 | %3d | %3d |" % (confusion_matrix[0, 0],
                                   confusion_matrix[0, 1]))
print ("Actual   |-----|-----|")
print ("       1 | %3d | %3d |" % (confusion_matrix[1, 0],
                                   confusion_matrix[1, 1]))
print ("         |-----|-----|")
Model Score 0.84 

Confusion Matrix  [[101   9]
 [ 20  49]]
          Predicted
         |  0  |  1  |
         |-----|-----|
       0 | 101 |   9 |
Actual   |-----|-----|
       1 |  20 |  49 |
         |-----|-----|
In [43]:
# 模型详细评估
from sklearn.metrics import classification_report
print(classification_report(test_y, 
                            predict_y, 
                            target_names=['Not Survived', 'Survived']))
              precision    recall  f1-score   support

Not Survived       0.83      0.92      0.87       110
    Survived       0.84      0.71      0.77        69

 avg / total       0.84      0.84      0.83       179

In [ ]:

机器学习系列目录

1. 机器学习系列1:算法基础-Logistic回归
2. 机器学习系列2:算法基础-K-Means
3. 机器学习系列3:算法基础-决策树
4. 机器学习系列4:进阶算法-SVM
当前阅读>  5. 机器学习系列5:进阶算法-随机森林
6. 数学教学系列6:ARMA模型
7. 数学教学系列7:拉格朗日对偶问题
8. 数学教学系列8:梯度下降法
9. 数学教学系列9:B-S期权定价模型