Kaggle竞赛——Titanic泰坦尼克之灾(0.76315==>0.79186)

Kaggle竞赛网址:https://www.kaggle.com/c/titanic
上一章:Kaggle竞赛——Titanic泰坦尼克之灾(保姆级基础版)

本次Kaggle泰坦尼克之灾分析过程大致分为:

1步:了解数据(上一章)
第2步:分析数据之间的关系(上一章)
第3步:缺失项数据处理(上一章)
第4步:特征因子化(上一章)
第5步:处理特殊值或浮动较大的数值(上一章)
第6步:数据筛选(上一章)
第7步:数据建模(上一章)
第8步:测试集预处理(上一章)
第9步:结果预测(上一章)
第10步:模型优化(本章)
第11步:模型融合(本章)

好的,基于上一章的分析与讲解,我们大致的得到了一个简单baseline。那么我们下一步该怎么优化呢?

先思考一下:
1.如果不断地做特征工程,产生的特征越来越多,用这些特征去训练模型,会对我们的训练集拟合得越来越好,同时也可能逐步地丧失泛化能力,从而在测试集上表现不佳,也就是发生过拟合问题;
2.如果模型在测试集上表现不佳,除掉上面说的过拟合问题,也有可能是欠拟合问题,也就是说在训练集上,其实拟合的也不是那么好;
a. 对过拟合而言,通常以下策略对结果优化是有用的:做一下特征筛选,挑出较好的特征子集为训练提供更多的数据,从而弥补原始数据的bias问题,学习到的模型也会更准确;
b. 而对于欠拟合而言,通常需要更多的特征,更复杂的模型来提高模型的准确度。

第10步:逻辑回归优化
10.1 模型系数分析

# 模型系数关联分析
pd.DataFrame({"columns":list(train_df.columns)[1:], "coef":list(clf.coef_.T)}) # LR模型系数

在这里插入图片描述
从上表格可以看出:

  1. SibSp和Parch一定程度上呈现的是一个负相关,这个需要看看进一步验证;
  2. 有Cabin(客舱)值会很大程度拉升最后获救概率;(但事实上从最上面的有无Cabin记录的Survived分布图上看出,即使有Cabin记录的乘客也有一部分遇难了,估计这个属性上我们挖掘还不够。)
  3. 登船港口S会有一定程度拉低获救的概率,另外俩港口没啥作用;(但我们从之前的统计图上并没有看到S港口的获救率非常低,可以考虑把登船港口这个feature去掉试试)
  4. Sex属性,如果是female会极大提高最后获救的概率,而male会很大程度拉低这个概率;
  5. Pclass属性,头等舱乘客最后获救的概率会上升,而乘客等级为3会极大地拉低这个概率;
  6. Age是一个负相关,意味着在我们的模型里,年龄越小,越有获救的优先权;(需要看看原数据上是否合理)
  7. 船票Fare有小幅度的正相关。(并不意味着这个feature作用不大,有可能是细化的程度还不够)

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

# 交叉验证
from sklearn.model_selection import cross_val_score, train_test_split

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

在这里插入图片描述
10.3 特征挖掘

# 分割数据
split_train, split_cv = 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, solver='liblinear')
clf.fit(train_df.values[:,1:], train_df.values[:,0])

# 对cross validation数据进行预测
cv_df = split_cv.filter(regex='Survived|Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass_.*')
predictions = clf.predict(cv_df.values[:,1:])
predictions
# split_cv[ predictions != cv_df.values[:,0]].drop()
# # 出现这个报错:ValueError: Need to specify at least one of 'labels', 'index' or 'columns'
# 去除预测错误的case看原始dataframe数据
# split_cv['PredictResult'] = predictions
origin_data_train = pd.read_csv("Train.csv")
bad_cases = origin_data_train.loc[origin_data_train['PassengerId'].isin(split_cv[predictions != cv_df.values[:,0]]['PassengerId'].values)]
bad_cases

在这里插入图片描述
对比bad case:仔细看看预测错的样本,到底是哪些特征有问题,还是处理得不够细?
列一些可能做的优化操作:

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

在这里插入图片描述

# 继续特征挖掘
data_train = pd.read_csv("Train.csv")
data_train['Sex_Pclass'] = data_train.Sex + "_" + data_train.Pclass.map(str) # 性别_乘客等级合并
data_train

在这里插入图片描述
10.4 补全缺失值

from sklearn.ensemble import RandomForestRegressor
 
# 使用 RandomForestClassifier 填补缺失的年龄属性
def set_missing_ages(df):
    
    # 把已有的数值型特征取出来丢进Random Forest Regressor中
    age_df = df[['Age','Fare', 'Parch', 'SibSp', 'Pclass']]

    # 乘客分成已知年龄和未知年龄两部分
    known_age = age_df[age_df.Age.notnull()].values
    unknown_age = age_df[age_df.Age.isnull()].values

    # y即目标年龄
    y = known_age[:, 0]

    # X即特征属性值
    X = known_age[:, 1:]

    # fit到RandomForestRegressor之中
    rfr = RandomForestRegressor(random_state=0, n_estimators=2000, n_jobs=-1)
    rfr.fit(X, y)
    
    # 用得到的模型进行未知年龄结果预测
    predictedAges = rfr.predict(unknown_age[:, 1::])
    
    # 用得到的预测结果填补原缺失数据
    df.loc[ (df.Age.isnull()), 'Age' ] = predictedAges 
    
    return df, rfr

def set_Cabin_type(df):
    df.loc[ (df.Cabin.notnull()), 'Cabin' ] = "Yes"
    df.loc[ (df.Cabin.isnull()), 'Cabin' ] = "No"
    return df

10.5 特征因子化

data_train, rfr = set_missing_ages(data_train)
data_train = set_Cabin_type(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')
dummies_Sex_Pclass = pd.get_dummies(data_train['Sex_Pclass'], prefix= 'Sex_Pclass')

df = pd.concat([data_train, dummies_Cabin, dummies_Embarked, dummies_Sex, dummies_Pclass, dummies_Sex_Pclass], axis=1)
df.drop(['Pclass', 'Name', 'Sex', 'Ticket', 'Cabin', 'Embarked', 'Sex_Pclass'], axis=1, inplace=True)
df

在这里插入图片描述
10.6 处理浮动较大的数值

# 处理数据集中浮动较大的数值到(-1,1)之间
import sklearn.preprocessing as preprocessing
scaler = preprocessing.StandardScaler()

# fit的数据需要以2维([[]])的形式传入
age_scale_param = scaler.fit(df[['Age']])
# 将fit的数据处理后的结果以np.array的形式返回
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

在这里插入图片描述
10.7 筛选数据

# 筛选数据(结果+特征)
# 利用正则表达式
train_df = df.filter(regex='Survived|Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass.*')
train_np = train_df.values

10.8 数据建模

# y即Survival结果
y = train_np[:, 0]
# X即特征属性值
X = train_np[:, 1:]

from sklearn import linear_model
# fit到RandomForestRegressor之中
clf = linear_model.LogisticRegression(C=1.0, penalty='l1', tol=1e-6, solver='liblinear')
clf.fit(X, y)
clf

在这里插入图片描述
10.9 测试集预处理

# 测试集预处理
data_test = pd.read_csv("test.csv")

# 缺失值处理
data_test.loc[ (data_test.Fare.isnull()), 'Fare' ] = 0
data_test['Sex_Pclass'] = data_test.Sex + "_" + data_test.Pclass.map(str)

# 对test_data做和train_data中一致的特征变换
# 用同样的RandomForestRegressor模型填上丢失的年龄
tmp_df = data_test[['Age','Fare', 'Parch', 'SibSp', 'Pclass']]
null_age = tmp_df[data_test.Age.isnull()].values

# 根据特征属性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')
dummies_Sex_Pclass = pd.get_dummies(data_test['Sex_Pclass'], prefix= 'Sex_Pclass')

# 合成新的数据集
df_test = pd.concat([data_test, dummies_Cabin, dummies_Embarked, dummies_Sex, dummies_Pclass, dummies_Sex_Pclass], axis=1)
df_test.drop(['Pclass', 'Name', 'Sex', 'Ticket', 'Cabin', 'Embarked', 'Sex_Pclass'], 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)
df_test

在这里插入图片描述
10.10 结果预测

# 筛选数据(特征)
test = df_test.filter(regex='Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass.*')
predictions = clf.predict(test)
result = pd.DataFrame({'PassengerId':data_test['PassengerId'].values, 'Survived':predictions.astype(np.int32)})
result.to_csv("result_2.csv", index=False)

第11步:模型融合
模型融合可以比较好地缓解训练过程中产生的过拟合问题,从而对于结果的准确度提升有一定的帮助。

思路
由于我这里只用了logistic regression,所以只能从数据上入手了,每次训练的时候不用全部训练集,而是每次取训练集的子集做训练。这样一来,虽然用的是同一个机器学习算法,但是得到的模型却是不一样的;
同时,因为没有任何一份子集是全的,因此即使出现过拟合,也是在子训练集上出现过拟合,而不是全体数据上,这样做一个融合,可能对最后的结果有一定的帮助(常用的是scikit-learn里面的Bagging)。

# 导入sklearn中的BaggingRegressor
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.values

# 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, solver='liblinear')
# 根据需要进行调参
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)
result = pd.DataFrame({'PassengerId':data_test['PassengerId'].values, 'Survived':predictions.astype(np.int32)})
result.to_csv("result_bagging2.csv", index=False)

至此,我们通过了对特征进行简单加工处理,使用了scikit-learn里面的Bagging进行模型融合之后,我们的分数有上升了一个档次啦~ 然后把预测的结果上传到kaggle上,看看你们的分数上涨了多少!!!
我的分数是从0.76315(10861名)到0.79186(1051名)的提升,总参赛人数14251人,后续的分数排名上可能还会往上冲一冲,一起加油把!

在这里插入图片描述

当然,本次也仅仅是针对个别特征、个别算法做的优化工作。其实像模型融合,还有很多比较常见常用的手段。比如:简单加权融合回归,stacking/blending ,还有boosting,这个大家后面可以自己了解了解,多尝试尝试。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Apollo丶小杨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值