Scikit-Learn 與 TensorFlow 機器學習實用指南學習筆記 3 —— 資料獲取與清洗

紅色石頭發表於2018-12-04

本章將完整地介紹一個端對端(End-to-End)機器學習專案。假如你是某個房地產公司剛僱傭的資料科學家,你所要做的事情主要分成以下幾個步驟:

1.整體規劃。

2.獲取資料。

3.發現、視覺化資料,增加直觀印象。

4.為機器學習準備資料。

5.選擇模型並進行訓練。

6.除錯模型。

7.給出解決方案。

8.部署、監控、維護系統

1. 使用真實資料

學習機器學習時,最好使用真實資料,而不是“人造”資料。幸運的是,有許多開源的資料集可以免費使用,涉及許多行業領域。下面列舉一些:

這一章我們將使用來自 StatLib 倉庫的 California 房屋價格資料集(如下圖所示)。這份資料集來自 1990 年的普查統計。這份資料集雖然年代有點久了,但不妨礙我們使用。我們已經對該資料集進行了一些處理,便於學習。

2. 整體規劃

歡迎來到機器學習房地產公司!你的第一個任務就是根據 California 普查資料來建立一個房價預測模型。這份普查資料包含了 California 每個地區的人口、收入中位數、房價中位數等資訊,每個地區人口大約 600 到 3,000 人。

你的模型應該對這些資料進行學習,然後根據提供的其它資訊,預測任意地區的房價中位數。

2.1 劃定問題

首先第一個問題就是問你的老闆商業目標是什麼,構建一個模型可能不是最終的目標。公司期望如何使用這個模型並從中獲利?這很重要,因為它決定了你如何劃定問題,選擇什麼演算法,使用什麼效能測量方式來評估模型,以及在除錯模型上花費多大的力氣。

你的老闆回答說你的模型輸出(預測地區房價中位數)將連同許多其它訊號傳輸到另外一個機器學習系統(如下圖所示)。這個下游系統將決定是否對該地區投資房地產。得到正確的預測非常重要,因為它直接影響到收益。

管道(pipeline)

資料處理元件的序列叫做資料管道(pipeline)。管道在機器學習系統中很常見,因為有許多資料要處理和轉換。

管道的各個元件是非同步進行的。每個元件都會輸入大量資料並處理,然後將結果傳輸給管道的下一個元件,下一個元件繼續處理並輸出結果,依次進行。每個元件相對獨立,元件之間的介面就是簡單的資料儲存。這讓系統更加簡單且容易掌控(藉助資料流程圖),不同的團隊可以專注於各自的元件。而且,即便是某個元件崩潰了,下游元件仍然能使用之前上游輸出的資料進行正常工作(至少在一段時間內)。這讓整個系統更加健壯。

然而從另一方面來說,如果不能及時發現崩潰的元件,下游元件輸入資料得不到及時更新,整個系統的效能也會下降。

下一個問題就是詢問當前是如何預測房價的,作為你的模型的效能參考。你的老闆回答說當前房價是由專家們進行人工預測的,方法是收集各個地區大量最新資訊(除了房價),然後使用複雜的規則進行估計。這種做法成本高、費時間,而且正確率也不高,錯誤率達到了 15%。

好了,設計系統需要的所有資訊已經準備好了。首先,你需要劃定問題:這是監督式,非監督式,還是增強學習?這是分類任務,迴歸任務,還是其它任務?應該使用批量學習還是線上學習技術?在真正開始之前請先回答這些問題。

回答出來了嗎?我們一起來看一下:這是一個典型的監督式學習任務,因為訓練樣本的標籤是已知的(每個例項都有它的期望輸出,例如各地區的房價中位數)。這也是典型的迴歸問題,因為我們的目標是預測房價。這也是多元迴歸問題,因為系統將使用多個特徵進行預測(例如地區人課、收入中位數等)。在第一章預測居民幸福指數時,只有一個特徵,人均 GDP,是一個單變數回歸問題。最後,因為沒有連續的資料流輸入到系統,資料更新不是很頻繁,而且資料量較小,所佔記憶體不大,因此採用批量學習即可。

如果資料量很大,可以把整個資料集劃分到不同的伺服器上進行訓練(使用 MapReduce 技術,後面將會講到),或者你也可以使用線上學習技術。

2.2 效能指標

下一步就要選擇評估模型的效能指標。迴歸問題典型的效能指標是均方根誤差(Root Mean Square Error, RMSE),即測量系統預測誤差的標準差。例如,RMSE = 50,000 意味著有大約 68% 的預測值與真實值誤差在 $50,000 之內,大約有 95% 的預測值與真實值誤差在 $100,000 之內。計算 RMSE 的公式如下:

RMSE(X,h)=\sqrt{\frac1m\sum_{i=1}^m(h(x^{(i)})-y^{(i)})^2}

符號

這個公式引入了一些常見的機器學習符號:

(a). m 表示樣本個數,如果有 2,000 個樣本例項,則 m=2000

(b). x^{(i)} 表示第 i 個樣本的所有特徵值,y^{(i)} 表示第 i 個樣本的標籤(真實值)。例如第 1 個地區,經度 -118.29°,維度 33.91°,人口 1416,收入中位數 $ 28372,實際房價中位數為 $156400。則有:

(c). X 是包含所有樣本特徵值的矩陣(除了樣本標籤)。每一行是一個例項,第 i 行是 x^{(i)} 的轉置,記為 (x^{(i)})^T。如下所示:

(d). h 是系統預測函式,也稱為假設(hypothesis)。當給出一個例項的特徵向量 x^{(i)} 時,系統預測輸出為 \hat y=h(x^{(i)})。例如第一個地區的預測房價為 158400,即 \hat y^{(1)}=158400,則預測誤差為:\hat y^{(1)}-y^{(1)}=2000

(e). RMSE(X,h) 是損失函式。

除了 RMSE 之外,還有其它效能指標。例如出現某些離群點,這種情況下可以使用平均絕對誤差(Mean Absolute Error, MAE)作為效能指標。公式如下:

MAE(X,h)=\frac1m\sum_{i=1}^m|h(x^{(i)})-y^{(i)}|

RMSE 和 MAE 都是用來測量兩個向量 h(x^{(i)})y^{(i)} 之間的距離,或稱為範數。常見的範數包括:

  • l_2 範數,如 RMSE,計算的是兩個向量的歐式距離,記為 ∥\cdot∥_2∥\cdot∥
  • l_1 範數,如 MAE,計算的是兩個向量的曼哈頓距離,記為 ∥\cdot∥_1

  • 其它範數 l_k。對於 n 維向量 v,它的 l_k 範數為: ∥v∥_k=(|v_0|^k+|v_1|^k+\cdots+|v_n|^k)^{\frac1k}l_0 為漢明範數,計算的是向量的基數(即向量包含的元素個數),l_{\infty} 是切比雪夫範數,表示的是向量中最大的絕對值。

  • 範數指數越大,就越重視絕對值大的值,忽視絕對值小的值。這就是為什麼 RMSE 比 MAE 對離群點更加敏感。但是,如果離群點是呈指數減小的(類似正態分佈曲線),RMSE 通常也會表現得不錯。

2.3 檢查假設

最後,最好列出目前為止做得所有假設並驗證,這能幫助你儘早發現問題。例如你預測房價,然後傳輸到下游機器學習系統。但是,下游機器學習系統實際上把你預測得價格轉換成了不同類別(例如便宜、中等、昂貴),使用這些類別代替實際預測值。這種情況下,準確預測房價並不是特別重要了!你只需要對房價進行類別劃分即可。這樣的話,這就是一個分類問題而不是迴歸問題。這是需要提前弄清楚的,你可不想建立迴歸模型之後才發現事實。

幸運的是,在與下游機器學習系統溝通之後,確認這確實是一個迴歸問題。好了,接下來就開始真正地編寫程式了。

3. 獲取資料

完整的程式碼在 GitHub 上獲取,地址是:https://github.com/ageron/handson-ml。程式碼形式是 Jupyter Notebook。

3.1 建立工作環境

首先你需要安裝 Python,獲取地址:https://www.python.org/

接下來需要建立一個工作空間目錄,在終端輸入以下命令(在提示符 $ 之後):

$ export ML_PATH="$HOME/ml"     # You can change the path if you prefer
$ mkdir -p $ML_PATH

你還需要安裝一些 Python 模組:Jupyter、Numpy、Pandas、Matplotlib 和 Scikit-Learn。如果你已經都安裝好了,請直接跳過本節內容。如果沒有,你可以使用多種方式來安裝這些模組(包括它們的依賴)。你可以使用系統自帶的包管理系統(例如 Ubuntu 上的 apt-get,或 macOS 上的 MacPorts、HomeBrew);也可以安裝 Python 的科學計算環境 Anaconda,使用 Anaconda 的包管理系統;或者直接使用 Python 自帶的包管理系統 pip(自 Python 2.7.9 開始自帶的)。你可以在終端輸入以下命令來檢查 pip 是否安裝:

$ pip3 --version

pip 9.0.1 from […]/lib/python3.5/site-packages (python 3.5)

你應該安裝 pip 的最新版本,至少是 1.4 版本以上的,以支援二進位制模組的安裝(也稱為 wheels)。更新 pip 到最新版本的命令是:

pip3 install --upgrade pip

建立獨立環境

如果你想建立一個獨立的工作環境(強烈推薦!這樣可以使不同專案之間不會出現庫的衝突),輸入以下 pip 命令來安裝 virtualenv:

pip3 install --user --upgrade virtualenv

現在你可以建立一個獨立的 Python 環境了:

$ cd $ML_PATH
$ virtualenv env

每次你想啟用這個獨立環境,只需開啟一個終端輸入以下命令:

$ cd $ML_PATH
$ source env/bin/activate

補充一下,如果程式碼寫完,想關閉當前環境,輸入以下命令:

$ deactivate

一旦環境啟用之後,你使用 pip 安裝的所有包都僅限於該獨立環境中,Python 也只會訪問這些包(如果你想訪問系統其它包,可以在建立環境的時候使用 virtualenv 的 –system-site-packages 選項)。檢視 virtualenv 的文件獲取更多資訊。

現在,你可以使用簡單的 pip 命令來安裝所有需要的模組和它們的依賴了:

$ pip3 install --upgrade jupyter matplotlib numpy pandas scipy scikit-learn

為了檢查是否安裝成功,可以使用以下命令匯入所有模組:

$ python3 -c "import jupyter, matplotlib, numpy, pandas, scipy, sklearn"

沒有錯誤的話,就可以輸入以下命令開啟 Jupyter Notebook 啦!

$ jupyter notebook

然後,一個 Jupyter 伺服器就執行在你的終端了,監聽埠 8888。你可以在瀏覽器中輸入地址:http://localhost:8888/ 來訪問伺服器(通常在伺服器啟動時就自動開啟了)。顯示的目錄即為你建立的當前環境。

現在可以建立 Python notebook 了。點選右上角 “New”,選擇 “Python 3” 即可(如下圖所示)。

這個過程實際上做了三件事:1. 在當前工作空間裡建立一個新的 notebook 未命名檔案:Untitled.ipynb;2. 啟動 Jupyter Python 核來執行這個 notebook;3. 在新欄中開啟這個 notebook。你應該把這個 notebook 重新命名為 Housing.ipynb。

Notebook 包含一個單元格列表。每個單元格可以放入可執行程式碼或者格式化文件。現在,notebook 只有一個空的程式碼單元格,名為 “In [1]”。在該單元格中輸入:print(“Hello world!”),點選執行按鈕(如下圖所示)或按鍵 Shift+Enter,就會把當前單元格內容發給 notebook 的 Python 核心中,執行並返回輸出結果。結果顯示在單元格下面,且會在底部建立一個新的單元格。可以點選選單欄 Help 中的 User Interface Tour,學習更多 jupyter 的基本知識。

3.2 下載資料

本專案需要下載的資料集是壓縮檔案 housing.tgz,解壓後是 housing.csv 檔案,包含所有資料。

你可以在瀏覽器上載資料集,然後使用命令 tar xzf housing.tgz 解壓檔案,提取出 housing.csv 檔案。但是可以寫一個程式來自動下載並解壓。如果資料集有更新,你可以直接執行這個指令碼,免得重複下載。而且,如果要將資料集下載到很多電腦上,使用程式的方法更加簡單。

獲取資料集的函式定義為:

import os
import tarfile
from six.moves import urllib

DOWNLOAD_ROOT = "https://raw.githubusercontent.com/ageron/handson-ml/master/"
HOUSING_PATH = "datasets/housing"
HOUSING_URL = DOWNLOAD_ROOT + HOUSING_PATH + "/housing.tgz"

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()

直接執行函式:

fetch_housing_data()

將會在你的工作空間新建目錄 datasets/housing/。程式會自動下載 housing.tgz 檔案並解壓出 housing.csv 檔案到 datasets/housing/ 目錄下。

下面定義資料匯入函式:

import pandas as pd

def load_housing_data(housing_path=HOUSING_PATH):
    csv_path = os.path.join(housing_path, "housing.csv")
    return pd.read_csv(csv_path)

該函式會返回一個包含所有資料的 Pandas 的 DataFrame 物件。

3.3 快速檢視資料結構

先來看一下資料集的結構,執行以下語句,檢視前 5 行:

housing = load_housing_data()
housing.head()

顯示結果如下:

該資料集中每一行代表一個地區,每個地區包含 10 格特徵屬性,分別是:

  • ongitude
  • latitude

  • housing_median_age

  • total_rooms

  • total_bed rooms

  • population

  • households

  • median_income

  • median_house_value

  • ocean_proximity

使用 info() 方法來檢視資料的整體描述,尤其是包含的行數,每個屬性的型別和非空值的數量。

>>> housing.info()

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 20640 entries, 0 to 20639
Data columns (total 10 columns):
longitude             20640 non-null float64
latitude              20640 non-null float64
housing_median_age    20640 non-null float64
total_rooms           20640 non-null float64
total_bedrooms        20433 non-null float64
population            20640 non-null float64
households            20640 non-null float64
median_income         20640 non-null float64
median_house_value    20640 non-null float64
ocean_proximity       20640 non-null object
dtypes: float64(9), object(1)
memory usage: 1.6+ MB

可以看出資料集中總共有 20640 個例項。對於機器學習來說,資料量不算大,但非常適合入門使用。注意屬性 total_bedrooms 只有 20433 個非空值。意味著有 207 個地區缺少這個特徵值,我們將稍後處理這種情況。

所有屬性都是數值型別,除了 ocean_proximity。ocean_proximity 的型別是一個物件,因此可能是任何型別的 Python 物件,但一旦你從 CSV 檔案中匯入這個資料,那麼它一定是一個文字屬性。之前檢視前 5 行資料時,會發現該屬性都是一樣的,意味著 ocean_proximity 很可能是一個類別屬性。可以通過使用 value_counts() 方法來檢視該屬性有哪些類別,每個類別下有多少個樣本。

>>> housing["ocean_proximity"].value_counts()

<1H OCEAN     9136
INLAND        6551
NEAR OCEAN    2658
NEAR BAY      2290
ISLAND           5
Name: ocean_proximity, dtype: int64

我們再來看以下其它欄位。describe() 方法展示的是數值屬性的總結:

housing.describe()

注意,以上的結果,空值是不計入統計的。其中,count 表示總數,mean 表示均值,std 表示標準差,min 表示最小值,max 表示最大值。

另外一種對資料集有個整體感知的方法就是對每個數值屬性作柱狀圖。柱狀圖展示的是給定數值範圍(橫座標)內所包含的例項總數(縱座標)。你可以一次只畫一個屬性的柱狀圖,也可以對整個資料集使用 hist() 方法,將會對每個數值屬性繪製柱狀圖。例如,從柱狀圖種可以看到有超過 800 個地區的房價中位數在 $500000 左右。

%matplotlib inline # only in a Jupyter notebook
import matplotlib.pyplot as plt
housing.hist(bins=50, figsize=(20,15))
plt.show()

hist() 方法依賴於 Matplotlib(),而 Matplotlib() 又依賴於使用者指定的圖形後端來作圖。因此,在作圖之前你需要指定 Matplotlib 使用的後端,最簡單的做法是使用 Jupyter 的魔術命令 %matplotlib inline。這行命令會使用 Jupyter 自帶的後端並作圖。注意在 Jupyter notebook 種呼叫 show() 不是必須的,因為單元執行時 Jupyter 會自動顯示圖形。

在這些柱狀圖種注意以下幾點:

1.首先,收入中位數屬性看起來並不是用標準的美元值來表徵的。實際上收入中位數是經過了縮放和削頂處理的,削頂就是把大於 15 的都設為 15(實際上是 15.0001),把小於 0.5 的都設為 0.5(實際上是 0.4999)。在機器學習種,對特徵屬性進行預處理很常見。這不一定是個問題,但是你要試著明白資料是如何計算的。

2.房屋年齡中位數和房屋價格中位數也被削頂了。房價削頂可能是一個嚴重的問題,因為它是目標屬性(標籤)。削頂可能會讓機器學習演算法無法預測出界限之外的值。你應該好好檢查一下削頂到底有沒有影響,如果需要精準預測房價中位數,包括是界限之外的值,那麼你有兩種方法:

a. 對削頂的樣本進行重新採集,收集實際數值。

b. 直接在訓練集種丟棄這些削頂的樣本(同時也對測試集這麼做,因為如果房價中位數超過界限,預測結果可能就不好)。

3.這些屬性的量度不同。稍後我們將詳細討論這一問題。

4.最後,許多柱狀圖有很長的尾巴:它們向右的拖尾比向左長得多。這可能會讓一些機器學習演算法檢測模式變得更加困難。我們稍後會對這些屬性進行轉換,讓它們更加接近於正態分佈曲線。

3.4 建立測試集

在這個階段就擱置部分資料可能聽起來比較奇怪。畢竟我們只是對資料有個初步的認識,在決定使用哪種演算法之前應該對資料有更多的瞭解才是。沒錯,但是我們的大腦是個非常神奇的模式檢測系統,它很容易就過擬合:如果檢視了測試集,很容易就發現測試集中一些有趣的模式,致使我們傾向於選擇符合這些模式的機器學習模型。當測量測試集的泛化誤差時,結果往往會很好。但是,部署系統之後會發現模型在實際使用時表現得並不好。這種情況稱為資料窺視偏差(data snooping bias)。

建立測試集理論上很簡單:隨機選擇整個資料集大約 20% 的例項就可以了:

import numpy as np
def split_train_test(data, test_ratio):
    shuffled_indices = np.random.permutation(len(data))
    test_set_size = int(len(data) * test_ratio)
    test_indices = shuffled_indices[:test_set_size]
    train_indices = shuffled_indices[test_set_size:]
    return data.iloc[train_indices], data.iloc[test_indices]

然後直接呼叫該函式:

train_set, test_set = split_train_test(housing, 0.2)
print(len(train_set), "train +", len(test_set), "test")

16512 train + 4128 test

這種方法可行但並不完美!如果再一次執行程式,將會產生一個不同的測試集。多次之後,機器學習演算法幾乎已經遍歷了整個資料集,這恰恰是我們應該避免的。

一種解決辦法是把第一次分割的測試集儲存起來供下次直接使用。另一種辦法是在呼叫 np.random.permutation() 語句之前固定隨機數發生器的種子(例如 np.random.seed(42)),這樣每次產生的測試集都是相同的。

但是這兩種方法在資料集更新的時候都會失效。一種常用的解決方法是使用每個例項的標誌符來決定是否作為測試集(假設識別符號是唯一且不變的)。例如,可以計算每個例項識別符號的雜湊值,只保留雜湊值最後一個位元組,如果該位元組值小於等於 51(256 的 20%),則將該例項作為測試集。這保證了多次執行之後,測試集仍然不變,即時更新了資料集。新的測試集將會是所有新例項的 20%,且絕不會包含之前作為訓練集的例項。下面是這種方法的程式碼實現:

import hashlib

def test_set_check(identifier, test_ratio, hash):
    return hash(np.int64(identifier)).digest()[-1] < 256 * test_ratio

def split_train_test_by_id(data, test_ratio, id_column, hash=hashlib.md5):
    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]

雖然,housing 資料集沒有識別符號這一列,但是最簡單的辦法是使用行索引作為識別符號 ID:

housing_with_id = housing.reset_index() # adds an `index` column
train_set, test_set = split_train_test_by_id(housing_with_id, 0.2, "index")

如果使用行索引作為唯一識別符號,需要確保新的資料必須放置在原來資料集的後面,不能刪除行。如果做不到的話,可以使用一個最穩定的特徵作為識別符號。例如,一個地區的經度和維度一定是唯一且百萬年不變的,因此可以結合這兩個特徵來作為唯一識別符號:

housing_with_id["id"] = housing["longitude"] * 1000 + housing["latitude"]
train_set, test_set = split_train_test_by_id(housing_with_id, 0.2, "id")

Scikit-Learn 提供了一些劃分資料集的函式,最簡單的函式就是 train_test_split。該函式與之前定義的 split_train_test 基本一樣,只是增加了一些額外功能。第一,引數 random_state 可以固定隨機種子,效果跟之前介紹的一樣。第二,可以對多個行數相同的資料集進行同樣索引的劃分(這非常有用,例如輸入標籤在另外一個 DataFrame 中)。

from sklearn.model_selection import train_test_split
train_set, test_set = train_test_split(housing, test_size=0.2, random_state=42)

目前為止我們已經考慮了純隨機取樣方法。當資料量足夠大(特別是相對於特徵屬性個數)時,這種方法通常時可以的。但是如果資料量不夠多,就會有采樣偏差的風險。當一個調查公司想要諮詢 1000 個人,詢問他們一些問題時,他們的挑人的方法不是隨機抽樣,而是希望這 1000 個人對整個人口具有代表性。例如,美國人口中,女性佔 51.3%,男性佔 48.7%。因此,一個比較好的調查方式就是讓抽樣樣本保持這樣的性別比例:513 名女性,487 名男性。這種做法稱為分層抽樣(stratified sampling):將總人口分成均勻的子分組,稱為分層,從每個分層取樣合適數量的例項,以保證測試集對總人口具有代表性。如果取樣隨機抽樣,有 12% 的可能造成取樣偏差:女性人數低於 49% 或高於 54%,調查結果可能就會出錯。

假如專家告訴你收入中位數是預測房價中位數非常重要的屬性之一。你希望確保測試集能夠涵蓋整個資料集中所有的收入類別。因為收入中位數是連續數值,你首先需要建立收入類別屬性。讓我們更仔細地看一下收入中位數柱狀圖(經過處理)。

顯然,大部分收入中位數都在 2-5(萬美元) 之間,某些在 6 以上。資料集中每個分層都必須有足夠多數量的例項,否則對某分層重要性的估計可能出現偏差。這就意味著不能有太多分層,每個分層應該有足夠多的例項。下面的程式碼通過將收入中位數除以 1.5 來建立一個輸入類別屬性(除以 1.5 的目的就是為了防止類別過多)。使用 ceil 函式進行向上取整計算(得到離散類別),把所有大於 5 的歸類到類別 5 中。

housing["income_cat"] = np.ceil(housing["median_income"] / 1.5)
housing["income_cat"].where(housing["income_cat"] < 5, 5.0, inplace=True)

現在你就可以根據收入類別之間的比例來進行分層取樣,可以直接使用 Scikit-Learn 的 StratifiedShuffleSplit 類來實現:

from sklearn.model_selection import StratifiedShuffleSplit

split = StratifiedShuffleSplit(n_splits=1, test_size=0.2, random_state=42)
for train_index, test_index in split.split(housing, housing["income_cat"]):
    strat_train_set = housing.loc[train_index]
    strat_test_set = housing.loc[test_index]

我們來看一下實際效果是否符合預期,先計算整個資料集中各收入類別所佔的比例:

>>> housing["income_cat"].value_counts() / len(housing)

3.0    0.350533
2.0    0.318798
4.0    0.176357
5.0    0.114583
1.0    0.039729
Name: income_cat, dtype: float64

你可以使用類似的程式碼計算測試集中各收入類別的比例。下圖比較了整個資料集、純隨機取樣測試集、分層取樣測試集三者之間收入類比的比例。可以看出,分層取樣測試集的收入類別比例與整個資料集近似相同,而純隨機取樣測試集與整個資料集相比產生了較大的偏差。

現在你可以把 income_cat 屬性刪除,讓資料回到它的初始狀態(income_cat 屬性是為了進行分層取樣的):

for set in (strat_train_set, strat_test_set):
    set.drop(["income_cat"], axis=1, inplace=True)

我們之所以花很多時間在劃分測試集上,是因為在機器學習專案中這非常重要但卻容易被忽視。更重要的,這些概念在我們之後討論交叉驗證(cross-validation)時會很有用。現在,我們開始進入下一階段:探索資料。

專案地址:

https://github.com/RedstoneWill/Hands-On-Machine-Learning-with-Sklearn-TensorFlow


相關文章