介绍
kaggle是全球最好的一个数据科学竞赛平台,今天讲其中一个非常经典的预测比赛,一年前我就参加过这个比赛,当时的代码也保留了,现在运行的时候有几处python方法调用的兼容问题,其他都正常跑没有问题。
站在现在的角度来看这个比赛,感觉非常完整,覆盖了正常机器学习工作中的流程和问题。其实有很多公司会把它们的机器学习项目放到kaggle上,然后设置金奖,当有团队做出很好的效果和解决方案,它们就会买过来,可以理解为有了kaggle上的比赛经验,就和有实际的工作经验差不多,尤其是可以取到名次的同学,简历上直接写出来,就是加分项。
准备工作
注册账号并报名比赛,地址:https://www.kaggle.com/c/titanic
下载训练集和测试集数据,地址:https://www.kaggle.com/c/titanic/data
安装jupyter notebook,地址:http://codingpy.com/article/getting-started-with-jupyter-notebook-part-1/
具备pandas,numpy和matplotlib的基础
比赛描述
背景就是那个感人的爱情故事,Jack和Rose本来能够幸福的一起在美国生活,但是由于轮船的管理层傲慢和大意,在晚上可见度不高的情况下还要全速前进,结果看到冰山的时候来不及躲,还是撞上了。救生艇数量很少,船上的游客一部分获救,另一部分遇难。我们要做的就是根据船上一部分乘客的信息来预测一部分乘客是否获救。
参加比赛的步骤:
根据训练集数据和测试集数据生成自己的预测模型
按照预测模型来预测出892到1309条数据是否获救
按照比赛规定的格式生成csv文件,并上传到kaggle上,然后会反馈预测的准确率
流程
结合吴恩达老师讲的机器学习思路来解决这个问题:(课程地址:https://www.coursera.org/learn/machine-learning)
不要想着一下子就生成一个完美的模型,首先根据问题分析出一个基础模型,Base Model
分析Base Model的的状态,是拟合还是过拟合
分析Base Model中使用的特征效果如何,然后进行特征选择
收集预测错误的case,并分析这些Bad case产生的原因
生成Base Model
首先我们来分析一下训练集的数据,看看都有哪些字段:
执行下面两行代码
data_train = pd.read_csv("Train.csv")
data_train.head()
表中的字段各代表的意思是:
PassengerId : 乘客ID
Pclass : 乘客等级 1,2,3等舱位
Name : 乘客姓名
Sex : 性别
Age :年龄
SibSp : 堂兄弟/妹个数
Parch : 父母与小孩个数
Ticket : 船票信息
Fare : 票价
Cabin : 客舱
Embarked : 登船港口
相信看过电影的同学都记得最后上救生艇的场景:
1."Women and children first"
2.那个有钱的男的最后获救了
3.很多3等舱位的人被堵在下面不让上
根据这些可以得出,舱位等级,性别和年龄,是否有钱。这些特征可能会影响是否获救的概率。下面我们来通过图表来分析一下训练集:
舱位的等级和对应的获救情况,执行代码
fig = plt.figure()
fig.set(alpha=0.2)
Survived_n = data_train.Pclass[data_train.Survived == 0].value_counts()
Survived_y = data_train.Pclass[data_train.Survived == 1].value_counts()
df=pd.DataFrame({u'Survived':Survived_y, u'not Survived':Survived_n})
df.plot(kind='bar', stacked=True)
plt.xlabel(u"class")
plt.ylabel(u"count")
plt.show()
好吧,果然是越有钱,获救的概率越大,不评论了。转换到特征上就是Pclass这个字段等于1的时候,获救的概率大。
下面再来看一下性别影响的比例,代码如下:
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({u'man':Survived_m, u'women':Survived_f})
df.plot(kind='bar', stacked=True)
plt.xlabel(u"sex")
plt.ylabel(u"count")
plt.show()
没错,女性获救的比例高,Sex这个字段是female的时候,获救概率高。
好的,我们用LR(逻辑回归)来建模,这时候还需要做一个处理,就是 one-hot encoding (独热向量编码) ,因为逻辑回归建模时,需要输入的特征都是数值型特征
以Sex为例,原本一个属性维度,因为其取值可以是[‘male’,’female’],而将其平展开为’Sex_male’,’Sex_female’两个属性
原本Sex取值为male的,在此处的”Cabin_male”下取值为1,在”Cabin_female”下取值为0
原本Sex取值为female的,在此处的”Cabin_female”下取值为0,在”Cabin_male”下取值为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)
这样我们得到的属性都转为0,1的值了
建模
代码如下:
train_df = df.filter(regex='Survived|Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass_.*')
train_np = train_df.as_matrix()
y = train_np[:, 0]
X = train_np[:, 1:]
clf = linear_model.LogisticRegression(C=1.0, penalty='l1', tol=1e-6)
clf.fit(X, y)
clf
用正则取出我们要的属性值 ,其中y即Survival结果,x即特征属性值。
处理test的数据
把刚才做过的处理,同样的复制一份到test.csv上,代码如下:
data_test = pd.read_csv("test.csv")
data_test.loc[ (data_test.Fare.isnull()), 'Fare' ] = 0
tmp_df = data_test[['Age','Fare', 'Parch', 'SibSp', 'Pclass']]
null_age = tmp_df[data_test.Age.isnull()].as_matrix()
X = null_age[:, 1:]
predictedAges = rfr.predict(X)
data_test.loc[ (data_test.Age.isnull()), 'Age' ] = predictedAges
data_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'].reshape(-1, 1), age_scale_param)
df_test['Fare_scaled'] = scaler.fit_transform(df_test['Fare'].reshape(-1, 1), fare_scale_param)
生成结果
最后一步,把测试集的数据放到LR的模型中去预测,并且保存到csv文件中,代码如下:
test = df_test.filter(regex='Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass_.*|Name')
predictions = clf.predict(test)
result = pd.DataFrame({'PassengerId':data_test['PassengerId'].as_matrix(), 'Survived':predictions.astype(np.int32)})
result.to_csv("logistic_regression_predictions.csv", index=False)
上传结果
打开kaggle中的titanic的页面,找到submit predictions的按钮,点击上传刚才导出的csv文件,然后等待处理后,结果如下:
排名是7000多名,准确率76.555% 当然这只是Base Model,初步的一个处理,所以准确率不高,接下来我们需要做的就是调优。
总结:
完成了Base Model的建立,抽取了几个重要特征了,了解为什么要用one-hot encoding。
熟悉了参加kaggle比赛的流程,之后建议多拿kaggle里的比赛来练习。