Titanic生存預測2

背景音樂:保留 - 郭頂

上一篇:Titanic生存預測1,主要講了如何做的特征工程。

這一篇講如何訓練模型來實現預測。

%matplotlib inline
from sklearn import svm, tree, linear_model, neighbors, naive_bayes, ensemble, discriminant_analysis, gaussian_process
from xgboost import XGBClassifier
from sklearn.preprocessing import OneHotEncoder, LabelEncoder
from sklearn import feature_selection
from sklearn import model_selection
from sklearn import metrics
import pandas as pd
import time
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.preprocessing import StandardScaler

1. 讀取數據

path_data = '../../data/titanic/'
df = pd.read_csv(path_data + 'fe_data.csv')

df_data_y = df['Survived']
df_data_x = df.drop(['Survived', 'PassengerId'], 1)

df_train_x = df_data_x.iloc[:891, :]  # 前891個數據是訓練集
df_train_y = df_data_y[:891]

2. 特征選擇

我選擇用GBDT來進行特征選擇,這是由決策樹本身的算法特性所決定的,每次通過計算信息增益(或其他準則)來選擇特征進行分割,在預測的同時也對特征的貢獻進行了“衡量”,因此比較容易可視化~

cv_split = model_selection.ShuffleSplit(n_splits = 10, test_size = .3, train_size = .6, random_state = 0) 
gbdt_rfe = feature_selection.RFECV(ensemble.GradientBoostingClassifier(random_state=2018), step = 1, scoring = 'accuracy', cv = cv_split)
gbdt_rfe.fit(df_train_x, df_train_y)
columns_rfe = df_train_x.columns.values[gbdt_rfe.get_support()]
print('Picked columns: {}'.format(columns_rfe))
print("Optimal number of features : {}/{}".format(gbdt_rfe.n_features_, len(df_train_x.columns)))
plt.figure()
plt.xlabel("Number of features selected")
plt.ylabel("Cross validation score (nb of correct classifications)")
plt.plot(range(1, len(gbdt_rfe.grid_scores_) + 1), gbdt_rfe.grid_scores_)
plt.show()

結果顯示:

Picked columns: ['Age' 'Fare' 'Pclass' 'SibSp' 'FamilySize' 'Family_Survival' 'Sex_Code' 'Title_Master' 'Title_Mr' 'Cabin_C' 'Cabin_E' 'Cabin_X']
Optimal number of features : 12/24

大約在5個以上特征的時候,交叉驗證集的分數就已經趨于穩定了。說明在現有特征中,有貢獻的特征并不多……

最好的結果出現在12個特征的時候。但需要注意的是,比賽的比分不是由你的交叉驗證集決定,所以存在一定的偶然性,鑒于特征數量在比較長的跨度上表現接近,因此我覺得有機會的話,特征數量從5到24的每種選擇都值得一試。

我個人比較了24個特征和12個特征,表現最好的是24個全選……沒試其他的。

然后對特征進行標準化,用以訓練:

stsc = StandardScaler()
df_data_x = stsc.fit_transform(df_data_x)
print('mean:\n', stsc.mean_)
print('var:\n', stsc.var_)

df_train_x = df_data_x[:891]
df_train_y = df_data_y[:891]

df_test_x = df_data_x[891:]
df_test_output = df.iloc[891:, :][['PassengerId','Survived']]

3.模型融合

機器學習的套路是:

  1. 先選擇一個基礎模型,進行訓練和預測,最快建立起一個pipeline。
  2. 在此基礎上用交叉驗證和GridSearch對模型調參,查看模型的表現。
  3. 用模型融合進行多個模型的組合,用投票的方式(或其他)來預測結果。

一般來說,模型融合得到的結果會比單個模型的要好。

在這里,我跳過了步驟1和2,直接進行步驟3。

3.1 設置基本參數

vote_est = [
    ('ada', ensemble.AdaBoostClassifier()),
    ('bc', ensemble.BaggingClassifier()),
    ('etc', ensemble.ExtraTreesClassifier()),
    ('gbc', ensemble.GradientBoostingClassifier()),
    ('rfc', ensemble.RandomForestClassifier()),
    ('gpc', gaussian_process.GaussianProcessClassifier()),
    ('lr', linear_model.LogisticRegressionCV()),
    ('bnb', naive_bayes.BernoulliNB()),
    ('gnb', naive_bayes.GaussianNB()),
    ('knn', neighbors.KNeighborsClassifier()),
    ('svc', svm.SVC(probability=True)),
    ('xgb', XGBClassifier())
]

grid_n_estimator = [10, 50, 100, 300, 500]
grid_ratio = [.5, .8, 1.0]
grid_learn = [.001, .005, .01, .05, .1]
grid_max_depth = [2, 4, 6, 8, 10]
grid_criterion = ['gini', 'entropy']
grid_bool = [True, False]
grid_seed = [0]

grid_param = [
    # AdaBoostClassifier
    {
        'n_estimators':grid_n_estimator,
        'learning_rate':grid_learn,
        'random_state':grid_seed
    },
    # BaggingClassifier
    {
        'n_estimators':grid_n_estimator,
        'max_samples':grid_ratio,
        'random_state':grid_seed
    },
    # ExtraTreesClassifier
    {
        'n_estimators':grid_n_estimator,
        'criterion':grid_criterion,
        'max_depth':grid_max_depth,
        'random_state':grid_seed
    },
    # GradientBoostingClassifier
    {
        'learning_rate':grid_learn,
        'n_estimators':grid_n_estimator,
        'max_depth':grid_max_depth,
        'random_state':grid_seed,

    },
    # RandomForestClassifier
    {
        'n_estimators':grid_n_estimator,
        'criterion':grid_criterion,
        'max_depth':grid_max_depth,
        'oob_score':[True],
        'random_state':grid_seed
    },
    # GaussianProcessClassifier
    {
        'max_iter_predict':grid_n_estimator,
        'random_state':grid_seed
    },
    # LogisticRegressionCV
    {
        'fit_intercept':grid_bool,  # default: True
        'solver':['newton-cg', 'lbfgs', 'liblinear', 'sag', 'saga'],
        'random_state':grid_seed
    },
    # BernoulliNB
    {
        'alpha':grid_ratio,
    },
    # GaussianNB
    {},
    # KNeighborsClassifier
    {
        'n_neighbors':range(6, 25),
        'weights':['uniform', 'distance'],
        'algorithm':['auto', 'ball_tree', 'kd_tree', 'brute']
    },
    # SVC
    {
        'C':[1, 2, 3, 4, 5],
        'gamma':grid_ratio,
        'decision_function_shape':['ovo', 'ovr'],
        'probability':[True],
        'random_state':grid_seed
    },
    # XGBClassifier
    {
        'learning_rate':grid_learn,
        'max_depth':[1, 2, 4, 6, 8, 10],
        'n_estimators':grid_n_estimator,
        'seed':grid_seed
    }
]

3.2 訓練

對于每個模型都進行調參再組合,不過有的迭代次數較多,為了節省時間我就用了RandomizedSearchCV來簡化(還沒來得及試驗全部GridSearchCV)。

start_total = time.perf_counter()
N = 0
for clf, param in zip (vote_est, grid_param):  
    start = time.perf_counter()     
    cv_split = model_selection.ShuffleSplit(n_splits = 10, test_size = .3, train_size = .6, random_state = 0) 
    if 'n_estimators' not in param.keys():
        print(clf[1].__class__.__name__, 'GridSearchCV')
        best_search = model_selection.GridSearchCV(estimator = clf[1], param_grid = param, cv = cv_split, scoring = 'accuracy')
        best_search.fit(df_train_x, df_train_y)
        best_param = best_search.best_params_
    else:
        print(clf[1].__class__.__name__, 'RandomizedSearchCV')
        best_search2 = model_selection.RandomizedSearchCV(estimator = clf[1], param_distributions = param, cv = cv_split, scoring = 'accuracy')
        best_search2.fit(df_train_x, df_train_y)
        best_param = best_search2.best_params_
    run = time.perf_counter() - start

    print('The best parameter for {} is {} with a runtime of {:.2f} seconds.'.format(clf[1].__class__.__name__, best_param, run))
    clf[1].set_params(**best_param) 

run_total = time.perf_counter() - start_total
print('Total optimization time was {:.2f} minutes.'.format(run_total/60))

4. 預測

投票有兩種方式——軟投票和硬投票。

  • 硬投票:少數服從多數。
  • 軟投票:沒研究過,有文章表明,計算的是加權平均概率,預測結果是概率高的。

如果沒有先驗經驗,那么最好是兩種投票方式都算一遍,看看結果如何。

對于Titanic生存預測,我發現每次都是硬投票的結果要好。

grid_hard = ensemble.VotingClassifier(estimators = vote_est , voting = 'hard')
grid_hard_cv = model_selection.cross_validate(grid_hard, df_train_x, df_train_y, cv = cv_split, scoring = 'accuracy')
grid_hard.fit(df_train_x, df_train_y)

print("Hard Voting w/Tuned Hyperparameters Training w/bin score mean: {:.2f}". format(grid_hard_cv['train_score'].mean()*100)) 
print("Hard Voting w/Tuned Hyperparameters Test w/bin score mean: {:.2f}". format(grid_hard_cv['test_score'].mean()*100))
print("Hard Voting w/Tuned Hyperparameters Test w/bin score 3*std: +/- {:.2f}". format(grid_hard_cv['test_score'].std()*100*3))
print('-'*10)

grid_soft = ensemble.VotingClassifier(estimators = vote_est , voting = 'soft')
grid_soft_cv = model_selection.cross_validate(grid_soft, df_train_x, df_train_y, cv = cv_split, scoring = 'accuracy')
grid_soft.fit(df_train_x, df_train_y)

print("Soft Voting w/Tuned Hyperparameters Training w/bin score mean: {:.2f}". format(grid_soft_cv['train_score'].mean()*100)) 
print("Soft Voting w/Tuned Hyperparameters Test w/bin score mean: {:.2f}". format(grid_soft_cv['test_score'].mean()*100))
print("Soft Voting w/Tuned Hyperparameters Test w/bin score 3*std: +/- {:.2f}". format(grid_soft_cv['test_score'].std()*100*3))

結果為:

Hard Voting w/Tuned Hyperparameters Training w/bin score mean: 89.70
Hard Voting w/Tuned Hyperparameters Test w/bin score mean: 85.97
Hard Voting w/Tuned Hyperparameters Test w/bin score 3*std: +/- 5.95
----------
Soft Voting w/Tuned Hyperparameters Training w/bin score mean: 90.02
Soft Voting w/Tuned Hyperparameters Test w/bin score mean: 85.52
Soft Voting w/Tuned Hyperparameters Test w/bin score 3*std: +/- 6.07

硬投票得出的預測結果,在測試集上的分數較高,標準差較小,優選硬投票。

5. 提交結果:

用硬投票作為預測的方案,得到結果并提交。

df_test_output['Survived'] = grid_hard.predict(df_test_x)
df_test_output.to_csv('../../data/titanic/hardvote.csv', index = False)

在官網上提交結果,給出的分數是0.81339。


后記

Titanic這個項目很值得一試,在實踐的過程中,我參考了一些參賽者在kaggle上分享的kernel,收益良多。

但作為入門項目,重在參與,后面有空了再做一遍,看是否能有提高。

接下來,我會嘗試參加貓狗大戰
也就是編寫一個算法來分類圖像是否包含狗或貓。
這對人類,狗和貓來說很容易,但用算法如何實現呢?拭目以待。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,702評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,615評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,606評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,044評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,826評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,227評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,307評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,447評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,992評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,807評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,001評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,550評論 5 361
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,243評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,667評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,930評論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,709評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,996評論 2 374