本次實驗是Kaggle上的一個入門比賽——Titanic: Machine Learning from Disaster。比賽選擇了泰坦尼克號海難做爲背景,並提供了樣本數據及測試數據,要求咱們根據樣本數據內容創建一個預測模型,對於測試數據中每一個人是否獲救作個預測。樣本數據包括891條乘客信息及獲救狀況,測試數據有418條乘客信息。樣本數據的樣例以下:python
咱們的目標就是根據上述字段信息以及乘客的獲救狀況,實現一個預測乘客是否存活的模型。下面咱們來看一下用到的模型。git
經過分析案例,咱們能夠看出這是一個很明顯的二分類問題,即判斷乘客是否遇難,關於二分類的模型不少,這裏用到了三種模型:線性迴歸模型、邏輯迴歸模型跟隨機森林模型。本次實驗也參考了 寒小陽的CSDN博客以及網頁雲課堂的免費公開課。下面就對這三個模型進行介紹。github
線性迴歸模型很簡單,能夠當作多項式方程的擬合問題。只有一個自變量,稱爲一元線性迴歸;有多於一個的變量,稱爲多元線性迴歸。對於一元線性迴歸,常常採用最小二乘的方法擬合出一條最逼近各點的曲線,以下圖所示:正則表達式
多元線性迴歸自變量不止一個,形式如式子:Y = a1X1+a2X2+a3X3+a4X4+a5X5+.....+anXn。一元線性迴歸是找一條擬合直線,而對於多元線性迴歸則是找到一個超平面,使這個超平面距離各點的距離最小。app
其實不管是一元線性迴歸仍是多元線性迴歸,它們的通式是Y = ω‘X+b。經過訓練數據,找到最合適的w‘和b,也就實現了模型的求解。dom
這樣,咱們輸入不一樣的自變量,就能夠找到對應的因變量,達到預測的目的。機器學習
線性迴歸存在一個最大的問題就是,它的自變量是連續變化的,是區間變量,而現實生活中不少變量不是連續的,例如屬性變量或者序列變量。在這裏,像咱們樣本數據中的年齡屬性就是序列變量,由於年齡正常狀況下不存在小數,不可能咱們說一我的12.25歲;數據中的船艙等級、性別、登船港口等都是屬性變量,這些變量的取值都是固定的。函數
因此對於現實生活中不少實例,線性迴歸模型再也不很適用,這時候就要考慮邏輯迴歸模型。學習
其實邏輯迴歸能夠看作廣義的線性迴歸,只不過它經過函數L把 w‘X+b對應一個隱狀態p,p = L(ω‘X+b),至關於對於結果Y又用一個函數L進行修飾獲得L(Y)。若是沒有函數L則是線性迴歸,若是函數L是多項式函數,就是多項式迴歸,而若是L是logistic函數,就是logistic迴歸。測試
logistic迴歸的因變量能夠是二分類的,也能夠是多分類的,可是二分類的更爲經常使用,也更加容易解釋,多類可使用softmax方法進行處理。實際中最爲經常使用的就是二分類的logistic迴歸。
咱們一般用0和1來表示二分類的結果,而ω‘X+b獲得的值可能不是0-1範圍內,咱們須要找到一個函數對於ω‘X+b結果進行處理,使其值在[0,1]裏面。因而便找到Sigmoid Function做爲咱們的L函數,它的函數式以下:
Sigmoid Function的函數圖像以下,它的繪製函數是:
1 import matplotlib.pyplot as plt 2 import numpy as np 3 4 def Sigmoid(x): 5 return 1.0 / (1.0 + np.exp(-x)) 6 7 x= np.arange(-10, 10, 0.1) 8 #Sigmoid函數 9 h = Sigmoid(x) 10 plt.plot(x, h) 11 #座標軸上加一條豎直的線(0位置) 12 plt.axvline(0.0, color='k') 13 plt.axhspan(0.0, 1.0, facecolor='1.0', alpha=1.0, ls='dotted') 14 plt.axhline(y=0.5, ls='dotted', color='k') 15 #y軸標度 16 plt.yticks([0.0, 0.5, 1.0]) 17 #y軸範圍 18 plt.ylim(-0.1, 1.1) 19 plt.show()
咱們又把L函數叫作激活函數,激活函數不僅Sigmoid Function一種,還有tanh函數、ReLU函數等等,函數的選擇要根據適用場景來定。
咱們經過Sigmoid Function把結果值變爲[0,1]之間的數值,那麼怎麼才能求出最合適的ω'跟b呢?
對於單個樣本,這裏就是每個乘員數據,咱們定義了損失函數(Loss Functon)來評價預測的結果。它的形式是這樣的:Ŀ(a,y),這裏的a就是L(ω‘X+b),y則是預測的真值。Ŀ是一個衡量預測值與真值大小的函數,最經常使用的就是對數形式的函數,爲:Ŀ(a,y) = -[yloga +(1-y)log(l-y)]。
而成本函數(Cost Function)則是全部樣本函數的加權求平均。咱們要想求出最合適的ω'跟b,就要是成本函數的值最小,首先對ω'跟b賦予初值,這時候咱們對成本函數求導,利用反向傳播,能夠獲得一次dω'跟db。
經過梯度降低法ω' = ω'+dω',b = b+db就獲得更新後的ω'跟b。最後經過一步步的梯度降低,使得成本函數C的值最小,這時候咱們就找到了最優的ω'跟b。以下圖所示,經過不斷調整ω'跟b,使得損失函數C到達最低點。
固然,咱們的實現過程很簡單,只須要幾行代碼就能夠搞定。
在談隨機森林模型前,須要理解決策樹模型。最簡單的一個決策樹只有一個分支,例如考到60分以上是及格,60分如下是不及格。當影像因素不少事,會根據這些因素創建不少決策層,最終獲得結果。以下圖所示,就是一個決策樹:
決策樹模型很容易產生過擬合現象,模型泛化能力很弱。基於決策樹模型,又出現了隨機森林模型,經過選擇選擇任意數量的決策樹,經過從樣本數據數據中有放回的隨機抽取一些樣本去訓練這些決策樹,最終的結果是綜合全部決策樹的判斷給出最合理的決策。
說了這麼多模型,其實模型的實現過程很簡單,由於python的sklearn庫已經把上面這些函數統統封裝好了,只須要調用便可。
在進行實驗以前,這裏先說明一下用到的庫函數。pandas、sklearn、numpy以及繪圖庫函數matplotlib。
機器學習的絕大多數運算是矩陣運算,須要輸入的數據是數值型。而咱們這裏不少數據是字符型,咱們首先須要對於數據進行預處理。
首先讀入咱們的數據:
1 # 正則表達式模塊 2 import re 3 4 # 因爲年齡中有空值,須要先用平均值對年齡的缺失值進行填充,由於矩陣運算只能是數值型,不能是字符串 5 titanic['Age'] = titanic['Age'].fillna(titanic['Age'].mean()) 6 # 同理,因爲Embarked(登船地點)裏面也有空值,因此也須要用出現最多的類型對它進行一個填充 7 titanic['Embarked'] = titanic['Embarked'].fillna('S') 8 9 # 對於性別中的male與female,用0和1來表示。首先看性別是否只有兩個值 10 # 對於登船地點的三個值S C Q,也用0 1 2分別表示 11 # print(titanic['Sex'].unique()) 12 # print(titanic['Embarked'].unique()) 13 titanic.loc[titanic['Sex'] == 'male', 'Sex'] = 0 14 titanic.loc[titanic['Sex'] == 'female', 'Sex'] = 1 15 16 titanic.loc[titanic['Embarked'] == 'S', 'Embarked'] = 0 17 titanic.loc[titanic['Embarked'] == 'C', 'Embarked'] = 1 18 titanic.loc[titanic['Embarked'] == 'Q', 'Embarked'] = 2 19 20 # 加上其他的屬性特性 21 titanic["FamilySize"] = titanic["SibSp"] + titanic["Parch"] 22 23 # 姓名的長度 24 titanic["NameLenght"] = titanic["Name"].apply(lambda x: len(x)) 25 26 27 # 定義提取姓名中Mr以及Mrs等屬性 28 def get_title(name): 29 title_search = re.search(' ([A-Za-z]+)\.', name) 30 if title_search: 31 return title_search.group(1) 32 return "" 33 34 35 titles = titanic["Name"].apply(get_title) 36 # 對於姓名中的一些稱呼賦予不一樣的數值 37 title_mapping = {'Mr': 1, 'Miss': 2, 'Mrs': 3, 'Master': 4, 'Dr': 5, 'Rev': 6, 'Major': 7, 'Mlle': 8, 'Col': 9, 38 'Capt': 10, 'Ms': 11, 'Don': 12, 'Jonkheer': 13, 'Countess': '14', 'Lady': 15, 'Sir': 16, 'Mme': 17} 39 for k,v in title_mapping.items(): 40 titles[titles == k] = v 41 titanic['Titles'] = titles
這時候,咱們須要畫圖看一下這些數據對於最後獲救的結果影響到底有多大。
首先是乘客船艙等級獲救狀況統計:
1 # 導入圖表函數 2 import matplotlib.pyplot as plt 3 from pylab import * 4 # 圖表漢字正常顯示 5 mpl.rcParams['font.sans-serif'] = ['SimHei'] 6 # 圖表負值正常顯示 7 matplotlib.rcParams['axes.unicode_minus'] = False 8 9 # 查看各等級乘客等級的獲救狀況 10 fig = plt.figure() 11 # 設置圖表顏色的alpha參數 12 fig.set(alpha=0.2) 13 14 Suvived_0 = titanic.Pclass[titanic.Survived == 0].value_counts() 15 Suvived_1 = titanic.Pclass[titanic.Survived == 1].value_counts() 16 df = pandas.DataFrame({u"獲救": Suvived_1, u"未獲救": Suvived_0}) 17 df.plot(kind='bar', stacked=True) 18 plt.title(u'各乘客等級的獲救狀況') 19 plt.xlabel(u'乘客等級') 20 plt.ylabel(u'人數') 21 plt.show()
再看一下不一樣性別的獲救狀況:
1 # 按性別分組 2 fig = plt.figure() 3 fig.set(alpha=0.2) 4 5 Survived_m = titanic.Survived[titanic.Sex == 0].value_counts() 6 Survived_f = titanic.Survived[titanic.Sex == 1].value_counts() 7 df = pandas.DataFrame({u'男性': Survived_m, u'女性': Survived_f}) 8 df.plot(kind='bar', stacked=True) 9 plt.title(u'不一樣性別獲救狀況') 10 plt.xlabel(u'性別') 11 plt.ylabel(u'人數') 12 plt.show()
還有不一樣年齡的獲救狀況統計:
1 # 不一樣年齡獲救狀況 2 fig = plt.figure() 3 fig.set(alpha=0.2) 4 plt.scatter(titanic.Survived, titanic.Age) 5 plt.ylabel(u'年齡') 6 plt.grid(b=True, which='major', axis='y') 7 plt.title(u'不一樣年齡的獲救狀況(1爲獲救)') 8 plt.show()
不一樣港口登陸乘客的獲救狀況:
1 # 不一樣港口登陸乘客獲救狀況 2 fig = plt.figure() 3 fig.set(alpha=0.2) 4 Survived_0 = titanic.Embarked[titanic.Survived == 0].value_counts() 5 Survived_1 = titanic.Embarked[titanic.Survived == 1].value_counts() 6 df = pandas.DataFrame({u'獲救': Survived_1, u'未獲救': Survived_0}) 7 df.plot(kind='bar', stacked=True) 8 plt.xlabel(u'登陸港口') 9 plt.ylabel(u'人數') 10 plt.title(u'不一樣港口登陸乘客獲救狀況')
好吧,經過圖表分析,咱們得知其中的一些要素對因而否獲救確實存在影像。此時咱們已經得到了10個影響屬性,分別是:Pclass", "Sex", "Age", "SibSp", "Parch", "Fare", "Embarked", "Titles", "FamilySize", "NameLenght"。
那麼咱們有必要對這10個字段分別計算一下重要度,也就是對於結果的影像。咱們經過對於每一個字段,隨機添加一些噪音值,經過對於結果值的影像來判斷相應的重要程度。例如一個屬性添加了不少噪音值,可是對於結果預測的準確率沒有太大影響,咱們就認爲這個屬性重要程度相對較低,反之亦然,統計代碼及結果以下圖所示:
1 # 各特徵屬性的重要程度 2 from sklearn.feature_selection import SelectKBest, f_classif 3 4 5 selector = SelectKBest(f_classif, k=5) 6 selector.fit(titanic[presictors], titanic["Survived"]) 7 # 獲取每一個數據的重要值 8 scores = -np.log10(selector.pvalues_) 9 10 # 畫圖表示,看看哪一些屬性對結果影響較大,即重要值高 11 plt.bar(range(len(presictors)), scores) 12 plt.xticks(range(len(presictors)), presictors, rotation='vertical') 13 14 plt.show()
咱們發現,船艙等級(Pclass)、性別(Sex)、船票價格(Fare)、名字稱謂(Title)以及名字長短(NameLength)的重要性很大,其它的重要性則不是很高(數值越大重要性越大)。頭等艙的確實有必定的優點,女性比男性更有優點(女人跟小孩優先)以及姓名長度跟稱謂也有影響(姓名長度影響座位分配?),船票價格跟船艙等級是正相關,因此也很重要。
好了,模型咱們也已經初步瞭解,數據也已經通過預處理,那麼咱們就開始訓練模型吧。
在此以前,先討論一下交叉驗證。因爲咱們的測試數據是沒有存活數據的,也就是咱們須要根據樣本數據訓練出來的模型給出測試數據對應的存活結果,再在Kaggle平臺上提交結果。可是,咱們又怎麼知道咱們訓練模型的效果呢?有種方法叫作交叉驗證,它的思想就是對於訓練樣本分紅幾份,例如三份,而後分別取出不一樣的兩份做爲訓練樣本,另外一份做爲測試樣本,共取3次,最後取平均就獲得了最終的最終的精度。最終的結果值就是預測出正確值數量/測試樣本的數量。sklearn裏面也提供了交叉驗證的模型,因此很方便就能夠測試咱們的模型是否準確啦。
首先咱們看一下線性迴歸模型的訓練及測試結果:
1 # 導入線性迴歸模型跟邏輯迴歸模型 2 from sklearn.linear_model import LinearRegression, LogisticRegression 3 4 presictors = ["Pclass", "Sex", "Age", "SibSp", "Parch", "Fare", "Embarked", "Titles", "FamilySize", "NameLenght"] 5 alg = LinearRegression() 6 # 在訓練集上進行三次交叉驗證 7 # kf = KFold(titanic.shape[0], n_folds=3, random_state=1) 8 kf = KFold(n_splits=2) 9 predictions = [] 10 for train, test in kf.split(titanic): 11 # 從train中取出分割後的訓練數據 12 train_predictors = titanic[presictors].iloc[train, :] 13 # 取出存活數量做爲訓練目標 14 train_target = titanic["Survived"].iloc[train] 15 # 使用訓練樣本跟訓練目標訓練迴歸函數 16 alg.fit(train_predictors, train_target) 17 # 咱們如今能夠在測試集上作預測 18 test_predictions = alg.predict(titanic[presictors].iloc[test, :]) 19 predictions.append(test_predictions) 20 21 # 檢查迴歸的效果 22 predictions = np.concatenate(predictions, axis=0) 23 24 predictions[predictions > .5] = 1 25 predictions[predictions <= .5] = 0 26 accuracy = sum(predictions == titanic["Survived"])/len(predictions) 27 print(accuracy)
好啦,而後咱們能夠看咱們模型的預測準確率是0.792368125701459。畢竟這是用線性迴歸作的,咱們能夠再試一下邏輯迴歸,看一下效果是否會更好。
1 alg = LogisticRegression(random_state=1) 2 scores = cross_val_score(alg, titanic[presictors], titanic["Survived"], cv=3) 3 print(scores.mean())
結果出來了,模型的預測準確度是0.8103254769921437,確實比線性迴歸的效果要好,那麼咱們再試一下隨機森林的結果。
1 # 導入隨機森林模型 2 from sklearn.ensemble import RandomForestClassifier 3 4 alg = RandomForestClassifier(random_state=1, n_estimators=100, min_samples_split=4, min_samples_leaf=2) 5 # presictors = ["Pclass", "Sex", "Age", "SibSp", "Parch", "Fare", "Embarked", "Titles", "FamilySize", "NameLenght"] 6 kf = KFold(n_splits=3) 7 scores = cross_val_score(alg, titanic[presictors], titanic["Survived"], cv=kf.split(titanic)) 8 print(scores.mean())
模型的預測準確度是0.8294051627384961,精度再一次提升。
到目前爲止,咱們已經嘗試了線性迴歸、邏輯迴歸以及隨機森林實現了泰坦尼克號生存預測。這時候,咱們在會考慮,模型精度能不能進一步提升呢?模型融合能夠是機器學習中提升精度的一大殺器。咱們試着把邏輯迴歸跟隨機森林這兩種模型結合到一塊兒,綜合兩種模型的預測得出更合理的結果,同時,由於隨機森林的精度更高,因此咱們對於隨機森林賦予權重爲3,邏輯迴歸模型的權重賦予1,下面咱們看一下實現過程:
1 algorithms = [ 2 [RandomForestClassifier(random_state=1, n_estimators=100, min_samples_split=4, min_samples_leaf=2), ["Pclass", "Sex", 3 "Age", "SibSp", "Parch", "Fare", "Embarked", "Titles", "FamilySize", "NameLenght"]], 4 [LogisticRegression(random_state=1), ["Pclass", "Sex", "Age", "SibSp", "Parch", "Fare", "Embarked", "Titles", 5 "FamilySize", "NameLenght"]] 6 ] 7 # 交叉驗證 8 kf = KFold(n_splits=3) 9 predictions = [] 10 for train, test in kf.split(titanic): 11 train_target = titanic["Survived"].iloc[train] 12 full_test_predictions = [] 13 # 對於每個測試集都作出預測 14 for alg, predictors in algorithms: 15 alg.fit(titanic[predictors].iloc[train, :], train_target) 16 test_predictions = alg.predict_proba(titanic[predictors].iloc[test, :].astype(float))[:, 1] 17 full_test_predictions.append(test_predictions) 18 test_predictions = (full_test_predictions[0]*3 + full_test_predictions[1])/4 19 test_predictions[test_predictions <= .5] = 0 20 test_predictions[test_predictions > .5] = 1 21 predictions.append(test_predictions) 22 23 # 把全部的預測結果放到集合當中 24 predictions = np.concatenate(predictions, axis=0) 25 26 # 計算與訓練數據真值比較的精度 27 accuracy = sum(predictions == titanic["Survived"])/len(predictions) 28 print(accuracy)
這時候咱們獲得最終的精度爲0.8249158249158249,好吧對比單純的隨機森林精度並無提升。
同時咱們還能夠對模型進行進一步的優化,例如上面有一些字段屬性的重要程度很低,那麼咱們就能夠綜合取捨,這樣不只能夠提升咱們模型的精度,還能夠提升咱們模型的泛化能力。同時,咱們還能夠嘗試其它的模型,以及多種模型的組合,以此來提升咱們預測的精度。
固然,此次模型訓練的數據很是少,由於泰塔尼克號所有的乘客約2200人,模型的訓練可能須要更多數據的訓練,在模型精度和泛化能力之間尋找一個合理平衡,也就是防止過擬合跟欠擬合的問題。
本次實驗採用的python版本是3.6.0,實驗用到的數據以及源代碼在個人GitHub中(首頁左上角點擊便可進入),你們能夠下載實驗。
此次實驗就講到這裏啦,歡迎你們提出寶貴的意見!