66

人工智障也刷题!Kaggle入门之实战泰坦尼克号

 5 years ago
source link: https://knownsec-fed.com/2018-12-04-ren-gong-zhi-zhang-ye-shua-ti-kaggle-ru-men-zhi-shi-zhan-tai-tan-ni-ke-hao/?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

ABBv2eR.jpg!web

人工智障也刷题!Kaggle入门之实战泰坦尼克号

背景

关于Kaggle

  • https://www.kaggle.com/
  • 这是一个为你提供完美数据,为你提供实际应用场景,可以与小伙伴在数据挖掘领域high的不要不要的的地方啊!!!

Kaggle是一个用来学习、分享和竞赛的线上数据实验平台,有点类似KDD—CUP(国际知识发现和数据挖掘竞赛),企业或者研究者可以将背景、数据、期望指标等发布在kaggle上,用竞赛的方式向全世界的数据科学家和爱好者寻求解决方案。热爱数(dong)据(shou)挖(zhe)掘(teng)的小伙伴们可以下载/分析数据,使用统计/机器学习/数据挖掘等方面的知识,建立算法模型,得出结果并提交,排名靠前可能还会有奖励哦!

关于泰坦尼克号之灾

https://www.kaggle.com/c/titanic 点击这里0.0

  • 问题背景页

UFbEne6.png!web

  • 下载Data的页面

iEBbii2.png!web

  • 泰坦尼克号问题背景

    • 就是大家从小到大被洗脑的“u jump I jump”的「jack和rose」的故事了。游艇在撞击了一个冰山后沉没了。乘客们都惊慌失措,副船长「lady and kid first」,所以模型不会向抛硬币那样看脸决定你是否获救。而是有着一定背景的,至于出了女士和孩子优先,还有哪些值得我们考虑,这就是稍后我们在特征工程中解决的问题了。
    • 训练和测试数据是一些乘客的个人信息以及存活情况,尝试应用这些数据来建立一个合适的模型进行预测。
    • 这是一个二分类问题(survived或者not),本文尝试用logistic regression来处理问题
  • 说明

    • 「没有所谓的算法优劣,也没有绝对高性能的机器学习算法,只有在特定的场景、数据和特征下更合适的机器学习的算法。」由于还只是在学习阶段,对于XGBC、随机森林、SVC还不了解,本文所用的算法只是logistic regression。

初识数据

在Data下我们会看到官方给的train.csv和test.csv两个文件,分别是训练和测试数据。我们可以使用virtualenv来创建一个“隔离”的python应用环境(虚拟环境)。在这里,你不需要考虑系统原有库的版本,只需要pip来管理你需要用到的一切。

import pandas as pd   
import numpy as np    
from pandas import Series,DataFrame

data_train = pd.read_csv("./train.csv")
pd.DataFrame(data_train)

pandas是常用的python数据处理包,把csv文件读入成dataframe格式,在jupyter notebook中,可以看到我们的数据长什么样: jaM7RbF.png!web

我们就可以把它看作一张excel表格,共有12列,891行(代表在train_csv中共有891个乘客)。Survived字段代表该乘客是否获救(1代表获救,0代表没有获救),其余是一些个人信息

  • passengerId => 乘客的ID
  • Pclass => 乘客所在舱位的等级(1、2、3等舱)
  • Name => 姓名
  • Sex => 性别
  • Age => 年龄
  • SibSp => 兄弟姐妹个数
  • Parch => 父母与小孩个数
  • Ticket => 船票信息
  • Fare => 票价
  • Cabin => 客舱信息
  • Embarked => 登船所在港口
data_train.info()

Mre6NfF.png!web 这里告诉了我么一些关于data_train的基本信息,比如共有891个乘客,但是有些字段的数据不全。比如Age(年龄)、Cabin(客舱信息)。

data_train.describe()

Nzi2eaY.png!web 在这里我们看到了什么信息呢?从mean行中约有0.38的人最后获救了,乘客的平均年龄为29.7岁,平均票价为32.20

数据初步分析

每个乘客大概有12种属性提供给我们,仅仅对于上面两行操作对于数据的了解还无法为我们提供想法和思路,我们现在不知道哪些有用哪些对于模型没有用,所以到了最关键的特征工程环节。我们知道最终的output是Suvived,现在需要寻找最后的Survived与各个属性之间有着什么样的内在关系。从大副的口中已经得知「要首先考虑妇女和儿童」。

for x in data1_x:
    if data1[x].dtype != 'float64' :
        print('Survival Correlation by:', x)
        print(data1[[x, Target[0]]].groupby(x, as_index=False).mean())
        print('-'*10, '\n')
        
print(pd.crosstab(data1['Title'],data1[Target[0]]))

INvEzyJ.png!web 在本图中,我们发现女性有0.74活下来,而男性只有0.18。歪果盆友果然很尊重lady,lady first践行得不错。性别无疑要作为重要特征加入最后的模型之中。在Pclass中,客舱为等级1的乘客获救的概率高很多,嗯这个一定也影响最后获救的结果(等级高的乘客一定也更有钱)。

fig = plt.figure()
fig.set(alpha=0.2)  
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':Survived_1, 'unSurvived':Survived_0})
df.plot(kind='bar', stacked=True)
plt.title("Survived status of all passenger classes")
plt.xlabel("passanger's level") 
plt.ylabel("number") 
plt.show()

yAnqMnZ.png!web 从本图中我们可以看到,明显如果你是等级为1的乘客,你的获救概率就会很高。对了,这也是会最终影响输出的获救结果的一个特征。

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("age")# plots an axis lable
plt.ylabel("dendity") 
plt.title("Age distribution of passengers levels")
plt.legend(('first level', 'second level','third level'),loc='best')

UJVrErU.png!web 从各等级乘客的年龄分布中,我们可以看到「不同舱位/乘客等级可能和财富/地位有关系,最后获救概率可能会不一样」,所以年龄也会是影响我们最终结果的原因之一。

简单数据预处理

我们对大体数据已经看过一遍了,对于感兴趣的属性也有了大概的了解。现在我们需要 简单处理一下这些数据,为机器学习建模做点准备了。先从最突出的数据属性开始吧,Cabin和Age,因为这两项有些乘客的信息不包含它们,有丢失的数据对于下一步的工作影响太大。

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

再说Age:

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

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

本例中,因为Cabin不是影响最终结果的特征之一。所以直接考虑别的需要用到的特征(性别,等级,等级),并将其中的类目型转化为数值型特征,我们可以使用pandas的“get dummies”来完成这个工作,并接在原来的“data train”上

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_Embarked, dummies_Sex, dummies_Pclass], axis=1)
df.drop(['Pclass', 'Name', 'Sex', 'Ticket', 'Embarked'], axis=1, inplace=True)
df

EJv6nmq.png!web na!我们将这些类目属性成功转化为0,1的数值属性了。这样看来,好像差不多都完成了,可是如果再看看Age和Fare两个属性,乘客关于这两个属性的数值变化幅度也太大了!!如果大家了解逻辑回归与梯度下降的话,会知道各属性之间的scale差距太大,将对收敛速度造成很大的伤害(甚至不收敛)…所以我们先用scikit-learn里面的preprocessing模块对这两个属性做一个处理(就是将变化幅度较大的特征化到[-1,1]内)

import sklearn.preprocessing as preprocessing
scaler = preprocessing.StandardScaler()
age_scale_param = scaler.fit(df['Age'])
df['Age_scaled'] = scaler.fit_transform(df['Age'], age_scale_param)
fare_scale_param = scaler.fit(df['Fare'])
df['Fare_scaled'] = scaler.fit_transform(df['Fare'], fare_scale_param)
df

QZRZVzI.png!web

嗯,这样初级的数据处理就完成的差不多了

建模

我么把需要的feature字段提取出来,转成numpy格式,使用scikit-learn中的LogisticRegression建模。

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

OK!!!通过这样建模后,我们得到一个model,然后再把test.csv通过同样的数据简单处理后,就可以得到预测结果了。

系统优化

等等,你以为这样就完了么。这其实只完成了刚刚开始的一步,我们只是做了一个baseline model,一切都还是基础的,我们还需要优化。

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

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

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK