請支持正版圖書, 購買鏈接
下方內容里面很多鏈接需要我們科學上網,請大家自備梯子,實在不會再請留言,節約彼此時間。
源碼在底部,請自行獲取,謝謝!
當開始着手進行一個端到端的機器學習項目,大致需要以下幾個步驟:
- 觀察大局
- 分析業務,確定工作方向與性能指標
- 獲得數據
- 借助框架分析數據
- 機器學習算法的數據准備
- 選擇和訓練模型
- 微調模型
- 展示解決方案
- 啟動、監控和維護系統
接下來,我將對每一個部分自己的心得進行總結。
一、觀察大局
當開始一個真實機器學習項目時,需要針對項目的特點,有針對性進行分析。任何項目都有其最終的目的,總的說來,我覺得首先要考慮的是業務需求,即用這個模型要解決什么事情,需要的程度如何等等的問題。假設項目是一個預測的問題,那么我需要知道的是我上游環節能給我什么樣的數據,下游的環節需要我提供怎樣的數據流,是具體的價格(要精確到何種地步?)?還是一個等級划分?這些都決定了自己在這個項目上所花費的精力以及使用的模型、測量方法等一系列問題,總之要求不一樣,進行的處理就不一樣,這就是大局觀。
二、分析業務,確定工作方向與性能指標
因為我們是要進行機器學習項目,所以應該根據業務對我們的項目要構造的機器學習系統的種類進行划分。從長遠來看,機器學習系統大致有以下幾種:
- 是否在人類監督下訓練:監督式學習、無監督學習、半監督學習與強化學習
- 是否可以動態地進行增量學習:在線學習和批量學習
- 是簡單地將新的數據點和已知的數據點進行匹配,還是對訓練的數據進行有針對性的模型預測:基於實例的學習與基於模型的學習
這里,以文中的”房價預測“為例。
1、分析機器學習系統的類別
這里可以先提前給出部分數據,如下圖。讓我們來思考一下,這個預測問題應該划分為什么類別。
我們可以看到,圖中數據有很多屬性,由於我們最終做的是房價預測而數據中你會發現有一列屬性很耀眼”median_house_value“,這個屬性是該地區的房價中位數屬性,那我就明白了一件事,那就是我要預測的結果應該與該地區的房價中位數差不多才是正確的預測結果,所以我的機器學習系統應該是一個有監督的學習系統,是一個多變量回歸的問題。
2、測試指標的選擇
因為已經確定了是一個對變量的回歸問題,因此測試指標就應該對應多變量回歸問題進行。
回歸問題的典型性能衡量指標首選是均方根誤差(RMSE),這里首先給出它的公式供大家參考:
注:其中的m是數據集實例中的實例數量,x是數據集中所有實例的所有特征值的矩陣,h(x)為系統的預測函數,即我們要預測的結果,y為真實值。
上式的意思就是描述預測值與真實值之間的差異,RMSE的值越小,說明預測效果越好,預測值與真實值的偏差越小。
(這里附一個鏈接,關於講解均方根誤差、標准差、方差之間的關系。總的說來,標准差是數據序列與均值的關系,而均方根誤差是數據序列與真實值之間的關系。因此,標准差是用來衡量一組數自身的離散程度,而均方根誤差是用來衡量觀測值同真值之間的偏差,它們的研究對象和研究目的不同,但是計算過程類似)
如果獲得的數據中存在許多的離群區域,我們可以考慮用平均絕對誤差(MAE),這里給出公式:
這個公式可以很好的解決偏離抵消的情況,真實反映出預測值與真實值的偏差,當然MAE的值越小越好。
上述兩個公式相比,我們可以發現,RMSE對異常值更加敏感,因為當出現一個較大的偏差的異常值時,會極大影響結果的數值,MAE則影響相對小。
所以,RMSE更加適合數據分布為鍾形的曲線(類似正態分布)的情況,而MAE更加適合數據偏離散的情況。
三、獲得數據
我的建議是要獲取真實的數據進行操作。書中列出了很多的開源數據:
- 流行的開放數據存儲庫:
— UC Irvine Machine Learning Repository
- 源門戶站點(他們會列出很多開放的數據庫):
- 其他的一些許多流行的開放數據庫頁面:
— Wikipedia’s list of Machine Learning datasets
這里開放的數據許多是國外數據庫,訪問需要科學上網,如急切需要但不知如何科學上網,請與我聯系(nfuquan AT gmail.com)
以后會更新更多的關於我們中文的數據庫,盡情期待。
四、借助框架分析數據
用Anaconda組件中的Jupyter Notebook說明,編程語言為python3.5。
1、Pandas
具體它的功能就不去介紹了,可以附一個鏈接,自行查看。
這里集中說明幾個函數的功能,方便以后自己能快速查看。
- pandas.DataFrames.head()
這個函數是讀取DataFrames格式數據,默認讀取5個元素,當然可以在參數中傳遞自己想讀取的函數的個數。
- pandas.DataFrames.info()
這個函數是列出DataFrames格式中的索引信息、行信息、非空值信息以及內存使用信息,這個函數可以清楚地告訴我們數據的異常狀態,從而讓我們在數值上進行處理。
- pandas.DataFrames.describe()
這個函數顯示數據的各個屬性摘要,同時針對數值數據進行了數據的一些計算。
- pandas.DataFrames[“屬性”].value_counts()
這個函數是對屬性中的內容進行統計。
- pandas.DataFrames.hist()
首先應該在首行加入%matplotlib inline (該式只能在jupyter notebook中使用,意思是直接編譯顯示結果)。
這個函數是將數據每個屬性制作為一個直方圖。有很多的參數,具體請查看鏈接
上述函數給我們一個極大的便利就是針對數據的具體情況可以進行逐個分析。例如,我們可以看到收入中位數“median_income”最大值為15,這個不符合常識,收入因該至少在五位數才對,因此需要針對該數據對數據提供方進行詢問,經詢問,得知該屬性進行了屬性的縮放。在直方圖中,我們一般應該注意的方面是數據的范圍以及數量、分布,為什么要關注這些呢,因為要結合實際。舉個例子,比如我們發現房屋價格的中位數最大到50萬美元,而且數量很多,而且根據數據的分布,感覺存在異常,如果對數據敏感的話,我們應該有這樣的疑問:是否這個數據是一個限制條件,就是說將這個數據以上的數據全部都設置為50萬,這會導致我們的學習算法永遠不會估計出超過50萬元的數值,這個結果是否可接受,需要考慮。如果要求是可以預測出50萬元以上的房屋價格,我們可以針對50萬以上的房屋價格重新收集數據或者除去50萬元以上的數據,同時允許我們的機器學習算法預測出50萬元以上的結果。直方圖還表現了一個特征是有幾個屬性值分布不均勻,有可能會影響機器學習系統難以預測的某些模式。
書中介紹了一個典型偏誤“數據窺探偏誤”,這里先解釋一下:人腦容易過度匹配,當發現某些數據模式有特征時,可能就會只訓練某些有特征的數據,而這些數據很有可能不代表普遍規則,如果機器學習算法采用這些數據做訓練,就會導致結果可能很好,但是在預測實際內容時,會缺乏泛化能力。我們需要科學的數據划分!
數據集划分:訓練集(訓練+驗證)、測試集
假設數據量較大5000-10000內,可以用隨機選擇的方式,按照訓練80%,測試20%進行分布。假設數據量在100000以上,可以考慮將訓練集的比例增加,測試集比例降低。如果數據很少,則隨機選擇數據划分可能存在問題,要按照一定規則分布的規律中進行抽樣選擇。以房屋價格預測為例,預測的價格與人們的收入中位數屬性密切相關,因此我們應該以收入中位數的屬性特征進行樣本的划分:
上圖是收入中位數數據,我們可以看出,收入在2-4人數比較多,同時看出基本上在收入上是一個連續的分布,不能進行一個分層的抽樣,因此,我們應該將上述數據進行一個划分,分成5類或者6類(這個分類可以自己定)。
操作步驟:1、使用numpy框架的ceil()函數進行取整操作
2、然后使用where()函數,將取整后的類別值大於5的類別統一歸為類別
上述操作我們獲得了一個分類標簽屬性“income_cat”,注意這個標簽只是為了區別各個數據的收入中位數類別,因此這個標簽可以在使用完畢之后進行刪除。
使用sklearn框架中的Stratified-Shuffle Split類可以進行分層抽樣。
上面的語句的意思是首先對需要分層的類別進行一個預設置,然后通過一個for循環按照數據中的“income_cat”屬性中的分層情況進行划分,得出分布一樣訓練集和測試集。具體的類別怎么操作,請查閱鏈接
使用pandas.DataFrame中的drop()可以將函數中的某個屬性進行刪除,因此我們在使用完“income_cat”后在測試集中與訓練集中將該屬性刪除:
至此,我們按照科學的方式對數據進行了划分,使得測試集與訓練集的分布一致,這是一個合理科學的划分方式。
當我們將測試集與訓練集划分好后,測試集不宜改動,我們應該重點關注訓練集,並從中獲取更多的易於構建我們機器學習系統的信息。
訓練集的探索:
首先,創建一個訓練集副本!任何探索都不能改變原始數據集合,應該創建副本,在副本上進行探索!使用copy()函數進行拷貝。
還是回歸到數據集的屬性上,“longitiude”與“latitude”是經緯度,這個數據是不會變的,相比其他的數據來說,這個數據在顯示上更容易,因此我們可以對該數據進行可視化操作。
plot()函數是繪圖函數,具體的介紹,請看鏈接
在上述函數中有個屬性是alpha,改變該值,可以提高顯示圖像的疏密程度,這對於我們的數據探索有幫助,因此:
可以看出,沿海的房屋數據更多,內陸的房屋數據較少些,沿海地區中也存在集中現象。
在此基礎上,我們將房價信息添加到上圖中。
使用jet的一個預定義的顏色表表示房屋價值,每個園的半徑顯示出該地區人口數量:
從這張圖,信息量就大多了。首先,我們可以觀察到,越靠近沿海地區(再解釋一下,該數據是加利福尼亞州的房屋價格數據,因此根據美國地理位置,沿海地區一目了然)房屋價格更高,同時人口的分布大致呈兩條弧線,沿海地區與內陸地區的人口分布在總體上大致保持一致。
因為數據的量不大,我們可以讓計算機對數據的相關性進行一個計算。
使用corr()函數,計算各屬性與房價中位數的關系。
相關性的值,越靠近1說明正相關性越強,越靠近-1說明負相關性越強,越靠近0說明兩者之間沒有相關性。注意,這里corr()計算的是一種線性相關關系,不是非線性相關關系。
從上圖,房價中位數與收入中位數有很強的相關性。
使用Pandas中的scatter_matrix()函數可以繪制各屬性之間的相關性,如果不加限制條件,函數將把所有屬性與其他屬性的相關關系全部計算。我們的數據中存在11個屬性,因此如果不加限制條件,會繪制11*11=121個圖形。選取與房價中位數直覺上最相關的屬性計算,這里選取“median_income”、“total_rooms”、“housing_median_age”三個屬性計算相關性:
這張圖的對角線需要說明一下,因為相同的屬性的相關性肯定是1,因此顯示直方圖。
我們重點關注一下“income_value”與“house_median_value”的關系:
從圖中可以看出,在50萬有一條橫線,在35萬、45萬處好像也存在直線,我們在繼續的數據處理中應該對這些點進行去除,防止系統重現這些怪異數據。
除了上述針對原屬性的介紹,我們其實也可以針對屬性進行組合,形成新屬性。
我們從之前給出的屬性,我們可以進行分析,這里再給出我們的屬性:
在屬性中,我們能觀察到“total_bedrooms”、“total_rooms”、“population”、“households”可能存在一定聯系,那么是否可以對這幾個屬性進行一個數據融合形成一個新屬性呢?當然可以,並且有助於深刻探索數據。書中針對該方面新創新了3個屬性:
上述屬性含義分別是:平均每房屋的房間數、平均房間中的卧室數、平均人口持有房屋數
我們可以通過計算相關性,看看新屬性對“house_median_value”的關系:
“bedrooms_per_room”與房屋中位數屬性相關度比其他大部分屬性高,說明新屬性的創造是有效的。
五、機器學習算法的數據准備
針對給機器學習算法的數據准備,我認為有以下幾個方面:
- 不完全數據的合理補全
- 非數值屬性的變換
- 新增新屬性,刪除相關性差的屬性
- 數據數值的合理化(歸一化、均值化等)
從技術的角度,我們應該采用一套自動化的數據轉換,而不是每次都手動轉換。原因有以下幾點:
- 可以在任意數據集上重現轉換(比如:獲得更新的數據庫之后)
- 逐漸建立起一個轉換函數的函數庫,可重用
- 在實時系統中使用這些函數來轉換新數據再給算法
- 可嘗試多種轉換系統,查看哪個轉換組合最佳
首先,同樣創建一個去除預測值的訓練集(X_Train),將預測值傳入新的list中(Y_train)。
准備工作完畢后,正式開始:
不完全數據的合理補全:
從之前的info()函數,我們已經得出有些數據是不全的,比如“total_bedrooms”。有三種辦法對該屬性值進行處理:
- 放棄缺失的地區
- 放棄該屬性
- 將缺失值設置為某一個合理值
由於這個屬性缺失數量比較小,因此打算采用第三種方式進行。
sklearn中的Imputer類可以處理該問題,首先應創建該類實例。由於這個類處理時需要純數值的數據,因此我們要預先將非數值的數據進行刪除后再做處理:
Imputer.fit()是計算各屬性的策略值,並保存在imputer.statistics_中。這樣做的好處就是我們不知道未來的數據中哪個部分存在缺失,我們可以針對這些缺失做出處理。
然后開始替換:
這個imputer實例將把housing_num中的缺失值替換為之前計算好的數值。轉換后的X是一個numpy框架定義的數組,如果想轉換為pandas的DataFrames,可以進行如下操作:
文本數據的處理:
“ocean_proximity”是一個文本屬性,要變為數值屬性,算法才能更好的工作。
sklearn中的LabelEncoder是實現這個功能的類。
廢話不多說,直接上代碼,馬上就懂:
從結果可以發現,這個函數的功能是將文本集合映射為數字的過程。這個映射為:
內容和序號實現了映射,0對應‘1H OCEAN’,以此類推。
這里,我們需要考慮一個問題:映射的方式是沒有錯的,但是畢竟是數值,數值就存在大小,從數值上說1就是比4小,因此這個差異可能會導致學習算法的精准度,因此采用one-hot編碼更好些!如果不曉得,請點此鏈接查詢。
sklearn類提供了一個OneHotEncoder類來提供此服務。
先上代碼,然后解釋:
reshape()函數是將numpy數組的形狀進行轉換,這里的-1需要解釋一下:一個參數為-1時,那么reshape函數會根據另一個參數的維度計算出數組的另外一個shape屬性值。
fit_transform()函數是將fit()函數和transform()函數結合的函數,它的作用是先將數據做規則的操作,然后再對數據轉換為標准形式。
housing_cat_1hot將會轉變為一個scipy的矩陣,是一個稀疏的矩陣。當然如果需要轉換為numpy類型的矩陣,只需要做如下操作:
toarray()函數可以轉為numpy類型的數組。
LabelBinarizer類可以將上述兩個步驟合並,同時通過傳送sparse_output=True給LabelBinarizer類的構造函數可以獲得稀疏矩陣。
自定義轉換器:
這個步驟的意義在於,使用框架中的方法總不能很好的契合我們的實際需要,我們可以利用框架中的方法,通過自己定義轉換器,將框架中的方法最有效的利用到我們的轉換器中,這樣可以節省很多時間!
先上代碼,再分析:
我們首先要明白一個概念,然后再來解釋為什么要這樣做,這個概念是鴨子類型,不懂的小伙伴請點擊鏈接看一下含義。
這個類是組合屬性添加類,參數是基本估計器和轉換估計器,這里給個鏈接,需要的朋友們去鏈接里面看一下這兩個東西是啥。首先我們知道數據屬性的順序,因此可以直接預定義需要查找到每個屬性的索引序號,room_ix,bedroom_ix等都是其索引序號。然后是__init__()函數,這個是構造函數,用來初始化某個需要的屬性標識,默認為Ture(也就是默認是要添加的!)。定義fit()函數,將參數self、X(外部傳遞的參數)傳遞給它,同時返回的self是一個具有fit()函數處理過的結果。設置transform()函數,同樣將參數傳遞給它。這個函數就會根據我們在構造函數中傳入的參數add_bedrooms_per_room的true或者False來判斷是否需要添加這個屬性,如果不添加,我們就只會添加rooms_per_household與population_per_household兩個屬性。np.c_函數是按行連接兩個矩陣,就是把兩矩陣左右相加,要求行數相等。至此,類的設置就完畢了。
CombineAttributesAdder()這個就是初始化我們的類,並且給這個類提供參數,如果不填則默認需要添加add_bedrooms_per_room這個屬性。最后,使用類中transform()函數,將屬性添加進來得到一個新的添加了新屬性的矩陣。
特征縮放:
如果數據大小存在非常明顯的差異,那么有可能會導致機器學習算法性能不佳。同比例的縮放所有屬性有兩種常用的方法:最大最小縮放和標准化。
- 最大最小縮放
將值重新縮放使其最終范圍歸於0到1之間,實現方法是將值減去最小值並除以最大值和最小值的差。我們可以用sklearn中的MinMaxScaler的轉換器,當然不想范圍是0-1可以通過傳遞超參數feature_range進行更改。
- 標准化
首先減去平均值,除以方差,使得結果的分布具有單位方差。可以使用standadScaler()函數。
標准化並不會將結果綁定到一個范圍中,有可能不適合某些對輸入數據有要求的處理,但是它相比上一個縮放方式,受到異常值的影響更小,比如假設某地區平均收入是1000(這是一個錯誤數據),縮放就會將所有數據從0-15降到0-0.015,影響較大,但如果是標准化,異常值不會干擾其他正常數據。
具體使用見下面部分!
轉換流水線:
sklearn有一個很好的思想就是將轉換操作工廠流水線化,它有一個非常nice的類,叫做Pipeline類,這個類可以幫我們實現對數據集處理的自動化操作!
Pipeline類的構造函數會通過一系列的名稱/估算器的配對來定義步驟序列。先上代碼:
這一步可能會有很多童鞋報錯,有的會說缺少sklearn_features的包,有的會報參數個數的錯誤,這里副個鏈接,有問題請點擊這個鏈接。
除了導入了需要的包之外,我們首先看到定義了兩個參數,一個是list類型的數值文本屬性名稱(),一個是類別參數,因為這里只有“ocean_proximity”這個屬性是文本屬性,因此我們只將這個屬性賦值給cat_attribs中。
首先,要先自定義一下這個DataFrameSelector,先給出代碼:
這里給出的就是按照參數給出的數值屬性進行轉換。
接下來開始定義Pipeline,我們只需要在構造方法中傳入我們需要的順序即可。比如num_pipeline,在構造函數中傳入我們需要執行的函數DataFrameSelector()、Imputer()、CombineAttributesAdders()、StandardScaler(),同樣方法可對其他的流水線進行了定義。
每一條子流水線都是從選擇器轉換器開始,然后挑出所需屬性(數值或者分類),生成的DataFrame再轉換為numpy數組就可以進行訓練了。
至此,針對算法的數據准備基本到此告一段落。
六、選擇和訓練模型
在這個階段,應該做的就是選擇一個合適的模型,訓練該模型,並評估預測的質量了。
首先可以嘗試選擇一個線性回歸模型。
代碼怎么用,這里不解釋了,看代碼就知道了。這里通過fit()函數后,lin_reg就是我們通過訓練集得出的第一個訓練模型,接下來可以通過predict來進行預測。我們可以通過訓練集的一些數據進行:
我們從總數據中選出五個數據進行測試,雖然測試結果不盡如人意,但是系統可以工作了,這個還是很讓人興奮的!
那么,怎么評估算法的性能呢?我們使用RMSE。
從之前的數據中,大多地區的房價中位數在12萬美元到26萬美元之間,我們的預測基本與之相差6萬8的誤差,說明我們的系統對數據擬合存在嚴重不足。當這種情況發生時,可能是由於特征信息不能提供足夠的信息讓我們做出更好的預測還有就是算法不夠強大。因此到了這一步,你就有兩種選擇,一個是需要更多新屬性來支持算法,還有一個就是選擇更強大的模型或者減少模型約束(當然,我們選擇的線性模型沒有什么約束,談不上這一點…)。
我們用更強大的算法試試!
用決策樹模型,試試效果:
我擦,RMSE最后的結果竟然為0?!完美的算法?of course not! 很大程度上,不存在完美的算法,應該是數據嚴重過擬合了。。。
用K折交叉驗證的方式,能夠還原算法的本來面貌。
交叉驗證,說白了就是將訓練集科學分成K份,每次選擇1份做驗證集,其他為訓練集,分別訓練K次,選出性能最好的模型作為最后的模型:
這里從cross_val_score的參數中可以發現,第一個參數是模型,第二個參數是訓練集,第三個參數是標簽,第四個參數是選擇打分類型,第五個參數是選擇那個K,最后得出的score就是一個K大小的數組,每個里面放着一次的得分。
從結果上看,平均得分在69549,上下浮動2000左右。
我們再看看線性回歸模型:
線性回歸跑分是69088,比決策樹稍差。
我們最后再看看隨機森林模型。
隨機森林模型顯然不如決策樹模型。
我們嘗試了很多種不同的模型,原因是在這個階段我們需要嘗試不同模型,選出其中有潛力的模型,千萬不要花太多時間去調整每個模型的超參數,確定要使用的模型才是這個階段最關鍵的!
至於模型的過擬合問題或者其他什么問題,我們等確定模型后再調試即可。
每個模型都應該妥善的保存,這里介紹一個python中的pickel模塊或者sklearn.externals的joblib模塊:
七、微調模型
假設,你現在已經確定了好幾個有潛力的模型,那么這時候需要微調它們了。
微調的一個方法是對模型的超參數進行調整,這個過程很耗時,你需要科學的工具幫助你。使用sklearn中的GridSearchCV來幫助你。
這里面param_grid首先評估第一個dict中的n_estimator和max_features的所有可能的組合(3*4=12),然后嘗試第二個dict中的參數組合(2*3=6)。最后需要嘗試6+12=18種組合,並對每個模型進行5次訓練。當運行完畢后,可獲得最佳參數組合:
同時,我可以得到最好的估算器:
評估分數也可以得出:
對了,有些數據准備也可以當作超參數來處理,比如之前的自定義轉換器中是否添加“add_bedrooms_per_room”。
當然若超參數數量非常大,一般選擇RandomizedSearchCV,這個函數會在每次迭代中為每個超參數隨機選擇一個值,對一定數量的迭代進行評估,這里不詳細介紹了,具體請Google。
檢查最佳模型,我們還可以得出每個屬性的相對重要程度:
說明一下最下面那個array[]是之前的ocean_proximity屬性。
好了,我們微調過后,通過測試集評估系統吧。
這個結果比之前就好多了!
八、展示解決方案
這里可以通過制作PPT等方式,強調系統學習到了什么、什么屬性是有用的、算法基於了什么假設、系統目前存在的限制有什么,用清晰可視化的方式呈現!
九、啟動、監控和維護系統
這里就是即將讓項目進入實戰的環節,這時候應該為生產環境做准備,特別是將生產數據接入系統,當然為了防止出錯,我們必須要進行代碼測試!
監控代碼也要編寫,這是為了定期檢查系統的實時性能,在性能下降后觸發警報!
任何模型幾乎都經不起時間的演化,隨着時間的演化,適合現階段的參數可能不是該模型存在的參數,模型會漸漸“腐化”,所以我們應該定期使用新的數據訓練模型,讓模型保持年輕。同時做好模型備份,防止最新迭代的模型出現錯誤好馬上回滾。
還要注意的是,模型的預測結果需要專家進行分析,確保環節安全。
最后要注意的是要監控輸入系統的數據的質量,及時查找出質量較差的數據,防止污染系統。
好啦,我的心得在這里就算總結完畢了。這個總結對我來說,我又重新認識了一遍項目的流程,這個對我收益很大,以后要多多堅持自己的總結,加油呀!
圖中代碼可通過以下方式獲得:
1、我的Github:https://github.com/niufuquan1/MyStudy_For_sklearn_tensorflow
2、百度雲分享:https://pan.baidu.com/s/1xjBGBFbM7hqp-tUO3oNEtA (sio0)
轉載請注明出處,謝謝!
有任何問題,請在下方留言,博主看到會進行回復。