这篇随笔记录我解答Kaggle”入门第一题“——Titanic问题的历程。将随着我对这个题目的思考而不断更新。
题目描述:
题目的数据集包含了当年乘坐泰坦尼克号出行的1309名乘客(失事时,船上据称有2224名船员和乘客)的各类信息,其中训练集891行,测试集418行。包含的特征如下:
变量名 | 中文译名 | 详情 |
PassengerId | 乘客编号 | 无意义,仅用于为乘客进行编号,从1-1309 |
survival | 是否幸存 | 本次预测项目的目标——TestSet将其抹去,并作为评分标准。 在历史上,这个乘客有没有幸存下来。如果活下来标为1,没有存活则标为0. |
pclass | 船舱等级 | 1=头等舱; 2=二等舱; 3=三等舱 (Kaggle将其作为乘客社会地位的象征) |
sex | 乘客性别 | 标为male与female。 |
name | 姓名 | 乘客的全名。emmmmm,这个特征一来不太好用,二来适合用来作弊....... |
Age | 年龄 | 乘客的年龄,有很多NaN(其实在网上能够找到......) |
sibsp | 同辈亲属 | 按照Kaggle的说法,这是sibling(兄弟姐妹)、spouse(配偶)的合写。 对于某个乘客而言,如果船上有他们的sibling或者spouse,那么有几个sibsp就是几,parch下同。 ”Sibling“定义为”兄弟姐妹、义兄弟(stepbrother)、义姐妹(stepsister)”; "Spouse"仅限于正式夫妻。情人、未婚夫妻(mistresses, fiances)不计入 |
parch | 长辈晚辈 | ”parch“是”parent“和”child“的缩写。仅限于父母、子女、继子女。 Kaggle特别指出,有些孩子由保姆跟随旅行,他们的parch为0 |
ticket | 船票编号 | 船票的编号,编号的形式有一点奇特,有的是纯数字,有的带有一些英文字母 |
fare | 票价 | 乘客购票的价格 |
cabin | 客舱号 | 乘客当时所处的客舱号码,有少量的数据(大多数的NaN) |
Embarked | 登船港口 | C = Cherbourg(法国瑟堡),S = Southampton(英国南安普顿), Q = Queenstown( 昆士敦,今称科夫Cobh,位于爱尔兰),有少量的NaN |
Kaggle要求提交一份CSV文件,包含两列418行,第一列是PassengerId,第二列是survived,即预测结果。提交后大约五分钟即可得到评分,每日可提交最多10次,成绩只保存两个月。不要理会LeaderBoard上那些满分的”大佬“,据讨论版的人说,那些人是作弊的——毕竟数据集里连乘客的名字都有,按照历史名单比对一下,”做个答案“出来也不是什么费劲的事,专注于自己的正确率就好了,在今天(2019年11月9日)更新的leaderboard上,84%的预测正确率已经可以达到第200名(前1.5%)。
Chapter 1 -- 脑袋一热——原始型SVM
有必要提到的是,我的第一次尝试是在学术论坛的观众席上做出的,从开始读题到提交不超过一个半小时......
数据处理
除了因变量survived之外,Kaggle给出了十种特征。
在这次临时起意中,sibsp/parch/pclass这三个离散变量被完整保留。而name/cabin/ticket由于”意义不明“被我直接切除,换句话说,在这次分析过程中,我只采用了7种特征。
- sex - female 标为0, male标为1
- age - 缺失值采用平均值填补(这里是一处败笔,后来复盘的时候觉得这里应该考虑进行回归,并考虑年龄缺失这一现象是否与乘客幸存与否有关),并进行Z-Score做标准化。
- fare - 用Z-score做标准化
- embarked - 一分为三,做成三个0-1变量Cherbourg/Southampton/Queenstown,删除embarked列。其中Southampton的比例为70%以上(历史上Titanic航行的出发地)。
调参和预测
SVM调参利用了sklearn.model_selection下的GridSearchCV函数,这个函数适合在训练集较小的情况下进行调参,而大样本情形下效率很低(我又闻到了CPU的香气),其对不同参数组合的评价方式可以进行设定,默认采用Accuracy即分类精度。Python下的代码实现如下:
1 svc = SVC() 2 Parameters = [{'C' : [1,1.5,2,2.5,3,3.5,4,4.5,5], 3 'gamma' : [0.01,0.05,0.1,0.5,1], 4 'kernel' : ['rbf']}, 5 {'C':[1,2,3,4,5,6,7,8,9,10], 6 'kernel':['linear']}] 7 clf= GridSearchCV(svc,Parameters,cv = 5, n_jobs = 6) 8 clf.fit(X_train,Y_train) 9 print(clf.best_params_) 10 #Result: C = 2, gamma = 0.1 if using Gaussian Kernel
GridSearchCV的参数有很多种,这里填写的四种依次是分类器名(svc),候选参数集(Parameters),K折交叉验证的折数(cv),并行计算进程数(n_jobs)。并用clf.fit来对训练集的数据进行拟合,得到最佳的参数组合。这里设定的候选参数集有两类:一类是高斯核(‘rbf’),需要C和Gamma两个参数(罚项系数和分布幅宽),另一类是线性核(”linear"),仅需C一项系数。最后程序返回的结果是:
{'C': 2, 'gamma': 0.1, 'kernel': 'rbf'}
返回结果表示,C为2,gamma为0.1的高斯核形式具有最好的预测准确率。"clf.best_params_"返回的是一个字典,在下面设定正式的SVM模型参数时,可以直接调用这个字典的数据。将这组参数组合输入SVM中进行拟合和预测,即可得到最后的预测结果,为了检测这个SVM的预测性能,我做了一次五折交叉验证:
1 from sklearn.model_selection import StratifiedKFold 2 from sklearn.metrics import precision_score, recall_score, f1_score 3 4 skf = StratifiedKFold(n_splits= 5) 5 clfer = SVC(kernel = 'rbf', C=clf.best_params_['C'],gamma = clf.best_params_['gamma']) 6 precision = [];recall = []; f1 = [] 7 results = [] 8 X = np.array(X_train); Y = np.array(Y_train) 9 for train_index, test_index in skf.split(X,Y): 10 X_TRAIN, X_TEST = X[train_index], X[test_index] 11 Y_TRAIN, Y_TEST = Y[train_index], Y[test_index] 12 #五折数据集分开,并自动分为4+1,且各数据集的0/1比例是完全相同的。 13 #下一步进行具体的SVM训练和评估。 14 clfer.fit(X_TRAIN,Y_TRAIN,None) 15 result = clfer.predict(X_TEST) 16 precision.append(precision_score(Y_TEST,result)) 17 recall.append(recall_score(Y_TEST,result)) 18 f1.append(f1_score(Y_TEST,result)) 19 results.append(result)
StratifiedKFold据说可以尽可能均匀地将0观测点和1观测点均匀地分到各折,使得每次的数据集都具有同等的训练价值。在最后对五次交叉验证的precision、recall、f1进行计算,评估预测的精确程度。基本上这个算法可以保证F1值高于75%,precision比较高(80%以上),而recall相对较低(70%-75%),考虑可能出现了较多的False Negative。这一次的Kaggle提交得分为0.78948,提交后位列3347/14227。