1. 程式人生 > >Titanic:資料探勘入門的第一步

Titanic:資料探勘入門的第一步

關鍵詞: 資料探勘 機器學習 預測 Python

這個專案是kaggle上的一個入門級專案,專門給新手做的,這個專案比較簡單,但是涉及到了資料探勘和機器學習的各個方面,有關的文章和解釋和很多,所以呢,這個比較適合新手上車
看完這個教程並從頭到尾跟著做,恭喜你,已經入了資料探勘的坑,如果你要在kaggle上完成這個專案,其中的步驟我就不一一贅述,請移步kaggle

泰坦尼克號的失事大家都應該有所瞭解,這是歷史上最著名的船隻失事事件。1912年4月15日,泰坦尼克號在處女航途中撞上冰山沉沒,造成2224名乘客和船組人員中1502人遇難。這一聳人聽聞的悲劇震驚了國際社會,從此為船隻制定了更好的安全條例。
泰坦尼克號沉沒事件是一個歷史悠久的悲劇,不僅被拍成了電影,寫成了歌,現在它又被用作例子來學習資料探勘。用這個廣為人知的事件作為背景來入門,可以說非常有文化底蘊了。
雖然說生還概率什麼的總有一些運氣的成分,但是這裡面毫無疑問的有一些因素可以讓乘客的生還概率大大增加,比如女性、孩子、以及社會上層的乘客,所以我們才有了可挖掘的餘地,如果是想要預測一件完全玄學的事情,那我建議你去空間轉一下錦鯉

要求

It is your job to predict if a passenger survived the sinking of the Titanic or not.
For each PassengerId in the test set, you must predict a 0 or 1 value for the Survived variable.

這個專案你要做的,就是分析哪些乘客更容易生還,然後預測一些乘客中哪些人能夠成功生還,0表示不能生還,1表示能夠生還。
宣告:這個文章的notebook是fork自 i,Coder 的 EDA To Prediction(DieTanic)

,大家喜歡可以去為他vote一下哦

本專案資料探勘的步驟

一. 探索性資料分析

1. 對特徵的分析
2. 尋找可能的特徵之間的聯絡或者趨勢

二. 特徵工程以及資料清洗

1. 增加一些新的特徵
2. 去除一些冗餘特徵
3. 將特徵轉化為適合建模的資料

三. 建立預測模型

1. 執行基本的演算法
2. 交叉驗證
3. 演算法整合
4. 匯入特徵提取

探索性資料分析

這個步驟的作用是對整體資料進行一個概覽,將資料用視覺化形式表現出來,然後初步判斷可能的特徵。

import numpy as np 
import pandas as pd
import matplotlib.pyplot as
plt import seaborn as sns plt.style.use('fivethirtyeight') import warnings warnings.filterwarnings('ignore')

首先匯入相應的包,在這個專案中我們主要用到numpy+pandas+matplotlib+seaborn+sklearn

numpy是Python的一個開源的科學計算包,提供了強大的矩陣處理、向量處理、使用的線性代數、傅立葉變換等運算庫。

pandas提供了高效操作大型資料集所需的工具,可以利用它快速便捷的處理資料,我們主要用到它提供的dataframe資料結構來儲存我們的資訊。

matplotlib是一個Python的2D繪相簿,主要用於資料視覺化

seaborn也是Python的一個繪相簿,基於matplotlib進行開發封裝的,matplotlib雖然很強大,但是本身很複雜,畫的圖要經過大量調整才能變精緻,而seaborn十分好用,操作簡單,並且能理解pandas的dataframe。

sklearn是機器學習領域最知名的Python模組之一,包括了很多種機器學習的方式。

讀取資料

要獲取資料的童鞋請到Titanic專案相關資料鏈接這裡下,一共三個,放在你的.py檔案相同目錄就好了,像這樣:

三個csv檔案是下載的資料檔案

data=pd.read_csv('train.csv')

從檔案中讀取資料,儲存到一個dataframe中

data.head()

顯示頭5行資料

第一步是處理空資料,我們來看看有多少空資料 使用dataframe的isnull().sum()方法

data.isnull().sum() #checking for total null values


可以看到Age和Cabin的空資料比較多,這是我們應該處理的,但是別急,我們這是探索性資料分析,等下一步資料清洗的時候再處理

對整體生還率有一個感性認識

我們來看看到底有多慘烈吧

f,ax=plt.subplots(1,2,figsize=(18,8))
data['Survived'].value_counts().plot.pie(explode=[0,0.1],autopct='%1.1f%%',ax=ax[0],shadow=True)
ax[0].set_title('Survived')
ax[0].set_ylabel('')
sns.countplot('Survived',data=data,ax=ax[1])
ax[1].set_title('Survived')
plt.show()

執行結果如下:

這裡使用了matplotlib和seaborn兩個框架來畫圖,左邊的圖是用matplotlib來畫的,右邊是用seaborn來畫的
首先用plt的subplots()方法來獲得影象區域,引數一和二分別表示一行,兩張圖,第三個引數是影象的長寬。
返回的第一個f表示figure,整個影象區域,ax表示子圖索引,ax[0]表示第一個子圖,在左邊,ax[1]表示第二個子圖,在右邊,使用的時候就讓它返回f,ax。這裡可以看出這兩個框架是怎麼聯合使用的,matplotlib來生圖表區域,seaborn來生成子圖,看到第二個圖的繪畫方式你能夠理解為什麼seaborn會更受推崇,它的引數非常簡潔,想要一張直方圖的話就呼叫countplot方法,第一個引數是你想要統計的列,輸入標籤名來表示,第二個引數是你的資料集,這裡傳入data,第三個引數指定圖表表現在哪個子圖上,這裡指定ax[1]。

通過圖表我們可以得知,在891名乘客中,僅有350生還,生還率有38.4%,我們接下來還要探究一下為什麼這些人可以生還

下一步,我們分析一下,在我們的資料集裡面,有哪幾種類型的資料

認識資料集的資料型別

因為針對不同的資料型別,我們有不同的處理方法,因此,我們要先了解我們的資料集裡面,有哪些資料型別
我們所有的影響因素有:

標籤 釋義 取值型別
PassengerId 乘客ID int型資料
Surviveed 是否生還 0或者1
Pclass 乘客的社會等級 1或者2或者3
Name 乘客名字 string
Sex 乘客性別 male或者female
Age 乘客年齡 float一位小數
SibSP 在船上的家庭人員數 int型資料
Parch 在船上的雙親數 int型資料
Ticket 船票編號 string
Fare 船票費用 float四位小數
Cabin 船艙號 string
Embarked 上船港口 C或者Q或者S

一共有12個標籤,看上去什麼都給我們整理好了,我們還需要處理啥呢,但是接下來我會向你證明他們的處理是十分必要的。

在資料探勘領域,資料的屬性分為四種:

  1. 標稱屬性(norminal),比如紅色、藍色、綠色
  2. 布林屬性(binary),比如生存或者死亡
  3. 序數屬性(ordinary),比如乘客社會等級(貴族、爵士、商人……)
  4. 數值屬性(numeric),比如年齡(12,32,55……)

有同學可能會問了,標稱屬性和序數屬性有什麼不同?其實標稱屬性的每個值代表一種類別、一種狀態,可以看做是列舉的,其中每一個值跟其他值沒有聯絡;而序數屬性的每個值之間是有序的,比如飲料的大小有小杯、中杯、大杯,那這個就是序數屬性,飲料的口味有草莓味、香橙味和葡萄味,那個就是標稱屬性,現在明白了哇。

下面我們按照這個分類一一來看看我們的標籤

標稱屬性

標稱屬性有Name,Ticket,Cabin,Embarked。其中大部分是沒什麼用的。
Name,Ticket和生還率沒有什麼太大的聯絡,我們先暫時不管它
Cabin的資料缺失率達到了687/891=77.1%,就別指望它了吧
我們來看看Embarked標籤和生還率的關係

f,ax=plt.subplots(1,2,figsize=(18,8))
sns.countplot('Embarked',data=data,order=['Q','C','S'],ax=ax[0])
ax[0].set_title('total number: Embarked')
sns.countplot('Embarked',hue='Survived',order=['Q','C','S'],data=data,ax=ax[1])
ax[1].set_title('Embarked:survived or not')
plt.show()


從圖中我們可以看到,登船的人數按Q,C,S逐漸增加,最多的港口是S港口,有600+的乘客;在生還率方面呢,Q港口和S港口上來的人存活人數都比遇難人數要多,C港口上來的人遇難人數比存活人數要多,這說明什麼呢?QS港口上來的乘客逃生意識比較強,或者他們是被考慮應該優先逃生的那群人,要知道,同一個港口上船的人有可能是同一社會階層的。

布林屬性

布林屬性有Sex,Survived。這兩個屬性都比較重要,下面我們來分析一下sex

f,ax=plt.subplots(1,2,figsize=(18,8))
data[['Sex','Survived']].groupby(['Sex']).mean().plot.bar(ax=ax[0])
ax[0].set_title('Survived vs Sex')
sns.countplot('Sex',hue='Survived',data=data,ax=ax[1])
ax[1].set_title('Sex:Survived vs Dead')
plt.show()


很有趣,男性的總數其實是比女性多的,但是女性倖存的數量幾乎是男性的兩倍,女性的倖存率是大概75%,而男性的倖存率是18-19%。
“女士優先”這個原則在這裡起了主導作用,這也告訴我們,在預測的時候,應該把性別當做一個很重要的指標來看待。

序數屬性

序數屬性有Pclass,代表乘客的社會等級,也就是階層,1代表階層最高,3代表最低

pd.crosstab(data.Pclass,data.Survived,margins=True).style.background_gradient(cmap='summer_r')

f,ax=plt.subplots(1,2,figsize=(18,8))
data['Pclass'].value_counts().plot.bar(color=['#CD7F32','#FFDF00','#D3D3D3'],ax=ax[0])
ax[0].set_title('Number Of Passengers By Pclass')
ax[0].set_ylabel('Count')
sns.countplot('Pclass',hue='Survived',data=data,ax=ax[1])
ax[1].set_title('Pclass:Survived vs Dead')
plt.show()


俗話說“money cannot buy everything”,我反手就是一個板磚,Pclass1的倖存率達到了63%,Pclass2的也有48%,而Pclass3的呢,只有25%。
窮人真是連活下去的權利都沒有,嘖嘖嘖。

數值屬性

數值屬性比較多,有Age,SibSp,Parch,Fare,PassengerId。其中PassengerId是為了方便記錄進行隨機編的號,對我們的分析毫無影響,不管它,先來看一下Age。

Age

這裡要使用到圖表裡面的violin圖,這種圖比較適合連續性變數與離散變數之間的關係表示,來看看圖就知道了
我們之前說過,倖存率跟性別和社會等級關係非常大,那麼我們就用兩個圖表示:倖存率與年齡與性別,倖存率與年齡與社會等級

f,ax=plt.subplots(1,2,figsize=(18,8))
sns.violinplot("Pclass","Age", hue="Survived", data=data,split=True,ax=ax[0])
ax[0].set_title('Pclass and Age vs Survived')
ax[0].set_yticks(range(0,110,10))
sns.violinplot("Sex","Age", hue="Survived", data=data,split=True,ax=ax[1])
ax[1].set_title('Sex and Age vs Survived')
ax[1].set_yticks(range(0,110,10))
plt.show()


emmmmm,乍一看好像沒什麼大發現,但是仔細看看,可以發現以下幾點:
1. 兒童無論性別,無論社會等級,倖存的概率都會大很多(危急時刻保護兒童優先已經成為常識)
2. 除了第一等級的人群,18-35歲的男性死的最多(因為已婚或者比較有奉獻精神)
3. 對於女性而言,22-40歲的人倖存機率都很大,跟社會等級無關

SibSp Parch

那再來看一下SibSp和Parch,他們都是表示乘客的家庭親屬情況的,通常我們都會覺得,拖家帶口的不容易逃生,因為誰也割捨不下,但是他們又是最容易逃生的,因為會有人幫著你,拉你一把什麼的,到底事實是什麼樣的呢,我們用資料說話。
因為SibSp和Parch的數量所造成的倖存率影響是一樣的(你的兄弟姐妹物件能幫你,你的父母也能,你的兄弟姐妹物件能拖你後腿你的父母也一樣)
所以我們把他們合起來

data['Family_size']=data['SibSp']+data['Parch']

我們新建一個列’Family_size’來儲存這個結果,而不是修改原來的任何一個數據,要養成好習慣

現在我們再來看一下親屬數量和倖存與否之間的關係

f,ax=plt.subplots(1,2,figsize=(18,8))
sns.countplot('Family_size',data=data,ax=ax[0])
ax[0].set_title('count of Family_size')
sns.countplot('Family_size',hue='Survived',data=data,ax=ax[1])
ax[1].set_title('Survived vs Family size')
plt.show()


居然還有拖家帶口10個人的,好吧我們不管他。我們從圖上可以看到,當一個人是孤零零一人的時候,生還的機率是非常小的,當有3個及以下家屬的時候,生還的機率才會增加,但是家屬數量大於3的時候,生還機率又變小了,看來帶上一些家屬隨行還是更加安全的做法,畢竟,在最困難的時候,永遠都是你的家人stand by your side.

Fare

下面再來看看Fare,我們再來用直方圖看一下吧!

f,ax=plt.subplots(1,2,figsize=(18,8))
sns.countplot('Fare',data=data,ax=ax[0])
ax[0].set_title('distribution of Fare')
sns.countplot('Fare',hue='Survived',data=data,ax=ax[1])
ax[1].set_title('Survived vs Fare')
plt.show()


噗……錯了
應該用分佈圖,因為船費好像不是統一價格

再來

f,ax=plt.subplots(1,3,figsize=(20,8))
sns.distplot(data[data['Pclass']==1].Fare,ax=ax[0])
ax[0].set_title('Fares in Pclass 1')
sns.distplot(data[data['Pclass']==2].Fare,ax=ax[1])
ax[1].set_title('Fares in Pclass 2')
sns.distplot(data[data['Pclass']==3].Fare,ax=ax[2])
ax[2].set_title('Fares in Pclass 3')
plt.show()


這就好看多了嘛!
你可以看到不同人的船費是不同的,為什麼呢,我也不懂

好啦!所有標籤我們都看完了,大概總結一下:

  1. 影響最大的兩個因素是sex 和 Pclass
  2. 年齡:10歲以下的小孩,22-40歲的女性,生存機率都很大
  3. 登船港口:從C口登上的乘客生還機率比較高,甚至高過了大部分是等級1的乘客的S口
  4. 親屬:攜帶1至3位親屬的乘客生還率都比較高

下面我們再進行更詳細的資料分析

特徵工程以及資料清洗

處理空資料

前面說過了,Age屬性裡面有好多缺失資料,怎麼填補它們呢?
我們很容易想到用平均值來代替,但是問題是,這些乘客裡面的年齡段是很廣的,你怎麼可以給一個4歲的小孩安排一個29歲的平均值呢,我們得找一個辦法大致推測出這個年齡缺失的人他的年齡可能在什麼範圍,再看看我們還有哪些資料可以用呢?等級、性別和船費好像都不能說明什麼,十歲和四十歲的人可能在這些屬性上都有可能一樣。
我們想到了稱呼,注意Name裡面:

data.Name


我們可以利用這些稱呼,來判斷大致年齡,所以我們要先從Name裡面提取出稱呼來,我們用正則表示式:[A-Za-z]+).
我們新開一列資料,用來儲存我們提取出的稱呼

data['Initial']=0
for i in data:
    data['Initial']=data.Name.str.extract('([A-Za-z]+)\.') #lets extract the Salutations

來看看我們提取的結果:

pd.crosstab(data.Initial,data.Sex).T.style.background_gradient(cmap='summer_r') #Checking the Initials with the Sex


看來還有一些錯誤拼寫,大辣雞!我們就原諒他們吧,我們辛苦一點,手動替換。

data['Initial'].replace(['Mlle','Mme','Ms','Dr','Major','Lady','Countess','Jonkheer','Col','Rev','Capt','Sir','Don'],['Miss','Miss','Miss','Mr','Mr','Mrs','Mrs','Other','Other','Other','Mr','Mr','Mr'],inplace=True)

然後再來看一下我們分好組的乘客年齡段是怎麼樣的

data[['Initial','Age']].groupby(['Initial']).mean()


還是能判斷出來一些年齡段的
好,現在我們來填充那些空的資料

data.loc[(data.Age.isnull())&(data.Initial=='Mr'),'Age']=33
data.loc[(data.Age.isnull())&(data.Initial=='Mrs'),'Age']=36
data.loc[(data.Age.isnull())&(data.Initial=='Master'),'Age']=5
data.loc[(data.Age.isnull())&(data.Initial=='Miss'),'Age']=22
data.loc[(data.Age.isnull())&(data.Initial=='Other'),'Age']=46

再來確認一下有沒有遺漏的

data['Age'].isnull().sum()

好了我們再看Cabin,可是它的缺失率是在太高,就算填補回來,準確率也會大大下降,我們不能冒著使用錯誤資料的方法強行湊整,只能放棄

去除冗餘資料

我們去掉Cabin和將SibSp、Parch合在一起之後,還有10個標籤,是不是會有一些標籤是多餘的呢?
我們可以用pandas的協方差運算和seaborn的熱力圖來一探究竟

sns.heatmap(data.corr(),annot=True,cmap='RdYlGn',linewidths=0.2) #data.corr()-->correlation matrix
fig=plt.gcf()
fig.set_size_inches(10,8)
plt.show()


這個圖很炫酷,我簡單教大家怎麼看,要看懂這個圖,得明白相關係數是個什麼鬼,它是表示兩個變數之間一個隨著另一個如何變化的關係,分為正相關、負相關和壓根不相關,相關係數大於零即為正相關,即一個變大,另一個跟著變大,相關係數小於零則為負相關,即一個變大另一個變小。在這個圖裡,我們可以看到對角線都是1,因為自己和自己肯定是正相關的。如果兩個資料有冗餘,那麼他們的相關係數一定接近於1,我們可以看到SibSp和Parch的相關係數達到了0.41,這也證實了我們將他們合在一塊是合理的。

特徵工程

將年齡分段

對於後面的機器學習來說,越連續性的變數越不利,機器學習大部分是個分類器(clasifier),你提供的訓練資料集類別越多,就越難以聚合,因此我們要將它們從連續型值變成聚合值,一般使用分箱(binning)或者規範化(normalization)。
我們在這裡用分箱的辦法將年齡分成5個段,80/5=16,所以箱子大小是16

data['Age_band']=0
data.loc[data['Age']<=16,'Age_band']=0
data.loc[(data['Age']>16)&(data['Age']<=32),'Age_band']=1
data.loc[(data['Age']>32)&(data['Age']<=48),'Age_band']=2
data.loc[(data['Age']>48)&(data['Age']<=64),'Age_band']=3
data.loc[data['Age']>64,'Age_band']=4

來看一下結果:

data['Age_band'].value_counts().to_frame().style.background_gradient(cmap='summer')

新增Family_size和Alone

Family_size的增加應該屬於特徵工程的範疇,只不過我們在之前的分析之後順手分好了
現在加上一個Alone屬性,因為家屬過多或者獨身一人都會導致生還率變低,因此有必要將這個人是否孤身一人單獨列出作為一個特徵

data['Alone']=0
data.loc[data.Family_Size==0,'Alone']=1

將船費分段聚合

前面也說到了,船費變動很大,因人而異,所以我們要將船費分段表示以構建對後面機器學習有利的特徵
我們會使用到的是pandas的qcut方法,跟分箱類似,這個方法會根據我們傳入的箱子數這個引數來進行分箱,很省事

data['Fare_Range']=pd.qcut(data['Fare'],4)

來看一下船費段與倖存率的關係:

data.groupby(['Fare_Range'])['Survived'].mean().to_frame().style.background_gradient(cmap='summer_r')

但是現在每個箱子裡面仍然是連續性值,還不利於後面的機器學習,我們將每個箱子定義為一個值,就像Age_band那樣

data['Fare_cat']=0
data.loc[data['Fare']<=7.91,'Fare_cat']=0
data.loc[(data['Fare']>7.91)&(data['Fare']<=14.454),'Fare_cat']=1
data.loc[(data['Fare']>14.454)&(data['Fare']<=31),'Fare_cat']=2
data.loc[(data['Fare']>31)&(data['Fare']<=513),'Fare_cat']=3

將string值轉換為數字值

機器學習還不能處理的是string類的值的簡單分類,因此我們還是將他們轉化為int好一點

data['Sex'].replace(['male','female'],[0,1],inplace=True)
data['Embarked'].replace(['S','C','Q'],[0,1,2],inplace=True)
data['Initial'].replace(['Mr','Mrs','Miss','Master','Other'],[0,1,2,3,4],inplace=True)

丟棄不要的值

有一些值我們已經充分利用過,或者沒法用,這時候我們就要狠心一點,將他們從資料集中丟棄,不然白浪費我們那麼多資源和時間用在機器學習上

Name:這個值對我們的預測沒什麼用
Age:我們已經有了Age_band,這個就沒什麼作用了
Ticket:我們已經有了Fare_cat,這個也沒什麼用了
Fare:我們已經有了Fare_cat,這個也沒什麼用了
Cabin:缺失資料太多,放棄
Fare_range:我們已經有了Fare_cat,這個也沒什麼用了
PassengerId:沒用

data.drop(['Name','Age','Ticket','Fare','Cabin','Fare_Range','Initial','SibSp','Parch','PassengerId'],axis=1,inplace=True)

再看看最終的相關係數熱力圖:

sns.heatmap(data.corr(),annot=True,cmap='RdYlGn',linewidths=0.2,annot_kws={'size':20})
fig=plt.gcf()
fig.set_size_inches(18,15)
plt.xticks(fontsize=14)
plt.yticks(fontsize=14)
plt.show()

建立預測模型

終於到了最激動人心的一步了,是不是以為自己即將要接觸高深複雜,blingbling的神經網路,人工智慧了?
彆著急,其實也就那麼一回事

我們在sklearn中使用這6個機器學習方法:

  1. Logistic Regression 邏輯迴歸
  2. Support Vector Machines(Linear and radial) 支援向量機(線性和徑向兩種核心)
  3. Random Forest 隨機森林
  4. K-Nearest Neighbours 也叫KNN或者K最近鄰分類演算法
  5. Naive Bayes 樸素貝葉斯
  6. Decision Tree 決策樹

第一步當然是引入sklearn框架

#importing all the required ML packages
from sklearn.linear_model import LogisticRegression #logistic regression
from sklearn import svm #support vector Machine
from sklearn.ensemble import RandomForestClassifier #Random Forest
from sklearn.neighbors import KNeighborsClassifier #KNN
from sklearn.naive_bayes import GaussianNB #Naive bayes
from sklearn.tree import DecisionTreeClassifier #Decision Tree
from sklearn.model_selection import train_test_split #training and testing data split
from sklearn import metrics #accuracy measure
from sklearn.metrics import confusion_matrix #for confusion matrix

下面來劃分訓練集和測試集,並提取標籤

train,test=train_test_split(data,test_size=0.3,random_state=0,stratify=data['Survived'])
train_X=train[train.columns[1:]]
train_Y=train[train.columns[:1]]
test_X=test[test.columns[1:]]
test_Y=test[test.columns[:1]]
X=data[data.columns[1:]]
Y=data['Survived']

將訓練集和測試集以3:1劃分,並且指定’Survived’為結果指標
這裡sklearn呼叫每個機器學習演算法的方式都十分傻瓜式,得到一個模型,再把訓練集塞進去就行了,再呼叫一個預測方法,把測試集往裡頭一塞就完事,同時它自己也提供了衡量方法,方便你對每個演算法的準確率進行衡量。
下面來看看每個演算法的表現吧

徑向支援向量機

model=svm.SVC(kernel='rbf',C=1,gamma=0.1)
model.fit(train_X,train_Y)
prediction1=model.predict(test_X)
print('Accuracy for rbf SVM is ',metrics.accuracy_score(prediction1,test_Y))

線性支援向量機

model=svm.SVC(kernel='linear',C=0.1,gamma=0.1)
model.fit(train_X,train_Y)
prediction2=model.predict(test_X)
print('Accuracy for linear SVM is',metrics.accuracy_score(prediction2,test_Y))

邏輯迴歸

model = LogisticRegression()
model.fit(train_X,train_Y)
prediction3=model.predict(test_X)
print('The accuracy of the Logistic Regression is',metrics.accuracy_score(prediction3,test_Y))

決策樹

model=DecisionTreeClassifier()
model.fit(train_X,train_Y)
prediction4=model.predict(test_X)
print('The accuracy of the Decision Tree is',metrics.accuracy_score(prediction4,test_Y))

KNN

model=KNeighborsClassifier() 
model.fit(train_X,train_Y)
prediction5=model.predict(test_X)
print('The accuracy of the KNN is',metrics.accuracy_score(prediction5,test_Y))

高斯樸素貝葉斯

model=GaussianNB()
model.fit(train_X,train_Y)
prediction6=model.predict(test_X)
print('The accuracy of the NaiveBayes is',metrics.accuracy_score(prediction6,test_Y))

隨機森林

model=RandomForestClassifier(n_estimators=100)
model.fit(train_X,train_Y)
prediction7=model.predict(test_X)
print('The accuracy of the Random Forests is',metrics.accuracy_score(prediction7,test_Y))

但是呢,機器學習所建立的模型準確度是受很多因素影響的,並且模型的精確性也不是衡量一個演算法的魯棒性的唯一指標,並且對於不同的測試集,它的偏向性也不同,測試的資料改變,它的效能也會發生改變,這就是模型變數(model variance)
我們使用交叉驗證(Cross Validation)來克服這一缺陷

K疊交叉驗證法

這個演算法是這樣的:把所有資料集分為K個子集,比方說5個子集,我們保留一個子集作為測試集,然後訓練其他4個子集,然後我們每次都改變這個測試集,對訓練集進行訓練,例如,第一次將A集作為測試集,BCDE集用來訓練,第二次將B作為測試集,ACDE用來訓練,依次迴圈。

from sklearn.model_selection import KFold #for K-fold cross validation
from sklearn.model_selection import cross_val_score #score evaluation
from sklearn.model_selection import cross_val_predict #prediction
kfold = KFold(n_splits=10, random_state=22) # k=10, split the data into 10 equal parts
xyz=[]
accuracy=[]
std=[]
classifiers=['Linear Svm','Radial Svm','Logistic Regression','KNN','Decision Tree','Naive Bayes','Random Forest']
models=[svm.SVC(kernel='linear'),svm.SVC(kernel='rbf'),LogisticRegression(),KNeighborsClassifier(n_neighbors=9),DecisionTreeClassifier(),GaussianNB(),RandomForestClassifier(n_estimators=100)]
for i in models:
    model = i
    cv_result = cross_val_score(model,X,Y, cv = kfold,scoring = "accuracy")
    cv_result=cv_result
    xyz.append(cv_result.mean())
    std.append(cv_result.std())
    accuracy.append(cv_result)
new_models_dataframe2=pd.DataFrame({'CV Mean':xyz,'Std':std},index=classifiers)       
new_models_dataframe2

plt.subplots(figsize=(12,6))
box=pd.DataFrame(accuracy,index=[classifiers])
box.T.boxplot()


new_models_dataframe2['CV Mean'].plot.barh(width=0.8)
plt.title('Average CV Mean Accuracy')
fig=plt.gcf()
fig.set_size_inches(8,5)
plt.show()

混淆矩陣

再介紹一個方法,混淆矩陣可以給出預測正確和預測錯誤的數量。

f,ax=plt.subplots(3,3,figsize=(12,10))
y_pred = cross_val_predict(svm.SVC(kernel='rbf'),X,Y,cv=10)
sns.heatmap(confusion_matrix(Y,y_pred),ax=ax[0,0],annot=True,fmt='2.0f')
ax[0,0].set_title('Matrix for rbf-SVM')
y_pred = cross_val_predict(svm.SVC(kernel='linear'),X,Y,cv=10)
sns.heatmap(confusion_matrix(Y,y_pred),ax=ax[0,1],annot=True,fmt='2.0f')
ax[0,1].set_title('Matrix for Linear-SVM')
y_pred = cross_val_predict(KNeighborsClassifier(n_neighbors=9),X,Y,cv=10)
sns.heatmap(confusion_matrix(Y,y_pred),ax=ax[0,2],annot=True,fmt='2.0f')
ax[0,2].set_title('Matrix for KNN')
y_pred = cross_val_predict(RandomForestClassifier(n_estimators=100),X,Y,cv=10)
sns.heatmap(confusion_matrix(Y,y_pred),ax=ax[1,0],annot=True,fmt='2.0f')
ax[1,0].set_title('Matrix for Random-Forests')
y_pred = cross_val_predict(LogisticRegression(),X,Y,cv=10)
sns.heatmap(confusion_matrix(Y,y_pred),ax=ax[1,1],annot=True,fmt='2.0f')
ax[1,1].set_title('Matrix for Logistic Regression')
y_pred = cross_val_predict(DecisionTreeClassifier(),X,Y,cv=10)
sns.heatmap(confusion_matrix(Y,y_pred),ax=ax[1,2],annot=True,fmt='2.0f')
ax[1,2].set_title('Matrix for Decision Tree')
y_pred = cross_val_predict(GaussianNB(),X,Y,cv=10)
sns.heatmap(confusion_matrix(Y,y_pred),ax=ax[2,0],annot=True,fmt='2.0f')
ax[2,0].set_title('Matrix for Naive Bayes')
plt.subplots_adjust(hspace=0.2,wspace=0.2)
plt.show()

結果如下:

模型調參

機器學習就像一個黑盒子,放一堆資料進去,嘩的一下一個模型出來,但是我們可以對這個黑盒子的一些引數進行調整,來得到更好的模型,在這裡我們對兩個模型進行調參:支援向量機和隨機森林

支援向量機

from sklearn.model_selection import GridSearchCV
C=[0.05,0.1,0.2,0.3,0.25,0.4,0.5,0.6,0.7,0.8,0.9,1]
gamma=[0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0]
kernel=['rbf','linear']
hyper={'kernel':kernel,'C':C,'gamma':gamma}
gd=GridSearchCV(estimator=svm.SVC(),param_grid=hyper,verbose=True)
gd.fit(X,Y)
print(gd.best_score_)
print(gd.best_estimator_)


我們對支援向量機的C值和gamma值分別不斷進行嘗試,在其中獲得效果最好的一組引數,至於這兩個引數是什麼意義,怎麼影響模型的效率,這裡就不贅述了,感興趣的童鞋請自行研究 :-)

隨機森林

n_estimators=range(100,1000,100)
hyper={'n_estimators':n_estimators}
gd=GridSearchCV(estimator=RandomForestClassifier(random_state=0),param_grid=hyper,verbose=True)
gd.fit(X,Y)
print(gd.best_score_)
print(gd.best_estimator_)