经典又兼具备趣味性的Kaggle案例泰坦尼克号问题

kaggle入门——逻辑回归应用之kaggle泰坦尼克之灾 原文连接:https://blog.csdn.net/han_xiaoyang/article/details/49797143#commentBox
https://www.kesci.com/home/project/5bfe39b3954d6e0010681cd1

背景

关于Kaggle

  • 我是kaggle地址,翻我牌子
  • 这就是哪个无数【数据挖掘先驱】们,在回答”枪我有了,哪能找到靶子练练手啊?“时候的答案

Kaggel是一个要数据有数据,要实际应用场景有场景,要一起在数据挖掘领域high得不要不要的小伙伴就有小伙伴的地方啊!!!
据挖掘竞赛),企业或者研究者可以将问题背景、数据、期望指标等发布到Kaggle上,以竞赛的形式向广大的数据科学家征集解决方案。而热爱数(dong)据(shou)挖(zhe)掘(teng)的小伙伴们可以下载/分析数据,使用统计/机器学习/数据挖掘等知识,建立算法模型,得出结果并提交,排名top的可能会有奖金哦!

关于泰坦尼克号之灾

  • 下面是问题的背景页

  • 可下载Data的页面

  • 下面是小伙伴们最爱的forum页面,你会看到各种神级人物厉(qi)害(pa)的数据处理/建模想法,你会直视『世界真奇妙』。

  • 泰坦尼克号问题背景

    • 就是那个大家都熟悉的『Jack and Rose』的故事,豪华游艇倒了,大家都惊恐逃生,可是救生艇的数量有限,无法人人都有,副船长发话了『lady and kid first!』,所以是否获救其实并非随机,而是基于一些背景有rank先后的。
    • 训练和测试数据是一些乘客的个人信息以及存活状况,要尝试根据它生成合适的模型并预测其他人的存活状况。
    • 对,这是一个二分类问题,是我们之前讨论的logistic regression所能处理的范畴。

初探数据

先看看数据长什么样,在Date下我们的train.csv和test.csv两个文件分别存着官方给的训练和测试数据。

import pandas as pd #数据分析
import numpy as np #科学计算
from pandas import Series,DataFramedata_train = pd.read_csv("e:/data/Titanic_data/train.csv")
data_train.columns

输出

Index(['PassengerId', 'Survived', 'Pclass', 'Name', 'Sex', 'Age', 'SibSp','Parch', 'Ticket', 'Fare', 'Cabin', 'Embarked'],dtype='object')

看到大概有以下这些字段

PassengerId =>乘客ID

Pclass => 乘客登记(1/2/3等舱位)

Name => 乘客姓名

Sex =>性别

Age => 年龄

SibSp => 堂兄弟/妹个数

Parch => 父母与小孩个数

Ticket => 船票信息

Fare => 票价

Cabin => 客舱

Embarked =>登船港口

查看数据集信息

data_train.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名乘客,但是有些属性数据不全,比如说:

  • Age(年龄) 属性只有714名乘客是有记录
  • Cabin(客舱) 更是只有204名乘客是已知的

    再查看一下数值的具体情况

data_train.describe()


由mean字段的值,大概0.383838的人最后获救了,2/3等舱人数要比1等舱多,平均乘客年龄大概是29.7岁…
上面的简单描述性信息并没有什么用,需要更细力度的分析数据

数据初步分析

仅仅上面的对数据的了解,依旧无法提供给我们想法和思路。再深入一点看看数据

查看每个/多个 属性和最后的Survived之间有什么样的关系

乘客各属性分布

import matplotlib.pyplot as plt
%matplotlib inline
fig = plt.figure()
fig.set(alpha=0.2) #设定图表颜色alpha参数plt.subplot2grid((2,3),(0,0)) #在一张大图里分列几个小图
plt.rcParams['font.sans-serif'] = ['SimHei'] #指定默认字体
plt.rcParams['axes.unicode_minus'] = False #解决中文显示为方框的问题
data_train.Survived.value_counts().plot(kind='bar') #柱状图
plt.title("获救情况 (1为获救)") #标题
plt.ylabel("人数")plt.subplot2grid((2,3),(0,1))
data_train.Pclass.value_counts().plot(kind='bar')
plt.ylabel("人数")
plt.title("乘客等级分布")plt.subplot2grid((2,3),(0,2))
plt.scatter(data_train.Survived,data_train.Age) #散点图
plt.ylabel("年龄")
plt.grid(b=True,which='major',axis='y') #显示y轴网格
plt.title("按年龄看获救分布 (1为获救)")plt.subplot2grid((2,3),(1,0),colspan=2)
data_train.Age[data_train.Pclass == 1].plot(kind='kde')
data_train.Age[data_train.Pclass == 2].plot(kind='kde')
data_train.Age[data_train.Pclass == 3].plot(kind='kde')
plt.xlabel("年龄")
plt.ylabel("密度")
plt.title("各等级的乘客年龄分布")
plt.legend(('头等舱','2等舱','3等舱'),loc='best')  #显示图例plt.subplot2grid((2,3),(1,2))
data_train.Embarked.value_counts().plot(kind='bar')
plt.title("各登船口岸上船人数")
plt.ylabel("人数")
plt.show()


在图上可以看出来,被救的人300多点,不到半数;3等舱人数非常多,遇难和获救的人年龄跨度似乎都很广;3个不同舱年龄总体趋势似乎也一致,2/3等舱乘客20多岁的人最多,1等舱40岁左右的最多(似乎符合财富和年龄的分配);登船港口人数按照S、C、Q递减,而且s远对于另外两个港口。

现在可能会有一些想法了:

  • 不同仓位/乘客等级了能和财富/地位有关系,最后获救的概率可能会不一样
  • 年龄对获救概率也一定是有影响的,副船长说 小孩和女士先走
  • 和登船港口是不是也有关系呢?也许登船港口不同,人的身份地位不同?
    口说无凭,空想无益,老老实实再来统计统计,看看这些属性值的统计分布。

属性与获救结果的关联统计

查看各乘客等级的获救情况

fig = plt.figure()
fig.set(alpha=0.2) #设定图表颜色alpha参数Survived_0 = data_train.Pclass[data_train.Survived == 0].value_counts()
survived_1 = data_train.Pclass[data_train.Survived == 1].value_counts()
df = pd.DataFrame({'未获救':Survived_0,'获救':survived_1})
df.plot(kind='bar',stacked=True) #stacked表示叠加
plt.title('各乘客等级的获救情况')
plt.xlabel('乘客等级')
plt.ylabel('人数')
plt.show()

<matplotlib.figure.Figure at 0x1d088f2ba20>

明显等级为1的乘客,获救的概率高很多。这一定是影响救结果的一个特征。

查看性别的获救情况

#### 查看性别的获救情况
fig = plt.figure()
fig.set(alpha=0.2)Survived_m = data_train.Survived[data_train.Sex == 'male'].value_counts()
Survived_f = data_train.Survived[data_train.Sex == 'female'].value_counts()df=pd.DataFrame({'男性':Survived_m,'女性':Survived_f})
df.plot(kind='bar',stacked=True)
plt.title('按性别查看获救情况')
plt.xlabel('性别')
plt.ylabel('人数')
plt.show()

<matplotlib.figure.Figure at 0x1d0a6e6a828>

外国人果然尊重lady,lady first践行的不错。性别无疑也要作为重要特征加入最后的模型之中。

来个详细版的

#然后来看看各种舱级别情况下各性别的获救情况
fig=plt.figure()
fig.set(alpha=0.65)
plt.title('舱等级和性别的获救情况')ax1 = fig.add_subplot(141)
data_train.Survived[data_train.Sex == 'female'][data_train.Pclass != 3].value_counts().plot(kind='bar',label='female high class',color='#FA2479')
ax1.set_xticklabels(["未获救","获救"],rotation=0)
plt.legend(["女性/低级舱"],loc='best')ax2=fig.add_subplot(142,sharey=ax1)
data_train.Survived[data_train.Sex == 'female'][data_train.Pclass == 3].value_counts().plot(kind='bar',label='female low class',color='pink')
ax2.set_xticklabels(["未获救","获救"],rotation=0)
plt.legend(["女性/低级舱"],loc='best')ax3=fig.add_subplot(143,sharey=ax1)
data_train.Survived[data_train.Sex == 'male'][data_train.Pclass != 3].value_counts().plot(kind='bar',label='male high class',color='lightblue')
ax3.set_xticklabels(["未获救","获救"],rotation=0)
plt.legend(["男性/高级舱"],loc="best")ax4=fig.add_subplot(144,sharey=ax1)
data_train.Survived[data_train.Sex == 'male'][data_train.Pclass == 3].value_counts().plot(kind='bar',label='male low class',color='steelblue')
ax4.set_xticklabels(["未获救","获救"],rotation=0)
plt.legend(["男性/低级舱"],loc="best")

<matplotlib.legend.Legend at 0x1d0a0dfaf60>

可以看出之前的判断是正确的

查看各登船港口的获救情况

fig = plt.figure()
fig.set(alpha=0.2) Survived_0 = data_train.Embarked[data_train.Survived == 0].value_counts()
Survived_1 = data_train.Embarked[data_train.Survived == 1].value_counts()
df=pd.DataFrame({"未获救":Survived_0,"获救":Survived_1})
df.plot(kind='bar',stacked=True)
plt.title("各登船港口乘客的获救情况")
plt.xlabel("登船港口")
plt.ylabel("人数")plt.show()

<matplotlib.figure.Figure at 0x1d0c61b08d0>

看堂兄弟/妹,孩子/父母有几人,对是否获救的影响

gg = data_train.groupby(['SibSp','Survived'])['PassengerId'].count()
print(gg)gp = data_train.groupby(['Parch','Survived'])['PassengerId'].count()
print(gp)


没有看出特别明显的规律。作为备选特征先放一放。

ticket cabin的分析

ticket是船票编号,应该是unique的,和最后的结果没有太大的关系,先不纳入考虑的特征范畴
cabin只有204个乘客有值,我们先看一下它的一个分布

data_train.Cabin.value_counts()


数据三三两两的很不集中,也许前面的ABCD是指加班位置,然后编号是房间号
Cabin属性,应该算作类目型的,本来缺失值就多,还如此不集中,第一感觉是,如果直接按照类目特征处理的话,太散了,估计每个因子化后的特征都拿不到什么权重。加上有那么多缺失值,就下那边Cabin缺失与否作为条件(虽然这部分信息缺失可能只是未登记,或者只是丢失了而已,所以这样做未必妥当),现在有无Cabin信息这个粗细力度上看看Survived的情况好了。

fig = plt.figure()
fig.set(alpha=0.2) #设定图标颜色alpha参数Survived_cabin = data_train.Survived[data_train.Cabin.notnull()].value_counts()
Survived_nocabin = data_train.Survived[data_train.Cabin.isnull()].value_counts()
df=pd.DataFrame({"有":Survived_cabin,"无":Survived_nocabin}).transpose()
df.plot(kind='bar',stacked=True)
plt.title("按Cabin有无看获救情况")
plt.xlabel("Cabin有无")
plt.ylabel("人数")
plt.show()

<matplotlib.figure.Figure at 0x1d093f26e10>

有Cabin记录的似乎获救概率稍高一些,先这么放着吧

简单数据预处理

大体数据的情况看了一遍,对感兴趣的属性也有个大概的了解了。下一步干啥?应该处理处理这些数据,为机器学习建模做点准备了。

这里说的数据预处理,其实就包括了很多kaggler津津乐道的feature engineering过程,非常有必要!

【特征工程太重要了!】

【特征工程太重要了!】

【特征工程太重要了!】

先从最突出的数据属性开始吧,对,Cabin和Age,有缺失数据实在是对下一步工作影响太大。

先说Cabin,暂时就按照上面说的,按Cabin有无数据,将这个属性处理成Yes和No两种类型吧。

再说Age:

通常遇到缺失值的情况,会有几种常见的处理方式

  • 如果缺失的样本占总数比例极高,我们可能就直接舍弃了,作为特征加入的话,可能反倒带入noice,影响最后的结果了。
  • 如果缺失的样本适中,而该属性非连续值特征属性(比如类目属性),那就把NaN作为一个新类别,加到类别特征中
  • 如果缺失值样本适中,而该属性为连续值特征属性,会开率给定一个step(比如这里的age,可以考虑每隔2/3岁为一个步长),然后把它离散化,之后把NaN作为一个type加到属性类目中。
  • 有些情况下,缺失值的个数并不是特别多,哪我们也可以试着根据已有的值,拟合一下数据,补充上。

本例中,后两种处理方式应该都是可行的,先试试拟合补全吧(虽然说没有特别多的背景可供我们拟合,这不一定是一个多么好的选择)

我们这里使用scikit-learn种的RandomForest来拟合一下缺失的年龄数据(注:RandomForest是一个用在原始数据中做不同采样,建立多颗DesionTree,再进行average等等来降低过拟合现象,提高结果的机器学习算法,之后会介绍)

from sklearn.ensemble import RandomForestRegressor### 使用 RandomForestClassifier 填补缺失的年龄属性
def set_missing_ages(df):# 把已有的数值型特征取出来丢进Random Forest Regressor中age_df = df[['Age','Fare', 'Parch', 'SibSp', 'Pclass']]#乘客分成已知年龄和未知年龄两部分known_age = age_df[age_df.Age.notnull()]unknown_age = age_df[age_df.Age.isnull()]#y即目标年龄y = known_age.iloc[:,0]#x即特征属性X = known_age.iloc[:,1:]#fit到RandomForestRegressor之中rfr = RandomForestRegressor(random_state=0,n_estimators=2000,n_jobs=-1)rfr.fit(X,y)#用得到的模型进行未知年龄结果预测predictedAges = rfr.predict(unknown_age.iloc[:,1::])#用得到的预测结果填补原缺失数据df.loc[(df.Age.isnull()),'Age'] = predictedAgesreturn df,rfr
def set_Cabin_type(df):df.loc[(df.Cabin.notnull()),'Cabin'] = 'Yes'df.loc[(df.Cabin.isnull()),'Cabin'] = 'No'return dfdata_train,rfr = set_missing_ages(data_train)
data_train = set_Cabin_type(data_train)
data_train.info()


嗯,目的达到了,OK了

因为逻辑回归建模时,需要输入的特征 都是数值型特征,通常会先对类目型的特征因子化。什么叫因子化呢?举个例子:

以Cabin为例。原本一个属性维度,因为其取值可以是[‘yes’,‘no’],而将其平展开为’Cabin_yes’,'Cabin_no’两个属性

  • 原本Cabin取值为yes的,在此处的"Cabin_yes"下取值为1,在"Cabin_no"下取值为0
  • 原本Cabin取值为no的,在此处的"Cabin_yes"下取值为0,在"Cabin_no"下取值为1
    我们使用pandas的"get_dummies"来完成这个工作,并拼接在原来的"data_train"之上,如下所示。
dummies_Cabin = pd.get_dummies(data_train['Cabin'],prefix='Cabin')
dummies_Embarked  = pd.get_dummies(data_train['Embarked'],prefix='Embarked')
dummies_Sex = pd.get_dummies(data_train['Sex'],prefix='Sex')
dummies_Pclass = pd.get_dummies(data_train['Pclass'],prefix='Pclass')
df = pd.concat([data_train,dummies_Cabin,dummies_Embarked,dummies_Sex,dummies_Pclass],axis=1)
df.drop(['Pclass','Name','Sex','Ticket','Cabin','Embarked'],axis=1,inplace=True) #inplace=True:不创建新的对象,直接对原始对象进行修改
df.columns

df.info()


成功的把类目属性全部转化成0,1的属性值了。
这样,看起来,需要的属性值都有课,且都是数值类型的。?
但是仔细已经按Age和Fare两个属性,乘客的数值幅度变化太大,如果了解逻辑回归与梯度下降的话,会知道,各属性之间scale差距太大,将对收敛速度造成几万点伤害,设置不收敛…所以先用scikit-learn里面的preprocessing模块对这两列做一个scaling,其实就是将一些变化幅度较大的特征化到[-1,1]之内。

import sklearn.preprocessing as preprocessing
scaler = preprocessing.StandardScaler()
#知道shape属性是多少,但是想变成只有一列,行数不知道多少,通过`z.reshape(-1,1)`,Numpy自动计算
age_scale_param = scaler.fit(df['Age'].values.reshape(-1,1))
df['Age_scaled'] = scaler.fit_transform(df['Age'].values.reshape(-1,1),age_scale_param)
fare_scale_param = scaler.fit(df['Fare'].values.reshape(-1,1))
df['Fare_scaled'] = scaler.fit_transform(df['Fare'].values.reshape(-1,1),fare_scale_param)
df

逻辑回归建模

把需要的feature字段取出来

from sklearn import linear_model#用正则表达式取出我们要的属性值
train_df = df.filter(regex='Survived|Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass_.*')#y即Survival结果:第0列
y = train_df.iloc[:,0]#X即特征属性:第1列及后序列
X = train_df.iloc[:,1:]#fit到LogisticRegression之中
clf = linear_model.LogisticRegression(C=1.0,penalty='l1',tol=1e-6)
clf.fit(X,y)clf

X.shape

(891, 14)
good 很顺利,我们得到了一个model.
先淡定!淡定!不是直接把test.csv直接丢到model里就能拿到结果啊…我们的"test_data"也要做和"train_data"一样的预处理啊!!

data_test = pd.read_csv("e:/data/Titanic_data/test.csv")
data_test.loc[(data_test.Fare.isnull()),'Fare'] = 0
#接着我们对test_data做和train_data中一致的特征变换
#首先用同样变得RandomForestRegressor模型填上丢失的年龄
tmp_df = data_test[['Age','Fare','Parch','SibSp','Pclass']]
null_age = tmp_df[data_test.Age.isnull()].as_matrix()
#根据特征属性Xy预测年龄并补上
X = null_age[:,1:]
predictedAges = rfr.predict(X)
data_test.loc[(data_test.Age.isnull()),'Age'] = predictedAgesdata_test = set_Cabin_type(data_test)
dummies_Cabin = pd.get_dummies(data_test['Cabin'],prefix='Cabin')
dummies_Embarked = pd.get_dummies(data_test['Embarked'],prefix='Embarked')
dummies_Sex = pd.get_dummies(data_test['Sex'],prefix='Sex')
dummies_Pclass = pd.get_dummies(data_test['Pclass'],prefix='Pclass')df_test = pd.concat([data_test,dummies_Cabin,dummies_Embarked,dummies_Sex,dummies_Pclass],axis=1)
df_test.drop(['Pclass','Name','Sex','Ticket','Cabin','Embarked'],axis=1,inplace=True)
df_test['Age_scaled'] = scaler.fit_transform(df_test['Age'].values.reshape(-1,1),age_scale_param)
df_test['Fare_scaled'] = scaler.fit_transform(df_test['Fare'].values.reshape(-1,1),fare_scale_param)
df_test


418 rows × 17 columns
不错不错,数据很ok,差最后一步了。下面就做预测结果吧!!

test = df_test.filter(regex='Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass_.*')
predictions = clf.predict(test)
result = pd.DataFrame({'PassengerId':data_test['PassengerId'],'Survived':predictions.astype(np.int32)})
result.to_csv("e:/data/Titanic_data/logistic_regression_predictions.csv",index=False)
pd.read_csv('e:/data/Titanic_data/logistic_regression_predictions.csv')


418 rows × 2 columns
在Kaggle的Make a submission页面,提交上结果。如下:

0.76555,结果还不错,毕竟,这只是简单分析处理过后的一个baseline模型。

逻辑回归系统优化

模型系数关联分析

提交只是万里长征第一步,这才刚撸完baseline model,模型还需要优化

看过Andrew Ng老师的machine Learning课程的同学们知道,现在应该分析分析模型现在的状态了,是过/欠拟合,以确定我们需要更多的特征还是更多的数据,或者其他操作。
我们有一条很著名的learning curves对吧。

不过在现在的场景下,先不着急做这个事情,我们这个baseline系统还有些粗糙,先再挖掘挖掘。

  • 首先,Name和Ticket两个属性被我们完整舍弃了(好吧,其实是因为这俩属性,几乎每一条记录都是一个完全不同的值,我们并没有找到很直接的处理方式)。
  • 然后,我们想想,年龄的拟合本身也未必是一件非常靠谱的事情,我们依据其余属性,其实并不能很好地拟合预测出未知的年龄。再一个,以我们地日常经验,小朋友和老人可能
    得到地照顾会多一些,这样看的话,年龄作为一个连续值,给一个固定的系数,应该和年龄是一个正相关或者负相关,西湖体现不出两头受照顾的实际情况,
    所以,说不定把年龄离散化,按区段分作类别属性会更合适一些。

    上面只是瞎想的,是不是这么回事呢,老老实实先把得到的model系数和feature关联起来看看。

LR模型系数
pd.DataFrame({"columns":list(train_df.columns)[1:],"coef":list(clf.coef_.T)})

交叉验证

  • 【要做交叉验证
  • 要做交叉验证
  • 要做交叉验证
    嗯,重要的事情说三遍。通常情况下,这么做cross validation:把train.csv分成两部分,一部分训练我们需要的模型,另一部分数据上看我们预测算法的效果。

    用scilit-learn的cross_validation来帮我们完成小数据集上的这个工作。

    先简单看看cross validation情况下的打分

from sklearn.model_selection import cross_val_score,train_test_split#简单看看打分情况
clf = linear_model.LogisticRegression(C=1.0,penalty='l1',tol=1e-6)
all_data = df.filter(regex='Survived|Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass_.*')
X = all_data.iloc[:,1:]
y = all_data.iloc[:,0]
print(cross_val_score(clf,X,y,cv=5))

[ 0.81564246 0.81564246 0.78651685 0.78651685 0.81355932]
结果是[ 0.81564246 0.81564246 0.78651685 0.78651685 0.81355932]
似乎比Kaggle上的结果略高,毕竟用的不是同一份数据集评估的。
既然要做交叉验证,干脆就把交叉验证里面bad case拿出来看看,看看人眼审核,是否能发现什么蛛丝马迹,是忽略了哪些信息,使得这些乘客被判定错了。再把bad case上面得到的想法和前面系数分析的合在一起,然后逐个试试。
下面做数据分割,并再原始数据集上瞄一眼bad case:

#分隔数据,按照训练数据:测试数据 = 7:3
split_train,split_cv = train_test_split(df,test_size=0.3,random_state=42)train_df = split_train.filter(regex='Survived|Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass_.*')#生成模型
clf = linear_model.LogisticRegression(C=1.0,penalty='l1',tol=1e-6)
clf.fit(train_df.iloc[:,1:],train_df.iloc[:,0])#对cross validation数据进行预测
cv_df = split_cv.filter(regex='Survived|Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass_.*')
predictions = clf.predict(cv_df.iloc[:,1:])origin_data_train = pd.read_csv("e:/data/Titanic_data/train.csv")
bad_cases = origin_data_train.loc[origin_data_train['PassengerId'].isin(split_cv[predictions != cv_df.iloc[:,0]]['PassengerId'].values)]
bad_cases


可以跑一遍自己试试,拿到bad cases之后,仔细看看。也会有一些猜测和想法。其中会有一部分印证在系数分析部分的猜测,那这些优化的想法的优先级可以放高一些。

现在有了"train_df"和"cv_df"两个数据部分,前者用于训练model,后者用于评定和选择模型。可以开始可劲折腾了。

我们随便列一些可能可以做的优化操作:

  • Age属性不使用现在的拟合方式,而是根据名称中的『Mr』『Mrs』『Miss』等的平均值进行填充。
  • Age不做成一个连续值属性,而是使用一个步长进行离散化,变成离散的类目feature。
  • Cabin再细化一些,对于有记录的Cabin属性,我们将其分为前面的字母部分(我猜是位置和船层之类的信息) 和 后面的数字部分(应该是房间号,有意思的事情是,如果你仔细看看原始数据,你会发现,这个值大的情况下,似乎获救的可能性高一些)。
  • Pclass和Sex俩太重要了,我们试着用它们去组出一个组合属性来试试,这也是另外一种程度的细化。
  • 单加一个Child字段,Age<=12的,设为1,其余为0(你去看看数据,确实小盆友优先程度很高啊)
  • 如果名字里面有『Mrs』,而Parch>1的,我们猜测她可能是一个母亲,应该获救的概率也会提高,因此可以多加一个Mother字段,此种情况下设为1,其余情况下设为0
  • 登船港口可以考虑先去掉试试(Q和C本来就没权重,S有点诡异)
  • 把堂兄弟/兄妹 和 Parch 还有自己 个数加在一起组一个Family_size字段(考虑到大家族可能对最后的结果有影响)
  • Name是一个我们一直没有触碰的属性,我们可以做一些简单的处理,比如说男性中带某些字眼的(‘Capt’, ‘Don’, ‘Major’, ‘Sir’)可以统一到一个Title,女性也一样。

大家接着往下挖掘,可能还可以想到更多可以细挖的部分。我这里先列这些了,然后我们可以使用手头上的”train_df”和”cv_df”开始试验这些feature engineering的tricks是否有效了

试验的过程比较漫长,也需要有耐心,而且我们经常会面临很尴尬的状况,就是我们灵光一闪,想到一个feature,然后坚信它一定有效,结果试验下来,效果还不如试验之前的结果。恩,需要坚持和耐心,以及不断的挖掘。

learning curves

有一个很可能发生的问题是,我们不断的做feature engineering,产生的特征越来越多,用这些特征去训练模型,
会对我们的训练集拟合的越来越好,同时也可能在逐步丧失泛化能力,从而在待预测的数据上表现不佳,也就是发生过拟合问题。

从另一个角度上说,如果模型在待预测的数据上表现不佳,除掉上面说的过拟合问题,也有可能是欠拟合问题,也就是说在训练集上,其实拟合的也不是那么好。》

过拟合和欠拟合解释起来就是:

  • 过拟合就像是你班上学数学比较刻板的同学,老师讲过的题目,一字不漏全记下来了,于是老师再出一样的题目,分分钟精确出结果。but数学考试,
    总是碰到新题目,所以成绩不咋地。
  • 欠拟合就像是,差生。练老师讲的练习题也记不住,于是连老师出一样题目复习的周测都做不好,考试更是可想而知了。
    而在机器学习的问题上,对于过拟合和欠拟合两种情形,优化的方式是不同的。

对过拟合而言,通常以下策略对结果优化是有用的:

  • 做一下feature selection,挑出较好的feature的subset来做training
  • 提供更多的数据,从而弥补原始数据的bias问题,学习到的model也会更准确

而对于欠拟合而言,通常需要更多的feature,更复杂的模型来提高准确度。

著名的learing curve可以帮我们判定我们的模型现在所处状态。我们以样本数为横坐标,训练和交叉验证集上的错误率为纵坐标,两种状态分别如下两张图所示:过拟合(overfitting/high variace),欠拟合(underfitting/high bias)

我们也可可以把错误率替换成准确率,得到另一种形式的learning curve(sklearn 里面是这么做的)。

回到我们的问题,我们用scikit-learn里面的learning_curve来帮我们分辨我们的模型状态。举个例子,我们画一下我们最先得到的baseline model的learning curve

import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import learning_curve#用sklearn的learning_curve 得到training_score和cv_scoer,使用matplotlib画出learning curve
def plot_learing_curve(estimator,title,X,y,ylim=None,cv=None,n_jobs=1,train_size=np.linspace(.05,1.,20),verbose=0,plot=True):'''画出data在某模型上的learning curve.参数解释--------estimator:使用的分类器title:表格的标题X:输入的feature,numpy类型y:输入的target vactorylim:tuple格式的(ymin,ymax),设定图像中纵坐标的最低点和最高点cv:做cross-validation的时候,数据分成的份数n_jobs:并行的任务数(默认1)'''train_sizes,train_scores,test_scores = learning_curve(estimator,X,y,cv=cv,n_jobs=n_jobs,train_sizes=train_size,verbose=verbose)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)if plot:plt.figure()plt.title(title)if ylim is not None:plt.ylim(*ylim)plt.xlabel(u"训练样本数")plt.ylabel(u"得分")plt.gca().invert_yaxis()plt.grid()plt.fill_between(train_sizes, train_scores_mean - train_scores_std, train_scores_mean + train_scores_std, alpha=0.1, color="b")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="b", label=u"训练集上得分")plt.plot(train_sizes, test_scores_mean, 'o-', color="r", label=u"交叉验证集上得分")plt.legend(loc="best")plt.draw()plt.gca().invert_yaxis()plt.show()midpoint = ((train_scores_mean[-1] + train_scores_std[-1]) + (test_scores_mean[-1] - test_scores_std[-1])) / 2diff = (train_scores_mean[-1] + train_scores_std[-1]) - (test_scores_mean[-1] - test_scores_std[-1])return midpoint, diffplot_learing_curve(clf, u"学习曲线", X, y)


从实际数据上看,我们得到的learning curve 没有推到的那么光滑,但是可以大致看出来,训练集和交叉验证集上的得分走势曲线还是符合预期的。

目前的曲线来看,我们的model并不处于overfitting的状态,(overfintting的表现一般是训练集上得分高,而交叉验证集上要低很多,中间的gap比较大)。因此我们可以再做一些feature engineering的工作,添加一些新产出的特征或组合特征到模型中。

模型融合(model ensemble)

好了,终于到这一步了,我们要祭出机器学习/数据挖掘上通常最后会用到的大杀器了。嗯,模型融合。

  • 模型融合很重要
  • 模型融合很重要
  • 模型融合很重要

重要的事情说三遍。

什么是模型融合呢,举例子来直观理解一下。

大家都看过知识问答的综艺节目中,求助现场观众的时候,让观众投票,最高的答案作为自己的答案的形式吧,每个人都有一个判定结果,最后我们相信答案在大多数人手里。

再举一个通俗一点的例子。你和你班某数学大神关系好,每次作业都模仿他的,于是大多数情况下,
他做对了,你也对了。突然有一天大神脑子犯糊涂,手一抖,写错了一个数,于是…嗯,你也跟着错了。另一个场景是,你跟你班5个大神的关系都很好,每次都把他们作业拿过来,对比一下,再自己做,如果哪天某大神犯糊涂了,写错了,but另外4个写对了,那你肯定相信另外4个人的是正确答案吧?

最简单的模型融合大概就是这么个意思,比如分类问题,当我们手上有一堆在同一份数据集上训练得到的分类器(比如logistic regression,SVM,KNN,random forest,神经网络),那我们让它们都分别去做判定,然后对结果做投票统计,取票数最多的结果为最后结果。

bingo.问题就这么完美的解决了。

话说回来,回到我们现在的问题,你看,我们现在只讲了logistic regression。如果我们还想用这个融合思想取提高我们的结果,我们应该怎么做呢?

既然这个时候模型没得选,而欧美让你就在数据上动动手脚。如果模型出现过拟合,一定是在我们的训练上出现拟合度过度造成的对吧。

我们干脆就不要用全部的训练集,每次取训练集的一个subset,做训练,这样,我们虽然用的是同一个机器学习算法但是得到的模型却是不一样的;同时,因为我们没有任何一份子数据集是全的,因此即使粗线过拟合,也是在子训练集上,而不是全体数据上,这样做一个融合,可能
对最后的结果有一个帮助。对,这就是常用的Bagging

from sklearn.ensemble import BaggingRegressor
train_df = df.filter(regex='Survived|Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass.*|Mother|Child|Family|Title')#y即Survived结果
y = train_df.iloc[:,0]#X即特征属性值
X = train_df.iloc[:,1:]#fir到BaggingRegressor之中
clf = linear_model.LogisticRegression(C=1.0,penalty='l1',tol=1e-6)
bagging_clf = BaggingRegressor(clf,n_estimators=20,max_samples=0.8,max_features=1.0,bootstrap=True,bootstrap_features=False,n_jobs=-1)
bagging_clf.fit(X,y)test = df_test.filter(regex='Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass.*|Mother|Child|Family|Title')
predictions = bagging_clf.predict(test)
result = pd.DataFrame({'PassengerId':data_test['PassengerId'],'Survived':predictions})
result.to_csv("e:/data/Titanic_data/logistic_regression_bagging_predictions.csv",index=False)
pd.read_csv('e:/data/Titanic_data/logistic_regression_bagging_predictions.csv')


然后再Make a submission,嗯,发现对结果还是有帮助的。

总结

对于任何机器学习的问题,不要一上来就追求尽善美,先用自己会的算法撸一个baseline的model出来, 然后进行后续的分析步骤,一步步提高。
在问题的结果过程中:

【对数据的认识太重要了!】
【数据中的特殊点/离群点的分析和处理太重要了!】
【特征工程(feature engineering)太重要了!】
【模型融合(model ensemble)太重要了!】
本文中用机器学习解决问题的过程大概如下所示:

kaggle 入门:逻辑回归应用之Kaggle泰坦尼克之灾相关推荐

  1. Kaggle泰坦尼克之灾:逻辑回归模型实现笔记(一)

    Kaggle泰坦尼克之灾:逻辑回归模型实现笔记(一) 1.引言 2.kaggle泰坦尼克之灾的介绍 3.笔记 用到的函数 第一步:了解数据 第二步:分析数据之间的关系 第三步:补全缺失值 第四步:特征 ...

  2. 机器学习系列(1)_数据分析之Kaggle泰坦尼克之灾

    本篇博客通过分析泰坦尼克号事故中乘客的信息,从而得出一些相关关系的判断,并且使用Python可视化的手段更加具体的展现. 注:本篇博客参考资料: 1.kaggle入门–泰坦尼克号之灾(某书) 2.机器 ...

  3. 机器学习系列(3)_逻辑回归应用之Kaggle泰坦尼克之灾

    作者: 寒小阳 时间:2015年11月. 出处:http://blog.csdn.net/han_xiaoyang/article/details/49797143 声明:版权所有,转载请注明出处,谢 ...

  4. 逻辑回归应用之Kaggle泰坦尼克之灾

    1.引言 先说一句,年末双十一什么的一来,真是非(mang)常(cheng)欢(gou)乐(le)!然后push自己抽出时间来写这篇blog的原因也非常简单: 写完前两篇逻辑回归的介绍和各个角度理解之 ...

  5. kaggle入门-泰坦尼克之灾

    kaggle入门-泰坦尼克之灾 引言 数据认识 总结 特征处理 建模预测 logistic分类模型 随机森林 SVM xgboost 模型验证 交叉验证 学习曲线 高偏差: 高方差 模型融合 总结 后 ...

  6. python泰坦尼克号案例分析_Python机器学习案例-泰坦尼克之灾

    泰坦尼克之灾案例是Kaggle入门的案例,本篇分析是参照https://github.com/Speedml/notebooks/blob/master/titanic/titanic-data-sc ...

  7. 机器学习实战 | 逻辑回归应用之“Kaggle泰坦尼克之灾”

  8. kaggle——Titanic泰坦尼克之灾

    1)目标 任务网址:kaggle_泰坦尼克号 1912年4月15日,在她的处女航中,被广泛认为的泰坦尼克号与冰山相撞后沉没.不幸的是,船上没有足够的救生艇供所有人使用,导致2224名乘客和机组人员中的 ...

  9. python__画图表可参考(转自:寒小阳 逻辑回归应用之Kaggle泰坦尼克之灾)

    出处:http://blog.csdn.net/han_xiaoyang/article/details/49797143 2.背景 2.1 关于Kaggle 我是Kaggle地址,翻我牌子 亲,逼格 ...

最新文章

  1. 首个单设备模拟神经元出现 可有效解决传统计算机所面临的问题
  2. We PE U盘安装win7系统
  3. python和java哪个-Java和Python现在都很热门,哪个更有前途?
  4. java版我的世界光追,老瓶装新酒 光追版《我的世界》将至你心动了吗
  5. lumion自动保存_Lumion所有快捷键,值得收藏
  6. 顺序查找、折半查找及索引顺序查找
  7. freemarker必须指定模板文件夹吗?
  8. 【2020年CSDN技术人内推活动开始啦】多家名企员工在线内推,快人一步拿Offer
  9. 视频教程-ThinkPHP5.0项目实战-PHP
  10. 极品五笔输入法2009_考场指南!2020年注会机考计算器使用指南及输入法切换
  11. OpenCms显示默认作者
  12. 浏览器下载文件的两种方式
  13. 将多个文件合并为一个文件
  14. 分析测试电视遥控总结
  15. 反汇编---汇编基础学习
  16. python实现标号法确定最短路径
  17. 交通灯系统51单片机设计(附Proteus仿真、C程序、原理图及PCB、论文等全套资料)
  18. 关于Deepin商店没有应用的解决办法
  19. shell脚本根据指定日期 得到月初,月末,季初,季末,年初,年末,周开始,周结束,旬初,旬末日期,报表日期
  20. 【登录时验证码一直错误】阿里云SLB负载均衡,如何session共享

热门文章

  1. 【值得收藏】如此心机的老婆,不难教出一个优秀的女儿。谁娶了都是福。
  2. 20222817《Linux内核原理与分析》第五周作业
  3. 导入 xlsx php,php如何使用phpexcel类导入excel表格数据
  4. centos如何安装软件
  5. 【教学】手写汉字识别,含训练代码,界面演示,1w字教学
  6. 介绍一个牛逼的Github项目
  7. 在Visual Basic6.0中,如何实现简单加减乘除的程序编写?
  8. [JZOJ5666]【GDOI2018Day2模拟4.18】法力风暴(分治NTT 模板)
  9. Spark中distinct、reduceByKey和groupByKey的区别与取舍
  10. vue的基础知识-vue基础入门