kaggle-Titanic

字数 3447阅读 375

一 经典又兼具备趣味性的Kaggle案例[泰坦尼克号问题

import os 
os.chdir("D:/kaggle/daima/lecture01/Feature_engineering_and_model_tuning/Kaggle_Titanic")
data_train = pd.read_csv("Train.csv")
data_train.columns#字段名
data_train.info() #字段类型 以及缺失情况
data_train.describe() #数值型

画多图 数据探索

import matplotlib.pyplot as plt
fig = plt.figure()
fig.set(alpha=0.2)  # 设定图表颜色alpha参数

plt.subplot2grid((2,3),(0,0))             # 在一张大图里分列几个小图
data_train.Survived.value_counts().plot(kind='bar')       # 0 /549 1  /342  x轴为第一列  多列的列名为lengend 
plt.title(u"获救情况 (1为获救)") # puts a title on our graph
plt.ylabel(u"人数")  

plt.subplot2grid((2,3),(1,0), colspan=2)
data_train[data_train.Pclass == 1].Age.plot(kind='kde')   # plots a kernel desnsity estimate of the subset of the 1st class passanges's age 
data_train[data_train.Pclass == 2].Age.plot(kind='kde')
data_train[data_train.Pclass == 3].Age.plot(kind='kde')  #条件是legend
plt.xlabel(u"年龄")# plots an axis lable
plt.ylabel(u"密度") 
plt.title(u"各等级的乘客年龄分布")
plt.legend((u'头等舱', u'2等舱',u'3等舱'),loc='best') # sets our legend for our graph.


#看看各乘客等级的获救情况
fig = plt.figure()
fig.set(alpha=0.2)  # 设定图表颜色alpha参数

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({u'获救':Survived_1, u'未获救':Survived_0})
df.plot(kind='bar', stacked=True)   #df一行一个bar
plt.title(u"各乘客等级的获救情况")
plt.xlabel(u"乘客等级") 
plt.ylabel(u"人数") 

plt.show()

#多因素交互影响
data_train.Survived[data_train.Sex == 'female'][data_train.Pclass != 3].value_counts().plot(kind='bar', label="female highclass", color='#FA2479')
ax1.set_xticklabels([u"获救", u"未获救"], rotation=0)
ax1.legend([u"女性/高级舱"], loc='best')
g = data_train.groupby(['SibSp','Survived'])
g.count()
df = pd.DataFrame(g.count()['PassengerId'])
df
#cabin的值计数太分散了,**绝大多数Cabin值只出现一次。感觉上作为类目,加入特征未必会有效
#那我们一起看看这个值的有无**,对于survival的分布状况,影响如何吧
fig = plt.figure()
fig.set(alpha=0.2)  # 设定图表颜色alpha参数

Survived_cabin = data_train.Survived[pd.notnull(data_train.Cabin)].value_counts()
优化写法:Survived_cabin2 = data_train.Survived[data_train.Cabin.notnull()].value_counts()
Survived_nocabin = data_train.Survived[pd.isnull(data_train.Cabin)].value_counts()
df=pd.DataFrame({u'有':Survived_cabin, u'无':Survived_nocabin}).transpose()
df.plot(kind='bar', stacked=True)
plt.title(u"按Cabin有无看获救情况")
plt.xlabel(u"Cabin有无") 
plt.ylabel(u"人数")
plt.show()

#似乎有cabin记录的乘客survival比例稍高,那先试试把这个值分为两类,有cabin值/无cabin值,一会儿加到类别特征好了

进行特征选择时,判断每一个特征(连续,离散,相等,数目,考虑派生特征)对幸存值的影响,去除关联特征

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

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

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

Age和Fare两个属性,乘客的数值幅度变化,也忒大了吧!!如果大家了解逻辑回归与梯度下降的话,会知道,各属性值之间scale差距太大,将对收敛速度造成几万点伤害值!甚至不收敛

把需要的feature字段取出来

# 我们把需要的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

对训练集和测试集做一样的操作

data_test = pd.read_csv("test.csv")
data_test.loc[ (data_test.Fare.isnull()), 'Fare' ] = 0
# 接着我们对test_data做和train_data中一致的特征变换
# 首先用同样的RandomForestRegressor模型填上丢失的年龄
tmp_df = data_test[['Age','Fare', 'Parch', 'SibSp', 'Pclass']]
null_age = tmp_df[data_test.Age.isnull()].as_matrix()
# 根据特征属性X预测年龄并补上
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'], age_scale_param)
df_test['Fare_scaled'] = scaler.fit_transform(df_test['Fare'], fare_scale_param)

test = df_test.filter(regex='Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass_.*')
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)

要判定一下当前模型所处状态(欠拟合or过拟合)learning curve

对过拟合而言,通常以下策略对结果优化是有用的:
做一下feature selection,挑出较好的feature的subset来做training
提供更多的数据,从而弥补原始数据的bias问题,学习到的model也会更准确
而对于欠拟合而言,我们通常需要更多的feature,更复杂的模型来提高准确度。

优化baseline系统

  1. 比如说Name和Ticket两个属性被我们完整舍弃了(好吧,其实是一开始我们对于这种,每一条记录都是一个完全不同的值的属性,并没有很直接的处理方式)
    name中包含title?
    ticket是否相同,相同为同一家人更容易幸存,不同幸存率低?
    去掉关联特征(兄弟,父母),增加派生特征(title)?
  2. 比如说,我们想想,年龄的拟合本身也未必是一件非常靠谱的事情
  3. 另外,以我们的日常经验,小盆友和老人可能得到的照顾会多一些,这样看的话,年龄作为一个连续值,给一个固定的系数,应该和年龄是一个正相关或者负相关,似乎体现不出两头受照顾的实际情况,所以,说不定我们把年龄离散化,按区段分作类别属性会更合适一些。 年龄分段,老人小孩受照顾,5岁一个年龄段
将model系数和feature关联

pd.DataFrame({"columns":list(train_df.columns)[1:], "coef":list(clf.coef_.T)})
数值正的越大越正相关,负的越大越负相关
我们先看看那些权重绝对值非常大的feature,在我们的模型上:

  • Sex属性,如果是female会极大提高最后获救的概率,而male会很大程度拉低这个概率。
  • Pclass属性,1等舱乘客最后获救的概率会上升,而乘客等级为3会极大地拉低这个概率。
  • 有Cabin值会很大程度拉升最后获救概率(这里似乎能看到了一点端倪,事实上从最上面的有无Cabin记录的Survived分布图上看出,即使有Cabin记录的乘客也有一部分遇难了,估计这个属性上我们挖掘还不够)
  • Age是一个负相关,意味着在我们的模型里,年龄越小,越有获救的优先权(还得回原数据看看这个是否合理)
  • 有一个登船港口S会很大程度拉低获救的概率,另外俩港口压根就没啥作用(这个实际上非常奇怪,因为我们从之前的统计图上并没有看到S港口的获救率非常低,所以也许可以考虑把登船港口这个feature去掉试试)。
  • 船票Fare有小幅度的正相关(并不意味着这个feature作用不大,有可能是我们细化的程度还不够,举个例子,说不定我们得对它离散化,再分至各个乘客等级上?)

观察完了,我们现在有一些想法了,但是怎么样才知道,哪些优化的方法是promising的呢?
因为test.csv里面并没有Survived这个字段(好吧,这是废话,这明明就是我们要预测的结果),我们无法在这份数据上评定我们算法在该场景下的效果…
而『每做一次调整就make a submission,然后根据结果来判定这次调整的好坏』其实是行不通的…

交叉验证

做cross validation:把train.csv分成两部分,一部分用于训练我们需要的模型,另外一部分数据上看我们预测算法的效果。

将train数据做交叉验证
from sklearn import cross_validation

简单看看打分情况
clf = linear_model.LogisticRegression(C=1.0, penalty='l1', tol=1e-6)
all_data=df.filter(regex='Survived|Age_.|SibSp|Parch|Fare_.|Cabin_.|Embarked_.|Sex_.|Pclass_.')
X = all_data.as_matrix()[:,1:]
y = all_data.as_matrix()[:,0]
print cross_validation.cross_val_score(clf, X, y, cv=5)

将bad case人眼审核,发现信息

既然我们要做交叉验证,那我们干脆先把交叉验证里面的bad case拿出来看看,看看人眼审核,是否能发现什么蛛丝马迹,是我们忽略了哪些信息,使得这些乘客被判定错了。再把bad case上得到的想法和前头系数分析的合在一起,然后逐个试试。

根据训练数据和交叉验证数据不断尝试

分割数据,按照 训练数据:cv数据 = 7:3的比例
split_train, split_cv = cross_validation.train_test_split(df, test_size=0.3, random_state=0)
train_df = split_train.filter(regex='Survived|Age_.|SibSp|Parch|Fare_.|Cabin_.|Embarked_.|Sex_.|Pclass_.')
生成模型
clf = linear_model.LogisticRegression(C=1.0, penalty='l1', tol=1e-6)
clf.fit(train_df.as_matrix()[:,1:], train_df.as_matrix()[:,0])
对cross validation数据进行预测
cv_df = split_cv.filter(regex='Survived|Age_.|SibSp|Parch|Fare_.|Cabin_.|Embarked_.|Sex_.|Pclass_.')
predictions = clf.predict(cv_df.as_matrix()[:,1:])
origin_data_train = pd.read_csv("/Users/HanXiaoyang/Titanic_data/Train.csv")
bad_cases = origin_data_train.loc[origin_data_train['PassengerId'].isin(split_cv[predictions != cv_df.as_matrix()[:,0]]['PassengerId'].values)]
bad_cases
拿到bad cases之后,仔细看看。也会有一些猜测和想法。其中会有一部分可能会印证在系数分析部分的猜测,那这些优化的想法优先级可以放高一些。

有了”train_df” 和 “cv_df” 两个数据部分,前者用于训练model,后者用于评定和选择模型。可以开始可劲折腾了。

我们随便列一些可能可以做的优化操作:
Age属性不使用现在的拟合方式,而是根据名称中的『Mr』『Mrs』『Miss』等的平均值进行填充。
Age不做成一个连续值属性,而是使用一个步长进行离散化,变成离散的类目feature。
Cabin再细化一些,对于有记录的Cabin属性,我们将其分为前面的字母部分(我猜是位置和船层之类的信息) 和 后面的数字部分(应该是房间号,有意思的事情是,如果你仔细看看原始数据,你会发现,这个值大的情况下,似乎获救的可能性高一些)。
Pclass和Sex俩太重要了,我们试着用它们去组出一个组合属性来试试,这也是另外一种程度的细化。
单加一个Child字段,Age<=12的,设为1,其余为0(你去看看数据,确实小盆友优先程度很高啊)
如果名字里面有『Mrs』,而Parch>1的,我们猜测她可能是一个母亲,应该获救的概率也会提高,因此可以多加一个Mother字段,此种情况下设为1,其余情况下设为0
登船港口可以考虑先去掉试试(Q和C本来就没权重,S有点诡异)
把堂兄弟/兄妹 和 Parch 还有自己 个数加在一起组一个Family_size字段(考虑到大家族可能对最后的结果有影响)
Name是一个我们一直没有触碰的属性,我们可以做一些简单的处理,比如说男性中带某些字眼的(‘Capt’, ‘Don’, ‘Major’, ‘Sir’)可以统一到一个Title,女性也一样。

最好的结果是在『Survived~C(Pclass)+C(Title)+C(Sex)+C(Age_bucket)+C(Cabin_num_bucket)Mother+Fare+Family_Size』

模型融合(model ensemble)

模型融合大概就是这么个意思,比如分类问题,当我们手头上有一堆在同一份数据集上训练得到的分类器(比如logistic regression,SVM,KNN,random forest,神经网络),那我们让他们都分别去做判定,然后对结果做投票统计,取票数最多的结果为最后结果。
模型融合可以比较好地缓解,训练过程中产生的过拟合问题,
Bagging 1 数据 2模型算法
现在只讲了LogisticRegression,因此在数据上改进
每次取训练集的一个subset,做训练,这样,我们虽然用的是同一个机器学习算法,但是得到的模型却是不一样的;同时,因为我们没有任何一份子数据集是全的,因此即使出现过拟合,也是在子训练集上出现过拟合,而不是全体数据上

from sklearn.ensemble import BaggingRegressor
train_df = df.filter(regex='Survived|Age_.|SibSp|Parch|Fare_.|Cabin_.|Embarked_.|Sex_.|Pclass.|Mother|Child|Family|Title')
train_np = train_df.as_matrix()
y即Survival结果
y = train_np[:, 0]
X即特征属性值
X = train_np[:, 1:]
fit到BaggingRegressor之中
clf = linear_model.LogisticRegression(C=1.0, penalty='l1', tol=1e-6)
bagging_clf = BaggingRegressor(clf, n_estimators=20, max_samples=0.8, max_features=1.0, bootstrap=True, bootstrap_features=False, n_jobs=-1)
bagging_clf.fit(X, y)
test = df_test.filter(regex='Age_.|SibSp|Parch|Fare_.|Cabin_.|Embarked_.|Sex_.|Pclass.|Mother|Child|Family|Title')
predictions = bagging_clf.predict(test)

Bagging & cross validation
cross validation用来验证
Bagging用来提高模型精度
https://www.ncbi.nlm.nih.gov/pmc/articles/PMC2367370/
I understand the ins and outs of the processes of both cross validation (partition the data set evenly, train on k-1 partitions, blah blah blah) and bagging (train M models composed of n observations picked at random with replacement, blah blah blah).
It seems to me that at the most basic level they're just sampling the same training set (albeit by different methods), but cross validation is used to get a more accurate estimate of model performance on unseen test data, and bagging is used to get a more accurate fit of the training data.
If this true (someone may have to straighten me out if I have the ideas confused), does that mean you may want to use cross validation and bagging together - to find an accurate model that more accurately evaluates model performance on unseen data? It seems a little over kill, but I'm not sure.
https://www.cnblogs.com/sddai/p/5696834.html
交叉验证
对于一个数据分析问题而言,可用的模型不只一个,不存在所谓最优的模型。你不能说,某个模型是最好的,其他模型都是不可取的,某个模型在某个问题下,可能解释能力优于其他模型,但这并不意味着在该类问题下,该模型就是万能的,可能换一种评价标准,这种模型就不是最好的。我们的任务是从几个备选模型中,按照某种评价标准,选择出较为合理的一个模型。
一个直接的想法是比较各个模型的对数据的拟合效果。例如,对于一个x,y数据而言,线性回归的残差平方和可能比非线性回归的残差平方和要小,这时我们说,线性回归拟合效果更好,线性回归模型是理想的选择。但是这种比较方式存在一种缺陷—过拟合问题。有些模型,对原始数据拟合相当好,但是它的预测效果却出奇的差。更重要的是,数据分析的最终目的并不是拟合数据,而是对未来进行预测。一个合理的模型一方面可以拟合原始数据,另一方面又应该可以以高准确率进行预测。所以进行模型选择时,要综合考虑这两方面因素。

Sklearn-GridSearchCV网格搜索
http://blog.csdn.net/cherdw/article/details/54970366(参数说明)

重要

Scikit-learn使用总结
http://python.jobbole.com/86910/

推荐阅读更多精彩内容