Kaggle——泰坦尼克号0.79665-top9%

前言

本次是在Kaggle的第一个竞赛《Titanic: Machine Learning from Disaster》,即预测泰坦尼克号的人员幸存情况,是一个二分类问题。
本次最后精度为0.79665,排名Top 9%。

image

1 数据分析

1.1 数据概述

首先我们将测试集和训练集一同合并起来查看

train=pd.read_csv('train.csv')
test=pd.read_csv('test.csv')
all=pd.concat([train,test],axis=0,ignore_index=True)
print(all.info())
image

•PassengerID(ID)——乘客编号可以删去
•Survived(存活与否)
•Pclass(客舱等级)
•Name(姓名)
•Sex(性别)
•Age(年龄)——存在缺失值
•Parch(直系亲友)
•SibSp(旁系)
•Ticket(票编号)
•Fare(票价)——2个缺失值
•Cabin(客舱编号)——过多的缺失值
•Embarked(上船的港口编号)——1个缺失值

训练集为891,测试集为418;特征数量为12,4个为数值特征,5个为字符串特征;4个特征存在缺失值。

1.2 特征可视化

本次使用Excel数据透视表结合python画图观察各特征与生存率之间的关系

①Pclass——级别越高的客舱的生存率会更高(有钱就有地位,有地位就有钱)

image

②Sex——女性的生存率明显高于男性(女士优先)

image

③Parch——直系亲属数量适中的生存率较高(一个人没有寄托,英雄般献出生存机会;一个家庭需要剩下一两个回去传承,但其余的都得献身,所以显得生存率较小)

image

④SibSp——旁系亲属数量适中的生存率较高(同上)

image

⑤Embarked——在C港口进船的生存率较高(可能C港口在发达城市,各个都有钱吗)

image

⑥Age——孩子(15岁前)生存率最高,年轻人生存率其次,而老年人(60岁后)生存率最低(老人愿意牺牲自己剩下的生命,留给还有大把未来的孩子和年轻人)

facet = sns.FacetGrid(all, hue="Survived",aspect=2)
facet.map(sns.kdeplot,'Age',shade=True)
facet.set(xlim=(0, all['Age'].max()))
facet.add_legend()
plt.xlabel('Age')
plt.ylabel('density')
plt.show()

⑦Fare——票价我们将其指数化可以看的更清楚,发现也是票价高的生存率会更高(钱钱钱!)

all['Fare']=all['Fare'].map(lambda x:np.log(x+1))
facet = sns.FacetGrid(all, hue="Survived",aspect=2)
facet.map(sns.kdeplot,'Fare',shade=True)
facet.set(xlim=(0, all['Fare'].max()))
facet.add_legend()
plt.xlabel('Fare')
plt.ylabel('density')
plt.show()
image

以上特征图像我们结合实际想一想,发现确实是有些关联的,交给模型领悟领悟这些美德和现实吧。当然还有一些特征值过于多的我们后续再处理。

2 特征工程

2.1 特征处理

2.1.1 提取新特征

①Title——从人们Name提取出来的称呼,可以分为有职位的,有地位的,妇女,女士等等。可以看出有地位的还有女性的生存率高一点。

all['Title'] = all['Name'].apply(lambda x:x.split(',')[1].split('.')[0].strip())
Title_Dict = {}
Title_Dict.update(dict.fromkeys(['Capt', 'Col', 'Major', 'Dr', 'Rev'], 'Officer'))
Title_Dict.update(dict.fromkeys(['Don', 'Sir', 'the Countess', 'Dona', 'Lady'], 'Royalty'))
Title_Dict.update(dict.fromkeys(['Mme', 'Ms', 'Mrs'], 'Mrs'))
Title_Dict.update(dict.fromkeys(['Mlle', 'Miss'], 'Miss'))
Title_Dict.update(dict.fromkeys(['Mr'], 'Mr'))
Title_Dict.update(dict.fromkeys(['Master','Jonkheer'], 'Master'))
all['Title'] = all['Title'].map(Title_Dict)

②Surname——从人们Name提取出来的,这是为了找寻一家人。

all['Surname']=all['Name'].apply(lambda x:x.split(',')[0].strip())

③FamilySize——一起上船的全家人数,可以有的是一个姓却不是一个家庭,可能有的是一个家庭却没有一起上船(同一票根的表示房间号)。发现和Parch、SibSp有异曲同工之妙,生存率较高的都为人数适中的。

all['Family']=all['Surname']+all['Ticket']
Family_Count =dict(all['Family'].value_counts())
all['Family']=all['Family'].apply(lambda x:Family_Count[x])

④allFamilySize——旁系加直系亲属,无论是否在船上的家族人数。(很尴尬,和上面差不多,看来基本都是一家人上船的)

all['allFamilySize']=all['SibSp']+all['Parch']+1

⑤together——舱房内的舍友人数,其中包括了一个家庭和不是一个家庭的,认为可能是相识的。(嗯哼,似曾相识,不过与上还是有点区别的)

Ticket_Count =dict(all['Ticket'].value_counts())
all['together'] = all['Ticket'].apply(lambda x:Ticket_Count[x])

2.1.2 特征分箱

分箱!分箱!这此预测最麻烦的就是过拟合,分箱可以减少计算并且有效降低过拟合

①Age——对孩子、年轻人、老年人进行分箱。(这里先对已知的年龄分箱)

all['Age'] = all[all['Age'].notnull()]['Age'].apply(lambda x:0 if x<=14 else (1 if x<=60 else 2))

②allFamilySize——对家族人数根据数量适中,不适中,过多进行分箱。(Parch、SibSp、FamilySize、allFamilySize分箱后都类似,取其中一个)

def Fam_label(s):
  if (s >=2) & (s <=4):
    return 2
  elif ((s >4) & (s <=7)) | (s ==1):
    return 1
  elif (s >7):
    return 0
all['allFamilySize']=all['allFamilySize'].apply(Fam_label)

③together——对舍友人数根据数量适中,不适中,过多进行分箱。(和其上还是很像,暂时不删除,留给模型判断先)

def Ticket_Label(s):
  if (s >=2) & (s <=4):
    return 2
  elif ((s >4) & (s <=8)) | (s ==1):
    return 1
  elif (s >8):
    return 0
all['together'] = all['together'].apply(Ticket_Label)

④Fare——根据图表的生存率转折进行分箱。(补充完缺失值再分箱)

(!!!后续调整时候,我试着把Fare调整会原数据,表现变好了。可能是我分箱的不准确或是调参的失误。)

train['Fare']=train['Fare'].map(lambda x:0 if x<1.5 else (1 if x<2.7 else 2))

2.2 补充缺失值

①Embarked和Fare——
Embarked缺失量为2,缺失Embarked信息的乘客的Pclass均为1,且Fare均为80,因为Embarked为C且Pclass为1的乘客的Fare中位数为80,所以缺失值填充为C。
Fare缺失量为1,缺失Fare信息的乘客的Embarked为S,Pclass为3,所以用Embarked为S,Pclass为3的乘客的Fare中位数填充。

fare=all[(all['Embarked'] =="S") & (all['Pclass'] ==3)].Fare.median()
all['Fare']=all['Fare'].fillna(fare)
all['Embarked'] = all['Embarked'].fillna('C')

②Age——缺失值较多,我们使用随机森林进行预测,特征选择了'Pclass','Fare','Title'(凭感觉,如果后续过拟合可以试下调整年龄预测的模型参数和特征)

from sklearn.ensembleimport RandomForestClassifier
age_df = all[['Age', 'Pclass','Fare','Title']]
age_df=pd.get_dummies(age_df)
known_age = age_df[age_df.Age.notnull()].as_matrix()
unknown_age = age_df[age_df.Age.isnull()].as_matrix()
y = known_age[:, 0]
X = known_age[:, 1:]
rfr = RandomForestClassifier(random_state=0, n_estimators=100, n_jobs=-1)
rfr.fit(X, y)
predictedAges = rfr.predict(unknown_age[:, 1::])
all.loc[(all.Age.isnull()), 'Age' ] = predictedAges

3 训练模型

最终我们选择了以下特征

all=all[['Fare','Age','allFamilySize','Pclass','Sex','Embarked','Survived']]
all=pd.get_dummies(all)
all.to_csv('all.csv',index=0)

3.1 随机森林模型

看了几篇文章都表示随机森林表现较可,尝试用随机森林调参并预测,成绩为0.78708。

def RF_():
  cv_params = {}
  other_params = {'n_estimators':100, 'max_depth':6, 'min_samples_leaf':2,
                    'max_features':'sqrt'}
  model = RandomForestClassifier(**other_params)
  m = GridSearchCV(estimator = model, param_grid = cv_params, scoring='roc_auc', cv=10)
  m.fit(train_x, train_y)
  evalute_result = m.cv_results_
  # print('每轮迭代运行结果:{0}'.format(evalute_result))
  best_params = m.best_params_
  best_score = m.best_score_
  print(best_params,best_score)
  name='RF'
      return m,name

发现结果还行

3.2 模型融合

同样我对另外几个模型(XGBClassifier、DecisionTreeClassifier、AdaBoostClassifier)也进行预测,结果随着我调参过于飘忽...干脆全部融合起来。

submit = pd.read_csv("gender_submission.csv")
def prediction(m,name):
    y_pred = m.predict(test)
    submit['Survived'] = y_pred
    submit['Survived'] = submit['Survived'].astype(int)
    submit.to_csv('prediction_{}'.format(name)+'.csv', index=False)
model=(XGB_ (), RF_(), DTC_(),XGB2_(),ABC_())
for i in model:
    m,name=i
    prediction(m,name)
#三模型融合
import pandas as pd
df1=pd.read_csv('prediction_RF.csv')
df2=pd.read_csv('prediction_XGB.csv')
df3=pd.read_csv('prediction_DTC.csv')

df=pd.merge(df1, df2, on = 'PassengerId')
df=pd.merge(df,df3,on='PassengerId')
print(df.head(5))
df.rename(columns={'Survived_x':'a','Survived_y':'b','Survived':'c'}, inplace=True)
print(df.head(5))
for i in df.index:
    # 三个结果相同
    if df.loc[i, 'a'] + df.loc[i, 'b'] + df.loc[i, 'c'] == 0 or df.loc[i, 'a'] + df.loc[i, 'b'] + df.loc[i, 'c'] == 3 :
        df.loc[i, 'd'] = (df.loc[i, 'a'] + df.loc[i, 'b'] + df.loc[i, 'c']) / 3
        df.loc[i, 'f'] = '三个结果相同'
        df.loc[i, 'e'] = 'abc'
    # 两个结果相同
    elif df.loc[i, 'a'] == df.loc[i, 'b'] and df.loc[i, 'b'] != df.loc[i, 'c']:
        df.loc[i, 'd'] = df.loc[i, 'a']
        df.loc[i, 'f'] = '两个结果相同'
        df.loc[i, 'e'] = 'ab'
    elif df.loc[i, 'a'] == df.loc[i, 'c'] and df.loc[i, 'b'] != df.loc[i, 'c']:
        df.loc[i, 'd'] = df.loc[i, 'a']
        df.loc[i, 'f'] = '两个结果相同'
        df.loc[i, 'e'] = 'ac'
    elif df.loc[i, 'b'] == df.loc[i, 'c'] and df.loc[i, 'a'] != df.loc[i, 'c']:
        df.loc[i, 'd'] = df.loc[i, 'b']
        df.loc[i, 'f'] = '两个结果相同'
        df.loc[i, 'e'] = 'bc'
    # 三个结果不相同
    else:
        df.loc[i, 'f'] = '三个结果不相同'
        df.loc[i, 'd'] = df.loc[i, 'b']
print('-'*125)
print('列中各元素数量')
print('-'*125)
print(df['f'].value_counts() )
print('-'*125)
print(df['d'].value_counts() )
print('-'*125)
print(df['e'].value_counts() )
print(df.head(5))

submit = pd.read_csv("gender_submission.csv")
submit['Survived'] = df['d']
submit['Survived'] = submit['Survived'].astype(int)
submit.to_csv('prediction_vote.csv', index=False)
#五模型融合
import pandas as pd
df1=pd.read_csv('prediction_RF.csv')
df2=pd.read_csv('prediction_XGB.csv')
df3=pd.read_csv('prediction_DTC.csv')
df4=pd.read_csv('prediction_ABC.csv')
df5=pd.read_csv('prediction_XGB2.csv')

df=pd.merge(df1, df2, on = 'PassengerId')
df=pd.merge(df,df3,on='PassengerId')
df=pd.merge(df, df4, on = 'PassengerId')
df=pd.merge(df,df5,on='PassengerId')

df.columns=['Id','a','b','c','d','e']
print(df.head(5))
df['S']=df['a']+df['b']+df['c']+df['d']+df['e']
df['S']=df['S'].apply(lambda x:1 if x>=3 else 0)

submit = pd.read_csv("gender_submission.csv")
submit['Survived'] = df['S']
submit['Survived'] = submit['Survived'].astype(int)
submit.to_csv('prediction_vote.csv', index=False)

后续发现三个模型融合比较好控制,五个模型反而降低了。因为是我第一次融合模型,估摸选的搭配和调参不对,可以再考究考究。

然而这里最大的问题!!!就是在交叉验证表现良好的,与提交上去的差了很多,还不一定为正比关系!啊啊啊,这就是过拟合的可怕吗?
于是我又回去各种尝试,试着删除一些特征,对一些特征分箱,改变一下预测年龄的特征等等。

最终最好成绩为三模型融合的0.79665,排名Top 9%。

小结

本次处理中发生较严重的过拟合问题,原因我想有三点:①数据集本身规模过小,太多因素不能从数据集中解释,这限制了得分在0.85以下才正常;②缺少具有代表性的特征,我并未提取出更为具有代表性的特征,例如我曾尝试将不同家庭分类,希望能将各自家庭中至少活下一两个的猜测做成特征,但是如果A家庭在训练集的成员都死亡,模型会容易将A家庭在测试集的成员都预测为死亡。这个问题我仍未想出方案。③模型调参无从下手,调参可以降低过拟合,但当其交叉验证的分数就变得不可信了,且提交成绩有限制时,我对调参颇为局促。

啊,从sofa到kaggle,发现这个kaggle果然是大平台,从排行榜的刷新就可以看得出其规模,当然我还发现许多想法不一的,还等我慢慢学。

主要参考:

kaggle入门--泰坦尼克号之灾(手把手教你)

kaggle 泰坦尼克号生存预测——六种算法模型实现与比较

sofasofa——形状识别(模型融合代码实现)

代码

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,542评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,822评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,912评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,449评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,500评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,370评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,193评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,074评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,505评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,722评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,841评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,569评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,168评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,783评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,918评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,962评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,781评论 2 354