人工智障也刷題!Kaggle 入門之實戰泰坦尼克號

header-74885309b5a5696773817498c4e00dc1-c1165.jpeg

背景

關於 Kaggle

  • https://www.kaggle.com/
  • 這是一個爲你提供完美數據,爲你提供實際應用場景,可以與小夥伴在數據挖掘領域 high 的不要不要的的地方啊!!!

Kaggle 是一個用來學習、分享和競賽的線上數據實驗平臺,有點類似 KDD—CUP(國際知識發現和數據挖掘競賽),企業或者研究者可以將背景、數據、期望指標等發佈在 kaggle 上,用競賽的方式向全世界的數據科學家和愛好者尋求解決方案。熱愛數(dong)據(shou)挖(zhe)掘(teng)的小夥伴們可以下載/分析數據,使用統計/機器學習/數據挖掘等方面的知識,建立算法模型,得出結果並提交,排名靠前可能還會有獎勵哦!

關於泰坦尼克號之災

https://www.kaggle.com/c/titanic

  • 問題背景頁

  • 下載 Data 的頁面

  • 泰坦尼克號問題背景

    • 就是大家從小到大被洗腦的“u jump I jump”的「jack 和 rose」的故事了。遊艇在撞擊了一個冰山後沉沒了。乘客們都驚慌失措,副船長「lady and kid first」,所以模型不會向拋硬幣那樣看臉決定你是否獲救。而是有着一定背景的,至於出了女士和孩子優先,還有哪些值得我們考慮,這就是稍後我們在特徵工程中解決的問題了。
    • 訓練和測試數據是一些乘客的個人信息以及存活情況,嘗試應用這些數據來建立一個合適的模型進行預測。
    • 這是一個二分類問題(survived 或者 not),本文嘗試用 logistic regression 來處理問題
  • 說明

    • 「沒有所謂的算法優劣,也沒有絕對高性能的機器學習算法,只有在特定的場景、數據和特徵下更合適的機器學習的算法。」由於還只是在學習階段,對於 XGBC、隨機森林、SVC 還不瞭解,本文所用的算法只是 logistic regression。

初識數據

在 Data 下我們會看到官方給的 train.csv 和 test.csv 兩個文件,分別是訓練和測試數據。我們可以使用 virtualenv 來創建一個“隔離”的 python 應用環境(虛擬環境)。在這裏,你不需要考慮系統原有庫的版本,只需要 pip 來管理你需要用到的一切。

import pandas as pd 
import numpy as np 
from pandas import Series,DataFrame

data_train = pd.read_csv("./train.csv")
pd.DataFrame(data_train)

pandas 是常用的 python 數據處理包,把 csv 文件讀入成 dataframe 格式,在 jupyter notebook 中,可以看到我們的數據長什麼樣:

我們就可以把它看作一張 excel 表格,共有 12 列,891 行(代表在 train_csv 中共有 891 個乘客)。Survived 字段代表該乘客是否獲救(1 代表獲救,0 代表沒有獲救),其餘是一些個人信息

  • passengerId => 乘客的 ID
  • Pclass => 乘客所在艙位的等級(1、2、3 等艙)
  • Name => 姓名
  • Sex => 性別
  • Age => 年齡
  • SibSp => 兄弟姐妹個數
  • Parch => 父母與小孩個數
  • Ticket => 船票信息
  • Fare => 票價
  • Cabin => 客艙信息
  • Embarked => 登船所在港口
data_train.info()

這裏告訴了我麼一些關於 data_train 的基本信息,比如共有 891 個乘客,但是有些字段的數據不全。比如 Age(年齡)、Cabin(客艙信息)。

data_train.describe()

在這裏我們看到了什麼信息呢?從 mean 行中約有 0.38 的人最後獲救了,乘客的平均年齡爲 29.7 歲,平均票價爲 32.20

數據初步分析

每個乘客大概有 12 種屬性提供給我們,僅僅對於上面兩行操作對於數據的瞭解還無法爲我們提供想法和思路,我們現在不知道哪些有用哪些對於模型沒有用,所以到了最關鍵的特徵工程環節。我們知道最終的 output 是 Suvived,現在需要尋找最後的 Survived 與各個屬性之間有着什麼樣的內在關係。從大副的口中已經得知「要首先考慮婦女和兒童」。

for x in data1_x:
 if data1[x].dtype != 'float64' :
 print('Survival Correlation by:', x)
 print(data1[[x, Target[0]]].groupby(x, as_index=False).mean())
 print('-'*10, '\n')
 
print(pd.crosstab(data1['Title'],data1[Target[0]]))

在本圖中,我們發現女性有 0.74 活下來,而男性只有 0.18。歪果盆友果然很尊重 lady,lady first 踐行得不錯。性別無疑要作爲重要特徵加入最後的模型之中。在 Pclass 中,客艙爲等級 1 的乘客獲救的概率高很多,嗯這個一定也影響最後獲救的結果(等級高的乘客一定也更有錢)。

fig = plt.figure()
fig.set(alpha=0.2) 
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({'Survived':Survived_1, 'unSurvived':Survived_0})
df.plot(kind='bar', stacked=True)
plt.title("Survived status of all passenger classes")
plt.xlabel("passanger's level") 
plt.ylabel("number") 
plt.show()

從本圖中我們可以看到,明顯如果你是等級爲 1 的乘客,你的獲救概率就會很高。對了,這也是會最終影響輸出的獲救結果的一個特徵。

data_train.Age[data_train.Pclass == 1].plot(kind='kde') 
data_train.Age[data_train.Pclass == 2].plot(kind='kde')
data_train.Age[data_train.Pclass == 3].plot(kind='kde')
plt.xlabel("age")# plots an axis lable
plt.ylabel("dendity") 
plt.title("Age distribution of passengers levels")
plt.legend(('first level', 'second level','third level'),loc='best')

從各等級乘客的年齡分佈中,我們可以看到「不同艙位/乘客等級可能和財富/地位有關係,最後獲救概率可能會不一樣」,所以年齡也會是影響我們最終結果的原因之一。

簡單數據預處理

我們對大體數據已經看過一遍了,對於感興趣的屬性也有了大概的瞭解。現在我們需要 簡單處理一下這些數據,爲機器學習建模做點準備了。先從最突出的數據屬性開始吧,Cabin 和 Age,因爲這兩項有些乘客的信息不包含它們,有丟失的數據對於下一步的工作影響太大。

先說 Cabin,暫時我們就按照剛纔說的,按 Cabin 有無數據,將這個屬性處理成 Yes 和 No 兩種類型吧。

再說 Age:

通常遇到缺值的情況,我們會有幾種常見的處理方式

  • 如果缺值的樣本佔總數比例極高,我們可能就直接捨棄了,作爲特徵加入的話,可能反倒帶入 noise,影響最後的結果了
  • 如果缺值的樣本適中,而該屬性非連續值特徵屬性(比如說類目屬性),那就把 NaN 作爲一個新類別,加到類別特徵中
  • 如果缺值的樣本適中,而該屬性爲連續值特徵屬性,有時候我們會考慮給定一個 step(比如這裏的 age,我們可以考慮每隔 2/3 歲爲一個步長),然後把它離散化,之後把 NaN 作爲一個 type 加到屬性類目中。
  • 有些情況下,缺失的值個數並不是特別多,那我們也可以試着根據已有的值,擬合一下數據,補充上。

本例中,因爲 Cabin 不是影響最終結果的特徵之一。所以直接考慮別的需要用到的特徵(性別,等級,等級),並將其中的類目型轉化爲數值型特徵,我們可以使用 pandas 的“get_dummies”來完成這個工作,並接在原來的“data_train”上

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')

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

na!我們將這些類目屬性成功轉化爲 0,1 的數值屬性了。這樣看來,好像差不多都完成了,可是如果再看看 Age 和 Fare 兩個屬性,乘客關於這兩個屬性的數值變化幅度也太大了!!如果大家瞭解邏輯迴歸與梯度下降的話,會知道各屬性之間的 scale 差距太大,將對收斂速度造成很大的傷害(甚至不收斂)... 所以我們先用 scikit-learn 裏面的 preprocessing 模塊對這兩個屬性做一個處理(就是將變化幅度較大的特徵化到 [-1,1] 內)

import sklearn.preprocessing as preprocessing
scaler = preprocessing.StandardScaler()
age_scale_param = scaler.fit(df['Age'])
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

嗯,這樣初級的數據處理就完成的差不多了

建模

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

OK!!!通過這樣建模後,我們得到一個 model,然後再把 test.csv 通過同樣的數據簡單處理後,就可以得到預測結果了。

系統優化

等等,你以爲這樣就完了麼。這其實只完成了剛剛開始的一步,我們只是做了一個 baseline model,一切都還是基礎的,我們還需要優化。

不過在現在的場景下,先不着急做這個事情,我們這個 baseline 系統還有些粗糙,先再挖掘挖掘。

  • 首先,Name 和 Ticket 兩個屬性被我們完整捨棄了 (好吧,其實是因爲這倆屬性,幾乎每一條記錄都是一個完全不同的值,我們並沒有找到很直接的處理方式)。
  • 然後,我們想想,年齡的擬合本身也未必是一件非常靠譜的事情,我們依據其餘屬性,其實並不能很好地擬合預測出未知的年齡。再一個,以我們的日常經驗,小盆友和老人可能得到的照顧會多一些,這樣看的話,年齡作爲一個連續值,給一個固定的係數,應該和年齡是一個正相關或者負相關,似乎體現不出兩頭受照顧的實際情況,所以,說不定我們把年齡離散化,按區段分作類別屬性會更合適一些。(大家去 kaggle 上可以看看大神的 kernels)

文 / joeCDC
數學愛好者

編 / 熒聲

本文已由作者授權發佈,版權屬於創宇前端。歡迎註明出處轉載本文。本文鏈接:

想要訂閱更多來自知道創宇開發一線的分享,請搜索關注我們的微信公衆號:創宇前端(KnownsecFED)。歡迎留言討論,我們會儘可能回覆。

歡迎點贊、收藏、留言評論、轉發分享和打賞支持我們。打賞將被完全轉交給文章作者。

感謝您的閱讀。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章