kaggle Titanic项目主页:https://www.kaggle.com/c/titanic
Titanic feature engineering
最新参考:https://ahmedbesbes.com/how-to-score-08134-in-titanic-kaggle-challenge.html
0 问题概述
根据给出的乘客特征(年龄、舱室等级、登陆港口等),预测乘客是否幸存,属于典型的分类问题。
数据文件:
- train.csv 训练集,包含1-891个记录
- test.csv 测试集,包含892-1309的记录,不包含是否生存的label
- gender_submission.csv 提交样例,测试集对应的是否生存label
1 训练集、测试集数据对比分析
1.1 特征缺失值情况对比
存在两个特殊情况,数据缺失补全时应特别注意;
Cabin特征存在大比例的缺失,可以将其忽略;
所有特征在训练集、测试集中的缺失情况基本一致,无需据此删除任何特征。
1.2 特征数据分布情况对比
通过绘图对所有10个特征在训练集、测试集中的分布情况进行对比,未发现任何明显差异,无需据此删除任何特征。
1 特征工程
参考:https://triangleinequality.wordpress.com/2013/09/08/basic-feature-engineering-with-the-titanic-data/
离散特征:name 、'Cabin'、 'Embarked'、'Pclass'、 'Sex'、'Ticket'
连续特征:age、fare、'SibSp'、Parch'
1.1 离散特征处理
1)name
乘客姓名作为一个整体时这些信息价值不大,此时它的作用和PassengerId相当。
但是,name中包含的Mrs, Miss, Mr and Master.信息很有价值,可以将name切分为first_name, last_name, mid_name三部分。这里只保留mid_name,其余两个暂时不用。
def name_split(data):
first_name, last_name, mid_name = [], [], []
for name in data['Name']:
name = re.split(r'[,.]\s*', name)
first_name.append(name[0])
mid_name.append(name[1])
last_name.append(name[2])
#data['first_name'] = first_name
data['mid_name'] = mid_name
#data['last_name'] = last_name
print(data['mid_name'].value_counts()) # 输出mid_name中的非重复元素
return data
>>
Mr 517
Miss 182
Mrs 125
Master 40
Dr 7
Rev 6
Mlle 2
Major 2
Col 2
Lady 1
Capt 1
Ms 1
Mme 1
Sir 1
Jonkheer 1
the Countess 1
Don 1
可以看出,mid_name中共含有十多个非重复元素。查找资料发现,称谓的说明包含了这些称谓的解释。
Dr.= doctor 医生/博士
Rev.= reverend,用于基督教的牧师,如the Rev. Mr.Smith
Dona,是西班牙语对女子的称谓,相当于英语的 Lady
Don,n. <西>(置于男士名字前的尊称)先生,堂
Master,小男孩,相当于汉语的"少爷"。
jonkheer是贵族
Col,Colonel,上校
capt船长; captain (陆军)上尉; (海军) 上校
major, 陆军少校意思有少校人意思
The Countless,女伯爵
Mlle,小姐
Mme是Madame的简写,<法>(用于已婚妇女姓名前的尊称)太太
方法1:利用专业知识对离散的字符特征进行分组
根据常识,称谓中一般包含以下三方面的信息:
- 【年龄】:Mrs, Miss, Mr and Master等。称谓中的年龄信息基本和age特征同步。
- 【性别】:基本和sex特征同步;
- 【社会等级】:尊贵的人/平民。👉尊贵的人:jonkheer、The Countless
- 【职业】:可分为船员、牧师、军人、医生等。
检查测试集中的称谓是否和训练集中一致:一致√
print(data['mid_name'].value_counts())
>>
Mr 757
Miss 260
Mrs 197
Master 61
Rev 8
Dr 8
Col 4
Ms 2
Major 2
Mlle 2
Mme 1
Don 1
Dona 1
Lady 1
the Countess 1
Sir 1
Jonkheer 1
Capt 1
featureGroupbyList(data, 'mid_name', [['Mme', 'Ms', 'Mrs'],
['Mlle', 'Miss'],
['Mr'],
['Master'],
['other'],
['Capt', 'Col', 'Dr', 'Major', 'Rev']],
"mid_nameGroup", ['Mrs', 'Miss', 'Mr', 'Master', 'Royalty', 'officer'])
因此,参考Titanic Kaggle Challenge,对'mid_name'进行分组:
1)以基数较多的Mr、Miss、Mrs和Master 为四个独立类别;
2)从剩余基数较少的mid_name中归纳出Royalty、officer两个新类。
方法2:利用特征数据-label标签之间的相关程度进行分组
这种方法忽略midname各取值的现实意义,完全根据label列的表现进行分组。
--1)考虑子图1中特征基数影响,将特征分为['Mr']、['Miss', 'Mrs', 'Master']、其他 三类。
用字符标识,onehot处理,训练集正常0.83,测试集降低为0.53588;
用数值标识,试验发现,离散特征标识的数值大小,对SVM模型的预测结果有显著影响!!!
当数值依次为-1、1、0时,训练集0.83,测试集0.636。
--2)忽略子图1中特征基数的影响,按照子图3中存活率比例,将所有特征划分为三类:
featureGroupbyList(data, 'mid_name', [['Lady', 'Mlle', 'Mme', 'Ms', 'Sir', 'the Countless'],
['other'],
['Capt', 'Don', 'Jonkheer', 'Major', 'Mr', 'Rev']],
"mid_nameGroup", ['high', 'medium', 'low'])
结果表明:
第二种方法不如第一种方法,应该依靠特征的实际意义进行处理。
另计:👉标识的数值依次为2、1000、0时,训练集0.83,测试集0.79904,竟然刷新了个人最高分,说明这个特征是有益的,且数值标识value有显著影响。
2)Pclass座舱等级
该特征虽然是用123数字表示,它本质是离散特征,应该进行onehot处理。
- 该属性本质是离散特征,改变它的数值标识大小为100、2、0时,测试集得分从0.79904小幅回跌至0.79425;
- 改变它的数值标识大小为100、10、1时,测试集得分为0.79425;
提示:后续的Fare特征补全中利用了该特征,因此修改Pclass的value时应该同步修改data['Fare'].fillna(data.Fare[data.Pclass == 3].median(), inplace=True)
3)sex性别
👉性别特征无缺失,属于典型的两种取值的离散特征,0/1数值编码(1个特征)、onehot编码(2个特征)是完全等效的。用数值编码等效于pd.dummpy(drop_first=True)。
data['Sex'] = data['Sex'].map({'male': 1, 'female': 0})
可以看出,登船的男性乘客数量显著多余女性,但是幸存率却显著低于女性,这说明了性别确实存在显著影响。但是,该特征属于典型的两种取值的离散特征,即使用1/100的数值编码,当0-1标准化处理后依然等效于0/1数值编码。
4)ticket票号
👉票号和座舱等级、座位位置(靠窗、过道)等有关,猜想是很有用的特征。
它的value形式如:A/5 21171 ,可大致划分为两部分,
-- 1)提取首字母
-- 2)提取数字的位数。注意:存在四个票后为LINE,不包含任何数字的ticket。
将 'TicketFirstLetter'特征进一步Group处理:
featureGroupbyList(data, 'TicketFirstLetter', [[3, 4, 5, 6, 7, 8, 'A', 'W'], [1, 9, 'P'], ['other']],
"TicketFirstLetterGroup", ['verylow', 'high', 'medium'])
5)cabin 舱室号码
print(data['Cabin'].unique())
>>
nan 'C85' 'C123' 'E46' 'G6' 'C103' 'D56' 'A6' 'C23 C25 C27' 'B78' 'D33'
'B30' 'C52' 'B28' 'C83' 'F33' 'F G73' 'E31' 'A5' 'D10 D12' 'D26' 'C110'
'B58 B60' 'E101' 'F E69' 'D47' 'B86' 'F2' 'C2' 'E33' 'B19' 'A7' 'C49'
提取首字母。只有cabin=T时存活率极低,其他取值并无区分性。
处理:featureGroupbyList(data, 'Cabin', [['T'], ['other']], "CabinGroup", [0, 1])
对于这种缺失值比例极大的特征,需要注意干扰。
6)embarked 登船港口
C = Cherbourg,
Q = Queenstown,
S = Southampton
缺失补全问题:训练集仅有2个缺失记录,测试集无缺失,因此没必要考虑是否用unknown来标记缺失值。由于该三种取值的字符型特征不好寻找相关特征,直接用众数填充。
data['Embarked'].fillna(data['Embarked'].mode()[0], inplace=True)
等价于:df['Embarked'][df['Embarked'].isnull()]=df.Embarked.dropna().mode().values
print('embark_fill value:', data['Embarked'].mode())
1.2 连续特征处理
1)SibSp 兄弟姐妹、配偶数量 + Parch 父母、儿女数量 = Family_Size
Sibling = brother, sister, stepbrother, stepsister
Spouse = husband, wife (mistresses and fiancés were ignored)
Parent = mother, father
Child = daughter, son, stepdaughter, stepson
featureGroupbyList(data, 'Family_Size', [[1, 2, 3], [0, 4, 5, 6], [7, 8, 9, 10]],
"Family_SizeGroup", ['medium', 'low', 'verylow'])
3)fare 票价
缺失补全问题:测试集中只有存在唯一一个fare缺失项。基于常识,我们猜想fare票价和Pclass舱室等级具有较强的相关性。
用皮尔逊相关系数查看:确实√,在所有特征列中,fare和Pclass的相关性最高。备注:Fare_Per_Person是基于Fare构造的新特征,需排除在外。
因此,查询到该fare缺失乘客Pclass = 3,所以用该舱室等级对应的fare平均值进行填充。
def fare_fill(data):
fare = data.Fare[data.Pclass == 3].mean()
print('fare fillvalues:', fare)
data.Fare.replace(np.NaN, fare, inplace=True)
return data
将Fare划分为:[0,51]、(51,...) 两个区间。结果如下图,FareGroup特征表现出了不错的区分度。
4)Age
age特征存在缺失值,但由于占比只有1-714/891=0.198,可以对其进行补全后利用。
补全思路:考虑到mid_name称谓中包含部分age信息,可通过前者对人群进行分类后,再按照子类别的age信息进行补全。注意:这里同时对训练集、测试集补全,最大限度利用已知数据信息!!
首先,查看age缺失记录中对应的mid_name种类和缺失数量。
print(data.loc[data.Age.isnull(), 'mid_name'].value_counts())
Mr 176
Miss 50
Mrs 27
Master 8
Ms 1
Dr 1
其次,再看看这几种人群的基数数量(见name特征处理部分)。他们的基数均偏多,基本可以用各自的age均值进行填充。
填充结果:
age fillvalues: {
'Mr': 32.25215146299484,
'Ms': 28,
'Mrs': 36.99411764705882,
'Miss': 21.774238095238097,
'Master': 5.482641509433963,
'Dr': 43.57142857142857}
可以看出,五种人群都匹配了较好的缺失填充年龄,和常识基本吻合。只是这个master:4.57 五岁的小宝宝?原来,master并不是硕士称谓,而是对小男孩的称呼。详见:http://blog.udn.com/article/article_print.jsp?uid=z1627384950&f_ART_ID=7592122
再将Age划分为40个连续区间,观察其是否可以进行group处理。
1.3 高阶特征
所谓高阶特征,是指通过特征间的乘除、幂指等运算得到的新特征。这里,暂时不把加减运算得到的新特征归为高阶特征,前面已对这种进行了分析。
高阶特征1:df['Age*Class']=df['Age']*df['Pclass']
理由:This is an interaction term, since age and class are both numbers we can just multiply them.
高阶特征2::df['Fare_Per_Person']=df['Fare']/(df['Family_Size']+1)
理由:Here we divide the fare by the number of family members traveling together, I’m not exactly sure what this represents, but it’s easy enough to add in.因为操作不麻烦,就构造了一个新特征,很随性。
总结1:One thing you can do to create new features is linear combinations of features. In a model like linear regression this should be unnecessary, but for a decision tree may find it hard to model such relationships.
【通过线性方法(加减)去构建新的特征,这对于线性回归等模型来说,是没有必要的,但是它对决策树等模型来说就很有意义。】因此,这种构建新特征的方法仍是可取的。
总结2:interaction term交互项至少对线性模型来说,是很有必要的,猜想它对决策树等模型也是有益的。interaction term交互项就是对两个或多个均数值型特征【相乘】,构造出的新特征。交互项=自变项×调节项,即一对存在主从、因果关系的特征相乘。
参考:因果關係的第三者-調節變項(Moderator / Moderation)
文献:Interpreting Interactions in Regression
以线性模型为例:
假设 减肥行为模型model = 截距 + a×减肥知识 + b×性别 + c×(性别×知识)+ 残差error
如果不自行构造(性别×知识)的interaction term交互项特征,仅靠减肥知识、性别两个原有特征,通过线性模型或其他常见模型是不能拟合出来的。
1.4 离散字符特征的连续化处理:
# 删除label列以及无用的离散特征原列,用于特征选择
x_use_all = x_all.drop(
['Survived', 'Cabin', 'Name', 'Parch', 'PassengerId', 'SibSp', 'mid_name',
'Ticket'], axis=1)
x_use_all = pd.get_dummies(x_use_all,drop_first=True) # 对选的use_feature进行onehot处理
print('use_feature经onehot处理后的特征共%d个,分别为:' % x_use_all.columns.size, x_use_all.columns) # 对所有字符特征进行onehot处理
1.5 特征选择
# 选择特征: 嵌入法,用于模型拟合
sfm = SelectFromModel(XGBClassifier(n_estimators=90, learning_rate=0.1, max_depth=6, min_child_weight=8, gamma=0),
threshold=0.004, max_features=15) # 设置特征选择的相关阈值
x_train = sfm.fit_transform(x_train_dummy, y_train)
x_test = sfm.transform(x_test_dummy)
print('-' * 20, '以下为特征选择结果信息:', '-' * 20)
feature_selected = x_train_dummy.columns[sfm.get_support(indices=False)]
# print('最终选取的特征共%d个,分别为:' % feature_selected.size, feature_selected)
features = pd.DataFrame()
features['feature'] = x_use_all.columns
features['importance'] = sfm.estimator_.feature_importances_
features.sort_values(by=['importance'], ascending=True, inplace=True)
features.index = range(features.index.size, 0, -1)
# features.set_index('feature', inplace=True)
print('最终选取的特征共%d个,按importances排序分别为:' % len(feature_selected))
print(features[-len(feature_selected):][::-1])
# features.plot(x='feature', y='importance', kind='barh', figsize=(25, 50))
# plt.show()
print('-' * 50)
2. 模型-调参-评估
2.1 LR调参
parameters_lr = {'penalty': ['l1'],
"C": [0.001, 0.01, 1],
'solver': ['liblinear','saga']
}
2.2 SVM调参
# SVM模型属于距离计算模型,需要对输入X进行归一化处理
x_train = MinMaxScaler().fit_transform(X=x_train)
x_test = MinMaxScaler().fit_transform(X=x_test)
# gridsearch
parameters_svm = {'C': np.arange(2.5, 7, 0.5),
'kernel': ['rbf'],
'gamma': np.arange(0.05, 0.7, 0.1),
# 'degree': [3, 4],
# 'probability': [True, False],
# 'class_weight': ['balanced', None]
}
调参总结:
1)SVM要计算margin,margin是一种距离计算,因此一定要对数据进行归一化,否则收敛会非常非常慢;
2)如下图,gridsearch时,应该先找到影响最大的参数,这里gamma参数的变化就给score带来了明显的提升。
经过对gamma参数的两轮调节,知道它的最优取值大概在3~5附近。
3)第二重要参数是C
经过两轮C参数调参,可以看出它的最优取值基本在1~5之间。
4)class_weight参数
原以为该问题中存在轻微的survival样本不均衡,设置class_weight=balanced会提升效果,但事实证明相反。
5)其他模型参数
degree : int, optional (default=3) 仅当kernel= ‘poly’多项式核函数时有效;
'decision_function_shape': ['ovo', 'ovr'] 参数对结果基本没有任何影响。
SVM调参结果:
best_score_: 0.8260381593714927
best_params_: {'C': 2, 'class_weight': None, 'gamma': 5, 'kernel': 'rbf', 'probability': True}
提交后的得分为:0.78468
除了前述的对模型参数优化以外,还可以:
- 针对模型特点,对特征数据进行修改;
- 对调参过程中的超参数(如交叉验证的CV参数)进行优化。
先对超参数CV进行优化:
for cv in [3, 5, 7, 9]:
print('cv=', cv)
model = grid(SVC(), parameters_svm, cv)
test_prediced = model.predict(x_test)
final_df = pd.DataFrame(data={'PassengerId': range(892, 892 + 418), 'Survived': test_prediced})
final_df.to_csv(r'test_pred_svm\test_pred__bySVM_gridsearch_noEmBark_cv=%d.csv' % cv, index=False)
print('-' * 20)
>>
cv= 3
best_score_: 0.8237934904601572
best_params_: {'C': 2, 'class_weight': None, 'gamma': 7, 'kernel': 'rbf', 'probability': True}
--------------------
cv= 5
best_score_: 0.8271604938271605 #得分最高
best_params_: {'C': 5, 'class_weight': None, 'gamma': 5, 'kernel': 'rbf', 'probability': True}
--------------------
cv= 7
best_score_: 0.8249158249158249
best_params_: {'C': 2, 'class_weight': None, 'gamma': 7, 'kernel': 'rbf', 'probability': True}
--------------------
cv= 9
best_score_: 0.8260381593714927
best_params_: {'C': 2, 'class_weight': None, 'gamma': 5, 'kernel': 'rbf', 'probability': True}
--------------------
将CV=5的预测结果提交kaggle,得分小幅提升至:0.78947
2.3 DT、GBDT调参
# 因为子模型是DT,因此需要进行归一化处理
x_train = MinMaxScaler().fit_transform(X=x_train)
x_test = MinMaxScaler().fit_transform(X=x_test)
parameters_gbdt = {'learning_rate': [0.1], # np.arange(0.001, 0.1, 0.005),
'n_estimators': [65], # np.arange(25, 100, 5),
# 树参数
'max_depth': [9], # np.arange(2, 10, 1),
'min_child_weight': [6], # np.arange(2, 10, 1), # 剪枝部分,肯定会降低训练集准确率,因此要逐个尝试
'gamma': [0.8], # [i / 10 for i in range(0, 10)],
# 'subsample': [i / 100 for i in range(10, 100, 5)], # [i / 100 for i in range(60, 100, 5)]
# 'colsample_bytree': [i / 100 for i in range(10, 100, 5)], # [i / 100 for i in range(60, 100, 5)]
# 'reg_alpha': [0], # [0, 1e-6, 1e-5, 1e-2, 0.1,1]
# 'reg_lambda': [0.05], # np.arange(0.01, 0.3, 0.02)
}
核心思想:
learning rate越小越好,learning rate和n_estimators互相制约
1)先在learning rate=0.1(偏大)时,找出最佳的n_estimators子树数量
发现n_estimators在30-35附近,此时应该根据训练集样本量、特征数来评估n_estimators取值是否合理?
由于这里样本数仅为900个,特征10个左右,无需那么多n_estimators,因此基本合理。如果n_estimators明显过小,则应该适当增大learning rate,重新测试;反之,减小learning rate。
这里,继续用np.arange(25, 40, 2)尝试:最佳为29
2)再优化tree参数,对结果影响最大的参数应该优先调节
参数:max_depth, min_child_weight, gamma, subsample, colsample_bytree
'max_depth': np.arange(2, 10, 1),
'min_child_weight': np.arange(1, 5, 1),
最佳取值为:'max_depth': 7, 'min_child_weight': 1,此时best_score_: 0.8462401795735129
再调gamma参数:'gamma': [i / 10.0 for i in range(0, 5)]
最优值为:0
再调subsample、colsample_bytree参数:
'subsample': [i / 10.0 for i in range(6, 10)],
'colsample_bytree': [i / 10.0 for i in range(6, 10)],
最优值为:'subsample': 0.8,'colsample_bytree': 0.7,此时0.8540965207631874
再以0.05为步长进行微调:
'subsample': [i / 100.0 for i in range(70, 90, 5)],
'colsample_bytree': [i / 100.0 for i in range(60, 80, 5)],
结果一样
最优值为:'reg_alpha': 1e-05,reg_lambda': 0.15,此时best_score_: 0.8552188552188552,小幅上升。
进一步优化:'reg_alpha':[0, 1e-6, 1e-5, 1e-2, 0.1],'reg_alpha':0,得分未变。
3)最后逐步降低learning rate,同时等比例放大n_estimators
先将earning rate降低1倍为0.05,n_estimators放大1倍为58,best_score_: 0.8406285072951739得分明显降低。
最终,best_score_: 0.8552188552188552
2.4 模型融合
所谓三个臭皮匠赛过诸葛亮,单个模型的结果不尽如人意,应该考虑进行融合处理,通常能够提升性能。
xgboost_pred = pd.read_csv(r"test_pred_DT\test_pred__byGBDT_gridsearch_with_cv=5.csv", engine='python', index_col=[0],
skiprows=[0], header=None, names=['xgboost_label'])
xgboost_prob = pd.read_csv(r"test_pred_DT\test_prob__byGBDT_gridsearch_with_cv=5.csv", engine='python', index_col=[0],
skiprows=[0], header=None, names=['xgboost_prob'])
svm_pred = pd.read_csv(r"test_pred_svm\test_pred__bySVM_gridsearch_withAgeGroup_cv=6.csv", engine='python',
index_col=[0], skiprows=[0], header=None, names=['svm_label'])
svm_prob = pd.read_csv(r"test_pred_svm\test_prob__bySVM_gridsearch_withAgeGroup_cv=6.csv", engine='python',
index_col=[0], skiprows=[0], header=None, names=['svm_prob'])
lr_pred = pd.read_csv(r"test_pred_LR\test_pred_byLR_gridsearch_cv=3.csv", engine='python',
index_col=[0], skiprows=[0], header=None, names=['lr_label'])
lr_prob = pd.read_csv(r"test_pred_LR\test_prob_byLR_gridsearch_cv=3.csv", engine='python',
index_col=[0], skiprows=[0], header=None, names=['lr_prob'])
all_pred = pd.concat([xgboost_pred, svm_pred, xgboost_prob, svm_prob, lr_pred, lr_prob], axis=1)
# all_pred['xgboost_0.5'] = all_pred['xgboost_prob'] - 0.5
# all_pred['svm_0.5'] = all_pred['svm_prob'] - 0.5
all_pred['mean'] = all_pred[['xgboost_prob', 'svm_prob', 'lr_prob']].mean(axis=1)
all_pred['mean'] = all_pred['mean'].apply(lambda x: 1 if x >= 0.5 else 0)
all_pred['Survived'] = all_pred['mean']
print(all_pred[all_pred['xgboost_label'] != all_pred['svm_label']])
all_pred['Survived'].to_csv(r'ensembel.csv', index_label='PassengerId', index=True, header=True)
这里用三个模型预测概率的mean进行判断。三个模型单独得分为:
LR: 0.78947
SVM: 0.79904
XGBoost: 0.79904
融合模型:0.80382
赛事评价
kaggle排名中有很多准确率达到1的结果,国内外网站上的讨论How do those awesome people improve their score to above 0.9, to an even 1, for the competition “Titanic: Machine Learning from Disaster” in Kaggle?
都认为可能是利用Titanic的公开性,进行了作弊。例如,https://en.wikipedia.org/wiki/Passengers_of_the_RMS_Titanic这个网站就有相关幸存者信息等。
而根据样本数据能够做到的实际最优效果可能是在0.8~0.9之间。为了避免这个问题,消除作弊者的影响,可尽量采用非公开事件的赛题,即不能通过公开渠道获取正确lable的问题。
参考:
- https://ahmedbesbes.com/how-to-score-08134-in-titanic-kaggle-challenge.html
- Megan Risdal Exploring Survival on the Titanic
- Edward Lu
- 删除了训练集中票价fare=0的样本数据
理由:票价为零属于一种特殊情况(劳力、苦役等?),通过查看,发现所有的这15个人都是男性、都是独行(没有家人同行)、且只有1/15的比例幸存。因此,作者认为这些数据属于无效的干扰数据。
疑问补充:测试集中情况呢?发现测试集中有2个票价为零的记录,且他们也都是男性、没有家人同行。
- Konstantin’s kernel Titanic [0.82] - [0.83] utilized the Family Survival feature which was introduced by Sina in his kernel Titanic best working Classifier
-
Chris Deotte’s Titantic Mega Model - [0.84210] combines Kaggle's top 6 Titanic models
很有意思,作者开头就说这篇文章 is not for beginners ,感觉很厉害的样子。
- 对各个特征列是否有贡献进行了分析,例如作者认为登船港口特征对模型没有任何贡献,为什么还要保留它。
👉尝试了一下,保留/删除Embarked特征前后,利用XGB分类模型,模型在训练集上的准确率从93%跌落至87.%,但提交到Kaggle后,在测试集上的表现反而从0.75提升至0.77。很神奇。 - 认为dummy这种类似onehot处理,对模型没有提升;
-
Tomi Gelo's model combines some of the features above together with the AutoML-tool TPOT to score 0.81 (top 4%)
https://github.com/dashjim/kaggle-titanic