使用housing.csv 訓練數據git
$$RESM = \sqrt{ \frac{1}{m}\sum_{i=1}^m(y'-y)^2}$$
$$MAE = \sqrt{ \frac{1}{m}\sum_{i=1}^m|y'-y|}$$算法
MSE 和 MAE 都是測量預測值和目標值兩個向量距離的方法。有多種測量距離的方法,或範數,更通常的K 階閔氏範數寫成。p=0時,ℓ0(漢明範數)只顯示了這個向量的基數(即,非零元素的個數),p趨於無窮時,ℓ∞(切比雪夫範數)是向量中最大的絕對值。
$$RESM = \sqrt{ \frac{1}{m}\sum_{i=1}^m|y'-y|^p}$$數組
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) # housing數據二八拆分
這個方法可行,可是並不完美:若是再次運行程序,就會產生一個不一樣的測試集。屢次運行以後,你(或你的機器學習算法)就會獲得整個數據集,這是須要避免的。安全
一個一般的解決辦法是使用每一個實例的識別碼,以斷定是否這個實例是否應該放入測試集(假設實例有單一且不變的識別碼)。例如,你能夠計算出每一個實例識別碼的哈希值,只保留其最後一個字節,若是值小於等於 51(約爲 256 的 20%),就將其放入測試集。這樣能夠保證在屢次運行中,測試集保持不變,即便更新了數據集。新的測試集會包含新實例中的 20%,但不會有以前位於訓練集的實例。下面是一種可用的方法:app
import hashlib # 參數identifier爲單一且不變的識別碼,能夠爲索引id # hash(np.int64(identifier)).digest()[-1]返回識別碼的哈希摘要值的最後一個字節 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]
不過,房產數據集沒有識別碼這一列。最簡單的方法是使用行索引做爲 ID:dom
housing_with_id = housing.reset_index() # 增長一個索引列,放在數據的第一列 train_set, test_set = split_train_test_by_id(housing_with_id, 0.2, "index")
若是使用行索引做爲惟一識別碼,你須要保證新數據放到現有數據的尾部,且沒有行被深處。若是作不到,則能夠用最穩定的特徵來建立惟一識別碼。例如,一個區的維度和經度在幾百萬年以內是不變的,因此能夠將二者結合成一個 ID。機器學習
若是你想簡單地拆分數據作預測模型示例,使用split_train_test進行拆分便可。Scikit-Learn 提供了一些函數,能夠用多種方式將數據集分割成多個子集。最簡單的函數是train_test_split,它的做用和以前的函數split_train_test很像,並帶有其它一些功能。首先,它有一個random_state參數,能夠設定前面講過的隨機生成器種子;第二,你能夠將種子傳遞到多個行數相同的數據集,能夠在相同的索引上分割數據集(這個功能很是有用,好比你有另外一個DataFrame做爲標籤):ide
from sklearn.model_selection import train_test_split train_set, test_set = train_test_split(housing, test_size=0.2, random_state=42)
另一種拆分形式:分層採樣
將人羣分紅均勻的子分組,稱爲分層,從每一個分層去除合適數量的實例,以保證測試集對總人數有表明性。例如,美國人口的 51.3% 是女性,48.7% 是男性。因此在美國,嚴謹的調查須要保證樣本也是這個比例:513 名女性,487 名男性做爲數據樣本。函數
數據集中的每一個分層都要有足夠的實例位於你的數據中,這點很重要。不然,對分層重要性的評估就會有誤差。這意味着,你不能有過多的分層,且每一個分層都要足夠大。後面的代碼經過將收入中位數除以 1.5(以限制收入分類的數量),建立了一個收入類別屬性,用ceil對值舍入(以產生離散的分類),而後將全部大於 5的分類納入到分類5 :性能
# 預處理,建立"income_cat"屬性 # 凡是會對原數組做出修改並返回一個新數組的,每每都有一個 inplace可選參數 # inplace=True,原數組名對應的內存值直接改變;inplace=False,原數組名對應的內存值並不改變,新的結果賦給一個新的數組. 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 # random_state爲隨機種子生成器,能夠獲得相同的隨機結果 # n_splits是將訓練數據分紅train/test對的組數,這裏彙總成一組數據 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] # 如今,你須要刪除income_cat屬性,使數據回到初始狀態: for set in (strat_train_set, strat_test_set): set.drop(["income_cat"], axis=1, inplace=True)
可視化數據尋找規律:
housing.plot(kind="scatter", x="longitude", y="latitude", alpha=0.4, s=housing["population"]/100, label="population", c="median_house_value", cmap=plt.get_cmap("jet"), colorbar=True, ) plt.legend()
每一個圈的半徑表示街區的人口(選項s),顏色表明價格(選項c)。咱們用預先定義的名爲jet的顏色圖(選項cmap),它的範圍是從藍色(低價)到紅色(高價):
這張圖說明房價和位置(好比,靠海)和人口密度聯繫密切。你還能夠很容易地使用corr()方法計算出每對屬性間的標準相關係數(也稱做皮爾遜相關係數):
corr_matrix = housing.corr()
如今來看下每一個屬性和房價中位數的關聯度:
>>> corr_matrix["median_house_value"].sort_values(ascending=False) median_house_value 1.000000 median_income 0.687170 total_rooms 0.135231 housing_median_age 0.114220 households 0.064702 total_bedrooms 0.047865 population -0.026699 longitude -0.047279 latitude -0.142826 Name: median_house_value, dtype: float64
相關係數的範圍是 -1 到 1。當接近 1 時,意味強正相關;例如,當收入中位數增長時,房價中位數也會增長。當相關係數接近 -1 時,意味強負相關;你能夠看到,緯度和房價中位數有輕微的負相關性(即,越往北,房價越可能下降)。最後,相關係數接近 0,意味沒有線性相關性。(沒有直接的線性關係,不是沒有關係)
另外一種檢測屬性間相關係數的方法是使用 Pandas 的scatter_matrix函數,它能畫出每一個數值屬性對每一個其它數值屬性的圖。由於如今共有 11 個數值屬性,你能夠獲得11 ** 2 = 121張圖。
from pandas.tools.plotting import scatter_matrix attributes = ["median_house_value", "median_income", "total_rooms", "housing_median_age"] scatter_matrix(housing[attributes], figsize=(12, 8))
獲得兩個屬性的散點圖
大多機器學習算法不能處理特徵丟失,所以先建立一些函數來處理特徵丟失的問題。前面,你應該注意到了屬性total_bedrooms有一些缺失值。有三個解決選項:
用DataFrame的dropna(),drop(),和fillna()方法,能夠方便地實現:
housing.dropna(subset=["total_bedrooms"]) # 選項1 housing.drop("total_bedrooms", axis=1) # 選項2 axis=0對行操做,axis=1對列操做 median = housing["total_bedrooms"].median() housing["total_bedrooms"].fillna(median) # 選項3
若是選擇選項 3,你須要計算訓練集的中位數,用中位數填充訓練集的缺失值,不要忘記保存該中位數。後面用測試集評估系統時,須要替換測試集中的缺失值,也能夠用來實時替換新數據中的缺失值。
Scikit-Learn 提供了一個方便的類來處理缺失值:Imputer。下面是其使用方法:首先,須要建立一個Imputer實例,指定用該屬性的中位數替換它的每一個缺失值:
from sklearn.preprocessing import Imputer imputer = Imputer(strategy="median") # 進行中位數賦值
由於只有數值屬性才能算出中位數,咱們須要建立一份不包括文本屬性ocean_proximity的數據副本:
housing_num = housing.drop("ocean_proximity", axis=1) # 去除ocean_proximity不爲數值屬性的特徵
如今,就能夠用fit()方法將imputer實例擬合到訓練數據:
imputer.fit(housing_num)
imputer計算出了每一個屬性的中位數,並將結果保存在了實例變量statistics_中。只有屬性total_bedrooms有缺失值,可是咱們確保一旦系統運行起來,新的數據中沒有缺失值,因此安全的作法是將imputer應用到每一個數值:
>>> imputer.statistics_ # 實例變量statistics_和housing_num數值數據獲得的中位數是同樣的 array([ -118.51 , 34.26 , 29. , 2119. , 433. , 1164. , 408. , 3.5414]) >>> housing_num.median().values array([ -118.51 , 34.26 , 29. , 2119. , 433. , 1164. , 408. , 3.5414])
如今,你就可使用這個「訓練過的」imputer來對訓練集進行轉換,經過將缺失值替換爲中位數:
X = imputer.transform(housing_num)
結果是一個普通的 Numpy 數組,包含有轉換後的特徵。若是你想將其放回到 PandasDataFrame中,也很簡單:
housing_tr = pd.DataFrame(X, columns=housing_num.columns) # 獲得處理缺失值後的DF數據
前面,咱們丟棄了類別屬性ocean_proximity,由於它是一個文本屬性,不能計算出中位數。大多數機器學習算法跟喜歡和數字打交道,因此讓咱們把這些文本標籤轉換爲數字。Scikit-Learn 爲這個任務提供了一個轉換器LabelEncoder:
# 簡單來講 LabelEncoder 是對不連續的數字或者文本進行編號 # le.fit([1,5,67,100]) # le.transform([1,1,100,67,5]) # 輸出: array([0,0,3,2,1]) >>> 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])
處理離散特徵這還不夠,Scikit-Learn 提供了一個編碼器OneHotEncoder,用於將整書分類值轉變爲獨熱向量。注意fit_transform()用於 2D 數組,而housing_cat_encoded是一個 1D 數組,因此須要將其變形:
>>> from sklearn.preprocessing import OneHotEncoder >>> encoder = OneHotEncoder() >>> housing_cat_1hot = encoder.fit_transform(housing_cat_encoded.reshape(-1,1)) >>> housing_cat_1hot <16513x5 sparse matrix of type '<class 'numpy.float64'>' with 16513 stored elements in Compressed Sparse Row format>
注意輸出結果是一個 SciPy 稀疏矩陣,而不是 NumPy 數組。當類別屬性有數千個分類時,這樣很是有用。通過獨熱編碼,咱們獲得了一個有數千列的矩陣,這個矩陣每行只有一個 1,其他都是 0。使用大量內存來存儲這些 0 很是浪費,因此稀疏矩陣只存儲非零元素的位置。你能夠像一個 2D 數據那樣進行使用,可是若是你真的想將其轉變成一個(密集的)NumPy 數組,只需調用toarray()方法:
>>> housing_cat_1hot.toarray() array([[ 0., 1., 0., 0., 0.], [ 0., 1., 0., 0., 0.], [ 0., 0., 0., 0., 1.], ..., [ 0., 1., 0., 0., 0.], [ 1., 0., 0., 0., 0.], [ 0., 0., 0., 1., 0.]])
使用類LabelBinarizer,咱們能夠用一步執行這兩個轉換(從文本分類到整數分類,再從整數分類到獨熱向量):
>>> from sklearn.preprocessing import LabelBinarizer >>> encoder = LabelBinarizer() >>> housing_cat_1hot = encoder.fit_transform(housing_cat) >>> housing_cat_1hot array([[0, 1, 0, 0, 0], [0, 1, 0, 0, 0], [0, 0, 0, 0, 1], ..., [0, 1, 0, 0, 0], [1, 0, 0, 0, 0], [0, 0, 0, 1, 0]])
注意默認返回的結果是一個密集 NumPy 數組。向構造器LabelBinarizer傳遞sparse_output=True,就能夠獲得一個稀疏矩陣。