使用Tensorflow做Kaggle入門題之泰坦尼克

本分享推薦使用PC閱讀並自己跟隨操作。全部完成大約需要一小時左右。因為代碼較長文章全部貼出無意義,參考代碼地址(gist.github.com/moshouj),網路定義寫在函數defGraph中,特徵定義寫在getFeatureArr中。

前期準備

  • Kaggle註冊(Your Home for Data Science)
  • 安裝Tensorflow(Mac老司機踩坑記錄:在Mac上安裝Tensorflow的注意點 - 知乎專欄)

目標

  • 實踐Tensorflow的API
    • 增加網路層
    • 定義交叉熵loss和l2 loss
    • 查看特定運算結果
    • 訓練和預測
  • 實踐機器學習的方法論
    • 過擬合
    • 欠擬合
    • 混淆矩陣

本文結構

  • 題目

    • 理解數據
    • 組織數據
  • 構建網路
  • 優化

  • 總結

t題目

Titanic: Machine Learning from Disaster

Overview裡面簡要介紹了任務目的:根據給出的900名乘客的個人信息和倖存與否的數據,預測400個乘客倖存與否。

點擊Data可以看到題目給出了三個文件。

train.csv文件

從第一列開始

  1. survivalt倖存與否t0 = No, 1 = Yes

  2. pclass 代表社會經濟狀況,數字越小狀況越好 1 = upper, 2 = mid, 3 = lower

  3. Name 全名
  4. sext性別

  5. Aget年齡(如果是估計值將用以xx.5表示,如果這個數字很小表示不滿一歲)

  6. sibspt在船上的旁系親屬和配偶的數量

  7. parcht在船上的父母子女的數量

  8. tickett票號

  9. faret船費

  10. cabint艙號
  11. embarkedt登船港口 C = Cherbourg, Q = Queenstown, S = Southampton

test.csv文件中除了倖存與否的信息刪除,後面每列前移,格式和train.csv文件一致。

gender_submission.csv給出了你生成的答案的格式,即一個兩列的csv文件,首行分別為該文件的首行。之後每一行對應test.csv中的每一行數據中的id,以及你判定的生存與否。

理解數據

One of the reasons that the shipwreck led to such loss of life was that there were not enough lifeboats for the passengers and crew. Although there was some element of luck involved in surviving the sinking, some groups of people were more likely to survive than others, such as women, children, and the upper-class.

題目已經硬點了,性別,年齡,階級(經濟狀況)是關鍵特徵。

我們看一下在這三個特徵和生存與否關係如何:

性別:

survivalt倖存與否t0 = No, 1 = Yes

上面這個圖分別代表訓練數據中男性和女性,倖存和未倖存的四種組合分別佔總體的比。

所以即使直接將所有女性判定為1,所有男性判定為0,仍然只會錯掉左上和右下,共21.32%,所以我們的目標就是利用剩餘的信息超越這個值。

然後對年齡以五年為一個區間,可以看到年齡小於10歲的比平均生存率高很多:

但是10歲以內的兒童,男:女 = 32:30,,女性佔比遠高於總體水平,因此性別可能存在影響。

而且同時無論是訓練數據還是測試數據,年齡有缺失。如何儘可能合理的估計和補上這個年齡是個關鍵的問題。並且一個非常有意思的現象是,年齡缺失的人生還率更高一點。

階級和生還率

上流階級生還率高很多。

組織數據

因為tensorflow的輸入需要是一個NumPy的Array,因此我們把每一行輸入轉換為數字,同時補全年齡信息。

此處有兩個相對比較重要的點

  1. 非數值特徵表達為數值特徵

  2. 特徵值的處理

關於非數值特徵表達為數值特徵,這一點上,一般我的做法是,如果該特徵的各個值之間有明確的大小關係且邏輯上可以度量接近程度,那麼該非數值特徵直接在特徵向量中佔一個位置,特徵值間的數值關係應該能反應其邏輯上的接近程度。

而如果某一項特徵,其值之間沒有邏輯上的接近程度,只是通過數值不同來表現該值在各個樣本間該特徵不同,可以考慮直接在特徵向量中分給K個位置(K表示該特徵的可能取值個數),然後把對應值的位置置1,其他置0

以本題為例:比如艙號首位字元,稱謂這兩個特徵就屬於第二種,因此實際上我開了8個位置給cabin首字元,然後把對應位置置1.稱謂同理

cabins = record.cabin.split( )n if len(cabins)>0:n firstChar = [cabin[0] for cabin in cabins if len(cabin)>0]n for f in firstChar:n cabinLead[tagListMap[cabinLead].index(f)] = 1n

其餘列因都是簡單數值類型,因此直接填充即可。

構建網路

我們首先用一個簡單的二層網路,本質上就是個二維的logits

def defGraph(featureVecLen):n X = tf.placeholder("float", [None, featureVecLen])n Y = tf.placeholder("float", [None, 2])nnn w_o = init_weights([featureVecLen,2], "w_o")n b_o = init_weights([2], "b_o")nn py_x = tf.nn.softmax(tf.matmul(ho, w_o) + b_o)n cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=py_x, labels =Y))n train_op = tf.train.AdamOptimizer(1e-4).minimize(cost)n predict_op = tf.argmax(py_x, 1)nn return predict_op, train_op, X, Y n

特徵向量也只年齡,階級,性別

用倒數200個樣本做測試集,其他的每次隨機抽取100個做訓練,迭代2000次。

在測試集上的結果(Accuracy):0.69

在訓練集上的結果(Accuracy):0.672566371681

感覺明顯的欠擬合

優化

改進網路,增加一個隱層:

def defGraph(featureVecLen):n X = tf.placeholder("float", [None, featureVecLen])n Y = tf.placeholder("float", [None, 2])nn w_1 = init_weights([featureVecLen,64], "w_1")n b_1 = init_weights([64], "b_1")nn h1 = tf.nn.relu(tf.matmul(X, w_1) + b_1)nn w_o = init_weights([64,2], "w_o")n b_o = init_weights([2], "b_o")nn py_x = tf.nn.softmax(tf.matmul(h1, w_o) + b_o)n cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=py_x, labels =Y))n train_op = tf.train.AdamOptimizer(1e-4).minimize(cost)n predict_op = tf.argmax(py_x, 1)nn return predict_op, train_op, X, Y n

在測試集上的結果(Accuracy):0.8

在訓練集上的結果(Accuracy):0.793931731985

看起來好了很多,但是還不夠好,只是剛剛達到了只靠性別判斷的程度。

再加一個隱層:

在測試集上的結果(Accuracy):0.84

在訓練集上的結果(Accuracy):0.793931731985

再加一個:

在測試集上的結果(Accuracy):0.82

在訓練集上的結果(Accuracy):0.815423514539

感覺上靠現在這三個數據已經停滯不前了,而且絕對出現了過擬合

下面我們喂進更多的數據,把船費,隨性人員都加入進來,並且把網路縮小回三層。

在測試集上的結果(Accuracy):0.86

在訓練集上的結果(Accuracy):0.839443742099

額外數據倒是有一些效果。我們先用現在這個模型提交一次吧!

得分 0.75120

講道理還不如直接年齡判斷,而且看起來有過擬合了。

加入l2 loss,並且繼續把其他數據添加進來。直覺上中間稱謂(Mr Miss)一定程度上可以幫助我們修復年齡的缺失,最後票號,艙號等等可能也存在對易遇難區域的判斷。

def defGraph(featureVecLen):n X = tf.placeholder("float", [None, featureVecLen])n Y = tf.placeholder("float", [None, 2])nn w_1 = init_weights([featureVecLen,256], "w_1")n b_1 = init_weights([256], "b_1")nn h1 = tf.nn.relu(tf.matmul(X, w_1) + b_1)nn w_o = init_weights([256,2], "w_o")n b_o = init_weights([2], "b_o")nn py_x = tf.nn.softmax(tf.matmul(h1, w_o) + b_o)n vars = tf.trainable_variables() n lossL2 = tf.add_n([ tf.nn.l2_loss(v) for v in varsn if b_ not in v.name ]) * 0.001n cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=py_x, labels =Y)+lossL2)n train_op = tf.train.AdamOptimizer(1e-4).minimize(cost)n predict_op = tf.argmax(py_x, 1)nn return predict_op, train_op, X, Y , tf.reduce_mean(lossL2) , costn

提交一個0.775,倒是不錯

效果比之前好了,但是可能還是存在過擬合。

現在沒有比較直觀的角度來進行提升了,因此我們輸出一下在測試集上的混淆矩陣,以及輸出現在的預測loss和l2 loss.

l2 loss:0.0138169

loss:0.51677

可見l2 loss佔總體loss的比是很低的,應該適度提高一下。

混淆矩陣現在看不出啥來,錯的挺平均的。bad case打出來也挺意外,不少被判活的年輕女性,但是實際是死了,還有一些下層中年大叔被判死但是實際上活下來了。試試提高一下l2 loss係數來一次,竟然提高了 0.78947。

把網路加到五層,提高l2 loss係數,提高訓練次數

def defGraph(featureVecLen):n X = tf.placeholder("float", [None, featureVecLen])n Y = tf.placeholder("float", [None, 2])nn w_1 = init_weights([featureVecLen,512], "w_1")n b_1 = init_weights([512], "b_1")nn w_2 = init_weights([512,256], "w_2")n b_2 = init_weights([256], "b_2") nn w_3 = init_weights([256,128], "w_3")n b_3 = init_weights([128], "b_3") nn w_4 = init_weights([128,16], "w_4")n b_4 = init_weights([16], "b_4") nn h1 = tf.nn.relu(tf.matmul(X, w_1) + b_1)n h2 = tf.nn.relu(tf.matmul(h1, w_2) + b_2)n h3 = tf.nn.relu(tf.matmul(h2, w_3) + b_3)n ho = tf.nn.relu(tf.matmul(h3, w_4) + b_4)nn w_o = init_weights([16,2], "w_o")n b_o = init_weights([2], "b_o")nn py_x = tf.nn.softmax(tf.matmul(ho, w_o) + b_o)n vars = tf.trainable_variables() n lossL2 = tf.add_n([ tf.nn.l2_loss(v) for v in varsn if b_ not in v.name ]) * 0.005n cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=py_x, labels =Y)+lossL2)n train_op = tf.train.AdamOptimizer(2e-5).minimize(cost)n predict_op = tf.argmax(py_x, 1)nn return predict_op, train_op, X, Y , tf.reduce_mean(lossL2) , costn

再來一次 0.77 慘不忍睹。。。

總結

對於少樣本數據,測試集比訓練集低的,要注意過擬合的問題。對於在訓練集上都不過80%的,要考慮沒訓練好(以及欠擬合)的問題。

對於試著入坑DL的各位,調參愉快。不過說實話對於訓練樣本有限的題,傳統ML方法效果更好。我看有用決策樹的輕鬆拿到0.78469,比這個的最好成績多錯一個。

有更好結果的大神360度轉體跪求務必留言教我!

本系列下期預告

Digit Recognizer | Kaggle


推薦閱讀:

【持續更新】機器學習特徵工程實用技巧大全
Zillow Prize競賽系列--(一)競賽簡介
Kaggle:Machine Learning from Disaster 進階學習,殺入Top10%
kaggle小黃車競賽 得分:0.41038

TAG:TensorFlow | Kaggle | 机器学习 |