圖:pixabay
雷克大會(2017機器人與人工智能大會)即將於2017年6月21日在北京新雲南皇冠假日酒店召開,活動報名渠道全面開啟,報名參會請點擊活動行鏈接
「機器人圈」導覽:如果你對機器學習感興趣,你一定不要錯過此文,作者以一個生動的例子,詳細闡述了Sklearn在創建分類器中的作用。文章編譯自kasperfred.com,作者是Kasper Fredenslund,文章略長,時長大約15分鐘,請耐心閱讀。
如果你熱愛機器學習,但是你又不知道該從何入手,也許你已經閱讀了一些理論,掌握了一定的基礎知識,可是你並不知道該如何將你學到的理論付諸實踐。
本教程的出發點就是助你“破冰”,引導你完成從導入和分析數據集到實施和訓練幾種不同的知名分類算法,並評估其性能的完整過程。
我將使用最小量的離散數學,旨在使用直覺來表達細節,使用生動具體的例子而不是密集的數學公式來傳達思想。如果你想知道為什麼,可以查看詳情。
通讀文章之後你就會明白:
·使用sklearn導入並轉換.csv文件中的數據
·檢查數據集並選擇相關特徵
·使用sklearn在數據中訓練不同的分類器
·分析結果,旨在改善你的模型
我們將使用你可以從Kaggle下載的鳶尾花數據集(Iris flower dataset),根據其萼片和花瓣特徵對花種進行分類。
也許你沒有聽說過Kaggle,那我來介紹一下,Kaggle擁有大量酷炫的數據集,是數據科學家分享他們研究成果的地方,對於初學者來說,這可是學習時可能用到的寶貴資源。
鳶尾花數據集相當小(僅由150個均勻分佈的樣本組成),而且表現良好,從而使其成為該項目的理想選擇。
你可能會問,為什麼使用這個毫無意義的數據集,而放棄其他那些有趣的數據集?原因是當我們學習數據分析時,使用簡單,表現良好的數據可以減少認知負荷,使我們能夠更好地理解我們正在使用的數據,從而使調試變得更容易。
當學習機器學習時,數據是沒有如何分析數據那麼重要的。
導入數據
一旦我們下載了數據,我們首先要做的就是加載它並檢查其結構。為此,我們將使用pandas。
Pandas是一個python庫,為我們提供了一個稱為DataFrame的數據處理通用接口。DataFrames本質上是具有行和列的excel電子表格,但是它沒有UI excel那麼花哨。相反,我們以編程方式進行所有數據操作。
除此之外,Pandas還具有附加的優點,從而使其可以以超級簡單的方式導入數據,因為它支持許多不同的格式,包括excel電子表格、csv文件,甚至HTML文檔。
import pandas as pd
導入我們要使用的庫之後,我們現在可以使用pandas中的read_csv()方法來讀取數據文件。
df = pd.read_csv("Iris.csv")
Pandas自動地將第一行解釋為列標題。如果你的數據集沒有指定第一行中的列標題,則可以將參數header=None傳遞給read_csv()函數,以將整個文檔解釋為數據。或者,你還可以傳遞列名稱作為header參數。
要確認pandas已經正確讀取csv文件,我們可以調用df.head()來顯示前五行。
print (df.head())
我們可以看到,pandas確實正確地導入了數據。Pandas還有一個整潔的函數,df.describe()來計算每一列的描述性統計,如下所示:
print (df.describe())
由於我們可以重新確認沒有缺失的值,我們準備開始分析數據,目的是選擇最相關的功能。
特徵選擇
在熟悉數據集之後,現在是時候選擇我們將要用於我們的機器學習模型的特徵了。
其實,你有足夠的理由來詢問為什麼要進行特徵選擇?我們不能只是拋出我們在模型中的所有數據,並且讓它弄清楚什麼是相關的嗎?
為了回答這個問題,瞭解“特徵與信息是不一樣的”這一概念就變得很重要了。
假設你想從一組特徵中預測房子的價格。我們可以問問自己,知道房子裡有多少個燈具和電源插座真的很重要嗎?人們買房子時有什麼想法嗎?它是否添加了任何信息,還是隻是為了數據而考慮數據?
添加許多不包含任何信息的特徵使得模型不必要地變慢,並且冒著弄亂模型來嘗試適應無信息的特徵的風險。除此之外,具有許多特徵會增加你的模型過度擬合(稍後詳解)的風險。
正如經驗所示,你希望以儘可能少的特徵來獲得儘可能多的數據信息。
我們也可以使用諸如主成分分析(PCA)這樣的組合技術,將諸如房間數、居住面積和窗戶數量的相關特徵從上述示例組合到更高級別的主要組件中去,例如大小。雖然我們在本教程中不會使用這些技術,但應該知道它們是存在的。
確定特徵相關性的一個有效方式是,通過繪製它們與其他特徵的關係來實現。下面我們使用plot.scatter()子類方法繪製兩軸之間的關係。
df.plot.scatter(x="SepalLengthCm", y="SepalWidthCm")
上圖正確地顯示了萼片長度和萼片寬度之間的關係,然而,很難看出是否有任何的分組,因為沒有指示一個數據點所代表的花的真實種類。
不過幸運的是,使用seaborn FacetGrid是很容易實現這一點的,我們可以使用列來驅動散點的顏色或色調。
sns.FacetGrid(df,
大家可以看到,這樣結果就好多了。
使用上述函數與不同特徵的組合,你會發現PetalLengthCm和PetalWidthCm聚集在相當明確的組中,如下圖所示。
不過值得注意的是,為何iris-versicolor和iris-virginca之間的界限直觀看起來是很模糊的。這可能會給一些分類器帶來麻煩,而且這值得在訓練時被牢記。
我怎麼知道該如何創建這些圖?
我在谷歌上搜索了一下。
在搜索機器學習的時候,你會發現能夠查找到的東西是至關重要的。有無數的東西要記住。花費大量時間嘗試記住這些事情是非常低效的。而查看你所不確定的東西則會顯得更有效率,並且會讓你的大腦自動記住你經常使用的東西。
能夠快速查找事物比記住整個sklearn文檔更有價值。
好的一面是,sklearn是可擴展的記錄,組織良好,且容易查找。除此之外,Sklearn還有一個非常和諧一致的界面;你可能會在整個教程中注意到它。
如果關聯不同的特徵以便選擇最好的,這聽起來像是一份浩大的工程,需要完成很多工作,不過我們應該注意的是,有自動化的方法可以來完成這個工作的,諸如kbest,和遞歸特徵消除(recursive feature elimination),而這兩個都可以在sklearn中得到。
準備由sklearn分類器訓練的數據
現在我們已經選擇了我們要使用的特徵(PetalLengthCm和PetalWidthCm),我們需要準備數據,以便用sklearn使用它。
目前,所有的數據都是在DataFrame中編碼的,但是sklearn並不適用於pandas的DataFrames,所以我們需要提取特徵和標籤,並將它們轉換成numpy數組。
分離標籤是非常簡單的,可以使用np.asarray()只用一行就可以完成。
labels = np.asarray(df.Species)
我們現在可以停止操作,因為我們可以使用上面的標籤來訓練分類器,但是,由於物種值的數據類型不是數字,而是字符串,我們在評估模型時就會遇到問題。
幸運的是,sklearn提供了一個漂亮的工具,可以將標籤字符串編碼為數字表示。 它通過遍歷標籤數組,並將第一個唯一標籤編碼為0,然後將下一個唯一標籤編碼為1,依此類推……
使用labelencoder如下標準sklearn接口協議,你會很快熟悉。
from sklearn.preprocessing import LabelEncoder
下表顯示了數據轉換之前和之後的標籤,並使用df.sample(5)進行創建。
After Before
可以看到每個獨一的字符串標籤現在都有一個與它相關聯的獨一整數。如果我們想要返回字符串標籤,我們可以使用le.inverse_transform(labels)。
特徵的編碼遵循類似的過程。
首先,我們要從DataFrame中刪除我們不想要的所有特徵列。 我們通過使用drop()方法來實現這個操作。
df_selected = df.drop(['SepalLengthCm', 'SepalWidthCm', "Id", "Species"], axis=1)
現在我們只剩下PetalLengthCm和PetalWidthCm列。
由於我們要使用多列,所以我們不能簡單地使用np.asarray()。相反,我們可以使用to_dict()方法和sklearn的DictVectorizer。
df_features = df_selected.to_dict(orient='records')
用於DictVectorizer類的sklearn接口與LabelEncoder類似。 一個明顯的區別是與fit_transform一起使用的.toarray()方法。
from sklearn.feature_extraction import DictVectorizer
既然我們已經有了數字特徵和標籤數組,剩下的要做的最後一件事就是把數據分成訓練集和測試集。
為什麼在訓練所有數據的時候要有一個測試集?這可能是你會問的問題。
有一個測試集有助於驗證模型,並可以檢查模型無法從訓練數據中泛化的過度擬合,而不是隻是記住答案;如果我們希望在未知數據上做得很好的話,其實這不併是理想的做法。測試集的目的是模擬模型即將在現實世界中呈現的未知數據。因此,不使用測試集進行訓練非常重要。
有時候,在使用特別容易過度擬合的算法時,你還可以使用一個驗證集,這是你想要避免甚至在考慮的,因為有時在優化模型時,有關測試集的信息可能會從調整模型參數(通常稱為超參數)缺失,從而導致測試集以及訓練集過度擬合。
但是,對於本教程來說,我們將只使用一個測試集和一個訓練集。
Sklearn有一個工具可以幫助將數據分成測試集和訓練集。
from sklearn.model_selection import train_test_split
有趣的是test_size和random_state這兩個參數。測試大小參數是將保留用於測試的總數據集的數字分數。一個80/20的分配通常被認為是一個很好的經驗法則,但可能需要在後來做些調整。
另一個值得注意的變量是random_state。它的值並不是很重要,因為它只是一個種子數,但隨機數據的行為是重要的。
為什麼?
數據集是按類型進行排序的,因此,如果我們只使用前兩種進行訓練的話,那麼在使用以前從未見過的第三種進行測試時,該模型就不會發揮作用了。
如果你以前從未見過某種事物,那麼很難對它進行正確分類。
選擇分類器
既然我們已經將數據分成了測試集和訓練集,那麼我們可以開始選擇一個分類器了。
當考慮我們的數據時,隨機森林(Random Forest)分類器是一個很好的起點。隨機森林是簡單、靈活的,因為它們與各種數據都可以進行很好的執行操作,而且很少出現過度擬合。因此,它們往往是一個很好的起點。
隨機森林的一個顯著的缺點是它們在本質上是非確定性的,所以每次訓練它們時都不一定會產生相同的結果。
雖然隨機森林是一個很好的起點,但在實際操作中,你將經常使用多個分類器,並查看哪些會獲得良好的結果。
你可以培養一種感覺,哪些算法通常在什麼問題表現得出色,從而來限制猜測;當然,從數學表達式出發做第一原則分析也是有助於這一點的實現的。
訓練分類器
既然我們已經選擇好一個分類器了,那現在就可以去實現它了。
在sklearn中實現一個分類器,需要遵循三個步驟。
·導入
·初始化
·訓練
在代碼中,看起來像下圖這樣:
# import
如果我們不知道一個分類器的準確度,即使它訓練有素也是沒有太多用處的。
我們可以通過在分類器上使用score()方法,從而快速瞭解該模型對數據的有效性。
# compute accuracy using test data
嗯,結果是98%!
這個結果對於三行代碼來說還是不錯的。當然這不是最難的問題,但考慮到這是我們的第一次嘗試,98%的結果還是很好的。
注意:如果得到一個稍微不同的結果,你不必擔心,它可以通過生成一個隨機決策樹,並對其預測進行平均,從而預期這個分類器是可以起作用的。
我們還可以計算訓練數據的準確性,並比較兩者以瞭解模型過度擬合的程度。該方法類似於我們如何計算測試精度,只有這次我們使用訓練數據作為評估。
# compute accuracy using training data
我們看到,我們訓練數據的準確性是98%,表明模型不是過度擬合的。
但對於那些完全嶄新的數據來說又是怎樣的呢?
假設我們已經找到了一個新的、獨特的鳶尾花,我們測量它花瓣的長度和寬度。
假設我們測量長度為5.2cm,寬度為0.9cm,我們怎樣才能找出哪種才是使用我們的新訓練模型的呢?
答案是利用predict()方法,如下圖所示。
flower = [5.2,0.9]
結果顯示,這個方法很棒。
我們現在知道分類物種。然而,其有用性是有限的,因為它不容易被人理解。
如果它返回的結果是物種標籤,那就容易得多了。
記得之前標籤編碼器上的inverse_transform()嗎?我們可以使用它來解碼ID組,如下所示:
flower = [5.2,0.9]
所以我們看到新花是變色鳶尾的一種。
評估結果
即使我們可以看到測試的準確度在98%,但模型會造成什麼樣的錯誤呢?這看起來似乎也非常有趣。
分類模型有兩種方法無法預測正確的結果:假陽性(False Positive) 和假陰性(False Negative)。
假陽性是當它真的是假的時候卻被猜測是真的。
假陰性是當它是真的時候卻被猜測是假的。
既然我們沒有運行一個二進制分類器(預測“是”或“否”),而是一個分類器,猜測一系列標籤中的屬於哪一個,每個錯誤都將是對某些標籤來說是假陽性,而對其它標籤來說是假陰性。
在機器學習中,我們經常使用精確率(Precision)和召回率(Recall)來代替假陽性和假陰性。
精確率試圖嘗試減少假陽性,而召回率嘗試減少假陰性。它們都是0到1之間的十進制數或分數,越高越好。
通常,精確率和召回率如下計算:
精確率
召回率
Sklearn已經內建了計算精確率和召回率的得數功能,所以我們不必。
from sklearn.metrics import recall_score, precision_score
如上所述,該模型的假陰性比假陽性稍多,但是通常是均勻分裂的。
調整分類器
目前,我們的Random Forests分類器僅使用默認參數值。然而,為了增加控制,我們可以改變一些或所有的值。
一個有趣的參數是min_samples_split。 此參數表示拆分決策樹所需的最小樣本。
從根本上說,模型捕獲的細節越少,同時也增加了過度擬合的可能性。而如果你給它一個很高的值,你將更好地記錄趨勢,而忽略細節。
默認情況下,它設置為2。
降低參數值沒有什麼意義,而且模型似乎沒有過度擬合,但是我們仍然可以嘗試將參數值從2提高到4。
當我們創建分類器時,我們可以指定分類器的參數:
clf = RandomForestClassifier(
就是這樣的。
Train Accuracy: 0.98
當我們重新訓練模型時,我們看到測試精度已經提高到完美的100%,但訓練準確率仍然在98%,表明還有更多的信息需要提取。
我們可以改變的另一個參數是標準參數,它表示如何測量分割的質量。
默認情況下,它設置為測量雜質的“gini”,但是sklearn也支持測量信息增益的熵。
我們可以使用熵來訓練分類器,而不是像我們之前那樣設置參數min_samples_split。
clf = RandomForestClassifier(
當我們用新的參數重新訓練模型時,沒有任何改變,這表明可能標準函數不是這種類型的數據/問題的重要影響因素。
Train Accuracy: 0.98
你可以在Sklearn的文檔頁面上閱讀Random Forest Classifier的所有調整參數。
其他分類器
改進模型的另一種方法是改變算法。
假設我們要使用支持向量機。
使用sklearn的支持向量分類器只需要我們改變兩行代碼:導入和初始化。你可以閱讀所有調整參數為Sklearn的文檔頁面Random Forest Classifier。
from sklearn.svm import SVC
就是這樣。
使用默認設置運行此功能可以使隨機森林分類器具有可比較的結果。
Train Accuracy: 0.95
我們也可以使用此方法使用sklearn支持的任何其他分類器。
結論
我們已經涵蓋了從數據讀取到pandas數據框架中的所有內容,以使用sklearn的數據中的相關功能來訓練分類器,並評估模型的精度來調整參數,如有必要,更改分類器算法。
你現在應該用必要的工具來調查未知數據集,並構建簡單的分類模型,即使我們還不熟悉的算法。
來源:kasperfred.com
作者:Kasper Fredenslund
文章做了不改變原意的修改