本章通過一個例子,介紹機器學習的整個流程。
2.1 使用真實數據集練手(Working with Real Data)
國外一些獲取數據的網站:
- Popular open data repositories:
- Meta portals (they list open data repositories):
- Other pages listing many popular open data repositories:
國內的一些數據源:
- 深圳市:http://opendata.sz.gov.cn/
- 貴州省:http://www.gzdata.gov.cn/
- 北京市:http://www.bjdata.gov.cn/
- 上海市:http://www.datashanghai.gov.cn/
- 青島市:http://data.qingdao.gov.cn/
- 廣州市:http://www.datagz.gov.cn/
本章選擇了加州房價數據集,代碼可以從https://github.com/ageron/handson-ml獲取。
2.2 分析整體情況(Look at the Big Picture)
我們的目的是使用加州的人口普查數據,建立模型,預測加州各區域的房價中位數。
訓練數據的特征包括加州各區域的人口、收入中位數、房價中位數等。
2.2.1 問題構建(Frame the Problem,提出問題,給出框架,提出假設)
首先問清楚老板的商業目的,以及當前的解決方案(如果有的話)。比如,了解到了當前方案的錯誤率大概15%,我們就要奮斗目標了。
接下來就可以分析,這是一個有監督學習、無監督學習、還是增強學習?分類任務還是回歸任務,或者別的什么?應該使用批量學習(batch learning)還是在線學習(online learning)?
如果數據量巨大,可以使用MapReduce技術,將數據分給多個服務器處理。也可是使用在線學習。
2.2.2 選擇性能衡量指標(Select a Performance Measure)
回歸問題典型的衡量指標選擇均方根誤差(Root Mean Square Error,RMSE),它揭示了預測值的標准偏差(standard deviation)。
\begin{align*}
RMSE(X,h) = \sqrt{\frac{1}{m}\sum_{i=1}^{m}(h(X^{(i)}) - y^{(i)})^2}
\end{align*}
有時候樣本中存在很多離群點(outlier) ,我們可能就會使用絕對誤差(Mean Absolute Error,MAE)。
\begin{align*}
RMSE(X,h) = \frac{1}{m}\sum_{i=1}^{m}\left | h(x^{(i)}) - y^{(i)} \right |
\end{align*}
RMSE和MAE都是向量距離的度量方式(預測值向量和目標值向量)。向量的距離,也可以為稱為向量的模(norm),有以下性質:
- RMSE對應於歐氏距離,也被稱作$l_2$距離,記做$\left \| \cdot \right \|_2$(或$\left \| \cdot \right \|$)。
- MAE對應於$l_1$距離,記做$\left \| \cdot \right \|_1$。
- 一般的,具有$n$的元素的向量$v$,$l_k$距離定義為$\left \| v \right \|_k = (\left | v_1 \right |^k + \left | v_2 \right |^k + \cdots + \left | v_n \right |^k)^{\frac{1}{k}}$。$l_0$僅僅給出向量的基數(比如非零元素的個數),$l_\infty$給出向量中最大元素的絕對值。
- 指數k最大,向量中最大元素的貢獻就越大。這就是為什么相對於MAE,RMSE對離群點更敏感。但如果誤差是指數級稀少(exponentially rare)的,例如鍾形曲線(bell-shaped curve),RMSE的表現很好,也是通常的選擇。
2.2.3 檢查假設(Check the Assumptions)
我們最好跟同事確認一下假設。例如,我們的房價預測值是給下游系統使用的。如果下游系統要把價格轉換為類別(例如高、中、低),那么我們的問題就成了分類。
2.3 獲取數據(Get the Data)
下面就開始動手操作了,代碼位於https://github.com/ageron/handson-ml。
2.3.1 創建工作空間(Create the Workspace)
安裝Python、安裝Jupyter Notebook
2.3.2 下載數據
fetch_housing_data函數負責,代碼里面有。
2.3.3 瀏覽數據(Take a Quick Look at the Data Structure)
介紹了pandas DataFrame里面的一些函數:head()、info()、describe()、value_counts()(針對類別屬性,列出各個類別,以及類別數量)。
Matplotlib里面的hist()函數繪制直方圖。

我們可以從直方圖中發現以下幾點:
- 收入中位數(median income)並不是以美元為單位的,而是經過了預處理。最高一致被設置為了大概15(高於某一值的數據,都被設置為了15,即使它可能是25),最低值被設置為0.5(低於某一值的都被設置為0.5)。這在機器學習中很常見,也沒什么問題。
- 房齡中位數(housing median age)和房價中位數(median house value)也被處理過了。例如后者,房價高於500,000的,都被設置為500,000,即使真實房價800,000。這就是為什么它們所對應的直方圖,最右列突然增高。但由於房價中位數是我們的目標屬性,如果我們需要預測真實的房價,可能會高於500,000,這就存在問題了。那么有兩種主要的解決方案:a、收集真實的房價。b、丟掉房價高於500,000的樣本。
- 這些屬性具有不同的取值范圍。這將在下文探索特征縮放是進行討論。
- 本多直方圖呈現重尾分布(tail heavy):左側距離中位數要遠於右側。這不利於一些機器學習算法進行模式識別(tail heavy)。后面將進行轉換,使這些屬性更符合鍾形分布(bell-shaped distributions)。
2.3.4 創建測試集(Create a Test Set)
在進一步分析數據之前, 應該創建一個測試集,並將其丟在一邊,不去分析。
這是為了防止過擬合,防止數據探測法偏見(data snooping bias)。
只需要隨機選擇20%的數據作為測試集即可。
具體怎么隨機選擇,作者介紹了很多實踐經驗比如可以用如下代碼實現:
from sklearn.model_selection import train_test_split # random_state = 42 to make this notebook's output identical at every run train_set, test_set = train_test_split(housing, test_size=0.2, random_state=42)
然后作者又介紹了為什么要分層抽樣(stratified sampling)。
2.4 數據的探索和可視化(Discover and Visualize the Data to Gain Insights)
這是對數據進一步的探索,寫一步不能考慮測試集,只分析訓練集。
2.4.1 地理數據可視化(Visualizing Geographical Data)
使用散點圖,分析了地理位置、人口、人均收入跟房價的關系。
2.4.2 相關性探索(Looking for Correlations)
計算相關系數(standard correlation coefficient)矩陣,分析其它屬性跟房價的相關系數。
相關系數取值-1到1,解決1時說明是正相關,接近-1時說明是負相關。接近0說明非線性相關。下圖顯示了一些標准數據集的相關系數:

相關系數只能度量線性相關(例如:“如果$x$增長,$y$通常增長/減少。”),這會完全地錯過非線性相關(例如:“如果$x$趨近於0,$y$通常增長。”)。例如上圖的第三行,這些數據相關系數為0,但它們顯然不獨立,存在非線性關系。
2.4.3 嘗試屬性組合(Experimenting with Attribute Combinations)
例如可以增加以下三個屬性:
housing["rooms_per_household"] = housing["total_rooms"]/housing["households"] housing["bedrooms_per_room"] = housing["total_bedrooms"]/housing["total_rooms"] housing["population_per_household"]=housing["population"]/housing["households"]
然后可以計算它們跟房價均值的相關系數。
2.5 為機器學習算法准備數據(Prepare the Data for Machine Learning Algorithms)
2.5.1 數據清洗(Data Cleaning)
大部分機器學習算法都無法處理存在控制的屬性。前面已經注意到total_bedrooms存在缺失值。修復這一問題有三個選擇:
- 去掉相應的樣本
- 去掉這一屬性
- 使用其它值對缺失值進行填充(0、均值、中位數等等)
這可以通過DataFrame的dropna(), drop(), and fillna()方法實現:
housing.dropna(subset=["total_bedrooms"]) # option 1
housing.drop("total_bedrooms", axis=1) # option 2
median = housing["total_bedrooms"].median()
housing["total_bedrooms"].fillna(median) # option 3
作者也介紹了怎么通過Scikit-Learn的Imputer實現這一目的。
2.5.2 處理文本和類型屬性(Handling Text and Categorical Attributes)
ocean_proximity是一個無法計算中位數的文本屬性。而大部分機器學習算法智能處理數字,這就需要將文本轉為數字。
可以使用Scikit-Learn的LabelEncoder:
>>> from sklearn.preprocessing import LabelEncoder >>> encoder = LabelEncoder() >>> housing_cat = housing["ocean_proximity"] >>> housing_cat_encoded = encoder.fit_transform(housing_cat) >>> housing_cat_encoded array([1, 1, 4, ..., 1, 0, 3])
這將['<1H OCEAN' 'INLAND' 'ISLAND' 'NEAR BAY' 'NEAR OCEAN']五個文本值編碼為了0-4。但這樣編碼存在一個問題:ML算法會假設,靠近的兩個數字比間隔遠的兩個數字更相似。未解決這一問題,可以采用one-hot編碼。
2.5.3 自定義轉換函數(Custom Transformers)
這一小節作者講述了如何自定義Scikit-Learn的轉換器(transformers)。
2.5.4 特征歸一化(Feature Scaling)
最大最小歸一化(min-max scaling)和標准化(Standardization)兩個方法。前者比后者更容易收到離群點的影響。
2.5.5 轉換流水線(Transformation Pipelines)
sklearn提供了一個類Pipeline,使得上述步驟可以進行流水操作。
2.6 選擇模型並訓練(Select and Train a Model)
作者選擇了線性回歸、決策樹、隨機森林三個模型。又介紹了交叉驗證。
2.7 模型調整(Fine-Tune Your Model)
2.7.1 網格搜索(Grid Search)
介紹了如何使用Scikit-Learn’s GridSearchCV進行網格搜索,選取最優超參數。
另外,需要注意的是,我們可以把數據預處理階段的一些操作視為超參數,並使用網格搜索找到最優方案。例如作者定義的CombinedAttributesAdder函數,有一個超參數add_bedrooms_per_room。相似的,使用網格搜索,可以自動尋找處理離群點、缺失值、特征選擇等等問題的最優解決方案。
2.7.2 隨機搜索(Randomized Search)
如果超參數的組合太少,隨機搜索是網格搜索的一個不錯的替代方案,也就是RandomizedSearchCV。與GridSearchCV搜索所有可能的參數組合不同,RandomizedSearchCV每次迭代的超參數都隨機選取。這有兩個好處:
- 如果使用隨機搜索迭代1000次,這將對每個超參數探索1000個不同值(網格搜索對每個超參數只會探索很少的幾個值)。
- 通過設置迭代次數,就可以方便地控制運算量。
2.7.3 模型融合(Ensemble Methods)
三個臭皮匠頂個諸葛亮,隨機森林也好過單獨的決策樹。由於不同的模型錯誤類型也可能不同,所有我們可以訓練多個模型,將預測結果進行融合。細節將在第7章介紹。
2.7.4 分析最優模型與其錯誤(Analyze the Best Models and Their Errors)
訓練好模型后,可以查到每個屬性的重要性,這就可以去掉一些不重要,與目標值不相關的屬性。
2.7.5 在測試集上進行評估(Evaluate Your System on the Test Set)
2.8 系統上線、監控、維護(Launch, Monitor, and Maintain Your System)
上線之后,需要定期檢查系統表現, 以及在崩潰是可以引發警報。不僅要檢測到突發情況,還有檢測到系統退化。因為隨着時間的推移,系統退化是很常見側,除非模型經常被最新的數據重新訓練。
系統表現評估,可以對系統的預測隨機采樣並進行評估。一般這是需要人工分析的。可能是領域專家,也可能是眾包平台(例如亞馬遜的Mechanical Turk或者CrowdFlower)的工作者。無論如何,都要在系統中增加人工處理流水線。
同時要確保輸入數據的質量。最好在數據輸入時,就監控到異常數據。在線學習上這一點尤其重要。
最后,應該經常使用最新的數據訓練模型,這一操作自動化程度越高越好。
