阅读 547

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

背景

关于 Kaggle

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

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

关于泰坦尼克号之灾

www.kaggle.com/c/titanic

  • 问题背景页

  • 下载 Data 的页面

  • 泰坦尼克号问题背景
  • 就是大家从小到大被洗脑的“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 中,可以看到我们的数据长什么样:

我们就可以把它看作一张 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()
复制代码

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

data_train.describe()
复制代码

在这里我们看到了什么信息呢?从 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]]))
复制代码

在本图中,我们发现女性有 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()
复制代码

从本图中我们可以看到,明显如果你是等级为 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')
复制代码

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

简单数据预处理

我们对大体数据已经看过一遍了,对于感兴趣的属性也有了大概的了解。现在我们需要 简单处理一下这些数据,为机器学习建模做点准备了。先从最突出的数据属性开始吧,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
复制代码

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
复制代码

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

建模

我么把需要的 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)

文 / joeCDC

数学爱好者

编 / 荧声

本文已由作者授权发布,版权属于创宇前端。欢迎注明出处转载本文。本文链接:knownsec-fed.com/2018-12-04-…

想要订阅更多来自知道创宇开发一线的分享,请搜索关注我们的微信公众号:创宇前端(KnownsecFED)。欢迎留言讨论,我们会尽可能回复。

感谢您的阅读。

关注下面的标签,发现更多相似文章
评论