[轉載] Python 機器學習經典例項

ld909發表於2020-11-27

參考連結: Python中的邏輯閘

內容介紹

在如今這個處處以資料驅動的世界中,機器學習正變得越來越大眾化。它已經被廣泛地應用於不同領域,如搜尋引擎、機器人、無人駕駛汽車等。本書首先通過實用的案例介紹機器學習的基礎知識,然後介紹一些稍微複雜的機器學習演算法,例如支援向量機、極端隨機森林、隱馬爾可夫模型、條件隨機場、深度神經網路,等等。

用最火的 Python 語言、通過各種各樣的機器學習演算法來解決實際問題!

書中介紹的主要問題如下。

探索分類分析演算法並將其應用於收入等級評估問題使用預測建模並將其應用到實際問題中瞭解如何使用無監督學習來執行市場細分探索資料視覺化技術以多種方式與資料進行互動瞭解如何構建推薦引擎理解如何與文字資料互動並構建模型來分析它使用隱馬爾科夫模型來研究語音資料並識別語音

作者簡介

Prateek Joshi,人工智慧專家,重點關注基於內容的分析和深度學習,曾在英偉達、微軟研究院、高通公司以及矽谷的幾家早期創業公司任職。

本書內容

譯者序

有一天,忽然想到自己整天面對著52個英文字母、9個數字、32個符號①和一個空格,經常加班沒有雙休日,好傻。時間不斷被各種噪聲碎片化,完全就是毛姆在《月亮和六便士》裡寫的,“If you look on the ground in search of a sixpence, you don't look up, and so miss the moon”,整天低頭刷手機,卻不記得舉頭望明月。生活也愈發無序,感覺漸漸被掏空。薛定諤的《生命是什麼》給我提了個醒,他在“以‘負熵’為生”(It Feeds On ‘negative Entropy’)一節指出:“要活著,唯一的辦法就是從環境裡不斷地汲取負熵。”在介紹了熵的概念及其統計學意義之後,他緊接著在“從環境中引出‘有序’以維持組織”(Organization Maintained By Extracting ‘Order’From The Environment)一節進一步總結:“一個有機體使本身穩定在較高的有序水平上(等於熵的相當低的水平上)的辦法,就是從環境中不斷地吸取秩序。”這個秩序(負熵、klog(1/n))可以是食物,也可以是知識,按主流叫法就是“正能量”(有些所謂正能量卻碰巧是增加系統無序水平的正熵)。於是,我開始漸漸放棄那些讓人沮喪的老梗,遠離那些引發混亂的噪聲,重新讀書,試著翻譯,學會去愛。這幾年最大的收穫就是明白了“隔行如隔山”的道理,試著循序漸進,教學相長,做力所能及之事,讓程式設計變簡單。

一般人都不喜歡程式設計,更不喜歡動手程式設計(時間消耗:編寫 & 測試 40%、重構 40%、風格 & 文件 20%),卻喜歡在心裡、嘴上程式設計:“先這樣,再那樣,如果要 XX,就 YY,最後就可以 ZZ 了。”分分鐘就可以說完幾萬行程式碼的專案,水還剩大半杯。一旦大期將近,即使要親自動手 Copy 程式碼,也會覺得苦堪搬磚,鍵盤不是紅與黑、螢幕不能左右推、小狗總是鬧跑追,不斷在數不清的理由中增加自己的熵。偶爾看程式設計書的目的也很明確,就是為了快速上手,找到答案。當然也是在 Google、StackOverflow、GitHub 網站上找不到答案之後,無可奈何之舉。程式設計書把看著複雜的知識寫得更復雜,雖然大多篇幅不輸“飛雪連天射白鹿,笑書神俠倚碧鴛”等經典,且綱舉目張、圖文並茂,甚至有作者愛引經據典,卻極少有令人拍案的驚奇之處。為什麼同樣是文以載道,程式設計書卻不能像武俠小說一樣簡單具體,反而顯得了無生趣,令人望而卻步?雖然程式設計的目的就是用計算機系統解決問題,但是大多數問題的知識都在其他領域中,許多作者在介紹程式設計技巧時,又試圖介紹一些並不熟悉的背景知識,顯得生澀難懂,且增加了書的厚度。

有時我們真正需要的,就是能快刀斬亂麻的程式碼。(Talk is cheap, show me the code.)程式設計與研究數理化不同,沒有任何假設、原命題、思維實驗,並非科學;與舞劍、奏樂、炒菜相似,都是手藝,只要基礎紮實,便結果立判。程式設計技巧也可以像劍譜、樂譜、食譜一般立竿見影,這本《Python 機器學習經典例項》正是如此,直接上程式碼,照著做就行,不用糾結為什麼。

機器學習是交叉學科,應用廣泛,目前主流方法為統計機器學習。既然是以統計學為基礎,那麼就不只是計算機與數學專業的私房菜了,機器學習在自然科學、農業科學、醫藥科學、工程與技術科學、人文與社會科學等多種學科中均可應用。如果你遇到了迴歸、分類、預測、聚類、文字分析、語音識別、影像處理等經典問題,需要快速用 Python 解決,那麼這本菜譜適合你。即使你對機器學習方法還一知半解,也不妨一試。畢竟是 Python 的機器學習,還能難到哪兒去呢?目前十分流行的 Python 機器學習庫 scikit-learn 是全書主角之一,功能全面,介面友好,許多經典的資料集和機器學習案例都來自Kaggle②。若有時間追根溯源,請研究周志華教授的《機器學習》西瓜書,周教授啃著西瓜把機器學習調侃得淋漓盡致,詳細的參考文獻尤為珍貴。但是想當作菜譜看,拿來就用,還是需要費一番功夫;若看書不過癮,還有吳恩達(Andrew Ng)教授在Coursera上的機器學習公開課③,機器學習入門最佳視訊教程,吳教授用的工具是 Matlab 的免費開源版本 Octave,你也可以用 Python 版演示教學示例。

學而時習之,不亦樂乎。學習程式設計技巧,解決實際問題,是一件快樂的事情。希望這本 Python 機器學習經典案例,可以成為你的負熵,幫你輕鬆化解那些陳年老梗。如果再努努力,也許陸汝鈐院士在《機器學習》序言中提出的6個問題④,你也有答案了。

示例程式碼:

"""列印ASCII字母表、數字、標點符號"""import stringfor item in [string.ascii_letters,             string.digits,             string.punctuation]:    print('{}\t{}'.format(len(item), item))

輸出結果:

52 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ10 012345678932 !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~

①見文末Python示例程式碼。

②Kaggle 是一個2010年成立的資料建模和資料分析競賽平臺,全球資料科學家、統計學家、機器學習工程師的聚集地,上面有豐富的資料集,經典的機器學習基礎教程,以及讓人流口水的競賽獎金,支援Python、R、Julia、SQLite,同時也支援 jupyter notebook 線上程式設計環境,2017年3月8日被谷歌收購。

③分免費版和付費版(購買結業證照),學習內容一樣。

 

④陸院士的6個問題分別是:1. 機器學習早期的符號機器學習,如何在統計機器學習主流中發展;2. 統計機器學習演算法中並不現實的“獨立同分布”假設如何解決;3. 深度學習得益於硬體革命,是否會取代統計機器學習;4. 機器學習用的都是經典的概率統計、代數邏輯,而目前僅有倒向微分方程用於預測,微分幾何的流形用於降維,只是數學領域的一角,其他現代數學理論是否可以參與其中;5. 機器學習方法仍不夠嚴謹,例如目前流形學習直接將高維資料集假設成微分流形,需要進一步完善;6. 大資料與統計機器學習是如何互動的。

流形學習,Manifold learning,科普見此。

前言

在如今這個處處以資料驅動的世界中,機器學習正變得越來越大眾化。它已經被廣泛地應用於不同領域,如搜尋引擎、機器人、無人駕駛汽車等。本書不僅可以幫你瞭解現實生活中機器學習的應用場景,而且通過有趣的菜譜式教程教你掌握處理具體問題的演算法。

本書首先通過實用的案例介紹機器學習的基礎知識,然後介紹一些稍微複雜的機器學習演算法,例如支援向量機、極端隨機森林、隱馬爾可夫模型、條件隨機場、深度神經網路,等等。本書是為想用機器學習演算法開發應用程式的 Python 程式設計師準備的。它不僅適合 Python 初學者(當然,熟悉 Python 程式設計方法將有助於體驗示例程式碼),而且也適合想要掌握機器學習技術的 Python 老手。

通過本書,你不僅可以學會如何做出合理的決策,為自己選擇合適的演算法型別,而且可以學會如何高效地實現演算法以獲得最佳學習效果。如果你在影像、文字、語音或其他形式的資料處理中遇到困難,書中處理這些資料的機器學習技術一定會對你有所幫助!

本書內容

第1章介紹各種迴歸分析的監督學習技術。我們將學習如何分析共享自行車的使用模式,以及如何預測房價。

第2章介紹各種資料分類的監督學習技術。我們將學習如何評估收入層級,以及如何通過特徵評估一輛二手汽車的質量。

第3章論述支援向量機的預測建模技術。我們將學習如何使用這些技術預測建築物裡事件發生的概率,以及體育場周邊道路的交通情況。

第4章闡述無監督學習演算法,包括 K-means 聚類和均值漂移聚類。我們將學習如何將這些演算法應用於股票市場資料和客戶細分。

第5章介紹推薦引擎的相關演算法。我們將學習如何應用這些演算法實現協同濾波和電影推薦。

第6章闡述與文字資料分析相關的技術,包括分詞、詞幹提取、詞庫模型等。我們將學習如何使用這些技術進行文字情感分析和主題建模。

第7章介紹與語音資料分析相關的演算法。我們將學習如何建立語音識別系統。

第8章介紹分析時間序列和有序資料的相關技術,包括隱馬爾可夫模型和條件隨機場。我們將學習如何將這些技術應用到文字序列分析和股市預測中。

第9章介紹影像內容分析與物體識別方面的演算法。我們將學習如何提取影像特徵,以及建立物體識別系統。

第10章介紹在影像和視訊中檢測與識別面部的相關技術。我們將學習使用降維演算法建立面部識別器。

第11章介紹建立深度神經網路所需的演算法。我們將學習如何使用神經網路建立光學文字識別系統。

第12章介紹機器學習使用的資料視覺化技術。我們將學習如何建立不同型別的圖形和圖表。

閱讀背景

Python 2.x 和 Python 3.x 的版本之爭尚未平息①。一方面,我們堅信世界會向更好的版本不斷進化,另一方面,許多開發者仍然喜歡使用 Python 2.x 的版本。目前許多作業系統仍然內建 Python 2.x。本書的重點是介紹 Python 機器學習,而非 Python 語言本身。另外,考慮到程式的相容性,書中用到了一些尚未被遷移到 Python 3.x 版本的程式庫,因此,本書依然選擇 Python 2.x 的版本。我們會盡最大努力保持程式碼相容各種 Python 版本,因為這樣可以讓你輕鬆地理解程式碼,並且很方便地將程式碼應用到不同場景中。

讀者物件

本書是為想用機器學習演算法開發應用程式的 Python 程式設計師準備的。它適合 Python 初學者閱讀,不過熟悉 Python 程式設計方法對體驗示例程式碼大有裨益。

內容組織

在本書中,你會頻繁地看到下面這些標題(準備工作、詳細步驟、工作原理、更多內容、另請參閱)。

為了更好地呈現內容,本書採用以下組織形式。

準備工作

這部分首先介紹本節目標,然後介紹軟體配置方法以及所需的準備工作。

詳細步驟

這部分介紹具體的實踐步驟。

工作原理

這部分通常是對前一部分內容的詳細解釋。

更多內容

這部分會補充介紹一些資訊,幫助你更好地理解前面的內容。

另請參閱

這部分提供一些參考資料。

排版約定

在本書中,你會發現一些不同的文字樣式。這裡舉例說明它們的含義。

嵌入程式碼、命令、選項、引數、函式、欄位、屬性、語句等,用等寬的程式碼字型顯示:“這裡,我們將25%的資料用於測試,可以通過test_size引數進行設定。”

程式碼塊用如下格式:

import numpy as npimport matplotlib.pyplot as pltimport utilities# Load input datainput_file = 'data_multivar.txt'X, y = utilities.load_data(input_file)

命令列輸入或輸出用如下格式:

$ python object_recognizer.py --input-image imagefile.jpg --model-fileerf.pkl --codebook-file codebook.pkl

新術語和重要文字將採用黑體字。你在螢幕上看到的內容,包括對話方塊或選單裡的文字,都將這樣顯示:“如果你將陣列改為(0, 0.2, 0, 0, 0),那麼 Strawberry 部分就會高亮顯示。”

讀者反饋

我們非常歡迎讀者的反饋。如果你對本書有些想法,有什麼喜歡或是不喜歡的,請反饋給我們,這將有助於我們出版充分滿足讀者需求的圖書。

一般性反饋請傳送電子郵件至 feedback@packtpub.com,並在郵件主題中註明書名。

如果你在某個領域有專長,並有意編寫一本書或是貢獻一份力量,請參考我們的作者指南。

客戶支援

你現在已經是引以為傲的 Packt 讀者了。為了能讓你的購買物超所值,我們還為你準備了以下內容。

下載示例程式碼

你可以用你的賬戶從此處下載所有已購買 Packt 圖書的示例程式碼檔案。如果你是從其他途徑購買的本書,可以訪問此處並註冊,我們將通過電子郵件把檔案傳送給你。

可以通過以下步驟下載示例程式碼檔案:

(1) 用你的電子郵件和密碼登入或註冊我們的網站;

(2) 將滑鼠移到網站上方的客戶支援(SUPPORT)標籤;

(3) 單擊程式碼下載與勘誤(Code Downloads & Errata)按鈕;

(4) 在搜尋框(Search)中輸入書名;

(5) 選擇你要下載程式碼檔案的書;

(6) 從下拉選單中選擇你的購書途徑;

(7) 單擊程式碼下載(Code Download)按鈕。

你也可以通過單擊 Packt 網站上本書網頁上的程式碼檔案(Code Files)按鈕來下載示例程式碼,該網頁可以通過在搜尋框(Search)中輸入書名獲得。以上操作的前提是你已經登入了 Packt 網站。

下載檔案後,請確保用以下軟體的最新版來解壓檔案:

WinRAR / 7-Zip for Windows ;Zipeg / iZip / UnRarX for Mac ;7-Zip / PeaZip for Linux 。

本書的程式碼包也可以在 GitHub 上獲得。另外,我們在這裡還有其他書的程式碼包和視訊,請需要的讀者自行下載。

下載本書的彩色圖片

我們也為你提供了一份 PDF 檔案,裡面包含了書中的截圖和圖表等彩色圖片,彩色圖片能幫助你更好地理解輸出的變化。下載地址

勘誤

雖然我們已盡力確保本書內容正確,但出錯仍舊在所難免。如果你在書中發現錯誤,不管是文字還是程式碼,希望能告知我們,我們將不勝感激。這樣做,你可以使其他讀者免受挫敗,也可以幫助我們改進本書的後續版本。如果你發現任何錯誤,請訪問這裡,選擇本書,單擊勘誤表提交表單(Errata Submission Form)的連結,並輸入詳細說明。②勘誤一經核實,你提交的內容將被接受,此勘誤會上傳到本公司網站或新增到現有勘誤表。

訪問這裡,在搜尋框中輸入書名,可以在勘誤(Errata)部分檢視已經提交的勘誤資訊。

盜版

任何媒體都會面臨版權內容在網際網路上的盜版問題,Packt 也不例外。Packt 非常重視版權保護。如果你發現我們的作品在網際網路上被非法複製,不管以什麼形式,都請立即為我們提供相關網址或網站名稱,以便我們尋求補救。

請把可疑盜版材料的連結發到 copyright@packtpub.com。

保護我們的作者,就是保護我們繼續為你帶來價值的能力,我們將不勝感激。

問題

如果你對本書內容存有疑問,不管是哪個方面的,都可以通過 questions@packtpub.com 聯絡我們,我們會盡最大努力解決。

①2020年之前應該不會終結。——譯者注

 

②中文版勘誤可以到這裡檢視和提交。——編者注

第01章:監督學習(上)

 

 

 

   

     1.1 簡介1.2 資料預處理技術

       1.2.1 準備工作1.2.2 詳細步驟1.3 標記編碼方法

 

在這一章,我們將介紹以下主題:

資料預處理技術標記編碼方法建立線性迴歸器(linear regressor)計算迴歸準確性儲存模型資料建立嶺迴歸器(ridge regressor)建立多項式迴歸器(polynomial regressor)估算房屋價格計算特徵的相對重要性評估共享單車的需求分佈

1.1 簡介

如果你熟悉機器學習的基礎知識,那麼肯定知道什麼是監督學習。監督學習是指在有標記的樣本(labeled samples)上建立機器學習的模型。例如,如果用尺寸、位置等不同引數建立一套模型來評估一棟房子的價格,那麼首先需要建立一個資料庫,然後為引數打上標記。我們需要告訴演算法,什麼樣的引數(尺寸、位置)對應什麼樣的價格。有了這些帶標記的資料,演算法就可以學會如何根據輸入的引數計算房價了。

無監督學習與剛才說的恰好相反,它面對的是沒有標記的資料。假設需要把一些資料分成不同的組別,但是對分組的條件毫不知情,於是,無監督學習演算法就會以最合理的方式將資料集分成確定數量的組別。我們將在後面章節介紹無監督學習。

建立書中的各種模型時,將使用許多 Python 程式包,像 NumPy、SciPy、scikit-learn、matplotlib 等。如果你使用 Windows 系統,推薦安裝相容 SciPy 關聯程式包的 Python 發行版,這些 Python 發行版裡已經整合了常用的程式包。如果你使用 Mac OS X 或者 Ubuntu 系統,安裝這些程式包就相當簡單了。下面列出來程式包安裝和使用文件的連結:

NumPy:http://docs.scipy.org/doc/numpy-1.10.1/user/install.htmlSciPy:http://www.scipy.org/install.htmlscikit-learn:http://scikit-learn.org/stable/install.htmlmatplotlib:http://matplotlib.org/1.4.2/users/installing.html

現在,請確保你的計算機已經安裝了所有程式包。

1.2 資料預處理技術

在真實世界中,經常需要處理大量的原始資料,這些原始資料是機器學習演算法無法理解的。為了讓機器學習演算法理解原始資料,需要對資料進行預處理。

1.2.1 準備工作

來看看 Python 是如何對資料進行預處理的。首先,用你最喜歡的文字編輯器開啟一個副檔名為.py 的檔案,例如 preprocessor.py。然後在檔案里加入下面兩行程式碼:

import numpy as npfrom sklearn import preprocessing

我們只是加入了兩個必要的程式包。接下來建立一些樣本資料。向檔案中新增下面這行程式碼:

data = np.array([[3,  -1.5,    2,  -5.4], [0,    4,    -0.3,  2.1], [1,    3.3,-1.9, -4.3]])

現在就可以對資料進行預處理了。

1.2.2 詳細步驟

資料可以通過許多技術進行預處理,接下來將介紹一些最常用的預處理技術。

1.均值移除(Mean removal)

通常我們會把每個特徵的平均值移除,以保證特徵均值為0(即標準化處理)。這樣做可以消除特徵彼此間的偏差(bias)。將下面幾行程式碼加入之前開啟的Python檔案中:

    data_standardized = preprocessing.scale(data)    print "\nMean =", data_standardized.mean(axis=0)    print "Std deviation =", data_standardized.std(axis=0)

現在來執行程式碼。開啟命令列工具,然後輸入以下命令:

    $ python preprocessor.py命令列工具中將顯示以下結果:    Mean = [  5.55111512e-17  -1.11022302e-16  -7.40148683e-17  -7.40148683e-17]    Std deviation = [ 1.  1.  1.  1.]你會發現特徵均值幾乎是`0`,而且標準差為`1`。

2.範圍縮放(Scaling)

資料點中每個特徵的數值範圍可能變化很大,因此,有時將特徵的數值範圍縮放到合理的大小是非常重要的。在 Python 檔案中加入下面幾行程式碼,然後執行程式:

    data_scaler = preprocessing.MinMaxScaler(feature_range=(0, 1))    data_scaled = data_scaler.fit_transform(data)    print "\nMin max scaled data =", data_scaled

範圍縮放之後,所有資料點的特徵數值都位於指定的數值範圍內。輸出結果如下所示:

    Min max scaled data:    [[ 1.            0.            1.            0.        ]     [ 0.            1.            0.41025641    1.        ]     [ 0.33333333    0.87272727    0.            0.14666667]]

3.歸一化(Normalization)

資料歸一化用於需要對特徵向量的值進行調整時,以保證每個特徵向量的值都縮放到相同的數值範圍。機器學習中最常用的歸一化形式就是將特徵向量調整為 L1範數,使特徵向量的數值之和為1。增加下面兩行程式碼到前面的 Python 檔案中:

    data_normalized = preprocessing.normalize(data, norm='l1')    print "\nL1 normalized data =", data_normalized

執行 Python 檔案,就可以看到下面的結果:

    L1    normalized    data:    [[    0.25210084    -0.12605042    0.16806723    -0.45378151]     [    0.             0.625        -0.046875       0.328125  ]     [    0.0952381      0.31428571   -0.18095238    -0.40952381]]

這個方法經常用於確保資料點沒有因為特徵的基本性質而產生較大差異,即確保資料處於同一數量級,提高不同特徵資料的可比性。

4.二值化(Binarization)

二值化用於將數值特徵向量轉換為布林型別向量。增加下面兩行程式碼到前面的 Python 檔案中:

    data_binarized = preprocessing.Binarizer(threshold=1.4).transform(data)    print "\nBinarized data =", data_binarized

再次執行 Python 檔案,就可以看到下面的結果:

    Binarized data:    [[    1.    0.    1.    0.]     [    0.    1.    0.    1.]     [    0.    1.    0.    0.]]

如果事先已經對資料有了一定的瞭解,就會發現使用這個技術的好處了。

5.獨熱編碼

通常,需要處理的數值都是稀疏地、散亂地分佈在空間中,然而,我們並不需要儲存這些大數值,這時就需要使用獨熱編碼(One-Hot Encoding)。可以把獨熱編碼看作是一種收緊(tighten)特徵向量的工具。它把特徵向量的每個特徵與特徵的非重複總數相對應,通過 one-of-k 的形式對每個值進行編碼。特徵向量的每個特徵值都按照這種方式編碼,這樣可以更加有效地表示空間。例如,我們需要處理4維向量空間,當給一個特性向量的第 n 個特徵進行編碼時,編碼器會遍歷每個特徵向量的第 n 個特徵,然後進行非重複計數。如果非重複計數的值是 K ,那麼就把這個特徵轉換為只有一個值是1其他值都是0的 K 維向量。增加下面幾行程式碼到前面的 Python 檔案中:

    encoder = preprocessing.OneHotEncoder()    encoder.fit([[0, 2, 1, 12], [1, 3, 5, 3], [2, 3, 2, 12], [1, 2, 4, 3]])    encoded_vector = encoder.transform([[2, 3, 5, 3]]).toarray()    print "\nEncoded vector =", encoded_vector

結果如下所示:

    Encoded vector:    [[ 0.  0.  1.  0.  1.  0.  0.  0.  1.  1.  0.]]

在上面的示例中,觀察一下每個特徵向量的第三個特徵,分別是1、5、2、4這4個不重複的值,也就是說獨熱編碼向量的長度是4。如果你需要對5進行編碼,那麼向量就是[0, 1, 0, 0]。向量中只有一個值是1。第二個元素是1,對應的值是5。

1.3 標記編碼方法

在監督學習中,經常需要處理各種各樣的標記。這些標記可能是數字,也可能是單詞。如果標記是數字,那麼演算法可以直接使用它們,但是,許多情況下,標記都需要以人們可理解的形式存在,因此,人們通常會用單詞標記訓練資料集。標記編碼就是要把單詞標記轉換成數值形式,讓演算法懂得如何操作標記。接下來看看如何標記編碼。

詳細步驟

(1) 新建一個 Python 檔案,然後匯入 preprocessing 程式包:

from sklearn import preprocessing

(2) 這個程式包包含許多資料預處理需要的函式。定義一個標記編碼器(label encoder),程式碼如下所示:

label_encoder = preprocessing.LabelEncoder()

(3) label_encoder物件知道如何理解單詞標記。接下來建立一些標記:

input_classes = ['audi', 'ford', 'audi', 'toyota', 'ford', 'bmw']

(4) 現在就可以為這些標記編碼了:

label_encoder.fit(input_classes)print "\nClass mapping:"for i, item in enumerate(label_encoder.classes_):    print item, '-->', i

(5) 執行程式碼,命令列工具中顯示下面的結果:

Class mapping:audi --> 0bmw --> 1ford --> 2toyota --> 3

(6) 就像前面結果顯示的那樣,單詞被轉換成從0開始的索引值。現在,如果你遇到一組標記,就可以非常輕鬆地轉換它們了,如下所示:

labels = ['toyota', 'ford', 'audi']encoded_labels = label_encoder.transform(labels)print "\nLabels =", labelsprint "Encoded labels =", list(encoded_labels)

命令列工具中將顯示下面的結果:

Labels = ['toyota', 'ford', 'audi']Encoded labels = [3, 2, 0]

(7) 這種方式比純手工進行單詞與數字的編碼要簡單許多。還可以通過數字反轉回單詞的功能檢查結果的正確性:

encoded_labels = [2, 1, 0, 3, 1]decoded_labels = label_encoder.inverse_transform(encoded_labels)print "\nEncoded labels =", encoded_labelsprint "Decoded labels =", list(decoded_labels)

結果如下所示:

Encoded labels = [2, 1, 0, 3, 1]Decoded labels = ['ford', 'bmw', 'audi', 'toyota', 'bmw']

可以看到,對映結果是完全正確的。

第01章:監督學習(中)

 

 

 

   

     1.4 建立線性迴歸器

       1.4.1 準備工作1.4.2 詳細步驟1.5 計算迴歸準確性

       1.5.1 準備工作1.5.2 詳細步驟1.6 儲存模型資料1.7 建立嶺迴歸器

       1.7.1 準備工作1.7.2 詳細步驟

 

1.4 建立線性迴歸器

迴歸是估計輸入資料與連續值輸出資料之間關係的過程。資料通常是實數形式的,我們的目標是估計滿足輸入到輸出對映關係的基本函式。讓我們從一個簡單的示例開始。考慮下面的輸入與輸出對映關係:

1 → 2

3 → 6

4.3 → 8.6

7.1 → 14.2

如果要你估計輸入與輸出的關聯關係,你可以通過模式匹配輕鬆地找到結果。我們發現輸出結果一直是輸入資料的兩倍,因此輸入與輸出的轉換公式就是這樣:

f(x) = 2x

這是體現輸入值與輸出值關聯關係的一個簡單函式。但是,在真實世界中通常都不會這麼簡單,輸入與輸出的對映關係函式並不是一眼就可以看出來的。

1.4.1 準備工作

線性迴歸用輸入變數的線性組合來估計基本函式。前面的示例就是一種單輸入單輸出變數的線性迴歸。

現在考慮如圖1-1所示的情況。

 

圖 1-1

線性迴歸的目標是提取輸入變數與輸出變數的關聯線性模型,這就要求實際輸出與線性方程預測的輸出的殘差平方和(sum of squares of differences)最小化。這種方法被稱為普通最小二乘法(Ordinary Least Squares,OLS)。

你可能覺得用一條曲線對這些點進行擬合效果會更好,但是線性迴歸不允許這樣做。線性迴歸的主要優點就是方程簡單。如果你想用非線性迴歸,可能會得到更準確的模型,但是擬合速度會慢很多。線性迴歸模型就像前面那張圖裡顯示的,用一條直線近似資料點的趨勢。接下來看看如何用 Python 建立線性迴歸模型。

1.4.2 詳細步驟

假設你已經建立了資料檔案 data_singlevar.txt,檔案裡用逗號分隔符分割欄位,第一個欄位是輸入值,第二個欄位是與逗號前面的輸入值相對應的輸出值。你可以用這個檔案作為輸入引數。

(1) 建立一個 Python 檔案 regressor.py,然後在裡面增加下面幾行程式碼:

import sysimport numpy as npfilename = sys.argv[1]X = []y = []with open(filename, 'r') as f:    for line in f.readlines():        xt, yt = [float(i) for i in line.split(',')]        X.append(xt)        y.append(yt)

把輸入資料載入到變數X和y,其中X是資料,y是標記。在程式碼的for迴圈體中,我們解析每行資料,用逗號分割欄位。然後,把欄位轉化為浮點數,並分別儲存到變數X和y中。

(2) 建立機器學習模型時,需要用一種方法來驗證模型,檢查模型是否達到一定的滿意度(satisfactory level)。為了實現這個方法,把資料分成兩組:訓練資料集(training dataset)與測試資料集(testing dataset)。訓練資料集用來建立模型,測試資料集用來驗證模型對未知資料的學習效果。因此,先把資料分成訓練資料集與測試資料集:

num_training = int(0.8 * len(X))num_test = len(X) - num_training# 訓練資料X_train = np.array(X[:num_training]).reshape((num_training,1))y_train = np.array(y[:num_training])# 測試資料X_test = np.array(X[num_training:]).reshape((num_test,1))y_test = np.array(y[num_training:])

這裡用80%的資料作為訓練資料集,其餘20%的資料作為測試資料集。

(3) 現在已經準備好訓練模型。接下來建立一個迴歸器物件,程式碼如下所示:

from sklearn import linear_model# 建立線性迴歸物件linear_regressor = linear_model.LinearRegression()# 用訓練資料集訓練模型linear_regressor.fit(X_train, y_train)

(4) 我們利用訓練資料集訓練了線性迴歸器。向fit方法提供輸入資料即可訓練模型。用下面的程式碼看看它如何擬合:

import matplotlib.pyplot as plty_train_pred = linear_regressor.predict(X_train)plt.figure()plt.scatter(X_train, y_train, color='green')plt.plot(X_train, y_train_pred, color='black', linewidth=4)plt.title('Training data')plt.show()

(5) 在命令列工具中執行如下命令:

$ python regressor.py data_singlevar.txt

就會看到如圖1-2所示的線性迴歸。

 

圖 1-2

(6) 在前面的程式碼中,我們用訓練的模型預測了訓練資料的輸出結果,但這並不能說明模型對未知的資料也適用,因為我們只是在訓練資料上執行模型。這隻能體現模型對訓練資料的擬合效果。從圖1-2中可以看到,模型訓練的效果很好。

(7) 接下來用模型對測試資料集進行預測,然後畫出來看看,程式碼如下所示:

y_test_pred = linear_regressor.predict(X_test)plt.scatter(X_test, y_test, color='green')plt.plot(X_test, y_test_pred, color='black', linewidth=4)plt.title('Test data')plt.show()

執行程式碼,可以看到如圖1-3所示的線性迴歸。

 

圖 1-3

1.5 計算迴歸準確性

現在已經建立了迴歸器,接下來最重要的就是如何評價迴歸器的擬合效果。在模型評價的相關內容中,用誤差(error)表示實際值與模型預測值之間的差值。

1.5.1 準備工作

下面快速瞭解幾個衡量回歸器擬合效果的重要指標(metric)。迴歸器可以用許多不同的指標進行衡量,部分指標如下所示。

平均絕對誤差(mean absolute error):這是給定資料集的所有資料點的絕對誤差平均值。均方誤差(mean squared error):這是給定資料集的所有資料點的誤差的平方的平均值。這是最流行的指標之一。中位數絕對誤差(median absolute error):這是給定資料集的所有資料點的誤差的中位數。這個指標的主要優點是可以消除異常值(outlier)的干擾。測試資料集中的單個壞點不會影響整個誤差指標,均值誤差指標會受到異常點的影響。解釋方差分(explained variance score):這個分數用於衡量我們的模型對資料集波動的解釋能力。如果得分1.0分,那麼表明我們的模型是完美的。R方得分(R2 score):這個指標讀作“R方”,是指確定性相關係數,用於衡量模型對未知樣本預測的效果。最好的得分是1.0,值也可以是負數。

1.5.2 詳細步驟

scikit-learn 裡面有一個模組,提供了計算所有指標的功能。重新開啟一個 Python 檔案,然後輸入以下程式碼:

import sklearn.metrics as smprint "Mean absolute error =", round(sm.mean_absolute_error(y_test, y_test_pred), 2)print "Mean squared error =", round(sm.mean_squared_error(y_test, y_ test_pred), 2)print "Median absolute error =", round(sm.median_absolute_error(y_ test, y_test_pred), 2)print "Explained variance score =", round(sm.explained_variance_ score(y_test, y_test_pred), 2)print "R2 score =", round(sm.r2_score(y_test, y_test_pred), 2)

每個指標都描述得面面俱到是非常乏味的,因此只選擇一兩個指標來評估我們的模型。通常的做法是儘量保證均方誤差最低,而且解釋方差分最高。

1.6 儲存模型資料

模型訓練結束之後,如果能夠把模型儲存成檔案,那麼下次再使用的時候,只要簡單地載入就可以了。

詳細步驟

用程式儲存模型的具體操作步驟如下。

(1) 在 Python 檔案 regressor.py 中加入以下程式碼:

import cPickle as pickleoutput_model_file = 'saved_model.pkl'with open(output_model_file, 'w') as f:    pickle.dump(linear_regressor, f)

(2) 迴歸模型會儲存在 saved_model.pkl 檔案中。下面看看如何載入並使用它,程式碼如下所示:

with open(output_model_file, 'r') as f:    model_linregr = pickle.load(f)y_test_pred_new = model_linregr.predict(X_test)print "\nNew mean absolute error =", round(sm.mean_absolute_ error(y_test, y_test_pred_new), 2)

(3) 這裡只是把迴歸模型從 Pickle 檔案載入到model_linregr變數中。你可以將列印結果與前面的結果進行對比,確認模型與之前的一樣。

1.7 建立嶺迴歸器

線性迴歸的主要問題是對異常值敏感。在真實世界的資料收集過程中,經常會遇到錯誤的度量結果。而線性迴歸使用的普通最小二乘法,其目標是使平方誤差最小化。這時,由於異常值誤差的絕對值很大,因此會引起問題,從而破壞整個模型。

1.7.1 準備工作

先看圖1-4。

 

圖 1-4

右下角的兩個資料點明顯是異常值,但是這個模型需要擬合所有的資料點,因此導致整個模型都錯了。僅憑直覺觀察,我們就會覺得如圖1-5的擬合結果更好。

 

圖 1-5

普通最小二乘法在建模時會考慮每個資料點的影響,因此,最終模型就會像圖1-4顯示的直線那樣。顯然,我們發現這個模型不是最優的。為了避免這個問題,我們引入正則化項的係數作為閾值來消除異常值的影響。這個方法被稱為嶺迴歸。

1.7.2 詳細步驟

接下來看看如何用 Python 建立嶺迴歸器。

(1) 你可以從 data_multi_variable.txt 檔案中載入資料。這個檔案的每一行都包含多個數值。除了最後一個數值外,前面的所有數值構成輸入特徵向量。

(2) 把下面的程式碼加入 regressor.py 檔案中。我們用一些引數初始化嶺迴歸器:

ridge_regressor = linear_model.Ridge(alpha=0.01, fit_ intercept=True, max_iter=10000)

(3) alpha引數控制迴歸器的複雜程度。當alpha趨於0時,嶺迴歸器就是用普通最小二乘法的線性迴歸器。因此,如果你希望模型對異常值不那麼敏感,就需要設定一個較大的alpha值。這裡把alpha值設定為0.01。

(4) 下面讓我們來訓練嶺迴歸器。

ridge_regressor.fit(X_train, y_train)y_test_pred_ridge = ridge_regressor.predict(X_test)print "Mean absolute error =", round(sm.mean_absolute_error    (y_ test, y_test_pred_ridge), 2)print "Mean squared error =", round(sm.mean_squared_error    (y_test, y_test_pred_ridge), 2)print "Median absolute error =", round(sm.median_absolute_error    (y_ test, y_test_pred_ridge), 2)print "Explain variance score =", round(sm.explained_variance_ score    (y_test, y_test_pred_ridge), 2)print "R2 score =", round(sm.r2_score(y_test, y_test_pred_ridge), 2)

執行程式碼檢查誤差指標。可以用同樣的資料建立一個線性迴歸器,並與嶺迴歸器的結果進行比較,看看把正則化引入迴歸模型之後的效果如何。

第01章:監督學習(下)

 

 

 

   

     1.8 建立多項式迴歸器

       1.8.1 準備工作1.8.2 詳細步驟1.9 估算房屋價格

       1.9.1 準備工作1.9.2 詳細步驟1.10 計算特徵的相對重要性1.11 評估共享單車的需求分佈

       1.11.1 準備工作1.11.2 詳細步驟1.11.3 更多內容

 

1.8 建立多項式迴歸器

線性迴歸模型有一個主要的侷限性,那就是它只能把輸入資料擬合成直線,而多項式迴歸模型通過擬合多項式方程來克服這類問題,從而提高模型的準確性。

1.8.1 準備工作

先看圖1-6。

 

圖 1-6

從圖1-6中可以看到,資料點本身的模式中帶有自然的曲線,而線性模型是不能捕捉到這一點的。再來看看多項式模型的效果,如圖1-7所示。

 

圖 1-7

圖1-7中的虛線表示線性迴歸模型,實線表示多項式迴歸模型。這個模型的曲率是由多項式的次數決定的。隨著模型曲率的增加,模型變得更準確。但是,增加曲率的同時也增加了模型的複雜性,因此擬合速度會變慢。當我們對模型的準確性的理想追求與計算能力限制的殘酷現實發生衝突時,就需要綜合考慮了。

1.8.2 詳細步驟

(1) 將下面的程式碼加入 Python 檔案 regressor.py 中:

from sklearn.preprocessing import PolynomialFeaturespolynomial = PolynomialFeatures(degree=3)

(2) 上一行將曲線的多項式的次數的初始值設定為3。下面用資料點來計算多項式的引數:

X_train_transformed = polynomial.fit_transform(X_train)

其中,X_train_transformed表示多項式形式的輸入,與線性迴歸模型是一樣大的。

(3) 接下來用檔案中的第一個資料點來檢查多項式模型是否能夠準確預測:

datapoint = [0.39,2.78,7.11]poly_datapoint = polynomial.fit_transform(datapoint)poly_linear_model = linear_model.LinearRegression()poly_linear_model.fit(X_train_transformed, y_train)print "\nLinear regression:", linear_regressor.predict(datapoint) [0]print "\nPolynomial regression:", poly_linear_model.predict(poly_datapoint)[0]

多項式迴歸模型計算變數資料點的值恰好就是輸入資料檔案中的第一行資料值。再用線性迴歸模型測試一下,唯一的差別就是展示資料的形式。執行程式碼,可以看到下面的結果:

Linear regression: -11.0587294983Polynomial regression: -10.9480782122

可以發現,多項式迴歸模型的預測值更接近實際的輸出值。如果想要資料更接近實際輸出值,就需要增加多項式的次數。

(4) 將多項式的次數加到10看看結果:

polynomial = PolynomialFeatures(degree=10)

可以看到下面的結果:

Polynomial regression: -8.20472183853

現在,你可以發現預測值與實際的輸出值非常地接近。

1.9 估算房屋價格

是時候用所學的知識來解決真實世界的問題了。讓我們用這些原理來估算房屋價格。房屋估價是理解迴歸分析最經典的案例之一,通常是一個不錯的切入點。它符合人們的直覺,而且與人們的生活息息相關,因此在用機器學習處理複雜事情之前,通過房屋估價可以更輕鬆地理解相關概念。我們將使用帶 AdaBoost 演算法的決策樹迴歸器(decision tree regressor)來解決這個問題。

1.9.1 準備工作

決策樹是一個樹狀模型,每個節點都做出一個決策,從而影響最終結果。葉子節點表示輸出數值,分支表示根據輸入特徵做出的中間決策。AdaBoost 演算法是指自適應增強(adaptive boosting)演算法,這是一種利用其他系統增強模型準確性的技術。這種技術是將不同版本的演算法結果進行組合,用加權彙總的方式獲得最終結果,被稱為弱學習器(weak learners)。AdaBoost 演算法在每個階段獲取的資訊都會反饋到模型中,這樣學習器就可以在後一階段重點訓練難以分類的樣本。這種學習方式可以增強系統的準確性。

首先使用 AdaBoost 演算法對資料集進行迴歸擬合,再計算誤差,然後根據誤差評估結果,用同樣的資料集重新擬合。可以把這些看作是迴歸器的調優過程,直到達到預期的準確性。假設你擁有一個包含影響房價的各種引數的資料集,我們的目標就是估計這些引數與房價的關係,這樣就可以根據未知引數估計房價了。

1.9.2 詳細步驟

(1) 建立一個新的 Python 檔案 housing.py,然後加入下面的程式碼:

import numpy as npfrom sklearn.tree import DecisionTreeRegressorfrom sklearn.ensemble import AdaBoostRegressorfrom sklearn import datasetsfrom sklearn.metrics import mean_squared_error, explained_variance_scorefrom sklearn.utils import shuffleimport matplotlib.pyplot as plt

(2) 網上有一個標準房屋價格資料庫,人們經常用它來研究機器學習。你可以在這裡下載資料。不過 scikit-learn 提供了資料介面,可以直接通過下面的程式碼載入資料:

housing_data = datasets.load_boston()

每個資料點由影響房價的13個輸入引數構成。你可以用housing_data.data獲取輸入的資料,用housing_data.target獲取對應的房屋價格。

(3) 接下來把輸入資料與輸出結果分成不同的變數。我們可以通過shuffle函式把資料的順序打亂:

X, y = shuffle(housing_data.data, housing_data.target, random_ state=7)

(4) 引數random_state用來控制如何打亂資料,讓我們可以重新生成結果。接下來把資料分成訓練資料集和測試資料集,其中80%的資料用於訓練,剩餘20%的資料用於測試:

num_training = int(0.8 * len(X))X_train, y_train = X[:num_training], y[:num_training]X_test, y_test = X[num_training:], y[num_training:]

(5) 現在已經可以擬合一個決策樹迴歸模型了。選一個最大深度為4的決策樹,這樣可以限制決策樹不變成任意深度:

dt_regressor = DecisionTreeRegressor(max_depth=4)dt_regressor.fit(X_train, y_train)

(6) 再用帶 AdaBoost 演算法的決策樹迴歸模型進行擬合:

ab_regressor =AdaBoostRegressor(DecisionTreeRegressor(max_depth=4), n_estimators=400, random_state=7)ab_regressor.fit(X_train, y_train)

這樣可以幫助我們對比訓練效果,看看 AdaBoost 演算法對決策樹迴歸器的訓練效果有多大改善。

(7) 接下來評價決策樹迴歸器的訓練效果:

y_pred_dt = dt_regressor.predict(X_test)mse = mean_squared_error(y_test, y_pred_dt)evs = explained_variance_score(y_test, y_pred_dt)print "\n#### Decision Tree performance ####"print "Mean squared error =", round(mse, 2)print "Explained variance score =", round(evs, 2)

(8) 現在評價一下 AdaBoost 演算法改善的效果:

y_pred_ab = ab_regressor.predict(X_test)mse = mean_squared_error(y_test, y_pred_ab)evs = explained_variance_score(y_test, y_pred_ab)print "\n#### AdaBoost performance ####"print "Mean squared error =", round(mse, 2)print "Explained variance score =", round(evs, 2)

(9) 命令列工具顯示的輸出結果如下所示:

#### 決策樹學習效果 ####Mean squared error = 14.79Explained variance score = 0.82#### AdaBoost演算法改善效果 ####Mean squared error = 7.54Explained variance score = 0.91

前面的結果表明,AdaBoost 演算法可以讓誤差更小,且解釋方差分更接近1。

1.10 計算特徵的相對重要性

所有特徵都同等重要嗎?在這個案例中,我們用了13個特徵,它們對模型都有貢獻。但是,有一個重要的問題出現了:如何判斷哪個特徵更加重要?顯然,所有的特徵對結果的貢獻是不一樣的。如果需要忽略一些特徵,就需要知道哪些特徵不太重要。scikit-learn 裡面有這樣的功能。

詳細步驟

(1) 畫出特徵的相對重要性,在 housing.py 檔案中加入下面幾行程式碼:

plot_feature_importances(dt_regressor.feature_importances_,        'Decision Tree regressor', housing_data.feature_names)plot_feature_importances(ab_regressor.feature_importances_,        'AdaBoost regressor', housing_data.feature_names)

迴歸器物件有一個feature_importances_方法會告訴我們每個特徵的相對重要性。

(2) 接下來需要定義plot_feature_importances來畫出條形圖:

def plot_feature_importances(feature_importances, title, feature_names):    # 將重要性值標準化    feature_importances = 100.0 * (feature_importances / max(feature_importances))    # 將得分從高到低排序    index_sorted = np.flipud(np.argsort(feature_importances))    # 讓X座標軸上的標籤居中顯示    pos = np.arange(index_sorted.shape[0]) + 0.5    # 畫條形圖    plt.figure()    plt.bar(pos, feature_importances[index_sorted], align='center')    plt.xticks(pos, feature_names[index_sorted])    plt.ylabel('Relative Importance')    plt.title(title)    plt.show()

(3) 我們從feature_importances_方法裡取值,然後把數值放大到0~100的範圍內。執行前面的程式碼,可以看到兩張圖(不帶 AdaBoost 演算法與帶 AdaBoost 演算法兩種模型)。仔細觀察圖1-8和圖1-9,看看能從決策樹迴歸器中獲得什麼。

 

圖 1-8

(4) 從圖1-8可以發現,不帶 AdaBoost 演算法的決策樹迴歸器顯示的最重要特徵是 RM。再看看帶 AdaBoost 演算法的決策樹迴歸器的特徵重要性排序條形圖,如圖1-9所示。

 

圖 1-9

加入 AdaBoost 演算法之後,房屋估價模型的最重要特徵是 LSTAT。在現實生活中,如果對這個資料集建立不同的迴歸器,就會發現最重要的特徵是 LSTAT,這足以體現 AdaBoost 演算法對決策樹迴歸器訓練效果的改善。

1.11 評估共享單車的需求分佈

本節將用一種新的迴歸方法解決共享單車的需求分佈問題。我們採用隨機森林迴歸器(random forest regressor)估計輸出結果。隨機森林是一個決策樹集合,它基本上就是用一組由資料集的若干子集構建的決策樹構成,再用決策樹平均值改善整體學習效果。

1.11.1 準備工作

我們將使用 bike_day.csv 檔案中的資料集獲取。這份資料集一共16列,前兩列是序列號與日期,分析的時候可以不用;最後三列資料是不同型別的輸出結果;最後一列是第十四列與第十五列的和,因此建立模型時可以不考慮第十四列與第十五列。

1.11.2 詳細步驟

接下來看看 Python 如何解決這個問題。如果你下載了本書原始碼,就可以看到 bike_sharing.py 檔案裡已經包含了完整程式碼。這裡將介紹若干重要的部分。

(1) 首先匯入一些新的程式包,如下:

import csvfrom sklearn.ensemble import RandomForestRegressorfrom housing import plot_feature_importances

(2) 我們需要處理 CSV 檔案,因此加入了 csv 程式包來讀取 CSV 檔案。由於這是一個全新的資料集,因此需要自己定義一個資料集載入函式:

def load_dataset(filename):    file_reader = csv.reader(open(filename, 'rb'), delimiter=',')    X, y = [], []    for row in file_reader:        X.append(row[2:13])        y.append(row[-1])    # 提取特徵名稱    feature_names = np.array(X[0])    # 將第一行特徵名稱移除,僅保留數值    return np.array(X[1:]).astype(np.float32), np.array(y[1:]).astype(np.float32), feature_names

在這個函式中,我們從 CSV 檔案讀取了所有資料。把資料顯示在圖形中時,特徵名稱非常有用。把特徵名稱資料從輸入數值中分離出來,並作為函式返回值。

(3) 讀取資料,並打亂資料順序,讓新資料與原來檔案中資料排列的順序沒有關聯性:

X, y, feature_names = load_dataset(sys.argv[1])X, y = shuffle(X, y, random_state=7)

(4) 和之前的做法一樣,需要將資料分成訓練資料和測試資料。這一次,我們將90%的資料用於訓練,剩餘10%的資料用於測試:

num_training = int(0.9 * len(X))X_train, y_train = X[:num_training], y[:num_training]X_test, y_test = X[num_training:], y[num_training:]

(5) 下面開始訓練迴歸器:

rf_regressor = RandomForestRegressor(n_estimators=1000, max_depth=10, min_samples_split=1)rf_regressor.fit(X_train, y_train)

其中,引數n_estimators是指評估器(estimator)的數量,表示隨機森林需要使用的決策樹數量;引數max_depth是指每個決策樹的最大深度;引數min_samples_split是指決策樹分裂一個節點需要用到的最小資料樣本量。

(6) 評價隨機森林迴歸器的訓練效果:

y_pred = rf_regressor.predict(X_test)mse = mean_squared_error(y_test, y_pred)evs = explained_variance_score(y_test, y_pred)print "\n#### Random Forest regressor performance ####"print "Mean squared error =", round(mse, 2)print "Explained variance score =", round(evs, 2)

(7) 由於已經有畫出特徵重要性條形圖的函式plot_feature_importances了,接下來直接呼叫它:

plot_feature_importances(rf_regressor.feature_importances_, 'Random Forest regressor', feature_names)

執行程式碼,可以看到如圖1-10所示的圖形。

 

圖 1-10

看來溫度(temp)是影響自行車租賃的最重要因素。

1.11.3 更多內容

把第十四列與第十五列資料加入資料集,看看結果有什麼區別。在新的特徵重要性條形圖中,除了這兩個特徵外,其他特徵都變成了0。這是由於輸出結果可以通過簡單地對第十四列與第十五列資料求和得出,因此演算法不需要其他特徵計算結果。在資料集載入函式load_dataset中,我們需要對for迴圈內的取值範圍稍作調整:

X.append(row[2:15])

現在再畫出特徵重要性條形圖,可以看到如圖1-11所示的柱形圖。

 

圖 1-11

與預想的一樣,從圖中可以看出,只有這兩個特徵是重要的,這確實也符合常理,因為最終結果僅僅是這兩個特徵相加得到的。因此,這兩個變數與輸出結果有直接的關係,迴歸器也就認為它不需要其他特徵來預測結果了。在消除資料集冗餘變數方面,這是非常有用的工具。

還有一份按小時統計的自行車共享資料 bike_hour.csv。我們需要用到第3~14列,因此先對資料集載入函式load_dataset做一點調整:

X.append(row[2:14])

執行程式碼,可以看到迴歸器的訓練結果如下:

#### 隨機森林學習效果 ####Mean squared error = 2619.87Explained variance score = 0.92

特徵重要性條形圖如圖1-12所示。

 

圖 1-12

圖1-12中顯示,最重要的特徵是一天中的不同時點(hr),這也完全符合人們的直覺;其次重要的是溫度,與我們之前分析的結果一致。

第02章:建立分類器(上)

 

 

 

   

     2.1 簡介2.2 建立簡單分類器

       2.2.1 詳細步驟2.2.2 更多內容2.3 建立邏輯迴歸分類器

       詳細步驟2.4 建立樸素貝葉斯分類器

       詳細步驟

 

在這一章,我們將介紹以下主題:

建立簡單分類器(simple classifier)建立邏輯迴歸分類器(logistic regression classifier)建立樸素貝葉斯分類器(Naïve Bayes classifier)將資料集分割成訓練集和測試集用交叉驗證(cross-validation)檢驗模型準確性混淆矩陣(confusion matrix)視覺化提取效能報告根據汽車特徵評估質量生成驗證曲線(validation curves)生成學習曲線(learning curves)估算收入階層(income bracket)

2.1 簡介

在機器學習領域中,分類是指利用資料的特性將其分成若干型別的過程。分類與上一章介紹的迴歸不同,迴歸的輸出結果是實數。監督學習分類器就是用帶標記的訓練資料建立一個模型,然後對未知資料進行分類。

分類器可以是實現分類功能的任意演算法,最簡單的分類器就是簡單的數學函式。在真實世界中,分類器可以是非常複雜的形式。在學習過程中,可以看到二元(binary)分類器,將資料分成兩類,也可以看到多元(multiclass)分類器,將資料分成兩個以上的型別。解決分類問題的資料手段都傾向於解決二元分類問題,可以通過不同的形式對其進行擴充套件,進而解決多元分類問題。

分類器準確性的估計是機器學習領域的重要內容。我們需要學會如何使用現有的資料獲取新的思路(機器學習模型),然後把模型應用到真實世界中。在這一章裡,我們將看到許多類似的主題。

2.2 建立簡單分類器

本節學習如何用訓練資料建立一個簡單分類器。

2.2.1 詳細步驟

(1)使用 simple_classifier.py 檔案作為參考。假設你已經和上一章一樣匯入了numpy和matplotlib.pyplot程式包,那麼需要建立一些樣本資料:

X = np.array([[3,1], [2,5], [1,8], [6,4], [5,2], [3,5], [4,7], [4,-1]])

(2) 為這些資料點分配一些標記:

y = [0, 1, 1, 0, 0, 1, 1, 0]

(3) 因為只有兩個類,所以y列表包含0和1。一般情況下,如果你有 N個類,那麼y的取值範圍就是從0到 N-1。接下來按照型別標記把樣本資料分成兩類:

class_0 = np.array([X[i] for i in range(len(X)) if y[i]==0])class_1 = np.array([X[i] for i in range(len(X)) if y[i]==1])

(4) 為了對資料有個直觀的認識,把影像畫出來,如下所示:

plt.figure()plt.scatter(class_0[:,0], class_0[:,1], color='black', marker='s')plt.scatter(class_1[:,0], class_1[:,1], color='black', marker='x')

這是一個散點圖(scatterplot),用方塊和叉表示兩類資料。在前面的程式碼中,引數marker用來表示資料點的形狀。用方塊表示class_0的資料,用叉表示class_1的資料。執行程式碼,可以看到如圖2-1所示的圖形。

(5) 在之前的兩行程式碼中,只是用變數X與y之間的對映關係建立了兩個列表。如果要你直觀地展示資料點的不同型別,在兩類資料間畫一條分割線,那麼怎麼實現呢?你只要用直線方程在兩類資料之間畫一條直線就可以了。下面看看如何實現:

line_x = range(10)line_y = line_x

(6) 用數學公式 y = x 建立一條直線。程式碼如下所示:

plt.figure()plt.scatter(class_0[:,0], class_0[:,1], color='black', marker='s')plt.scatter(class_1[:,0], class_1[:,1], color='black', marker='x')plt.plot(line_x, line_y, color='black', linewidth=3)plt.show()

 

圖 2-1

(7) 執行程式碼,可以看到如圖2-2所示的圖形。

 

圖 2-2

2.2.2 更多內容

用以下規則建立了一個簡單的分類器:如果輸入點(a, b)的a大於或等於b,那麼它屬於型別class_0;反之,它屬於class_1。如果對資料點逐個進行檢查,你會發現每個數都是這樣,這樣你就建立了一個可以識別未知資料的線性分類器(linear classifier)。之所以稱其為線性分類器,是因為分割線是一條直線。如果分割線是一條曲線,就是非線性分類器(nonlinear classifier)。

這樣簡單的分類器之所以可行,是因為資料點很少,可以直觀地判斷分割線。如果有幾千個資料點呢?如何對分類過程進行一般化處理(generalize)呢?下一節將介紹這一主題。

2.3 建立邏輯迴歸分類器

雖然這裡也出現了上一章介紹的迴歸這個詞,但邏輯迴歸其實是一種分類方法。給定一組資料點,需要建立一個可以在類之間繪製線性邊界的模型。邏輯迴歸就可以對訓練資料派生的一組方程進行求解來提取邊界。

詳細步驟

(1) 下面看看用 Python 如何實現邏輯迴歸。我們使用 logistic_regression.py 檔案作為參考。假設已經匯入了需要使用的程式包,接下來建立一些帶訓練標記的樣本資料:

import numpy as npfrom sklearn import linear_modelimport matplotlib.pyplot as pltX = np.array([[4, 7], [3.5, 8], [3.1, 6.2], [0.5, 1], [1, 2],[1.2, 1.9], [6, 2], [5.7, 1.5], [5.4, 2.2]])y = np.array([0, 0, 0, 1, 1, 1, 2, 2, 2])

這裡假設一共有3個類。

(2) 初始化一個邏輯迴歸分類器:

classifier = linear_model.LogisticRegression(solver='liblinear', C=100)

前面的函式有一些輸入引數需要設定,但是最重要的兩個引數是solver和C。引數solver用於設定求解系統方程的演算法型別,引數C表示正則化強度,數值越小,表示正則化強度越高。

(3) 接下來訓練分類器:

classifier.fit(X, y)

(4) 畫出資料點和邊界:

plot_classifier(classifier, X, y)

需要定義如下畫圖函式:

def plot_classifier(classifier, X, y):    # 定義圖形的取值範圍    x_min, x_max = min(X[:, 0]) - 1.0, max(X[:, 0]) + 1.0    y_min, y_max = min(X[:, 1]) - 1.0, max(X[:, 1]) + 1.0

預測值表示我們在圖形中想要使用的數值範圍,通常是從最小值到最大值。我們增加了一些餘量(buffer),例如上面程式碼中的1.0。

(5) 為了畫出邊界,還需要利用一組網格(grid)資料求出方程的值,然後把邊界畫出來。下面繼續定義網格:

    # 設定網格資料的步長    step_size = 0.01    # 定義網格    x_values, y_values = np.meshgrid(np.arange(x_min, x_max, step_size), np.arange(y_min, y_max, step_size))

變數x_values和y_values包含求解方程數值的網格點。

(6) 計算出分類器對所有資料點的分類結果:

    # 計算分類器輸出結果    mesh_output = classifier.predict(np.c_[x_values.ravel(), y_values.ravel()])    # 陣列維度變形    mesh_output = mesh_output.reshape(x_values.shape)

(7) 用彩色區域畫出各個型別的邊界:

    # 用彩圖畫出分類結果    plt.figure()    # 選擇配色方案    plt.pcolormesh(x_values, y_values, mesh_output, cmap=plt.cm.gray)

這基本算是一個三維畫圖器,既可以畫二維資料點,又可以用色彩清單(color scheme)表示不同區域的相關屬性。你可以在這裡找到所有的色彩清單。

(8) 接下來再把訓練資料點畫在圖上:

    plt.scatter(X[:, 0], X[:, 1], c=y, s=80, edgecolors='black', linewidth=1, cmap=plt.cm.Paired)    # 設定圖形的取值範圍    plt.xlim(x_values.min(), x_values.max())    plt.ylim(y_values.min(), y_values.max())    # 設定X軸與Y軸    plt.xticks((np.arange(int(min(X[:, 0])-1), int(max(X[:, 0])+1), 1.0)))    plt.yticks((np.arange(int(min(X[:, 1])-1), int(max(X[:, 1])+1), 1.0)))    plt.show()

其中,plt.scatter把資料點畫在二維圖上。X[:, 0]表示0軸(X軸)的座標值,X[:, 1]表示1軸(Y軸)的座標值。c=y表示顏色的使用順序。用目標標記對映cmap的顏色表。我們肯定希望不同的標記使用不同的顏色,因此,用y作為對映。座標軸的取值範圍由plt.xlim和plt.ylim確定。為了標記座標軸的數值,需要使用plt.xticks和plt.yticks。在座標軸上標出座標值,就可以直觀地看出資料點的位置。在前面的程式碼中,我們希望座標軸的最大值與最小值之前的刻度是單位刻度,還希望這些刻度值是整數,因此用int()函式對最值取整。

(9) 執行程式碼,就可以看到如圖2-3所示的輸出結果。

 

圖 2-3

(10) 下面看看引數C對模型的影響。引數C表示對分類錯誤(misclassification)的懲罰值(penalty)。如果把引數C設定為1.0,會得到如圖2-4所示的結果。

 

圖 2-4

(11) 如果把引數C設定為10000,會得到如圖2-5所示的結果。

 

圖 2-5

隨著引數C的不斷增大,分類錯誤的懲罰值越高。因此,各個型別的邊界更優。

2.4 建立樸素貝葉斯分類器

樸素貝葉斯分類器是用貝葉斯定理進行建模的監督學習分類器。下面看看如何建立一個樸素貝葉斯分類器。

詳細步驟

(1) 我們使用naive_bayes.py檔案作為參考。首先匯入兩個程式包:

from sklearn.naive_bayes import GaussianNBfrom logistic_regression import plot_classifier

(2) 下載的示例程式碼中有一個data_multivar.txt檔案,裡面包含了將要使用的資料,每一行資料都是由逗號分隔符分割的數值。從檔案中載入資料:

input_file = 'data_multivar.txt'X = []y = []with open(input_file, 'r') as f:    for line in f.readlines():        data = [float(x) for x in line.split(',')]        X.append(data[:-1])        y.append(data[-1])X = np.array(X)y = np.array(y)

我們已經把輸入資料和標記分別載入到變數X和y中了。

(3) 下面建立一個樸素貝葉斯分類器:

classifier_gaussiannb = GaussianNB()classifier_gaussiannb.fit(X, y)y_pred = classifier_gaussiannb.predict(X)

GaussianNB函式指定了正態分佈樸素貝葉斯模型(Gaussian Naive Bayes model)。

(4) 接下來計算分類器的準確性:

accuracy = 100.0 * (y == y_pred).sum() / X.shape[0]print "Accuracy of the classifier =", round(accuracy, 2), "%"

(5) 畫出資料點和邊界:

plot_classifier(classifier_gaussiannb, X, y)

可以看到如圖2-6所示的圖形。

 

圖 2-6

從圖2-6中可以發現,這裡的邊界沒有嚴格地區分所有資料點。在前面這個例子中,我們是對所有的資料進行訓練。機器學習的一條最佳實踐是用沒有重疊(nonoverlapping)的資料進行訓練和測試。理想情況下,需要一些尚未使用的資料進行測試,可以方便準確地評估模型在未知資料上的執行情況。scikit-learn 有一個方法可以非常好地解決這個問題,我們將在下一節介紹它。

第02章:建立分類器(中)

 

 

 

   

     2.5 將資料集分割成訓練集和測試集

       詳細步驟2.6 用交叉驗證檢驗模型準確性

       2.6.1 準備工作2.6.2 詳細步驟2.7 混淆矩陣視覺化

       詳細步驟2.8 提取效能報告

       詳細步驟2.9 根據汽車特徵評估質量

       2.9.1 準備工作2.9.2 詳細步驟

 

2.5 將資料集分割成訓練集和測試集

本節一起來看看如何將資料合理地分割成訓練資料集和測試資料集。

詳細步驟

(1) 增加下面的程式碼片段到上一節的 Python 檔案中:

from sklearn import cross_validationX_train, X_test, y_train, y_test = cross_validation.train_test_split(X, y, test_size=0.25, random_state=5)classifier_gaussiannb_new = GaussianNB()classifier_gaussiannb_new.fit(X_train, y_train)

這裡,我們把引數test_size設定成0.25,表示分配了25%的資料給測試資料集。剩下75%的資料將用於訓練資料集。

(2) 用分類器對測試資料進行測試:

y_test_pred = classifier_gaussiannb_new.predict(X_test)

(3) 計算分類器的準確性:

accuracy = 100.0 * (y_test == y_test_pred).sum() / X_test.shape[0]print "Accuracy of the classifier =", round(accuracy, 2), "%"

(4) 畫出測試資料的資料點及其邊界:

plot_classifier(classifier_gaussiannb_new, X_test, y_test)

(5) 可以看到如圖2-7所示的圖形。

 

圖 2-7

2.6 用交叉驗證檢驗模型準確性

交叉驗證是機器學習的重要概念。在上一節中,我們把資料分成了訓練資料集和測試資料集。然而,為了能夠讓模型更加穩定,還需要用資料集的不同子集進行反覆的驗證。如果只是對特定的子集進行微調,最終可能會過度擬合(overfitting)模型。過度擬合是指模型在已知資料集上擬合得超級好,但是一遇到未知資料就掛了。我們真正想要的,是讓機器學習模型能夠適用於未知資料。

2.6.1 準備工作

介紹如何實現交叉驗證之前,先討論一下效能指標。當處理機器學習模型時,通常關心3個指標:精度(precision)、召回率(recall)和F1得分(F1 score)。可以用引數評分標準(parameter scoring)獲得各項指標的得分。精度是指被分類器正確分類的樣本數量佔分類器總分類樣本數量的百分比(分類器分類結果中,有一些樣本分錯了)。召回率是指被應正確分類的樣本數量佔某分類總樣本數量的百分比(有一些樣本屬於某分類,但分類器卻沒有分出來)。

假設資料集有100個樣本,其中有82個樣本是我們感興趣的,現在想用分類器選出這82個樣本。最終,分類器選出了73個樣本,它認為都是我們感興趣的。在這73個樣本中,其實只有65個樣本是我們感興趣的,剩下的8個樣本我們不感興趣,是分類器分錯了。可以如下方法計算分類器的精度:

分類正確的樣本數量 = 65總分類樣本數量 = 73精度 = 65 / 73 = 89.04%

召回率的計算過程如下:

資料集中我們感興趣的樣本數量 = 82分類正確的樣本數量 = 65召回率 = 65 / 82 = 79.26%

一個給力的機器學習模型需要同時具備良好的精度和召回率。這兩個指標是二律背反的,一個指標達到100%,那麼另一個指標就會非常差!我們需要保持兩個指標能夠同時處於合理高度。為了量化兩個指標的均衡性,引入了 F1得分指標,是精度和召回率的合成指標,實際上是精度和召回率的調和均值(harmonic mean):

F1 得分=2×精度×召回率 / (精度+召回率)

上面示例中 F1得分的計算過程如下:

F1 得分=2×0.89×0.79 / (0.89+0.79)=0.8370

2.6.2 詳細步驟

(1) 下面看看如何實現交叉驗證,並提取效能指標。首先計算精度:

num_validations = 5accuracy = cross_validation.cross_val_score(classifier_gaussiannb,        X, y,scoring='accuracy', cv=num_validations)print "Accuracy: " + str(round(100*accuracy.mean(), 2)) + "%"

(2) 用前面的方程分別計算精度、召回率和F1得分:

f1 = cross_validation.cross_val_score(classifier_gaussiannb,        X, y, scoring='f1_weighted', cv=num_validations)print "F1: " + str(round(100*f1.mean(), 2)) + "%"precision = cross_validation.cross_val_score(classifier_ gaussiannb,        X, y, scoring='precision_weighted', cv=num_validations)print "Precision: " + str(round(100*precision.mean(), 2)) + "%"recall = cross_validation.cross_val_score(classifier_gaussiannb,        X, y, scoring='recall_weighted', cv=num_validations)print "Recall: " + str(round(100*recall.mean(), 2)) + "%"

2.7 混淆矩陣視覺化

混淆矩陣(confusion matrix)是理解分類模型效能的資料表,它有助於我們理解如何把測試資料分成不同的類。當想對演算法進行調優時,就需要在對演算法做出改變之前瞭解資料的錯誤分類情況。有些分類效果比其他分類效果更差,混淆矩陣可以幫助我們理解這些問題。先看看如圖2-8所示的混淆矩陣。

 

圖 2-8

在圖2-8中,我們可以看出不同型別的分類資料。理想情況下,我們希望矩陣非對角線元素都是0,這是最完美的分類結果。先看看class 0,一共52個樣本屬於class 0。如果對第一行資料求和,總數就是52。但是現在,只有45個樣本被正確地預測出來,分類器說另外4個樣本屬於class 1,還有3個樣本屬於class 2。用同樣的思路分析另外兩行資料,有意思的是,class 1裡面有11個樣本被錯誤地預測成了class 0,佔到了class 1總數的16%。這就是模型需要優化的切入點。

詳細步驟

(1) 我們用 confusion_matrix.py 檔案作為參考。首先看看如何從資料中提取混淆矩陣:

from sklearn.metrics import confusion_matrixy_true = [1, 0, 0, 2, 1, 0, 3, 3, 3]y_pred = [1, 1, 0, 2, 1, 0, 1, 3, 3]confusion_mat = confusion_matrix(y_true, y_pred)plot_confusion_matrix(confusion_mat)

這裡用了一些樣本資料,一共有4種型別,取值範圍是0~3,也列出了預測的標記型別。用confusion_matrix方法提取混淆矩陣,然後把它畫出來。

(2) 繼續定義混淆矩陣的畫圖函式:

# 顯示混淆矩陣def plot_confusion_matrix(confusion_mat):    plt.imshow(confusion_mat, interpolation='nearest', cmap=plt.cm.Paired)    plt.title('Confusion matrix')    plt.colorbar()    tick_marks = np.arange(4)    plt.xticks(tick_marks, tick_marks)    plt.yticks(tick_marks, tick_marks)    plt.ylabel('True label')    plt.xlabel('Predicted label')    plt.show()

這裡用imshow函式畫混淆矩陣,其他函式都非常簡單,只使用相關函式設定了圖形的標題、顏色欄、刻度和標籤。引數tick_marks的取值範圍是0~3,因為資料集中有4個標記型別。np.arange函式會生成一個numpy陣列。

(3) 執行程式碼,可以看到如圖2-9所示的圖形。

 

圖 2-9

從圖2-9中可以看出,對角線的顏色很亮,我們希望它們越亮越好。黑色區域表示0。在非對角線的區域有一些灰色區域,表示分類錯誤的樣本量。例如,當樣本真實標記型別是0,而預測標記型別是1時,就像在第一行的第二格看到的那樣。事實上,所有的錯誤分類都屬於class-1,因為第二列有3個不為0的格子。這在圖2-9中顯示得一目瞭然。

2.8 提取效能報告

也可以直接用 scikit-learn 列印精度、召回率和 F1得分。接下來看看如何實現。

詳細步驟

(1) 在一個新的 Python 檔案中加入下面的程式碼:

from sklearn.metrics import classification_reporty_true = [1, 0, 0, 2, 1, 0, 3, 3, 3]y_pred = [1, 1, 0, 2, 1, 0, 1, 3, 3]target_names = ['Class-0', 'Class-1', 'Class-2', 'Class-3']print(classification_report(y_true, y_pred, target_names=target_names))

(2) 執行程式碼,可以在命令列工具中看到如圖2-10所示的結果。

 

圖 2-10

不需要單獨計算各個指標,可以直接用這個函式從模型中提取所有統計值。

2.9 根據汽車特徵評估質量

接下來看看如何用分類技術解決現實問題。我們將用一個包含汽車多種細節的資料集,例如車門數量、後備箱大小、維修成本等,來確定汽車的質量。分類的目的是把車輛的質量分成4種型別:不達標、達標、良好、優秀。

2.9.1 準備工作

你可以從這裡下載資料集。

你需要把資料集中的每個值看成是字串。考慮資料集中的6個屬性,其取值範圍是這樣的:

buying:取值範圍是vhigh、high、med、low;maint:取值範圍是vhigh、high、med、low;doors:取值範圍是2、3、4、5等;persons:取值範圍是2、4等;lug_boot:取值範圍是small、med、big;safety:取值範圍是low、med、high。

考慮到每一行都包含字串屬性,需要假設所有特徵都是字串,並設定分類器。在上一章中,我們用隨機森林建立過迴歸器,這裡再用隨機森林建立分類器。

2.9.2 詳細步驟

(1) 參考 car.py 檔案中的原始碼。首先匯入兩個軟體包:

from sklearn import preprocessingfrom sklearn.ensemble import RandomForestClassifier

(2) 載入資料集:

input_file = 'path/to/dataset/car.data.txt'# 讀取資料X = []count = 0with open(input_file, 'r') as f:    for line in f.readlines():        data = line[:-1].split(',')        X.append(data)X = np.array(X)

每一行都包含由逗號分隔的單詞列表。因此,我們解析輸入檔案,對每一行進行分割,然後將該列表附加到主資料。我們忽略每一行最後一個字元,因為那是一個換行符。由於 Python 程式包只能處理數值資料,所以需要把這些屬性轉換成程式包可以理解的形式。

(3) 在上一章中,我們介紹過標記編碼。下面可以用這個技術把字串轉換成數值:

# 將字串轉化為數值label_encoder = []X_encoded = np.empty(X.shape)for i,item in enumerate(X[0]):    label_encoder.append(preprocessing.LabelEncoder())    X_encoded[:, i] = label_encoder[-1].fit_transform(X[:, i])X = X_encoded[:, :-1].astype(int)y = X_encoded[:, -1].astype(int)

由於每個屬性可以取有限數量的數值,所以可以用標記編碼器將它們轉換成數字。我們需要為不同的屬性使用不同的標記編碼器,例如,lug_boot屬性可以取3個不同的值,需要建立一個懂得給這3個屬性編碼的標記編碼器。每一行的最後一個值是類,將它賦值給變數y。

(4) 接下來訓練分類器:

# 建立隨機森林分類器params = {'n_estimators': 200, 'max_depth': 8, 'random_state': 7}classifier = RandomForestClassifier(**params)classifier.fit(X, y)

你可以改變n_estimators和max_depth引數的值,觀察它們如何改變分類器的準確性。我們將用一個標準化的方法處理引數選擇問題。

(5) 下面進行交叉驗證:

# 交叉驗證from sklearn import cross_validationaccuracy = cross_validation.cross_val_score(classifier,        X, y, scoring='accuracy', cv=3)print "Accuracy of the classifier: " + str(round(100*accuracy. mean(), 2)) + "%"

一旦訓練好分類器,我們就需要知道它是如何執行的。我們用三折交叉驗證(three-fold cross-validation,把資料分3組,輪換著用其中兩組資料驗證分類器)來計算分類器的準確性。

(6) 建立分類器的主要目的就是要用它對孤立的和未知的資料進行分類。下面用分類器對一個單一資料點進行分類:

# 對單一資料示例進行編碼測試input_data = ['vhigh', 'vhigh', '2', '2', 'small', 'low']input_data_encoded = [-1] * len(input_data)for i,item in enumerate(input_data):    input_data_encoded[i] = int(label_encoder[i].transform(input_data[i]))input_data_encoded = np.array(input_data_encoded)

第一步是把資料轉換成數值型別。需要使用之前訓練分類器時使用的標記編碼器,因為我們需要保持資料編碼規則的前後一致。如果輸入資料點裡出現了未知資料,標記編碼器就會出現異常,因為它不知道如何對這些資料進行編碼。例如,如果你把列表中的第一個值vhigh改成abcd,那麼標記編碼器就不知道如何編碼了,因為它不知道怎麼處理這個字串。這就像是錯誤檢查,看看輸入資料點是否有效。

(7) 現在可以預測出資料點的輸出型別了:

# 預測並列印特定資料點的輸出output_class = classifier.predict(input_data_encoded)print "Output class:", label_encoder[-1].inverse_transform(output_class)[0]

我們用predict方法估計輸出型別。如果輸出被編碼的輸出標記,那麼它對我們沒有任何意義。因此,用inverse_transform方法對標記進行解碼,將它轉換成原來的形式,然後列印輸出類。

第02章:建立分類器(下)

 

 

 

   

     2.10 生成驗證曲線

       詳細步驟2.11 生成學習曲線

       詳細步驟2.12 估算收入階層

       詳細步驟

 

2.10 生成驗證曲線

前面用隨機森林建立了分類器,但是並不知道如何定義引數。本節來處理兩個引數:n_estimators和max_depth引數。它們被稱為超引數(hyperparameters),分類器的效能是由它們決定的。當改變超引數時,如果可以看到分類器效能的變化情況,那就再好不過了。這就是驗證曲線的作用。這些曲線可以幫助理解每個超引數對訓練得分的影響。基本上,我們只對感興趣的超引數進行調整,其他引數可以保持不變。下面將通過視覺化圖片演示超引數的變化對訓練得分的影響。

詳細步驟

(1) 開啟上一節的 Python 檔案,加入以下程式碼:

# 驗證曲線from sklearn.learning_curve import validation_curveclassifier = RandomForestClassifier(max_depth=4, random_state=7)parameter_grid = np.linspace(25, 200, 8).astype(int)train_scores, validation_scores = validation_curve(classifier, X, y, "n_estimators", parameter_grid, cv=5)print "\n##### VALIDATION CURVES #####"print "\nParam: n_estimators\nTraining scores:\n", train_scoresprint "\nParam: n_estimators\nValidation scores:\n", validation_ scores

在這個示例中,我們通過固定max_depth引數的值來定義分類器。我們想觀察評估器數量對訓練得分的影響,於是用parameter_grid定義了搜尋空間。評估器數量會在25~200之間每隔8個數迭代一次,獲得模型的訓練得分和驗證得分。

(2) 執行程式碼,可以在命令列工具中看到如圖2-11所示的結果。

 

圖 2-11

(3) 把資料畫成圖形:

# 畫出曲線圖plt.figure()plt.plot(parameter_grid, 100*np.average(train_scores, axis=1), color='black')plt.title('Training curve')plt.xlabel('Number of estimators')plt.ylabel('Accuracy')plt.show()

(4) 得到的圖形如圖2-12所示。

 

圖 2-12

(5) 用類似的方法對max_depth引數進行驗證:

classifier = RandomForestClassifier(n_estimators=20, random_ state=7)parameter_grid = np.linspace(2, 10, 5).astype(int)train_scores, valid_scores = validation_curve(classifier, X, y, "max_depth",        parameter_grid, cv=5)print "\nParam: max_depth\nTraining scores:\n", train_scoresprint "\nParam: max_depth\nValidation scores:\n", validation_ scores

我們把n_estimators引數固定為20,看看max_depth引數變化對效能的影響。命令列工具的輸出結果如圖2-13所示。

(6) 把資料畫成圖形:

# 畫出曲線圖plt.figure()plt.plot(parameter_grid, 100*np.average(train_scores, axis=1), color='black')plt.title('Validation curve')plt.xlabel('Maximum depth of the tree')plt.ylabel('Accuracy')plt.show()

 

圖 2-13

(7) 執行程式碼,可以看到如圖2-14所示的圖形。

 

圖 2-14

2.11 生成學習曲線

學習曲線可以幫助我們理解訓練資料集的大小對機器學習模型的影響。當遇到計算能力限制時,這一點非常有用。下面改變訓練資料集的大小,把學習曲線畫出來。

詳細步驟

(1) 開啟上一節的 Python 檔案,加入以下程式碼:

# 學習曲線from sklearn.learning_curve import learning_curveclassifier = RandomForestClassifier(random_state=7)parameter_grid = np.array([200, 500, 800, 1100])train_sizes, train_scores, validation_scores = learning_ curve(classifier,        X, y, train_sizes=parameter_grid, cv=5)print "\n##### LEARNING CURVES #####"print "\nTraining scores:\n", train_scoresprint "\nValidation scores:\n", validation_scores

我們想分別用200、500、800、1100的訓練資料集的大小測試模型的效能指標。我們把learning_curve方法中的cv引數設定為5,就是用五折交叉驗證。

(2) 執行程式碼,可以在命令列工具中看到如圖2-15所示的結果。

 

圖 2-15

(3) 把資料畫成圖形:

# 畫出曲線圖plt.figure()plt.plot(parameter_grid, 100*np.average(train_scores, axis=1), color='black')plt.title('Learning curve')plt.xlabel('Number of training samples')plt.ylabel('Accuracy')plt.show()

(4) 得到的圖形如圖2-16所示。

 

圖 2-16

雖然訓練資料集的規模越小,彷彿訓練準確性越高,但是它們很容易導致過度擬合。如果選擇較大規模的訓練資料集,就會消耗更多的資源。因此,訓練資料集的規模選擇也是一個需要結合計算能力進行綜合考慮的問題。

2.12 估算收入階層

本節將根據14個屬性建立分類器評估一個人的收入等級。可能的輸出型別是“高於50K”和“低於或等於50K”。這個資料集稍微有點複雜,裡面的每個資料點都是數字和字串的混合體。數值資料是有價值的,在這種情況下,不能用標記編碼器進行編碼。需要設計一套既可以處理數值資料,也可以處理非數值資料的系統。我們將用美國人口普查收入資料集中的資料。

詳細步驟

(1) 我們將用 income.py 檔案作為參考,用樸素貝葉斯分類器解決問題。首先匯入兩個軟體包:

from sklearn import preprocessingfrom sklearn.naive_bayes import GaussianNB

(2) 載入資料集:

input_file = 'path/to/adult.data.txt'# 讀取資料X = []y = []count_lessthan50k = 0count_morethan50k = 0num_images_threshold = 10000

(3) 我們將使用資料集中的20 000個資料點——每種型別10 000個,保證初始型別沒有偏差。在模型訓練時,如果你的大部分資料點都屬於一個型別,那麼分類器就會傾向於這個型別。因此,最好使用每個型別資料點數量相等的資料進行訓練:

with open(input_file, 'r') as f:    for line in f.readlines():        if '?' in line:            continue        data = line[:-1].split(', ')        if data[-1] == '<=50K' and count_lessthan50k < num_images_threshold:            X.append(data)            count_lessthan50k = count_lessthan50k + 1        elif data[-1] == '>50K' and count_morethan50k < num_images_threshold:            X.append(data)            count_morethan50k = count_morethan50k + 1        if count_lessthan50k >= num_images_threshold and count_morethan50k >= num_images_threshold:            breakX = np.array(X)

同樣地,這也是一個帶逗號分隔符的檔案。我們還是像之前那樣處理,把資料載入到變數X。

(4) 我們需要把字串屬性轉換為數值資料,同時需要保留原有的數值資料:

# 將字串轉換為數值資料label_encoder = []X_encoded = np.empty(X.shape)for i,item in enumerate(X[0]):    if item.isdigit():        X_encoded[:, i] = X[:, i]    else:        label_encoder.append(preprocessing.LabelEncoder())        X_encoded[:, i] = label_encoder[-1].fit_transform(X[:, i])X = X_encoded[:, :-1].astype(int)y = X_encoded[:, -1].astype(int)

isdigit()函式幫助我們判斷一個屬性是不是數值資料。我們把字串資料轉換為數值資料,然後把所有的標記編碼器儲存在一個列表中,便於在後面處理未知資料時使用。

(5) 訓練分類器:

# 建立分類器classifier_gaussiannb = GaussianNB()classifier_gaussiannb.fit(X, y)

(6) 把資料分割成訓練資料集和測試資料集,方便後面獲取效能指標:

# 交叉驗證from sklearn import cross_validationX_train, X_test, y_train, y_test = cross_validation.train_test_split(X, y, test_size=0.25, random_state=5)classifier_gaussiannb = GaussianNB()classifier_gaussiannb.fit(X_train, y_train)y_test_pred = classifier_gaussiannb.predict(X_test)

(7) 提取效能指標:

# 計算分類器的F1得分f1 = cross_validation.cross_val_score(classifier_gaussiannb,        X, y, scoring='f1_weighted', cv=5)print "F1 score: " + str(round(100*f1.mean(), 2)) + "%"

(8) 接下來看看如何為單一資料點分類。我們需要把資料點轉換成分類器可以理解的形式:

# 對單一資料示例進行編碼測試input_data = ['39', 'State-gov', '77516', 'Bachelors', '13', 'Never-married', 'Adm-clerical', 'Not-in-family', 'White', 'Male', '2174', '0', '40', 'United-States']count = 0input_data_encoded = [-1] * len(input_data)for i,item in enumerate(input_data):    if item.isdigit():        input_data_encoded[i] = int(input_data[i])    else:        input_data_encoded[i] = int(label_encoder[count].transform(input_data[i]))        count = count + 1input_data_encoded = np.array(input_data_encoded)

(9) 這樣就可以進行分類了:

# 預測並列印特定資料點的輸出結果output_class = classifier_gaussiannb.predict(input_data_encoded)print label_encoder[-1].inverse_transform(output_class)[0]

和之前的分類案例一樣,我們用predict方法獲取輸出型別,然後用inverse_transform對標記進行解碼,將它轉換成原來的形式,然後在命令列工具中列印出來。

 

第03章:預測建模(上)

 

 

第03章:預測建模(下)

 

 

第04章:無監督學習——聚類(上)

 

 

第04章:無監督學習——聚類(中)

 

 

第04章:無監督學習——聚類(下)

 

 

第05章:構建推薦引擎(上)

 

 

第05章:構建推薦引擎(中)

 

 

第05章:構建推薦引擎(下)

 

 

第06章:分析文字資料(上)

 

 

第06章:分析文字資料(中)

 

 

第06章:分析文字資料(下)

 

 

第07章:語音識別(上)

 

 

第07章:語音識別(下)

 

 

第08章:解剖時間序列和時序資料(上)

 

 

第08章:解剖時間序列和時序資料(下)

 

 

第09章:影像內容分析(上)

 

 

第09章:影像內容分析(下)

 

 

第10章:人臉識別(上)

 

 

第10章:人臉識別(中)

 

 

第10章:人臉識別(下)

 

 

第11章:深度神經網路(上)

 

 

第11章:深度神經網路(中)

 

 

第11章:深度神經網路(下)

 

 

第12章:視覺化資料(上)

 

 

第12章:視覺化資料(下)

 

閱讀全文: http://gitbook.cn/gitchat/geekbook/5a3c787a902f0f2223e2526f

相關文章