Titanic: Machine Learning from Disaster

这是我第一次参加kaggle竞赛,虽然只是一个入门记得比赛,但我从中学到很多东西,在这里我希望记述一下这个比赛带给我的收获。

工作环境

  1. 操作系统 : Windows10 64位
  2. 语言: python
  3. 工具箱:numpy,keras,sklearn,pandas

任务概述

相信大家基本都看过或听说过泰坦尼克号这部影片,显然我们关注的不是那个凄美的爱情故事,而是希望通过机器学习的方法来预测船上乘客是否能生还。

数据集

训练集:共891个数据,数据内容如下
这里写图片描述
测试集:共418个待预测数据,数据内容仅出去PassengerId一列,其余内容想同,这里不再复述

实现步骤

  1. 清理数据
  2. 分析数据
  3. 建立模型
  4. 挑选超参数


    下面我来对以上四个步骤做详细说明

    清理数据

    在这一个步骤里我们会将所得到的891个训练数据进行清理工作,得到三组不同的数据,(注意这里说的三组并不是指train,validation,test三个数据集)
    首先我们来看一下数据的总体特征:
    . 数据类型
    这里写图片描述

    . 是否缺值
    这里写图片描述

    . 数据描述
    这里写图片描述
    通过上面的分析我们对于数据集有了一个充分的了解,也就理解了为什么要清理数据,我们看到数据有些特征是不重要的,或是非数值的,或是空的,我们需要根据个人经验对数据集做处理,以满足需求。
    现在我们开始清理数据


. 替换空项并去除无用项
通过上面的分析显然PassengerId,Cabin, Ticket属于无用项,剩余的特征我们替换空项

data_raw = pd.read_csv('train.csv')
data_val  = pd.read_csv('test.csv')
data1 = data_raw.copy(deep = True)
data_cleaner = [data1, data_val]
for dataset in data_cleaner:    
    #complete missing age with median
    dataset['Age'].fillna(dataset['Age'].median(), inplace = True)

    #complete embarked with mode
    dataset['Embarked'].fillna(dataset['Embarked'].mode()[0], inplace = True)

    #complete missing fare with median
    dataset['Fare'].fillna(dataset['Fare'].median(), inplace = True)

#delete the cabin feature/column and others previously stated to exclude in train dataset
drop_column = ['PassengerId','Cabin', 'Ticket']
data1.drop(drop_column, axis=1, inplace = True)
data_val.drop(drop_column, axis=1, inplace = True)
print(data1.isnull().sum())
print("-"*10)
print(data_val.isnull().sum())

这里写图片描述

. 提取新特征

###CREATE: Feature Engineering for train and test/validation dataset
for dataset in data_cleaner:    
    #Discrete variables
    dataset['FamilySize'] = dataset ['SibSp'] + dataset['Parch'] + 1

    dataset['IsAlone'] = 1 #initialize to yes/1 is alone
    dataset['IsAlone'].loc[dataset['FamilySize'] > 1] = 0 # now update to no/0 if family size is greater than 1

    #quick and dirty code split title from name: http://www.pythonforbeginners.com/dictionary/python-split
    dataset['Title'] = dataset['Name'].str.split(", ", expand=True)[1].str.split(".", expand=True)[0]


    #Continuous variable bins; qcut vs cut: https://stackoverflow.com/questions/30211923/what-is-the-difference-between-pandas-qcut-and-pandas-cut
    #Fare Bins/Buckets using qcut or frequency bins: https://pandas.pydata.org/pandas-docs/stable/generated/pandas.qcut.html
    dataset['FareBin'] = pd.qcut(dataset['Fare'], 4)

    #Age Bins/Buckets using cut or value bins: https://pandas.pydata.org/pandas-docs/stable/generated/pandas.cut.html
    dataset['AgeBin'] = pd.cut(dataset['Age'].astype(int), 5)

#cleanup rare title names
#print(data1['Title'].value_counts())
stat_min = 10 #while small is arbitrary, we'll use the common minimum in statistics: http://nicholasjjackson.com/2012/03/08/sample-size-is-10-a-magic-number/
title_names = (data1['Title'].value_counts() < stat_min) #this will create a true false series with title name as index

#apply and lambda functions are quick and dirty code to find and replace with fewer lines of code: https://community.modeanalytics.com/python/tutorial/pandas-groupby-and-python-lambda-functions/
data1['Title'] = data1['Title'].apply(lambda x: 'Misc' if title_names.loc[x] == True else x)
print(data1['Title'].value_counts())

这里写图片描述

label = LabelEncoder()
for dataset in data_cleaner:    
    dataset['Sex_Code'] = label.fit_transform(dataset['Sex'])
    dataset['Embarked_Code'] = label.fit_transform(dataset['Embarked'])
    dataset['Title_Code'] = label.fit_transform(dataset['Title'])
    dataset['AgeBin_Code'] = label.fit_transform(dataset['AgeBin'])
    dataset['FareBin_Code'] = label.fit_transform(dataset['FareBin'])

这里写图片描述
这里写图片描述

  • 生成行数据集
    我们会生成三个新的数据集,我会将新生成的数据集放到cache字典中保存
    这里我想再提一下dummy数据集的产生,这个数据集我们通过padas的get_dummy函数将原有数据集中的object,category类型的数据转换为one-hot类型
def split_example(data_raw):
    #define y variable aka target/outcome
    Target = ['Survived']

#define x variables for original features aka feature selection
    data_x = ['Sex','Pclass', 'Embarked', 'Title','SibSp', 'Parch', 'AgeBin', 'FareBin_Code', 'FamilySize', 'IsAlone'] #pretty name/values for charts
    data_x_calc = ['Sex_Code','Pclass', 'Embarked_Code', 'Title_Code','SibSp', 'Parch', 'Age', 'Fare'] #coded for algorithm calculation

 #define x variables for original w/bin features to remove continuous variables
    data_x_bin = ['Sex_Code','Pclass', 'Embarked_Code', 'Title_Code', 'FamilySize', 'AgeBin_Code', 'FareBin_Code']

    data_raw_dummy = pd.get_dummies(data_raw[data_x])
    data_x_dummy = data_raw_dummy.columns.tolist()

    train_x_bin, test_x_bin, train_y_bin, test_y_bin = model_selection.train_test_split(data_raw[data_x_bin], data_raw[Target] ,
     test_size = 0.2 ,random_state = 0)
    valid_x_bin, test_x_bin,valid_y_bin,test_y_bin = model_selection.train_test_split(test_x_bin, test_y_bin ,
     test_size = 0.2 ,random_state = 0)
    train_x_dummy, test_x_dummy, train_y_dummy, test_y_dummy = model_selection.train_test_split(data_raw_dummy[data_x_dummy], data_raw[Target],            random_state = 0)
    valid_x_dummy, test_x_dummy,valid_y_dummy,test_y_dummy = model_selection.train_test_split(test_x_dummy, test_y_dummy ,
     test_size = 0.2 ,random_state = 0)
    train_x, test_x, train_y, test_y = model_selection.train_test_split(data_raw[data_x_calc], data_raw[Target], random_state = 0)
    valid_x, test_x,valid_y,test_y = model_selection.train_test_split(test_x, test_y,
     test_size = 0.2 ,random_state = 0)
    cache = {'train_x':train_x,'train_y':train_y,'test_x':test_x,'test_y':test_y,
             'train_x_bin':train_x_bin,'train_y_bin':train_y_bin,'test_x_bin':test_x_bin,'test_y_bin':test_y_bin,
             'train_x_dummy':train_x_dummy,'train_y_dummy':train_y_dummy,'test_x_dummy':test_x_dummy,
             'test_y_dummy':test_y_dummy,'valid_x':valid_x,'valid_y':valid_y,'valid_x_bin':valid_x_bin,
             'valid_y_bin':valid_y_bin,'valid_x_dummy':valid_x_dummy,'valid_y_dummy':valid_y_dummy}
    return cache

我们的数据清理到这里就告一段落了。


分析数据

在这个步骤我们讨论准确率的底线,很明显,这是一个二分类问题,所以我们即使什么都不,单单通过产生随机数的方式也能达到0.5的准确率。
让我进一步分析我们的数据,采用概率知识来的到最终的准确率底线。
下面我给出数据集中不同特征与存活率的关系(注意:是与存活率的关系
这里写图片描述
这里写图片描述
这里写图片描述
接下来我给出数据集中不同特征存活与丧生人数:
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
结合概率论知识,如何我们将所有女性,以及其他特种中存活率最高的人数预测为全部存活,那我们的准确率大概有0.76.你也可以尝试使用将所有男性….预测为存活的方式,到此为止,我们已经求出了我们的算法至少要达到0.76的准确率。


建立模型

我使用一个含有4个隐含层,一个输出层的神经网络建立模型,隐含层每层的输出相同,为64个(你也可以尝试使用更多的隐含层,隐含层含有更多的输出单元
这里写图片描述
模型结构如上图所示
优化函数采用Adam优化算法


挑选超参数

在这里我介绍我挑选超参数的方法,通过我们的模型可以得知,超参数包括:隐含层层数,网络深度,Adam参数,Dropout层的keep_pro,Regularizer的参数lambd.
为了方便挑选,我只挑选各层Dropout的keep_pro,lambd我将各层设置为相同的,按我理解,隐含层+输出层的lambd加在一起等于原来我们自己使用numpy建立的那个lambd(说的好像很不清楚,只可意会不可言传,嘻嘻)。
对于这两个参数,我使用随机数来挑选合适的lambd以及各层keep_pro。效果还不错,开始的时候对于每个随机参数我运行1000个epoch来训练,我发现效率很低,其中有很多随机的超参数根本就没有运行这么多次的必要,后来,我决定先将50组超参数运行300次,通过valid数据集挑选出准确率高于0.7的超参数在运行600次,然后挑选出准确率高于0.8的超参数运行2000次,这种方式效率还是蛮高的。


扩展

在建立神经网络的时候我就在想,既然这是一个二分类问题,是否可以使用聚类的方法呢,将存活当成一类,将死亡当成一类,我觉得使用k-means也同样是可行;
在我的模型产生过拟合后,我想到可否通过Dimensionality Reduction方法将数据中的特征降维来解决过拟合,希望各位去尝试一下


感悟

该开始比赛时,我是很急躁的,将注意力全都放到了如何建立模型上去了,没有仔细的分析数据,对于数据的处理很是粗暴,不过当时可能还是沾沾自喜,直到模型建立好后训练,发现,我的模型对训练数据的预测准确率连0.9都到不了,而且还很不稳定,当时我才意识到一定是哪里出了问题,这次我静下心来,一点点看别人的kernel,发现是我清理数据这块出了问题,原来从一开始就本末倒置。到这里也就该结束了,原来在网上学了很多的可,上面虽然也有编程练习,但都是别人给好了框架,我只要填几行代码就完事大吉,这次从清理数据到挑选超参数完全是自己动手,感觉学到不少东西,感谢这次机会,也感谢不断努力的自己。


清理数据的想法来源于(https://www.kaggle.com/ldfreeman3/a-data-science-framework-to-achieve-99-accuracy)这个kernel。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值