第一部分:機(jī)器學(xué)習(xí)基礎(chǔ)
(解決簡(jiǎn)單問題)
一、機(jī)器學(xué)習(xí)概覽
二、一個(gè)完整的機(jī)器學(xué)習(xí)項(xiàng)目
三、分類
四、訓(xùn)練模型
五、支持向量機(jī)
六、決策樹
七、集成學(xué)習(xí)和隨機(jī)森林
八、降維
第二部分:神經(jīng)網(wǎng)絡(luò)與深度學(xué)習(xí)
(解決復(fù)雜問題)
九、啟動(dòng)并運(yùn)行 TensorFlow
十、人工神經(jīng)網(wǎng)絡(luò)介紹
十一、訓(xùn)練深層神經(jīng)網(wǎng)絡(luò)
十二、設(shè)備和服務(wù)器上的分布式 TensorFlow
十三、卷積神經(jīng)網(wǎng)絡(luò)
十四、循環(huán)神經(jīng)網(wǎng)絡(luò)
十五、自編碼器
十六、強(qiáng)化學(xué)習(xí)
附錄
附錄 C、SVM 對(duì)偶問題
附錄 D、自動(dòng)微分
一、機(jī)器學(xué)習(xí)概覽
機(jī)器學(xué)習(xí)概念
廣義概念:機(jī)器學(xué)習(xí)是讓計(jì)算機(jī)具有學(xué)習(xí)的能力,無需進(jìn)行明確編程。
工程概念:計(jì)算機(jī)程序利用經(jīng)驗(yàn) E 學(xué)習(xí)任務(wù) T,性能是 P,如果針對(duì)任務(wù) T 的性能 P 隨著經(jīng)驗(yàn) E 不斷增長(zhǎng),則稱為機(jī)器學(xué)習(xí)。
例如,你的垃圾郵件過濾器就是一個(gè)機(jī)器學(xué)習(xí)程序,它可以根據(jù)垃圾郵件(比如,用戶標(biāo)記的垃圾郵件)和普通郵件(非垃圾郵件,也稱作 ham)學(xué)習(xí)標(biāo)記垃圾郵件。用來進(jìn)行學(xué)習(xí)的樣例稱作訓(xùn)練集。每個(gè)訓(xùn)練樣例稱作訓(xùn)練實(shí)例(或樣本)。在這個(gè)例子中,任務(wù) T 就是標(biāo)記新郵件是否是垃圾郵件,經(jīng)驗(yàn)E是訓(xùn)練數(shù)據(jù),性能P需要定義:例如,可以使用正確分類的比例。這個(gè)性能指標(biāo)稱為準(zhǔn)確率,通常用在分類任務(wù)中。
機(jī)器學(xué)習(xí)特點(diǎn)
使用機(jī)器學(xué)習(xí)方法挖掘大量數(shù)據(jù),可以發(fā)現(xiàn)并不顯著的規(guī)律。這稱作數(shù)據(jù)挖掘。
機(jī)器學(xué)習(xí)善于:
- 需要進(jìn)行大量手工調(diào)整或需要擁有長(zhǎng)串規(guī)則才能解決的問題:機(jī)器學(xué)習(xí)算法通??梢院?jiǎn)化代碼、提高性能。
- 問題復(fù)雜,傳統(tǒng)方法難以解決:機(jī)器學(xué)習(xí)善于處理對(duì)于傳統(tǒng)方法太復(fù)雜或是沒有已知算法的問題。
- 環(huán)境有波動(dòng):機(jī)器學(xué)習(xí)算法可以適應(yīng)新數(shù)據(jù),自動(dòng)適應(yīng)改變。
- 洞察復(fù)雜問題和大量數(shù)據(jù):機(jī)器學(xué)習(xí)可以幫助人類進(jìn)行學(xué)習(xí)。
機(jī)器學(xué)習(xí)分類
機(jī)器學(xué)習(xí)有多種類型,可以根據(jù)如下規(guī)則進(jìn)行分類:
- 是否在人類監(jiān)督下進(jìn)行訓(xùn)練(監(jiān)督、非監(jiān)督、半監(jiān)督和強(qiáng)化學(xué)習(xí),根據(jù)訓(xùn)練時(shí)監(jiān)督的量和類型區(qū)分)
- 是否可以動(dòng)態(tài)漸進(jìn)學(xué)習(xí)(在線學(xué)習(xí) vs 批量學(xué)習(xí))
- 它們是否只是通過簡(jiǎn)單地比較新的數(shù)據(jù)點(diǎn)和已知的數(shù)據(jù)點(diǎn),或者在訓(xùn)練數(shù)據(jù)中進(jìn)行模式識(shí)別,以建立一個(gè)預(yù)測(cè)模型,就像科學(xué)家所做的那樣(基于實(shí)例學(xué)習(xí) vs 基于模型學(xué)習(xí))
規(guī)則并不僅限于以上的,你可以將他們進(jìn)行組合。例如,一個(gè)先進(jìn)的垃圾郵件過濾器可以使用神經(jīng)網(wǎng)絡(luò)模型動(dòng)態(tài)進(jìn)行學(xué)習(xí),用垃圾郵件和普通郵件進(jìn)行訓(xùn)練。這就讓它成了一個(gè)在線、基于模型、監(jiān)督學(xué)習(xí)系統(tǒng)。
監(jiān)督學(xué)習(xí)
監(jiān)督學(xué)習(xí):用來訓(xùn)練算法的訓(xùn)練數(shù)據(jù)中包含了答案,稱為標(biāo)簽。
一個(gè)典型的監(jiān)督學(xué)習(xí)任務(wù)是分類。垃圾郵件過濾器就是一個(gè)很好的例子:用許多帶有歸類(垃圾郵件或普通郵件)的郵件樣本進(jìn)行訓(xùn)練,過濾器必須還能對(duì)新郵件進(jìn)行分類。
另一個(gè)典型任務(wù)是預(yù)測(cè)目標(biāo)數(shù)值,例如給出一些特征(里程數(shù)、車齡、品牌等等)稱作預(yù)測(cè)值,來預(yù)測(cè)一輛汽車的價(jià)格。這類任務(wù)稱作回歸。要訓(xùn)練這個(gè)系統(tǒng),你需要給出大量汽車樣本,包括它們的預(yù)測(cè)值和標(biāo)簽(即它們的價(jià)格)。
屬性:數(shù)據(jù)類型(例如里程數(shù))
特征:屬性+值(例如里程數(shù)=15000)
降維:目的是簡(jiǎn)化數(shù)據(jù)、但是不能失去大部分信息,這樣可以運(yùn)行的更快,占用的硬盤和內(nèi)存空間更少,有些情況下性能也更好。做法之一是合并若干相關(guān)的特征。例如,汽車的里程數(shù)與車齡高度相關(guān),降維算法就會(huì)將它們合并成一個(gè),表示汽車的磨損。這叫做特征提取。
下面是一些重要的監(jiān)督學(xué)習(xí)算法:
- K近鄰算法
- 線性回歸
- 邏輯回歸
- 支持向量機(jī)(SVM)
- 決策樹和隨機(jī)森林
- 神經(jīng)網(wǎng)絡(luò)
非監(jiān)督學(xué)習(xí)
非監(jiān)督學(xué)習(xí):訓(xùn)練數(shù)據(jù)沒有加上標(biāo)簽,系統(tǒng)在沒有老師的條件下進(jìn)行學(xué)習(xí)。
下面是一些最重要的非監(jiān)督學(xué)習(xí)算法:
聚類:
- K均值
- 層次聚類分析(Hierarchical Cluster Analysis,HCA)
- 期望最大值
可視化和降維:
- 主成分分析(Principal Component Analysis,PCA)
- 核主成分分析
- 局部線性嵌入(Locally-Linear Embedding,LLE)
- t-分布鄰域嵌入算法(t-distributed Stochastic Neighbor Embedding,t-SNE)
關(guān)聯(lián)性規(guī)則學(xué)習(xí):
- Apriori 算法
- Eclat 算法
非監(jiān)督學(xué)習(xí)應(yīng)用場(chǎng)景:
- 博客訪客數(shù)據(jù)
你想運(yùn)行一個(gè)聚類算法,檢測(cè)相似訪客的分組。你不會(huì)告訴算法某個(gè)訪客屬于哪一類:它會(huì)自己找出關(guān)系,無需幫助。例如,算法可能注意到 40% 的訪客是喜歡漫畫書的男性,通常是晚上訪問,20% 是科幻愛好者,他們是在周末訪問等等。如果你使用層次聚類分析,它可能還會(huì)細(xì)分每個(gè)分組為更小的組。這可以幫助你為每個(gè)分組定位博文。
- 可視化算法
給算法大量復(fù)雜的且不加標(biāo)簽的數(shù)據(jù),算法輸出數(shù)據(jù)的2D或3D圖像。算法會(huì)試圖保留數(shù)據(jù)的結(jié)構(gòu)(即嘗試保留輸入的獨(dú)立聚類,避免在圖像中重疊),這樣就可以明白數(shù)據(jù)是如何組織起來的,也許還能發(fā)現(xiàn)隱藏的規(guī)律。
- 異常檢測(cè)(anomaly detection)
例如,檢測(cè)異常的信用卡轉(zhuǎn)賬以防欺詐,檢測(cè)制造缺陷,或者在訓(xùn)練之前自動(dòng)從訓(xùn)練數(shù)據(jù)集去除異常值。異常檢測(cè)的系統(tǒng)使用正常值訓(xùn)練的,當(dāng)它碰到一個(gè)新實(shí)例,它可以判斷這個(gè)新實(shí)例是像正常值還是異常值。
- 關(guān)聯(lián)規(guī)則學(xué)習(xí)
它的目標(biāo)是挖掘大量數(shù)據(jù)以發(fā)現(xiàn)屬性間有趣的關(guān)系。例如,假設(shè)你擁有一個(gè)超市。在銷售日志上運(yùn)行關(guān)聯(lián)規(guī)則,可能發(fā)現(xiàn)買了燒烤醬和薯片的人也會(huì)買牛排。因此,你可以將這些商品放在一起。
半監(jiān)督學(xué)習(xí)
半監(jiān)督學(xué)習(xí):一些算法可以處理部分帶標(biāo)簽的訓(xùn)練數(shù)據(jù),通常是大量不帶標(biāo)簽數(shù)據(jù)加上小部分帶標(biāo)簽數(shù)據(jù)。
一些圖片存儲(chǔ)服務(wù),比如 Google Photos,是半監(jiān)督學(xué)習(xí)的好例子。一旦你上傳了所有家庭相片,它就能自動(dòng)識(shí)別相同的人 A 出現(xiàn)在相片 1、5、11 中,另一個(gè)人 B 出現(xiàn)在相片 2、5、7 中。這是算法的非監(jiān)督部分(聚類)。現(xiàn)在系統(tǒng)需要的就是你告訴這兩個(gè)人是誰。只要給每個(gè)人一個(gè)標(biāo)簽,算法就可以命名每張照片中的每個(gè)人,特別適合搜索照片。
多數(shù)半監(jiān)督學(xué)習(xí)算法是非監(jiān)督和監(jiān)督算法的結(jié)合。例如,深度信念網(wǎng)絡(luò)(deep beliefnetworks)是基于被稱為互相疊加的受限玻爾茲曼機(jī)(restricted Boltzmann machines,RBM)的非監(jiān)督組件。RBM 是先用非監(jiān)督方法進(jìn)行訓(xùn)練,再用監(jiān)督學(xué)習(xí)方法進(jìn)行整個(gè)系統(tǒng)微調(diào)。
強(qiáng)化學(xué)習(xí)
強(qiáng)化學(xué)習(xí)非常不同。學(xué)習(xí)系統(tǒng)在這里被稱為智能體(agent),可以對(duì)環(huán)境進(jìn)行觀察,選擇和執(zhí)行動(dòng)作,獲得獎(jiǎng)勵(lì)(負(fù)獎(jiǎng)勵(lì)是懲罰)。然后它必須自己學(xué)習(xí)哪個(gè)是最佳方法(稱為策略,policy),以得到長(zhǎng)久的最大獎(jiǎng)勵(lì)。策略決定了智能體在給定情況下應(yīng)該采取的行動(dòng)。
例如,許多機(jī)器人運(yùn)行強(qiáng)化學(xué)習(xí)算法以學(xué)習(xí)如何行走。DeepMind 的 AlphaGo 也是強(qiáng)化學(xué)習(xí)的例子:它在 2016 年三月?lián)魯×耸澜鐕骞谲娎钍朗ㄗg者注:2017 年五月,AlphaGo 又擊敗了世界排名第一的柯潔)。它是通過分析數(shù)百萬盤棋局學(xué)習(xí)制勝策略,然后自己和自己下棋。要注意,在比賽中機(jī)器學(xué)習(xí)是關(guān)閉的;AlphaGo 只是使用它學(xué)會(huì)的策略。
批量學(xué)習(xí)
批量學(xué)習(xí):系統(tǒng)不能進(jìn)行持續(xù)學(xué)習(xí),必須用所有可用數(shù)據(jù)進(jìn)行訓(xùn)練(從頭開始訓(xùn)練)。
這通常會(huì)占用大量時(shí)間和計(jì)算資源,所以一般是線下做的。首先是進(jìn)行訓(xùn)練,然后部署在生產(chǎn)環(huán)境且停止學(xué)習(xí),它只是使用已經(jīng)學(xué)到的策略。這稱為離線學(xué)習(xí)。
如果你想讓一個(gè)批量學(xué)習(xí)系統(tǒng)明白新數(shù)據(jù)(例如垃圾郵件的新類型),就需要從頭訓(xùn)練一個(gè)系統(tǒng)的新版本,使用全部數(shù)據(jù)集(不僅有新數(shù)據(jù)也有老數(shù)據(jù)),然后停掉老系統(tǒng),換上新系統(tǒng)。
用全部數(shù)據(jù)集進(jìn)行訓(xùn)練會(huì)花費(fèi)大量時(shí)間,所以一般是每 24 小時(shí)或每周訓(xùn)練一個(gè)新系統(tǒng)。如果系統(tǒng)需要快速適應(yīng)變化的數(shù)據(jù)(比如,預(yù)測(cè)股價(jià)變化),就需要一個(gè)響應(yīng)更及時(shí)的方案。
在線學(xué)習(xí)
在線學(xué)習(xí):用數(shù)據(jù)實(shí)例持續(xù)地進(jìn)行訓(xùn)練,可以一次一個(gè)或一次幾個(gè)實(shí)例(稱為小批量)。每個(gè)學(xué)習(xí)步驟都很快且廉價(jià),所以系統(tǒng)可以動(dòng)態(tài)地學(xué)習(xí)到達(dá)的新數(shù)據(jù)。
在線學(xué)習(xí)很適合系統(tǒng)接收連續(xù)流的數(shù)據(jù)(比如,股票價(jià)格),且需要自動(dòng)對(duì)改變作出調(diào)整。如果計(jì)算資源有限,在線學(xué)習(xí)是一個(gè)不錯(cuò)的方案:一旦在線學(xué)習(xí)系統(tǒng)學(xué)習(xí)了新的數(shù)據(jù)實(shí)例,它就不再需要這些數(shù)據(jù)了,所以扔掉這些數(shù)據(jù)(除非你想滾回到之前的一個(gè)狀態(tài),再次使用數(shù)據(jù))。這樣可以節(jié)省大量的空間。
在線學(xué)習(xí)算法也可以當(dāng)機(jī)器的內(nèi)存存不下大量數(shù)據(jù)集時(shí),用來訓(xùn)練系統(tǒng)(這稱作核外學(xué)習(xí),out-of-core learning)。算法加載部分的數(shù)據(jù),用這些數(shù)據(jù)進(jìn)行訓(xùn)練,重復(fù)這個(gè)過程,直到用所有數(shù)據(jù)都進(jìn)行了訓(xùn)練。這個(gè)整個(gè)過程通常是離線完成的,可以把它想成持續(xù)學(xué)習(xí)。
在線學(xué)習(xí)系統(tǒng)的一個(gè)重要參數(shù)是,它們可以多快地適應(yīng)數(shù)據(jù)的改變:這被稱為學(xué)習(xí)速率。如果你設(shè)定一個(gè)高學(xué)習(xí)速率,系統(tǒng)就可以快速適應(yīng)新數(shù)據(jù),但是也會(huì)快速忘記老數(shù)據(jù)(你可不想讓垃圾郵件過濾器只標(biāo)記最新的垃圾郵件種類)。相反的,如果你設(shè)定的學(xué)習(xí)速率低,系統(tǒng)的惰性就會(huì)強(qiáng):即,它學(xué)的更慢,但對(duì)新數(shù)據(jù)中的噪聲或沒有代表性的數(shù)據(jù)點(diǎn)結(jié)果不那么敏感。
在線學(xué)習(xí)的挑戰(zhàn)之一是,如果壞數(shù)據(jù)被用來進(jìn)行訓(xùn)練,系統(tǒng)的性能就會(huì)逐漸下滑。如果這是一個(gè)部署的系統(tǒng),用戶就會(huì)注意到。例如,壞數(shù)據(jù)可能來自失靈的傳感器或機(jī)器人,或某人向搜索引擎?zhèn)魅肜畔⒁蕴岣咚阉髋琶?。要減小這種風(fēng)險(xiǎn),你需要密集監(jiān)測(cè),如果檢測(cè)到性能下降,要快速關(guān)閉(或是滾回到一個(gè)之前的狀態(tài))。你可能還要監(jiān)測(cè)輸入數(shù)據(jù),對(duì)反常數(shù)據(jù)做出反應(yīng)(比如,使用異常檢測(cè)算法)。
基于實(shí)例學(xué)習(xí)
基于實(shí)例學(xué)習(xí):系統(tǒng)先用記憶學(xué)習(xí)案例,然后使用相似度測(cè)量推廣到新的例子。
垃圾郵件過濾器也要能標(biāo)記類似垃圾郵件的郵件。這就需要測(cè)量?jī)煞忄]件的相似性。一個(gè)(簡(jiǎn)單的)相似度測(cè)量方法是統(tǒng)計(jì)兩封郵件包含的相同單詞的數(shù)量。如果一封郵件含有許多垃圾郵件中的詞,就會(huì)被標(biāo)記為垃圾郵件。
基于模型學(xué)習(xí)
基于模型學(xué)習(xí):建立這些樣本的模型,然后使用這個(gè)模型進(jìn)行預(yù)測(cè)。
# 使用 Scikit-Learn 訓(xùn)練并運(yùn)行線性模型,預(yù)測(cè)塞浦路斯人民生活滿意度
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import sklearn
# 加載數(shù)據(jù)
oecd_bli = pd.read_csv("oecd_bli_2015.csv", thousands=',')
gdp_per_capita = pd.read_csv("gdp_per_capita.csv",thousands=',',delimiter='\t',encoding='latin1', na_values="n/a")
# 準(zhǔn)備數(shù)據(jù)
country_stats = prepare_country_stats(oecd_bli, gdp_per_capita)
X = np.c_[country_stats["GDP per capita"]]
y = np.c_[country_stats["Life satisfaction"]]
# 可視化數(shù)據(jù)
country_stats.plot(kind='scatter', x="GDP per capita", y='Life satisfaction')
plt.show()
# 選擇線性模型
lin_reg_model = sklearn.linear_model.LinearRegression()
# 訓(xùn)練模型
lin_reg_model.fit(X, y)
# 對(duì)塞浦路斯進(jìn)行預(yù)測(cè)
X_new = [[22587]] # 塞浦路斯的人均GDP
print(lin_reg_model.predict(X_new)) # outputs [[5.96242338]]
# 1、斯洛文尼亞的人均 GDP(20732美元)和塞浦路斯差距很小,OECD 數(shù)據(jù)上斯洛文尼亞的生活滿意度是 5.7,可以預(yù)測(cè)塞浦路斯的生活滿意度也是 5.7
# 2、兩個(gè)臨近的國家,葡萄牙和西班牙的生活滿意度分別是 5.1 和 6.5。對(duì)這三個(gè)值進(jìn)行平均得到5.77,就和基于模型的預(yù)測(cè)值很接近。這個(gè)簡(jiǎn)單的算法叫做k近鄰回歸(這個(gè)例子中,k=3)
# clf = sklearn.linear_model.LinearRegression() 改為
# clf = sklearn.neighbors.KNeighborsRegressor(n_neighbors=3)
總結(jié)步驟:
- 研究數(shù)據(jù)
- 選擇模型
- 用訓(xùn)練數(shù)據(jù)進(jìn)行訓(xùn)練(即,學(xué)習(xí)算法搜尋模型參數(shù)值,使代價(jià)函數(shù)最?。?/li>
- 使用模型對(duì)新案例進(jìn)行預(yù)測(cè)(這稱作推斷),但愿這個(gè)模型推廣效果不差
主要挑戰(zhàn)
因?yàn)槟愕闹饕蝿?wù)是選擇一個(gè)學(xué)習(xí)算法并用一些數(shù)據(jù)進(jìn)行訓(xùn)練,會(huì)導(dǎo)致錯(cuò)誤的兩件事就是“錯(cuò)誤的算法”和“錯(cuò)誤的數(shù)據(jù)”。
錯(cuò)誤的數(shù)據(jù):
- 訓(xùn)練數(shù)據(jù)量不足
不同的機(jī)器學(xué)習(xí)算法,包括非常簡(jiǎn)單的算法,一旦有了大量數(shù)據(jù)進(jìn)行訓(xùn)練,在進(jìn)行去除語言歧義的測(cè)試中幾乎有相同的性能。對(duì)于復(fù)雜問題,數(shù)據(jù)比算法更重要。
- 沒有代表性的訓(xùn)練數(shù)據(jù)
如果樣本太小,就會(huì)有樣本噪聲(即,會(huì)有一定概率包含沒有代表性的數(shù)據(jù)),但是即使是非常大的樣本也可能沒有代表性,如果取樣方法錯(cuò)誤的話。這叫做樣本偏差。
- 低質(zhì)量數(shù)據(jù)
如果訓(xùn)練集中的錯(cuò)誤、異常值和噪聲(錯(cuò)誤測(cè)量引入的)太多,系統(tǒng)檢測(cè)出潛在規(guī)律的難度就會(huì)變大,性能就會(huì)降低。
花費(fèi)時(shí)間對(duì)訓(xùn)練數(shù)據(jù)進(jìn)行清理是十分重要的。事實(shí)上,大多數(shù)據(jù)科學(xué)家的一大部分時(shí)間是做清洗工作的。
如果一些實(shí)例是明顯的異常值,最好刪掉它們或嘗試手工修改錯(cuò)誤;如果一些實(shí)例缺少特征(比如,你的 5% 的顧客沒有說明年齡),你必須決定是否忽略這個(gè)屬性、忽略這些實(shí)例、填入缺失值(比如,年齡中位數(shù)),或者訓(xùn)練一個(gè)含有這個(gè)特征的模型和一個(gè)不含有這個(gè)特征的模型,等等。
- 不相關(guān)的特征
只有在訓(xùn)練數(shù)據(jù)包含足夠相關(guān)特征、非相關(guān)特征不多的情況下,才能進(jìn)行學(xué)習(xí)。機(jī)器學(xué)習(xí)項(xiàng)目成功的關(guān)鍵之一是用好的特征進(jìn)行訓(xùn)練。
這個(gè)過程稱作特征工程,包括:
特征選擇:選取最有用的特征進(jìn)行訓(xùn)練
特征提?。航M合存在的特征,生成一個(gè)更有用的特征(可以使用降維算法)
特征新建:收集新數(shù)據(jù)創(chuàng)建新特征
錯(cuò)誤的算法:
- 過擬合訓(xùn)練數(shù)據(jù)
模型在訓(xùn)練數(shù)據(jù)上表現(xiàn)很好,但是推廣效果不好(以偏概全)。
過擬合發(fā)生在相對(duì)于訓(xùn)練數(shù)據(jù)的量和噪聲,模型過于復(fù)雜的情況??赡艿慕鉀Q方案有:
簡(jiǎn)化模型:可以選擇一個(gè)參數(shù)更少的模型(比如使用線性模型,而不是高階多項(xiàng)式模型)、減少訓(xùn)練數(shù)據(jù)的屬性數(shù)、或限制一下模型(讓它更簡(jiǎn)單,降低過擬合的風(fēng)險(xiǎn),被稱作正則化regularization)
收集更多的訓(xùn)練數(shù)據(jù)
減小訓(xùn)練數(shù)據(jù)的噪聲(比如,修改數(shù)據(jù)錯(cuò)誤和去除異常值)
目標(biāo)是在完美擬合數(shù)據(jù)和保持模型簡(jiǎn)單性上找到平衡,確保算法的推廣效果。
- 欠擬合訓(xùn)練數(shù)據(jù)
當(dāng)模型過于簡(jiǎn)單時(shí)就會(huì)發(fā)生欠擬合。
例如,生活滿意度的線性模型傾向于欠擬合;現(xiàn)實(shí)要比這個(gè)模型復(fù)雜的多,所以預(yù)測(cè)很難準(zhǔn)確,即使在訓(xùn)練樣本上也很難準(zhǔn)確。
解決這個(gè)問題的選項(xiàng)包括:
選擇一個(gè)更強(qiáng)大的模型,帶有更多參數(shù)
用更好的特征訓(xùn)練學(xué)習(xí)算法(特征工程)
減小對(duì)模型的限制(比如,減小正則化超參數(shù))
模型驗(yàn)證
將你的數(shù)據(jù)分成兩個(gè)集合:訓(xùn)練集和測(cè)試集。
正如它們的名字,用訓(xùn)練集進(jìn)行訓(xùn)練,用測(cè)試集進(jìn)行測(cè)試。對(duì)新樣本的錯(cuò)誤率稱作推廣錯(cuò)誤(或樣本外錯(cuò)誤),通過模型對(duì)測(cè)試集的評(píng)估,你可以預(yù)估這個(gè)錯(cuò)誤。這個(gè)值可以告訴你,你的模型對(duì)新樣本的性能。
一般使用 80% 的數(shù)據(jù)進(jìn)行訓(xùn)練,保留20%用于測(cè)試。
如果訓(xùn)練錯(cuò)誤率低(即你的模型在訓(xùn)練集上錯(cuò)誤不多),但是推廣錯(cuò)誤率高,意味著模型對(duì)訓(xùn)練數(shù)據(jù)過擬合。
因此,評(píng)估一個(gè)模型很簡(jiǎn)單:只使用測(cè)試集?,F(xiàn)在假設(shè)你在兩個(gè)模型之間猶豫不決(比如一個(gè)線性模型和一個(gè)多項(xiàng)式模型):如何做決定呢?一種方法是兩個(gè)都訓(xùn)練,然后比較在測(cè)試集上的效果。
現(xiàn)在假設(shè)線性模型的效果更好,但是你想做一些正則化以避免過擬合。問題是:如何選擇正則化超參數(shù)的值?一種選項(xiàng)是用 100 個(gè)不同的超參數(shù)訓(xùn)練100個(gè)不同的模型。假設(shè)你發(fā)現(xiàn)最佳的超參數(shù)的推廣錯(cuò)誤率最低,比如只有 5%。然后就選用這個(gè)模型作為生產(chǎn)環(huán)境,但是實(shí)際中性能不佳,誤差率達(dá)到了 15%。發(fā)生了什么呢?答案在于,你在測(cè)試集上多次測(cè)量了推廣誤差率,調(diào)整了模型和超參數(shù),以使模型最適合這個(gè)集合。這意味著模型對(duì)新數(shù)據(jù)的性能不會(huì)高。
這個(gè)問題通常的解決方案是,再保留一個(gè)集合,稱作驗(yàn)證集合。用訓(xùn)練集和多個(gè)超參數(shù)訓(xùn)練多個(gè)模型,選擇在驗(yàn)證集上有最佳性能的模型和超參數(shù)。當(dāng)你對(duì)模型滿意時(shí),用測(cè)試集再做最后一次測(cè)試,以得到推廣誤差率的預(yù)估。
為了避免“浪費(fèi)”過多訓(xùn)練數(shù)據(jù)在驗(yàn)證集上,通常的辦法是使用交叉驗(yàn)證:訓(xùn)練集分成互補(bǔ)的子集,每個(gè)模型用不同的子集訓(xùn)練,再用剩下的子集驗(yàn)證。一旦確定模型類型和超參數(shù),最終的模型使用這些超參數(shù)和全部的訓(xùn)練集進(jìn)行訓(xùn)練,用測(cè)試集得到推廣誤差率。
(來自第二章P72)
評(píng)估決策樹模型的一種方法是用函數(shù)train_test_split來分割訓(xùn)練集,得到一個(gè)更小的訓(xùn)練集和一個(gè)驗(yàn)證集,然后用更小的訓(xùn)練集來訓(xùn)練模型,用驗(yàn)證集來評(píng)估。
另一種更好的方法是使用 Scikit-Learn 的交叉驗(yàn)證功能。下面的代碼采用了 K 折交叉驗(yàn)證(K-fold cross-validation):它隨機(jī)地將訓(xùn)練集分成十個(gè)不同的子集,稱為“折”,然后訓(xùn)練評(píng)估決策樹模型 10 次,每次選一個(gè)不用的折來做評(píng)估,用其它 9 個(gè)來做訓(xùn)練。結(jié)果是一個(gè)包含 10 個(gè)評(píng)分的數(shù)組。
模型微調(diào)
網(wǎng)格搜索
告訴GridSearchCV要試驗(yàn)有哪些超參數(shù),要試驗(yàn)什么值,GridSearchCV就能用交叉驗(yàn)證試驗(yàn)所有可能超參數(shù)值的組合。隨機(jī)搜索
當(dāng)超參數(shù)的搜索空間很大時(shí),最好使用RandomizedSearchCV,和類GridSearchCV很相似,但它不是嘗試所有可能的組合,而是通過選擇每個(gè)超參數(shù)的一個(gè)隨機(jī)值的特定數(shù)量的隨機(jī)組合。集成方法
將表現(xiàn)最好的模型組合起來。組合(集成)之后的性能通常要比單獨(dú)的模型要好(就像隨機(jī)森林要比單獨(dú)的決策樹要好),特別是當(dāng)單獨(dú)模型的誤差類型不同時(shí)。分析最佳模型和它們的誤差
RandomForestRegressor可以指出每個(gè)屬性對(duì)于做出準(zhǔn)確預(yù)測(cè)的相對(duì)重要性。用測(cè)試集評(píng)估系統(tǒng)
從測(cè)試集得到預(yù)測(cè)值和標(biāo)簽,運(yùn)行full_pipeline轉(zhuǎn)換數(shù)據(jù)(調(diào)用transform(),而不是fit_transform()?。?,再用測(cè)試集評(píng)估最終模型。
二、一個(gè)完整的機(jī)器學(xué)習(xí)項(xiàng)目
# Python機(jī)器學(xué)習(xí):Sklearn 與 TensorFlow 機(jī)器學(xué)習(xí)實(shí)用指南
# 參考資料:https://github.com/ageron/handson-ml
# 中文翻譯:https://github.com/it-ebooks/hands-on-ml-zh
# 第二章:加州房?jī)r(jià)模型
import os
import tarfile
import numpy as np
import pandas as pd
import hashlib
import seaborn as sns
import matplotlib.pyplot as plt
from pandas.plotting import scatter_matrix
from six.moves import urllib
import joblib
from sklearn.model_selection import train_test_split
from sklearn.model_selection import StratifiedShuffleSplit
from sklearn.model_selection import cross_val_score
from sklearn.impute import SimpleImputer as Imputer
from sklearn.pipeline import Pipeline
from sklearn.pipeline import FeatureUnion
from sklearn.preprocessing import LabelEncoder
from sklearn.preprocessing import OneHotEncoder
from sklearn.preprocessing import LabelBinarizer
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error
from sklearn.tree import DecisionTreeRegressor
from sklearn.ensemble import RandomForestRegressor
from sklearn.base import BaseEstimator, TransformerMixin
# 全局變量大寫,函數(shù)內(nèi)變量小寫
DOWNLOAD_ROOT = 'https://raw.githubusercontent.com/ageron/handson-ml/master/'
HOUSING_PATH = 'datasets/housing'
HOUSING_URL = DOWNLOAD_ROOT + HOUSING_PATH + '/housing.tgz'
# 下載數(shù)據(jù)
def fetch_housing_data(housing_url=HOUSING_URL, housing_path=HOUSING_PATH):
if not os.path.isdir(housing_path):
os.makedirs(housing_path)
tgz_path = os.path.join(housing_path, 'housing.tgz')
urllib.request.urlretrieve(housing_url, tgz_path) # 下載文件
housing_tgz = tarfile.open(tgz_path)
housing_tgz.extractall(path=housing_path) # 解壓
housing_tgz.close() # 如果使用with open結(jié)構(gòu),就不用close
# 加載數(shù)據(jù)
def load_housing_data(housing_path=HOUSING_PATH):
csv_path = os.path.join(housing_path, 'housing.csv')
return pd.read_csv(csv_path)
# 整體數(shù)據(jù)情況
def check_housing_data(data):
print(data.head())
print(data.info()) # 可以看到總房間數(shù)有207個(gè)缺失值,除了離大海距離ocean_proximity是object之外,其余都是數(shù)值
print(data['ocean_proximity'].value_counts())
print(data.describe()) # 數(shù)值屬性的概括,空值不計(jì)
# 每一列都作為橫坐標(biāo)生成一個(gè)圖,將橫坐標(biāo)分成50個(gè)柱,也就是50個(gè)區(qū)間(縱坐標(biāo)代表區(qū)間內(nèi)的個(gè)數(shù))
# 1、收入中位數(shù)貌似不是美元,數(shù)據(jù)經(jīng)過縮放調(diào)整,過高會(huì)變成15,過低會(huì)變成5
# 2、房屋年齡中位數(shù)housing_median_age和房屋價(jià)值中位數(shù)median_house_value也被設(shè)了上限,這可能是很嚴(yán)重的問題,因?yàn)楹笳呤茄芯繉?duì)象,機(jī)器學(xué)習(xí)算法可能學(xué)習(xí)到價(jià)格不會(huì)超出這個(gè)界限
# 3、這些屬性有不同的量度(特征縮放)
# 4、許多柱狀圖的尾巴很長(zhǎng),相較于左邊,他們?cè)谥形粩?shù)的右邊延伸過遠(yuǎn),可以在后面嘗試變換處理這些屬性,使其符合正態(tài)分布
housing.hist(bins=50, figsize=(20, 15))
plt.show()
# 創(chuàng)建測(cè)試集
def split_train_test(data, test_ratio):
shuffled_indices = np.random.permutation(len(data)) # 對(duì)數(shù)據(jù)長(zhǎng)度隨機(jī)排列,作為index
test_set_size = int(len(data) * test_ratio)
test_indices = shuffled_indices[:test_set_size] # 測(cè)試集index
train_indices = shuffled_indices[test_set_size:] # 訓(xùn)練集index
return data.iloc[train_indices], data.iloc[test_indices]
def test_set_check(identifier, test_ratio, hash):
return hash(np.int64(identifier)).digest()[-1] < 256 * test_ratio
# 如果再次運(yùn)行程序,就會(huì)得到一個(gè)不一樣的數(shù)據(jù)集,多次運(yùn)行之后,就可能會(huì)得到整個(gè)數(shù)據(jù)集,有以下方法可以避免
# 1、保存第一次運(yùn)行時(shí)得到的數(shù)據(jù)集,并在隨后的過程中加載
# 2、在生成隨機(jī)index之前,設(shè)置隨機(jī)數(shù)生成器的種子(比如np.random.seed(42),以產(chǎn)生總是相同的洗牌指數(shù)
# 但是一旦數(shù)據(jù)集更新,以上兩個(gè)方法都會(huì)失效。所以通常的解決辦法是使用每個(gè)實(shí)例的id來判斷這個(gè)實(shí)例是否應(yīng)該加入測(cè)試集(不是直接指定id)
def split_train_test_by_id(data, test_ratio, id_column='id', hash=hashlib.md5):
data.reset_index() # 不使用行索引作為id:保證新數(shù)據(jù)放在現(xiàn)有數(shù)據(jù)的尾部,且沒有數(shù)據(jù)被刪除
data['id'] = data['longitude'] * 1000 + data['latitude'] # 使用最穩(wěn)定的特征來創(chuàng)建id,在這里是經(jīng)緯度
ids = data[id_column]
in_test_set = ids.apply(lambda id_: test_set_check(id_, test_ratio, hash))
return data.loc[~in_test_set], data.loc[in_test_set] # iloc使用數(shù)字,loc使用索引
# 上面的方法都是隨機(jī)取樣方法,這里使用分層取樣(分層數(shù)不能過大,且每個(gè)分層要足夠大)
# 收入中位數(shù)是預(yù)測(cè)房?jī)r(jià)中位數(shù)非常重要的屬性,應(yīng)該保證測(cè)試集可以代表整體數(shù)據(jù)集中的多種收入分類
# 因?yàn)槭杖胫形粩?shù)是連續(xù)的數(shù)值屬性,所以要先創(chuàng)建收入類別屬性
def split_train_test_by_slice(data, test_ratio):
data['income_cat'] = np.ceil(data['median_income'] / 1.5) # 將收入中位數(shù)除以1.5,用ceil對(duì)值進(jìn)行舍入,以產(chǎn)生離散分類
data['income_cat'].where(data['income_cat'] < 5, 5.0, inplace=True) # 將所有大于5的分類歸入到分類5
# print(data['income_cat'].value_counts().sort_index()/len(data)) # 收入分類比例
# 按照收入分類進(jìn)行取樣,split返回分組后的數(shù)據(jù)在原數(shù)組中的索引
ss = StratifiedShuffleSplit(n_splits=1, test_size=test_ratio, random_state=42)
for train_index, test_index in ss.split(data, data['income_cat']):
strat_train_set = data.loc[train_index]
strat_test_set = data.loc[test_index]
for set in strat_train_set, strat_test_set:
set.drop('income_cat', axis=1, inplace=True) # 將數(shù)據(jù)復(fù)原
return strat_train_set, strat_test_set
# 地理數(shù)據(jù)可視化
def view_housing_data(data):
# 房屋分布散點(diǎn)圖:將每個(gè)點(diǎn)的透明度設(shè)置小一點(diǎn),可以更好的看出點(diǎn)的分布
# 說明房?jī)r(jià)和位置(比如,靠海)和人口密度聯(lián)系密切
# 可以使用聚類算法來檢測(cè)主要的聚集,用一個(gè)新的特征值測(cè)量聚集中心的距離。
# 盡管北加州海岸區(qū)域的房?jī)r(jià)不是非常高,但離大海距離屬性也可能很有用,所以這不是用一個(gè)簡(jiǎn)單的規(guī)則就可以定義的問題。
# figure1 = plt.figure()
data.plot(kind='scatter', x='longitude', y='latitude', alpha=0.4,
s=data['population']/100, label='population', # 每個(gè)圈的半徑表示街區(qū)的人口
c=data['median_house_value'], cmap=plt.get_cmap('jet'), colorbar=True) # 顏色代表價(jià)格
# 查找關(guān)聯(lián):因?yàn)閿?shù)據(jù)集并不是非常大,可以使用corr方法計(jì)算出每對(duì)屬性間的標(biāo)準(zhǔn)相關(guān)系數(shù)(也稱作皮爾遜相關(guān)系數(shù))
plt.figure()
corr_matrix = data.corr()
sns.heatmap(corr_matrix, vmax=1, square=True)
print(corr_matrix['median_house_value'].sort_values(ascending=False))
# 另一種檢測(cè)屬性間相關(guān)系數(shù)的方法:scatter_matrix方法,可以畫出每個(gè)數(shù)值屬性對(duì)每個(gè)其它數(shù)值屬性的圖,這里選擇相關(guān)性最大的四個(gè)屬性
# 如果將每個(gè)變量對(duì)自己作圖,主對(duì)角線(左上到右下)都會(huì)是直線圖,所以展示的是每個(gè)屬性的柱狀圖
plt.figure()
attributes = ['median_house_value', 'median_income', 'total_rooms', 'housing_median_age']
scatter_matrix(data[attributes], figsize=(12, 8))
# 最有希望用來預(yù)測(cè)房?jī)r(jià)中位數(shù)的屬性是收入中位數(shù),因此將這張圖放大,這張圖說明了幾點(diǎn):
# 1、相關(guān)性非常高;可以清晰地看到向上的趨勢(shì),并且數(shù)據(jù)點(diǎn)不是非常分散
# 2、我們之前看到的最高價(jià),清晰地呈現(xiàn)為一條位于 500000 美元的水平線。這張圖也呈現(xiàn)了一些不是那么明顯的直線:
# 一條位于 450000 美元的直線,一條位于 350000 美元的直線,一條在 280000 美元的線,和一些更靠下的線。
# 你可能希望去除對(duì)應(yīng)的街區(qū),以防止算法重復(fù)這些巧合。
data.plot(kind='scatter', x='median_income', y='median_house_value', alpha=0.1)
# 在上面幾步,你發(fā)現(xiàn)了一些數(shù)據(jù)的巧合,需要在給算法提供數(shù)據(jù)之前,將其去除。
# 你還發(fā)現(xiàn)了一些屬性間有趣的關(guān)聯(lián),特別是目標(biāo)屬性。
# 你還注意到一些屬性具有長(zhǎng)尾分布,因此你可能要將其進(jìn)行轉(zhuǎn)換(例如,計(jì)算其 log 對(duì)數(shù))
# 屬性組合試驗(yàn):嘗試多種屬性組合
# 如果你不知道某個(gè)街區(qū)有多少戶,該街區(qū)的總房間數(shù)就沒什么用。你真正需要的是每戶有幾個(gè)房間。
data['rooms_per_household'] = data['total_rooms'] / data['households']
# 相似的,總臥室數(shù)也不重要:你可能需要將其與房間數(shù)進(jìn)行比較。
data['rooms_per_bedroom'] = data['total_rooms'] / data['total_bedrooms']
# 每戶的人口數(shù)也是一個(gè)有趣的屬性組合。
data['population_per_household'] = data['population'] / data['households']
# 再看看相關(guān)矩陣:
# 與總房間數(shù)或臥室數(shù)相比,每臥室房間數(shù)與房?jī)r(jià)中位數(shù)的關(guān)聯(lián)性更強(qiáng),每戶的房間數(shù)也比街區(qū)的總房間數(shù)的關(guān)聯(lián)性更強(qiáng)
corr_matrix = data.corr()
print(corr_matrix['median_house_value'].sort_values(ascending=False))
plt.show()
rooms_ix, bedrooms_ix, population_ix, household_ix = 3, 4, 5, 6
class CombinedAttributesAdder(BaseEstimator, TransformerMixin):
def __init__(self, add_bedrooms_per_room=True): # no *args or **kargs
self.add_bedrooms_per_room = add_bedrooms_per_room
def fit(self, X, y=None):
return self # nothing else to do
def transform(self, X, y=None):
rooms_per_household = X[:, rooms_ix] / X[:, household_ix]
population_per_household = X[:, population_ix] / X[:, household_ix]
if self.add_bedrooms_per_room:
bedrooms_per_room = X[:, bedrooms_ix] / X[:, rooms_ix]
return np.c_[X, rooms_per_household, population_per_household, bedrooms_per_room]
else:
return np.c_[X, rooms_per_household, population_per_household]
# 自定義轉(zhuǎn)換器:
# 通過選擇對(duì)應(yīng)的屬性(數(shù)值或分類)、丟棄其它的,來轉(zhuǎn)換數(shù)據(jù),并將輸出 DataFrame 轉(zhuǎn)變成一個(gè) NumPy 數(shù)組
class DataFrameSelector(BaseEstimator, TransformerMixin):
def __init__(self, attribute_names):
self.attribute_names = attribute_names
def fit(self, X, y=None):
return self
def transform(self, X):
return X[self.attribute_names].values
# 數(shù)據(jù)清洗
# 參考:https://blog.csdn.net/weixin_41571493/article/details/82714759
def trans_housing_data(housing):
# 6.0、將預(yù)測(cè)值和標(biāo)簽分開
housing = housing.drop('median_house_value', axis=1) # drop和fillna等函數(shù)中設(shè)置inplace=True在原數(shù)據(jù)上修改,不設(shè)置默認(rèn)不修改,需要賦值給其他變量
# 6.1、處理缺失值:1、去掉缺失的記錄,2、去掉整個(gè)屬性,3、進(jìn)行賦值(0、平均值、中位數(shù)等)
# housing.dropna(subset=['total_bedrooms']) # 1
# housing.drop('total_bedrooms', axis=1) # 2
# median = housing['total_bedrooms'].median()
# housing['total_bedrooms'].fillna(median, inplace=True) # 3
# 自定義轉(zhuǎn)換器:sklearn是鴨子類型的(而不是繼承),需要依次執(zhí)行fit、transform和fit_transform方法
# sklearn提供Imputer類來處理缺失值
housing_num = housing.drop('ocean_proximity', axis=1) # 只有數(shù)值屬性才有中位數(shù)
imputer = Imputer(strategy='median') # imputer.statistics_屬性與housing_num.median().values結(jié)果一致
imputer.fit(housing_num) # 將imputer實(shí)例擬合到訓(xùn)練數(shù)據(jù)
x = imputer.transform(housing_num) # 將imputer應(yīng)用到每個(gè)屬性,結(jié)果為Numpy數(shù)組
housing_tr = pd.DataFrame(x, columns=housing_num.columns)
# print(housing_tr.describe()) # 缺失值補(bǔ)充完成,全部列都非空
# 6.2、處理文本和類別屬性:ocean_proximity
housing_cat = housing['ocean_proximity']
# 將文本屬性轉(zhuǎn)換為數(shù)值
encoder = LabelEncoder()
housing_cat_encoded = encoder.fit_transform(housing_cat) # 譯注:該轉(zhuǎn)換器只能用來轉(zhuǎn)換標(biāo)簽
# housing_cat_encoded, housing_categories = housing_cat.factorize()
# print(housing_cat_encoded[:20]) # 屬性轉(zhuǎn)換為數(shù)值完成
# print(encoder.classes_) # 屬性映射
# 這種做法的問題是,ML 算法會(huì)認(rèn)為兩個(gè)臨近的值比兩個(gè)疏遠(yuǎn)的值要更相似。顯然這樣不對(duì)(比如,分類 0 和 4 比 0 和 1 更相似)
# 要解決這個(gè)問題,一個(gè)常見的方法是給每個(gè)分類創(chuàng)建一個(gè)二元屬性:獨(dú)熱編碼(One-Hot Encoding),只有一個(gè)屬性會(huì)等于 1(熱),其余會(huì)是 0(冷)
encoder = OneHotEncoder()
housing_cat_1hot = encoder.fit_transform(housing_cat_encoded.reshape(-1, 1)) # reshape行轉(zhuǎn)列,結(jié)果是一個(gè)稀疏矩陣,只存放非零元素的位置
# print(housing_cat_1hot.toarray()[:10]) # 復(fù)原為密集數(shù)組
# 將以上兩步合并(LabelEncoder + OneHotEncoder)
encoder = LabelBinarizer()
housing_cat_1hot = encoder.fit_transform(housing_cat)
# print(housing_cat_1hot) # 默認(rèn)返回密集數(shù)組,設(shè)置sparse_output=True可得到一個(gè)稀疏矩陣
# 6.3、特征縮放:讓所有的屬性有相同的量度
# 方法1:線性函數(shù)歸一化(Min-Max scaling):值被轉(zhuǎn)變、重新縮放,直到范圍變成 0 到 1,轉(zhuǎn)換器MinMaxScaler
# 方法2:標(biāo)準(zhǔn)化(standardization):首先減去平均值(所以標(biāo)準(zhǔn)化值的平均值總是 0),然后除以方差,使得到的分布具有單位方差,轉(zhuǎn)換器StandardScaler
# 許多數(shù)據(jù)轉(zhuǎn)換步驟,需要按一定的順序執(zhí)行,sklearn提供了類Pipeline(轉(zhuǎn)換流水線)來完成
# 調(diào)用流水線的fit方法,就會(huì)對(duì)所有轉(zhuǎn)換器順序調(diào)用fit_transform方法,將每次調(diào)用的輸出作為參數(shù)傳遞給下一個(gè)調(diào)用,一直到最后一個(gè)估計(jì)器,它只執(zhí)行fit方法
num_attribs = list(housing_num)
cat_attribs = ['ocean_proximity']
# 處理缺失值
num_pipeline = Pipeline([
('selector', DataFrameSelector(num_attribs)),
('imputer', Imputer(strategy='median')),
('attribs_adder', CombinedAttributesAdder()),
('std_scaler', StandardScaler())
])
# housing_num_tr = num_pipeline.fit_transform(housing_num) # 單獨(dú)運(yùn)行流水線
# 處理類別屬性
cat_pipeline = Pipeline([
('selector', DataFrameSelector(cat_attribs)),
('label_binarizer', LabelBinarizer())
])
full_pipeline = FeatureUnion(transformer_list=[
('num_pipeline', num_pipeline),
('cat_pipeline', cat_pipeline)
])
# housing_prepared = full_pipeline.fit_transform(housing) # 運(yùn)行整個(gè)流水線,會(huì)報(bào)錯(cuò),暫時(shí)不使用
housing_prepared = housing_tr.copy() # 建立副本,不要修改原來的數(shù)據(jù)
housing_prepared['ocean_proximity'] = housing_cat_encoded # 合并填充缺失值的結(jié)果+類別屬性轉(zhuǎn)換為數(shù)值的結(jié)果
return housing_prepared
# 顯示模型評(píng)分
def display_scores(scores):
print("Scores:", scores)
print("Mean:", scores.mean())
print("Standard deviation:", scores.std())
# 選擇并訓(xùn)練模型
def train_housing_data(housing):
# 7.1、線性回歸模型
housing_labels = train_set['median_house_value'].copy()
lin_reg = LinearRegression()
lin_reg.fit(housing, housing_labels) # 訓(xùn)練模型
# 部分訓(xùn)練集驗(yàn)證
some_data = housing.iloc[:5]
some_labels = housing_labels[:5]
# some_data = full_pipeline.transform(some_data) # 先將訓(xùn)練集進(jìn)行處理
print(lin_reg.predict(some_data))
print(some_labels) # 將預(yù)測(cè)值與實(shí)際值進(jìn)行對(duì)比
# 用全部訓(xùn)練集來計(jì)算這個(gè)回歸模型的RMSE
housing_predicions = lin_reg.predict(housing) # 數(shù)據(jù)擬合
lin_mse = mean_squared_error(housing_labels, housing_predicions) # 計(jì)算誤差
lin_rmse = np.square(lin_mse)
print(lin_rmse) # 線性回歸模型的預(yù)測(cè)誤差非常大
# 7.2、決策樹模型:可以發(fā)現(xiàn)數(shù)據(jù)中復(fù)雜的非線性關(guān)系
tree_reg = DecisionTreeRegressor()
tree_reg.fit(housing, housing_labels) # 訓(xùn)練模型
housing_predicions = tree_reg.predict(housing)
tree_mse = mean_squared_error(housing_labels, housing_predicions)
tree_rmse = np.square(tree_mse)
print(tree_rmse) # 決策樹模型,沒有誤差?
# 7.3、隨機(jī)森林模型:通過用特征的隨機(jī)子集訓(xùn)練許多決策樹
forest_reg = RandomForestRegressor()
forest_reg.fit(housing, housing_labels) # 訓(xùn)練模型
housing_predicions = forest_reg.predict(housing)
forest_mse = mean_squared_error(housing_labels, housing_predicions)
forest_rmse = np.square(forest_mse)
print(forest_rmse)
# 使用交叉驗(yàn)證做更佳的評(píng)估
# 隨機(jī)地將訓(xùn)練集分成十個(gè)不同的子集,成為“折”,然后訓(xùn)練評(píng)估決策樹模型 10 次,每次選一個(gè)不用的折來做評(píng)估,用其它 9 個(gè)來做訓(xùn)練
# 結(jié)果是一個(gè)包含10 個(gè)評(píng)分的數(shù)組
lin_scores = cross_val_score(lin_reg, housing, housing_labels, scoring='neg_mean_squared_error', cv=10)
lin_rmse_scores = np.square(-lin_scores)
display_scores(lin_rmse_scores)
tree_scores = cross_val_score(tree_reg, housing, housing_labels, scoring='neg_mean_squared_error', cv=10)
tree_rmse_scores = np.square(-tree_scores)
display_scores(tree_rmse_scores) # 決策樹模型過擬合很嚴(yán)重,它的性能比線性回歸模型還差
forest_scores = cross_val_score(tree_reg, housing, housing_labels, scoring='neg_mean_squared_error', cv=10)
forest_rmse_scores = np.square(-forest_scores)
display_scores(forest_rmse_scores) # 現(xiàn)在好多了:隨機(jī)森林看起來很有希望
# 保存試驗(yàn)過的模型
# joblib.dump(my_model, "my_model.pkl")
# my_model_loaded = joblib.load("my_model.pkl")
if __name__ == '__main__':
pd.set_option('display.max_columns', 20) # 最大列數(shù)
pd.set_option('display.width', 1000) # 總寬度(字符數(shù))
# 1、獲取數(shù)據(jù)
# fetch_housing_data()
# 2、加載數(shù)據(jù)
housing = load_housing_data()
# 3、整體數(shù)據(jù)情況
# check_housing_data(housing)
# 4、拆分?jǐn)?shù)據(jù)集(很早就要進(jìn)行,如果提前查看了測(cè)試集,可能會(huì)發(fā)生過擬合的情況)
# train_set, test_set = split_train_test(housing, 0.2) # 使用index進(jìn)行采樣,每次的測(cè)試集都不一樣
# train_set, test_set = split_train_test_by_id(housing, 0.2, 'id') # 構(gòu)造id,使用id進(jìn)行采樣
# train_set, test_set = train_test_split(housing, test_size=0.2, random_state=42) # sklearn分割數(shù)據(jù)集,random_state生成器種子
train_set, test_set = split_train_test_by_slice(housing, 0.2) # 分層取樣
# 對(duì)訓(xùn)練集進(jìn)行研究
housing = train_set.copy()
# 5、地理數(shù)據(jù)可視化
# view_housing_data(housing)
# 6、數(shù)據(jù)清洗
housing = trans_housing_data(housing)
# 7、選擇并訓(xùn)練模型
train_housing_data(housing)
三、分類
# 下載數(shù)據(jù)集
from sklearn.datasets import fetch_mldata
mnist = fetch_mldata('MNIST original')
# 數(shù)據(jù)展示
import matplotlib
import matplotlib.pyplot as plt
some_digit = X[36000]
some_digit_image = some_digit.reshape(28, 28)
plt.imshow(some_digit_image, cmap = matplotlib.cm.binary, interpolation="nearest")
plt.axis("off")
plt.show()
# 創(chuàng)建測(cè)試集
import numpy as np
X_train, X_test, y_train, y_test = X[:60000], X[60000:], y[:60000], y[60000:]
# 打亂數(shù)據(jù)集,可以保證交叉驗(yàn)證的每一折都是相似的
shuffle_index = np.random.permutation(60000)
X_train, y_train = X_train[shuffle_index], y_train[shuffle_index]
# 訓(xùn)練一個(gè)二分類器,識(shí)別數(shù)字 5
y_train_5 = (y_train == 5) # True for all 5s, False for all other digits.
y_test_5 = (y_test == 5)
# 挑選一個(gè)分類器去訓(xùn)練它,隨機(jī)梯度下降分類器 SGD 能高效地處理非常大的數(shù)據(jù)集,一次只處理一條數(shù)據(jù),適合在線學(xué)習(xí)
from sklearn.linear_model import SGDClassifier
sgd_clf = SGDClassifier(random_state=42) # 數(shù)據(jù)集隨機(jī)程度
sgd_clf.fit(X_train, y_train_5)
# 預(yù)測(cè)圖片是否為數(shù)字 5
sgd_clf.predict([some_digit])
# 預(yù)測(cè)結(jié)果:真正例、假正例、真反例、假反例
# 對(duì)分類器進(jìn)行評(píng)估
# 1、K 折交叉驗(yàn)證:將訓(xùn)練集分成 K 折,然后用模型對(duì)其中一折進(jìn)行預(yù)測(cè),對(duì)其他折進(jìn)行訓(xùn)練
from sklearn.model_selection import cross_val_score
cross_val_score(sgd_clf, X_train, y_train_5, cv=3, scoring="accuracy") # 輸出精度
# 精度通常來說不是一個(gè)好的性能度量指標(biāo),此例中只有 10% 的圖片是數(shù)字 5,所以總是猜測(cè)某張圖片不是 5也會(huì)有 90% 的可能性是對(duì)的
# 2、混淆矩陣:輸出類別 A 被分類成類別 B 的次數(shù)
# 例如:為了知道分類器將 5 誤分為 3 的次數(shù),需要查看混淆矩陣的第五行第三列
# 為了計(jì)算混淆矩陣,首先你需要有一系列的預(yù)測(cè)值,這樣才能將預(yù)測(cè)值與真實(shí)值做比較
from sklearn.model_selection import cross_val_predict
from sklearn.metrics import confusion_matrix
y_train_pred = cross_val_predict(sgd_clf, X_train, y_train_5, cv=3) # 基于每一個(gè)測(cè)試折做出一個(gè)預(yù)測(cè)值
confusion_matrix(y_train_5, y_train_pred) # 得到一個(gè)混淆矩陣
# 3、準(zhǔn)確率/召回率
# 準(zhǔn)確率=真正例/(真正例+假正例)
# 準(zhǔn)確率(查準(zhǔn)):當(dāng)機(jī)器聲稱事件發(fā)生時(shí),事件真正發(fā)生的比例(虛警=0,準(zhǔn)確率=1)
# 召回率=真正例/(真正例+假反例)
# 召回率(查全):當(dāng)事件真正發(fā)生時(shí),機(jī)器成功預(yù)測(cè)它發(fā)生的比例(漏報(bào)=0,召回率=1)
# 醫(yī)院檢測(cè)疾病的儀器一般都把靈敏度閾值a設(shè)的很低:寧愿多虛警也要少漏報(bào)
# 死刑復(fù)核一般都把a(bǔ)值設(shè)的很高:寧愿多漏報(bào)也要少虛警
# F值 = 正確率 * 召回率 * 2 / (正確率 + 召回率),是準(zhǔn)確率和召回率的調(diào)和平均。
# 普通的平均值平等地看待所有的值,而調(diào)和平均會(huì)給小的值更大的權(quán)重。
from sklearn.metrics import precision_score, recall_score, f1_score
precision_score(y_train_5, y_pred) # == 4344 / (4344 + 1307)
recall_score(y_train_5, y_train_pred) # == 4344 / (4344 + 1077)
f1_score(y_train_5, y_pred)
p86
四、訓(xùn)練模型
五、支持向量機(jī)
六、決策樹
決策樹:一種多功能機(jī)器學(xué)習(xí)算法,既可以執(zhí)行分類任務(wù)也可以執(zhí)行回歸任務(wù),甚至包括多輸出(multioutput)任務(wù),是一種功能很強(qiáng)大的算法,可以對(duì)很復(fù)雜的數(shù)據(jù)集進(jìn)行擬合。
決策樹也是隨機(jī)森林的基本組成部分,而隨機(jī)森林是當(dāng)今最強(qiáng)大的機(jī)器學(xué)習(xí)算法之一。
決策樹的訓(xùn)練和可視化
# 在鳶尾花數(shù)據(jù)集上進(jìn)行一個(gè)決策樹分類器的訓(xùn)練
from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier
iris = load_iris()
X = iris.data[:, 2:] # petal length and width
y = iris.target
tree_clf = DecisionTreeClassifier(max_depth=2)
tree_clf.fit(X, y)
七、集成學(xué)習(xí)和隨機(jī)森林
決策樹與隨機(jī)森林
原理
隨機(jī)森林算法參數(shù)解釋
隨機(jī)森林算法參數(shù)解釋及調(diào)優(yōu)