Kaggle项目Titanic挑战最高分,特征工程

1. Titanic 项目介绍

Kaggle的 “https://www.kaggle.com/c/titanic/" 泰坦尼克号挑战是一个比赛,其目标是预测给定乘客的生存或死亡

这篇文章,我们将首先从探索数据分析(exploratory data analysis EDA)开始,然后跟踪特征工程,最后介绍预测模型。

下面的代码全部在Jupyter Notebook运行
Titanic Kaggle Challenge.ipynb

本教程涉及的主要库有:

  • Pandas 数据处理
  • Matplotlib 和 seaborn 用于数据可视化
  • Numpy 用于多维数组计算
  • sklearn 用于机器学习和预测建模

2. 安装程序

安装这些软件包的一个非常简单的方法是下载并安装 Anaconda

地址: https://www.continuum.io/downloads

https://conda.io/docs/install/quick.html#os-x-miniconda-install

Anaconda封装它们全部,可在所有平台(Windows,Linux和Mac OSX)上使用。

3. 探索性数据分析

在本节中,我们将做四件事。

  • 数据提取:我们将加载数据集,并先看看它。
  • 清洗:我们将填写缺失的值。
  • 绘图:我们将创建一些有趣的图表,希望从数据中发现相关性和隐藏的见解。
  • 假设:我们将从图表中制定假设。
from IPython.core.display import HTML
HTML("""
<style>
.output_png {
    display: table-cell;
    text-align: center;
    vertical-align: middle;
}
</style>
""")

我们导入有用的库。

# remove warnings
import warnings
warnings.filterwarnings('ignore')
# ---

%matplotlib inline
import pandas as pd
pd.options.display.max_columns = 100
pd.options.display.max_rows = 100

from matplotlib import pyplot as plt
import matplotlib
matplotlib.style.use('ggplot')

import numpy as np


3.1. 开始加载训练集

两个数据集可用:训练集和测试集。 我们将使用训练集来构建我们的预测模型,测试集对Kaggle评估系统生成一个输出文件。

data = pd.read_csv('data/train.csv')
data.head()

3.2. 数据集字段说明

在数据文件中,每一行是一个样本,代表一名乘客的信息,包含以下12个数据集字段说明

  • PassengerId 乘客ID
  • Survived 是否幸存
  • Pclass 客舱客级
  • Name 乘客姓名
  • Sex 乘客性别
  • Age 乘客年龄
  • SibSp 兄弟姐妹和配偶在船数量
  • ParCh 父母孩子在船数量
  • Ticket 船票号
  • Fare船票价格
  • Cabin客舱位置
  • Embarked 登船港口的编号

3.3. Pandas 允许您使用describe方法统计描述数字特征。

data.describe()
data.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 12 columns):
PassengerId    891 non-null int64
Survived       891 non-null int64
Pclass         891 non-null int64
Name           891 non-null object
Sex            891 non-null object
Age            714 non-null float64
SibSp          891 non-null int64
Parch          891 non-null int64
Ticket         891 non-null object
Fare           891 non-null float64
Cabin          204 non-null object
Embarked       889 non-null object
dtypes: float64(2), int64(5), object(5)
memory usage: 83.6+ KB

可以看到:

  • 数据包含 891 条记录,下标编号为 0 到 890。
  • 包含12个列
  • PassangerId、Survived、Pclass、Age、SibSp、Parch、Fare 几个字段为数值类型(64 位整形或64 位浮点型)
  • Name 、Sex 、Ticket 、Cabin 、Embarked 字段是对象,其实也就是字符串类型。
  • Age、Cabin、Embarked 二个字段存在缺失的情况, Age字段只有 714 个有效值,缺失177 个; Embarked 有889个值,缺失2个; Cabin 宇段仅有204 个值,缺失达到687 个。

  • “年龄”列中缺少177个值,解决方案是用中值年龄替换空值,对于异常值,这样比平均值鲁棒性更强。

data['Age'].fillna(data['Age'].median(), inplace=True)
data.describe()

3.4. 制作一些图表来看看基于性别的生存

survived_sex = data[data['Survived']==1]['Sex'].value_counts()
dead_sex = data[data['Survived']==0]['Sex'].value_counts()
df = pd.DataFrame([survived_sex, dead_sex])
df.index = ['Surv', 'Dead']
df.plot(kind='bar', stacked=True, figsize=(4,2))
<matplotlib.axes._subplots.AxesSubplot at 0x914d9b0>

png

性别变量似乎是一个决定性的特征,妇女更有可能生存。

3.5. 将生存与年龄变量联系起来

plt.hist 表示数据的频率分布的图形:

调用方式:

n, bins, patches = plt.hist(arr, bins=10, normed=0, facecolor='black', edgecolor='black',alpha=1,histtype='bar')
  • arr: 指定每个bin(箱子)分布的数据,对应x轴

  • bins : 直方图的柱数,指定bin(箱子)的个数,也就是总共有几条条状图 (水平尺寸相等的矩形对应于类间隔,称为bin,)

  • normed :是否将得到的直方图向量归一化。默认为0

  • color : 指定条状图的颜色

  • facecolor: 直方图颜色

  • edgecolor: 直方图边框颜色

  • alpha: 透明度

  • histtype: 直方图类型,‘bar’, ‘barstacked’, ‘step’, ‘stepfilled’

返回值 :

  • n: 直方图向量,是否归一化由参数normed设定

  • bins: 返回各个bin的区间范围

  • patches: 返回每个bin里面包含的数据,是一个list

figure = plt.figure(figsize=(4, 2))
plt.hist([data[data['Survived']==1]['Age'],  data[data['Survived']==0]['Age']], 
         stacked=True, color=['g','r'],bins =30, label=['Survived','Dead'])
plt.xlabel('Age')
plt.ylabel('Number of passengers')
plt.legend() #legend 显示图例
    <matplotlib.legend.Legend at 0x94837b8>

png

我们会注意到,少于10人的乘客比12岁以上且少于50岁的乘客更有可能生存。较老的乘客似乎也被抢救。

这两个图表可以确认,一旦遇到威胁的情况,水手和船长会遵循:“妇女和儿童第一!

from IPython.display import Image
Image("http://i.dailymail.co.uk/i/pix/2012/07/30/article-2181317-122F9398000005DC-649_468x420.jpg",height=200,width=200)

jpeg

3.6. 关注每位乘客的票价与生存息息相关

figure = plt.figure(figsize=(4,2))
plt.hist([data[data['Survived']==1]['Fare'],data[data['Survived']==0]['Fare']], stacked=True, color = ['g','r'],
         bins = 30,label = ['Survived','Dead'])
plt.xlabel('Fare')
plt.ylabel('Number of passengers')
plt.legend()
<matplotlib.legend.Legend at 0x9f3b588>

png

票价较低的乘客更有可能死亡。 换句话说,乘坐更昂贵的票,有更重要的社会地位更容易被抢救。

3.7. 将年龄,票价和生存结合起来

plt.figure(figsize=(5,2))
ax = plt.subplot()
ax.scatter(data[data['Survived']==1]['Age'], data[data['Survived']==1]['Fare'], c='green', s=20)
ax.scatter(data[data['Survived']==0]['Age'], data[data['Survived']==0]['Fare'], c='red',s=20)
ax.set_xlabel('Age')
ax.set_ylabel('Fare')
ax.legend(('survived','dead'),scatterpoints=1,loc='upper right',fontsize=10,)
<matplotlib.legend.Legend at 0x91e8320>

png

图上有一群明显的死亡乘客。 这些人是成年人(15至50岁)(最低票价)

3.8. 事实上,票价与等级相关,如下图所示。

ax = plt.subplot()
ax.set_ylabel('Average fare')
data.groupby('Pclass').mean()['Fare'].plot(kind='bar', figsize=(5,2), ax=ax)
<matplotlib.axes._subplots.AxesSubplot at 0x98275c0>

png

3.9. 登乘点如何影响生存。

survived_embark = data[data['Survived']==1]['Embarked'].value_counts()
dead_embark     = data[data['Survived']==0]['Embarked'].value_counts()
df = pd.DataFrame([survived_embark, dead_embark])
df.index = ['Survived','Dead']
df.plot(kind='bar',stacked=True, figsize=(4,2))
<matplotlib.axes._subplots.AxesSubplot at 0x92e89b0>

png

在这里没有明显的相关性。

4. 特征工程

在前一部分,我们分析了数据,并发现了一些有趣的相关性。 但是我们无法分析更复杂的特征,如名字或船票。

在这部分中,我们将重点介绍如何将这些特定特征转化为机器学习算法。

首先我们来定义print 函数,断言特征是否被处理。

def status(feature):
    print('Processing',feature, ': ok')

4.1. 加载数据

这里有一个技巧是将训练集和测试集合在一起。 特别是当您的测试集似乎具有训练集中不存在的功能时,这是非常有用的。 因此,如果我们不组合这两套,则在测试集上测试我们的模型将失败。

程序很简单:
我们从加载训练组和测试集开始。 我们创建一个名为combined的空dataframe。 然后我们附加测试到训练集上

import pandas as pd
def get_combined_data():
    # reading train data
    train = pd.read_csv('data/train.csv')

    # reading test data
    test = pd.read_csv('data/test.csv')

    # extracting and then removing the targets from the training data 
    targets = train.Survived
    train.drop('Survived',1,inplace=True)


    # merging train data and test data for future feature engineering
    combined = train.append(test)
    combined.reset_index(inplace=True)
    combined.drop('index',inplace=True,axis=1)

    return combined

combined = get_combined_data()
combined.shape
(1309, 11)

训练集和测试集被组合,行的总数(1309)是训练集和测试集的总和。

combined.head()

4.2. 提取乘客尊称titles

如果仔细看这些例子:

  • Braund, Mr. Owen Harris
  • Heikkinen, Miss. Laina
  • Oliva y Ocana, Dona. Fermina
  • Peter, Master. Michael J

你会注意到每个名字都有一个尊称! 这可以是一个简单的小姐或夫人,但有时候可能会像大师,爵士或多纳那样更复杂一些。 在这种情况下,我们可以通过简单地解析名字并提取尊称来引入关于社交状态的其他信息。

def get_titles():

    global combined

    # we extract the title from each name
    combined['Title'] = combined['Name'].map(lambda name:name.split(',')[1].split('.')[0].strip())

    # a map of more aggregated titles
    Title_Dictionary = {
                        "Capt":       "Officer",
                        "Col":        "Officer",
                        "Major":      "Officer",
                        "Jonkheer":   "Royalty",
                        "Don":        "Royalty",
                        "Sir" :       "Royalty",
                        "Dr":         "Officer",
                        "Rev":        "Officer",
                        "the Countess":"Royalty",
                        "Dona":       "Royalty",
                        "Mme":        "Mrs",
                        "Mlle":       "Miss",
                        "Ms":         "Mrs",
                        "Mr" :        "Mr",
                        "Mrs" :       "Mrs",
                        "Miss" :      "Miss",
                        "Master" :    "Master",
                        "Lady" :      "Royalty"

                        }

    # we map each title
    combined['Title'] = combined.Title.map(Title_Dictionary)

此功能解析名称并提取尊称。 然后,它将尊称映射到标题“Title”的类别。 我们选择:

  • Officer
  • Royalty
  • Mr
  • Mrs
  • Miss
  • Master

现在我们有一个名为Title的附加列,其中包含该信息。

get_titles()
combined.head()

不再有name功能。 出现新变量(Title_X)。 这些特征是二进制的。 例如,如果Title_Mr = 1,相应的Title是Mr.

4.3. 处理年龄

我们在第一部分看到,Age变量缺少177个值。 这是一个大数字(约13%的数据集)。
只要用平均值或中位年龄代替,可能不是最佳解决方案,因为年龄可能因团体和乘客类别而异。
为了理解为什么,我们按性别,标题和乘客类分组我们的数据集。

grouped_train = combined.head(891).groupby(['Sex','Pclass','Title'])
grouped_median_train = grouped_train.median()

grouped_test = combined.iloc[891:].groupby(['Sex','Pclass','Title'])
grouped_median_test = grouped_test.median()
grouped_median_train

看看中位数年龄列,这个值可以根据性别,Pclass和标题放在一起而有所不同。
例如:

  • 如果乘客是女性,Pclass 1和Royalty的平均年龄是39。
  • 如果乘客是男性,Pclass 3和具有先生的职称,中位数年龄是26岁。

让我们创建一个函数,根据这些不同的属性来填充失去的年龄组合。

def process_age():

    global combined

    # a function that fills the missing values of the Age variable

    def fillAges(row):
        if row['Sex']=='female' and row['Pclass'] == 1:
            if row['Title'] == 'Miss':
                return 30
            elif row['Title'] == 'Mrs':
                return 45
            elif row['Title'] == 'Officer':
                return 49
            elif row['Title'] == 'Royalty':
                return 39

        elif row['Sex']=='female' and row['Pclass'] == 2:
            if row['Title'] == 'Miss':
                return 20
            elif row['Title'] == 'Mrs':
                return 30

        elif row['Sex']=='female' and row['Pclass'] == 3:
            if row['Title'] == 'Miss':
                return 18
            elif row['Title'] == 'Mrs':
                return 31

        elif row['Sex']=='male' and row['Pclass'] == 1:
            if row['Title'] == 'Master':
                return 6
            elif row['Title'] == 'Mr':
                return 41.5
            elif row['Title'] == 'Officer':
                return 52
            elif row['Title'] == 'Royalty':
                return 40

        elif row['Sex']=='male' and row['Pclass'] == 2:
            if row['Title'] == 'Master':
                return 2
            elif row['Title'] == 'Mr':
                return 30
            elif row['Title'] == 'Officer':
                return 41.5

        elif row['Sex']=='male' and row['Pclass'] == 3:
            if row['Title'] == 'Master':
                return 6
            elif row['Title'] == 'Mr':
                return 26

    combined.Age = combined.apply(lambda r : fillAges(r) if np.isnan(r['Age']) else r['Age'], axis=1)

    status('age')
# 4. 丢失的年龄已被替换。
process_age()
Processing age : ok

4.4. 处理尊称 Title

def process_names():

    global combined
    # we clean the Name variable
    combined.drop('Name',axis=1,inplace=True)

    # encoding in dummy variable
    titles_dummies = pd.get_dummies(combined['Title'],prefix='Title')
    combined = pd.concat([combined,titles_dummies],axis=1)

    # removing the title variable
    combined.drop('Title',axis=1,inplace=True)

    status('names')
process_names()
combined.head()
Processing names : ok

4.5. 处理Fare船票价格

def process_fares():

    global combined
    # there's one missing fare value - replacing it with the mean.
    combined.Fare.fillna(combined.Fare.mean(),inplace=True)

    status('fare')
process_fares()
Processing fare : ok

4.6. 处理Embarked 登船港口的编号

def process_embarked():

    global combined
    # two missing embarked values - filling them with the most frequent one (S)
    combined.Embarked.fillna('S',inplace=True)

    # dummy encoding 
    embarked_dummies = pd.get_dummies(combined['Embarked'],prefix='Embarked')
    combined = pd.concat([combined,embarked_dummies],axis=1)
    combined.drop('Embarked',axis=1,inplace=True)

    status('embarked')
process_embarked()
Processing embarked : ok

4.7. 处理Cabin客舱位置

这个函数用 U(Unknow)代替NaN值。 然后将每个值映射到第一个字母,使用虚拟编码对客舱值进行编码。

def process_cabin():

    global combined

    # replacing missing cabins with U (for Uknown)
    combined.Cabin.fillna('U',inplace=True)

    # mapping each Cabin value with the cabin letter
    combined['Cabin'] = combined['Cabin'].map(lambda c : c[0])

    # dummy encoding ...
    cabin_dummies = pd.get_dummies(combined['Cabin'],prefix='Cabin')

    combined = pd.concat([combined,cabin_dummies],axis=1)

    combined.drop('Cabin',axis=1,inplace=True)

    status('cabin')
process_cabin()
combined.info()
Processing cabin : ok
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1309 entries, 0 to 1308
Data columns (total 26 columns):
PassengerId      1309 non-null int64
Pclass           1309 non-null int64
Sex              1309 non-null object
Age              1309 non-null float64
SibSp            1309 non-null int64
Parch            1309 non-null int64
Ticket           1309 non-null object
Fare             1309 non-null float64
Title_Master     1309 non-null float64
Title_Miss       1309 non-null float64
Title_Mr         1309 non-null float64
Title_Mrs        1309 non-null float64
Title_Officer    1309 non-null float64
Title_Royalty    1309 non-null float64
Embarked_C       1309 non-null float64
Embarked_Q       1309 non-null float64
Embarked_S       1309 non-null float64
Cabin_A          1309 non-null float64
Cabin_B          1309 non-null float64
Cabin_C          1309 non-null float64
Cabin_D          1309 non-null float64
Cabin_E          1309 non-null float64
Cabin_F          1309 non-null float64
Cabin_G          1309 non-null float64
Cabin_T          1309 non-null float64
Cabin_U          1309 non-null float64
dtypes: float64(20), int64(4), object(2)
memory usage: 266.0+ KB

现在已经没有丢失的值。

4.8. 处理Sex 乘客性别

此函数将字符串值male和female分别映射到1和0。

def process_sex():

    global combined
    # mapping string values to numerical one 
    combined['Sex'] = combined['Sex'].map({'male':1,'female':0})

    status('sex')
process_sex()
Processing sex : ok

4.9. 处理Pclass 客舱客级

该函数使用虚拟编码对Pclass(1,2,3)的值进行编码。

def process_pclass():

    global combined
    # encoding into 3 categories:
    pclass_dummies = pd.get_dummies(combined['Pclass'],prefix="Pclass")

    # adding dummy variables
    combined = pd.concat([combined,pclass_dummies],axis=1)

    # removing "Pclass"

    combined.drop('Pclass',axis=1,inplace=True)

    status('pclass')
process_pclass()
Processing pclass : ok

4.10. 处理Ticket 船票号

  • 这个函数首先通过提取票号前缀来预处理船票。 当提取前缀失败时返回XXX。
  • 然后使用虚拟编码对前缀进行编码。
def process_ticket():

    global combined

    # a function that extracts each prefix of the ticket, returns 'XXX' if no prefix (i.e the ticket is a digit)
    def cleanTicket(ticket):
        ticket = ticket.replace('.','')
        ticket = ticket.replace('/','')
        ticket = ticket.split()
        ticket = map(lambda t : t.strip() , ticket)
        ticket = list( filter(lambda t : not t.isdigit(), ticket) )
        if len(ticket) > 0:
            return ticket[0]
        else: 
            return 'XXX'


    # Extracting dummy variables from tickets:

    combined['Ticket'] = combined['Ticket'].map(cleanTicket)
    tickets_dummies = pd.get_dummies(combined['Ticket'],prefix='Ticket')
    combined = pd.concat([combined, tickets_dummies],axis=1)
    combined.drop('Ticket',inplace=True,axis=1)

    status('ticket')
process_ticket()
Processing ticket : ok

4.11. 处理家庭 SibSp 和 ParCh

  • SibSp 兄弟姐妹和配偶在船数量
  • ParCh 父母孩子在船数量
    这部分包括根据家庭的大小创建新的变量(大小是这样,我们创建的另一个变量)。

此函数产生了4个新特征:

  • FamilySize:包括乘客(他/她)在内的亲戚总数。
  • Sigleton: 一个布尔变量,描述size = 1家族
  • SmallFamily: 一个布尔变量,描述2 <= size <= 4家族
  • LargeFamily: 一个布尔变量,描述 5 < size 家族
def process_family():

    global combined
    # introducing a new feature : the size of families (including the passenger)
    combined['FamilySize'] = combined['Parch'] + combined['SibSp'] + 1

    # introducing other features based on the family size
    combined['Singleton'] = combined['FamilySize'].map(lambda s : 1 if s == 1 else 0)
    combined['SmallFamily'] = combined['FamilySize'].map(lambda s : 1 if 2<=s<=4 else 0)
    combined['LargeFamily'] = combined['FamilySize'].map(lambda s : 1 if 5<=s else 0)

    status('family')
process_family()
Processing family : ok

我们最终得到了68个特征。
可以看到,特征范围在不同的间隔,让我们以单位间隔规范化所有这些特征。(除了PassengerId。)

combined.shape
(1309, 68)
combined.head()
def scale_all_features():

    global combined

    features = list(combined.columns)
    features.remove('PassengerId')
    combined[features] = combined[features].apply(lambda x: x/x.max(), axis=0)

    print('Features scaled successfully !')
scale_all_features()
Features scaled successfully !

5. 模型

在这部分中,我们使用刚刚创建的乘客特征,构建一个统计模型。
你可以把这个模型看成是一个黑盒子,用来处理任何新乘客的信息,并决定他是否存活下来。

有各种各样的模型,从逻辑回归到决策树和更复杂的模型,如随机森林和梯度提升树。
我们将使用随机森林。随机森林已经证明在Kaggle比赛中效率很高。

有关为什么整体方法运行良好的更多细节,可以参考这些帖子:
http://mlwave.com/kaggle-ensembling-guide/
http://www.overkillanalytics.net/more-is-always-better-the-power-of-simple-ensembles/

回到我们的问题,我们现在必须:

  • 把组合数据集分为训练数据集和测试集
  • 使用训练数据集建立预测模型。
  • 使用训练数据集评估模型。
  • 使用测试集测试模型,生成并输出提交文件。
  • 请记住,我们必须重复2和3,直到达到可接受的评估分数。

5.1. 我们先从导入有用的库开始

from sklearn.pipeline import make_pipeline
from sklearn.ensemble import RandomForestClassifier
from sklearn.feature_selection import SelectKBest
from sklearn.cross_validation import StratifiedKFold
from sklearn.grid_search import GridSearchCV
from sklearn.ensemble.gradient_boosting import GradientBoostingClassifier
from sklearn.cross_validation import cross_val_score

为了评估我们的模型,我们将使用精度指标的5倍交叉验证。
为此,我们将定义一个小的评分函数。

def compute_score(clf, X, y,scoring='accuracy'):
    xval = cross_val_score(clf, X, y, cv = 5,scoring=scoring)
    return np.mean(xval)

从组合数据集中恢复训练数据集和测试集

def recover_train_test_target():
    global combined

    train0 = pd.read_csv('data/train.csv')

    targets = train0.Survived
    train = combined.ix[0:890]
    test = combined.ix[891:]

    return train,test,targets
train,test,targets = recover_train_test_target()

5.2. 特征选择

到目前为止,我们已经提出了68个特征,这个数字是相当大的。
当特征工程完成时,我们通过选择必不可少的特征来降低维度。
事实上,特征选择要有很多好处:

  • 它减少数据之间的冗余
  • 它加快了训练过程
  • 它减少过度拟合

可以使用基于树的估计器来计算特征重要性,这又可以用于丢弃不相关的特征。

from sklearn.ensemble import ExtraTreesClassifier
from sklearn.feature_selection import SelectFromModel
clf = ExtraTreesClassifier(n_estimators=200)
clf = clf.fit(train, targets)

我们来看看每个特征的重要性。

features = pd.DataFrame()
features['feature'] = train.columns
features['importance'] = clf.feature_importances_
# 5. features.sort(['importance'],ascending=False)
features.sort_values(by=['importance'], ascending=True, inplace=True)
features.set_index('feature', inplace=True)

features.plot(kind='barh', figsize=(10, 10))
<matplotlib.axes._subplots.AxesSubplot at 0xc382160>

png

你可能会注意到,

  • 与Title_Mr,Age,Fare和Sex有很大的关联。
  • 与Passenger_Id也有重要的相关性。

现在,我们将转换为训练数据集和测试集为更加紧凑简洁的数据集

好极了! 现在我们找到15个特征。

model = SelectFromModel(clf, prefit=True)
train_new = model.transform(train)
train_new.shape
(891, 16)
test_new = model.transform(test)
test_new.shape
(418, 16)

5.3. 超参数调整

我们将使用随机森林模型,随机森林相当方便。 然而,他们有一些参数进行调整,以获得预测任务的最佳模型。
要了解有关随机森林的更多信息,可以参考以下链接:https://www.analyticsvidhya.com/blog/2015/06/tuning-random-forest-model/

# 6. turn run_gs to True if you want to run the gridsearch again.
run_gs = False

if run_gs:
    parameter_grid = {
                 'max_depth' : [4, 6, 8],
                 'n_estimators': [50, 10],
                 'max_features': ['sqrt', 'auto', 'log2'],
                 'min_samples_split': [1, 3, 10],
                 'min_samples_leaf': [1, 3, 10],
                 'bootstrap': [True, False],
                 }
    forest = RandomForestClassifier()
    cross_validation = StratifiedKFold(targets, n_folds=5)

    grid_search = GridSearchCV(forest,
                               scoring='accuracy',
                               param_grid=parameter_grid,
                               cv=cross_validation)

    grid_search.fit(train, targets)
    model = grid_search
    parameters = grid_search.best_params_

    print('Best score: {}'.format(grid_search.best_score_))
    print('Best parameters: {}'.format(grid_search.best_params_))
else: 
    parameters = {'bootstrap': False, 'min_samples_leaf': 3, 'n_estimators': 50, 
                  'min_samples_split': 10, 'max_features': 'sqrt', 'max_depth': 6}

    model = RandomForestClassifier(**parameters)
    model.fit(train, targets)

现在,通过扫描超参数的所有几种组合构建模型,我们可以生成一个输出文件,以在Kaggle上提交。
这个解决方案可以在公开排行榜上得到了0.8134的准确度。

compute_score(model, train, targets, scoring='accuracy')
0.82719049346635054
output = model.predict(test).astype(int)
df_output = pd.DataFrame()
aux = pd.read_csv('data/test.csv')
df_output['PassengerId'] = aux['PassengerId']
df_output['Survived'] = output
df_output[['PassengerId','Survived']].to_csv('data/output.csv',index=False)

6. 结论

在这篇文章中,我们探索了一个由Kaggle带给我们的有趣数据集。

我们经历了基本的流程:

  • 数据探索和可视化:制定假设的初步步骤
  • 数据清理
  • 特征工程
  • 特征选择
  • 超参数调整
  • 模型生成和结果提交

有关这个挑战的文章很多,显然还有改进的余地。

  • 挖掘更多的数据,并最终构建新的特征。
  • 尝试不同的模型:逻辑回归,渐变增强树,XGboost,…
  • 尝试综合学习技巧(堆叠,混合)
  • 尝试ML模型的其他家族(神经网络?)

技术交流学习,请加QQ微信:631531977
目录