機器學習項目:boston_housing

機器學習工程師納米學位

模型評價與驗證

項目 1: 預測波士頓房價

歡迎來到預測波士頓房價項目!在此文件中,咱們已經提供了一些示例代碼,但你還須要完善更多功能才能讓項目成功運行。除非有明確要求,你無需修改任何已給出的代碼。習題的標題爲編程練習。每一部分都會有詳細的指導,須要實現的部分也會在註釋中以TODO標出。請仔細閱讀全部的提示!html

除了實現代碼外,你還必須回答一些問題。請仔細閱讀每一個問題,而且在問題後的'回答'文字框中寫出完整的答案。咱們的項目審閱者將會根據你對問題的回答和撰寫代碼所實現的功能來進行評分。python

提示:代碼 和 Markdown單元格 可經過 Shift + Enter 快捷鍵運行。另外,Markdown單元格 能夠經過雙擊進入編輯模式。

第一步. 導入數據

在這個項目中,你將使用咱們提供的波士頓房屋信息數據來訓練和測試一個模型,並對模型的性能和預測能力進行評估。咱們但願能夠經過該模型實現對房屋的價值預估,提升房地產經紀人的工做效率。算法

此項目的數據集來自kaggle原始數據,未通過任何處理。該數據集統計了2006年至2010年波士頓我的住宅銷售狀況,包含2900多條觀測數據(其中一半是訓練數據,即咱們的housedata.csv文件)。更多文檔信息能夠參考做者的文檔(能夠不看),以及項目附件data_description.txt文件(特徵描述文件,必定要看)。編程

運行下面區域的代碼以載入一些此項目所需的Python庫。若是成功返回提示語句,則說明載入成功。api

# 載入此項目須要的庫
import numpy as np
import pandas as pd
import visuals as vs # Supplementary code 補充的可視化代碼

import matplotlib.pyplot as plt
import seaborn as sns
plt.style.use('seaborn') # use seaborn style 使用seaborn風格

import warnings
warnings.filterwarnings('ignore')

%matplotlib inline
print('你已經成功載入全部庫!')
你已經成功載入全部庫!

編程練習 1:加載數據

你的第一個編程練習是加載波士頓房價數據。咱們已爲你導入了 Pandas ,你須要使用這個庫中的read_csv方法來執行必要的過程。數組

導入數據將會是你整個項目的開始dom

若是成功返回數據集的大小,表示數據集已載入成功。機器學習

# 1 TODO:載入波士頓房屋的數據集:使用pandas載入csv,並賦值到data_df
data_df = pd.read_csv('housedata.csv')

# 成功載入的話輸出訓練數據行列數目
print("Boston housing dataset has {} data points with {} variables each.".format(*data_df.shape))
Boston housing dataset has 1460 data points with 81 variables each.

第二步. 數據分析

這個部分,你要對已有的波士頓房地產數據進行初步的觀察與處理。同時,經過對數據的探索可讓你更好地理解數據。ide

因爲這個項目的最終目標是創建一個預測房屋價值的模型,咱們須要將數據集分爲特徵(features)目標變量(target variable)函數

  • 目標變量 'SalePrice',是咱們但願預測的變量。
  • 特徵:除 'SalePrice'外的屬性都是特徵,它們反應了數據點在某些方面的表現或性質。

編程練習 2:觀察數據

你的第二個編程練習是對波士頓房價的數據進行觀察,這一步會讓你掌握更多數據自己的信息。具體問題以下:

問題2.1:使用head方法打印並觀察前7條data_df數據

# 2.1 TODO: 打印出前7條data_df
print(data_df.head(7))
Id  MSSubClass MSZoning  LotFrontage  LotArea Street Alley LotShape  \
0   1          60       RL         65.0     8450   Pave   NaN      Reg   
1   2          20       RL         80.0     9600   Pave   NaN      Reg   
2   3          60       RL         68.0    11250   Pave   NaN      IR1   
3   4          70       RL         60.0     9550   Pave   NaN      IR1   
4   5          60       RL         84.0    14260   Pave   NaN      IR1   
5   6          50       RL         85.0    14115   Pave   NaN      IR1   
6   7          20       RL         75.0    10084   Pave   NaN      Reg   

  LandContour Utilities  ... PoolArea PoolQC  Fence MiscFeature MiscVal  \
0         Lvl    AllPub  ...        0    NaN    NaN         NaN       0   
1         Lvl    AllPub  ...        0    NaN    NaN         NaN       0   
2         Lvl    AllPub  ...        0    NaN    NaN         NaN       0   
3         Lvl    AllPub  ...        0    NaN    NaN         NaN       0   
4         Lvl    AllPub  ...        0    NaN    NaN         NaN       0   
5         Lvl    AllPub  ...        0    NaN  MnPrv        Shed     700   
6         Lvl    AllPub  ...        0    NaN    NaN         NaN       0   

  MoSold YrSold  SaleType  SaleCondition  SalePrice  
0      2   2008        WD         Normal     208500  
1      5   2007        WD         Normal     181500  
2      9   2008        WD         Normal     223500  
3      2   2006        WD        Abnorml     140000  
4     12   2008        WD         Normal     250000  
5     10   2009        WD         Normal     143000  
6      8   2007        WD         Normal     307000  

[7 rows x 81 columns]

問題2.2:Id特徵對咱們訓練數據沒有任何用處,在data_df中使用drop方法刪除'Id'列數據

# 2.2 TODO: 刪除data_df中的Id特徵(保持數據仍在data_df中,不更改變量名)
data_df.drop(columns=['Id'], inplace=True)

問題2.3:使用describe方法觀察data_df各個特徵的統計信息:

# 2.3 TODO:
data_df.describe(include=[np.number])
MSSubClass LotFrontage LotArea OverallQual OverallCond YearBuilt YearRemodAdd MasVnrArea BsmtFinSF1 BsmtFinSF2 ... WoodDeckSF OpenPorchSF EnclosedPorch 3SsnPorch ScreenPorch PoolArea MiscVal MoSold YrSold SalePrice
count 1460.000000 1201.000000 1460.000000 1460.000000 1460.000000 1460.000000 1460.000000 1452.000000 1460.000000 1460.000000 ... 1460.000000 1460.000000 1460.000000 1460.000000 1460.000000 1460.000000 1460.000000 1460.000000 1460.000000 1460.000000
mean 56.897260 70.049958 10516.828082 6.099315 5.575342 1971.267808 1984.865753 103.685262 443.639726 46.549315 ... 94.244521 46.660274 21.954110 3.409589 15.060959 2.758904 43.489041 6.321918 2007.815753 180921.195890
std 42.300571 24.284752 9981.264932 1.382997 1.112799 30.202904 20.645407 181.066207 456.098091 161.319273 ... 125.338794 66.256028 61.119149 29.317331 55.757415 40.177307 496.123024 2.703626 1.328095 79442.502883
min 20.000000 21.000000 1300.000000 1.000000 1.000000 1872.000000 1950.000000 0.000000 0.000000 0.000000 ... 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 1.000000 2006.000000 34900.000000
25% 20.000000 59.000000 7553.500000 5.000000 5.000000 1954.000000 1967.000000 0.000000 0.000000 0.000000 ... 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 5.000000 2007.000000 129975.000000
50% 50.000000 69.000000 9478.500000 6.000000 5.000000 1973.000000 1994.000000 0.000000 383.500000 0.000000 ... 0.000000 25.000000 0.000000 0.000000 0.000000 0.000000 0.000000 6.000000 2008.000000 163000.000000
75% 70.000000 80.000000 11601.500000 7.000000 6.000000 2000.000000 2004.000000 166.000000 712.250000 0.000000 ... 168.000000 68.000000 0.000000 0.000000 0.000000 0.000000 0.000000 8.000000 2009.000000 214000.000000
max 190.000000 313.000000 215245.000000 10.000000 9.000000 2010.000000 2010.000000 1600.000000 5644.000000 1474.000000 ... 857.000000 547.000000 552.000000 508.000000 480.000000 738.000000 15500.000000 12.000000 2010.000000 755000.000000
# data_df.info() #查看dataFrame的信息

編程練習3: 數據預處理

咱們的數據不多是百分百的‘乾淨’數據(即有用數據),總會在採集整理時有些」失誤「、「冗餘」,形成「髒」數據,因此咱們要從數據的正確性和完整性這兩個方面來清理數據。

  • 正確性:通常是指有沒有異常值,好比咱們這個數據集中做者的文檔所說:

I would recommend removing any houses with more than 4000 square feet from the data set (which eliminates these five unusual observations) before assigning it to students.
建議咱們去掉數據中'GrLivArea'中超過4000平方英尺的房屋(具體緣由能夠參考文檔),固然本數據集還有其餘的異常點,這裏再也不處理。

  • 完整性:採集或者整理數據時所產生的空數據形成了數據的完整性缺失,一般咱們會使用必定的方法處理不完整的數據。在本例中,咱們使用如下兩種方法,一是丟棄數據,即選擇丟棄過多空數據的特徵(或者直接丟棄數據行,前提是NA數據佔比很少),二是填補數據,填補的方法也不少,均值中位數衆數填充等等都是好方法。

問題3.1:正確性

請按下述步驟刪除一些不合理的數據

問題3.1.1:使用matplotlib庫中的scatter方法 繪製'GrLivArea''SalePrice'的散點圖,x軸爲'GrLivArea',y軸爲'SalePrice',觀察數據

# 3.1.1 TODO:繪製要求的圖形
plt.scatter(data_df['GrLivArea'], data_df['SalePrice'], c='blue', marker='.', s=30)
plt.xlabel('GrLivArea')
plt.ylabel('SalePrice')
plt.show()

ALT

問題3.1.2:經過上圖咱們能夠看到那幾個異常值,即'GrLivArea'大於4000,可是'SalePrice'又極低的數據,從data_df刪除這幾個異常值,刪除後從新繪製'GrLivArea''SalePrice'的關係圖,確認異常值已刪除。

# 3.1.2 
# TODO:從data_df中刪除 GrLivArea大於4000 且 SalePrice低於300000 的值
index = data_df[(data_df['GrLivArea'] > 4000) & (data_df['SalePrice'] < 300000)].index
data_df.drop(index=index, inplace=True)

# TODO:從新繪製GrLivArea和SalePrice的關係圖,確認異常值已刪除
plt.scatter(data_df['GrLivArea'], data_df['SalePrice'], c='blue', marker='.', s=50)
plt.xlabel('GrLivArea')
plt.ylabel('SalePrice')
plt.show()

圖片描述

問題3.2:完整性

請按下述步驟,補足數據的完整性

問題3.2.1:篩選出過多空數據的特徵,咱們這個項目定爲篩選出有超過25%爲空數據的特徵

limit_percent = 0.25
limit_value = len(data_df) * limit_percent
# 3.2.1 TODO 統計並打印出超過25%的空數據的特徵,你能夠考慮使用isna()
list(data_df.columns[data_df.isna().sum() > limit_value])
['Alley', 'FireplaceQu', 'PoolQC', 'Fence', 'MiscFeature']

若是你整理出的特徵是'Alley', 'FireplaceQu', 'PoolQC', 'Fence', 'MiscFeature',那就說明你統計對了,接着咱們查看data_description.txt文件,就會發現,這些並不是必定是空缺數據,而沒有游泳池,籬笆等也會用NA來表示,那麼就不須要刪除這些特徵了,而是用None來填充NA數據。

問題3.2.2:根據data_description.txt特徵描述,使用fillna方法填充空數據,具體哪種數據須要填充什麼已經整理好了,請按提示要求來進行填充

# 直接運行不用修改
# 肯定全部空特徵
missing_columns = list(data_df.columns[data_df.isnull().sum() != 0])
# 肯定哪些是類別特徵,哪些是數值特徵
missing_numerical = list(data_df[missing_columns].dtypes[data_df[missing_columns].dtypes != 'object'].index)
missing_category = [i for i in missing_columns if i not in missing_numerical]
print("missing_numerical:",missing_numerical)
print("missing_category:",missing_category)
missing_numerical: ['LotFrontage', 'MasVnrArea', 'GarageYrBlt']
missing_category: ['Alley', 'MasVnrType', 'BsmtQual', 'BsmtCond', 'BsmtExposure', 'BsmtFinType1', 'BsmtFinType2', 'Electrical', 'FireplaceQu', 'GarageType', 'GarageFinish', 'GarageQual', 'GarageCond', 'PoolQC', 'Fence', 'MiscFeature']
data_df.mode() # 取衆數,返回DataFrame
MSSubClass MSZoning LotFrontage LotArea Street Alley LotShape LandContour Utilities LotConfig ... PoolArea PoolQC Fence MiscFeature MiscVal MoSold YrSold SaleType SaleCondition SalePrice
0 20.0 RL 60.0 7200.0 Pave Grvl Reg Lvl AllPub Inside ... 0.0 Ex MnPrv Shed 0.0 6.0 2009.0 WD Normal 140000.0
1 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN Fa NaN NaN NaN NaN NaN NaN NaN NaN
2 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN Gd NaN NaN NaN NaN NaN NaN NaN NaN
# 須要填充衆數的特徵
fill_Mode = ['Electrical'] 
# 須要填充None的特徵
fill_None = ['Alley', 'MasVnrType', 'BsmtQual', 'BsmtCond', 'BsmtExposure', 'BsmtFinType1', 
             'BsmtFinType2', 'FireplaceQu', 'GarageType', 'GarageFinish', 'GarageQual', 
             'GarageCond', 'PoolQC', 'Fence', 'MiscFeature']
# 須要填充0的特徵
fill_0 = ['GarageYrBlt']
# 須要填充中位數的特徵
fill_median = ['LotFrontage', 'MasVnrArea']
# 3.4.1 TODO:按需填補上面數據
# 個人實現:
# # 填充衆數:
# mode = data_df[fill_Mode].mode().iloc[0,0]
# data_df.fillna(value={fill_Mode[0]: mode}, inplace=True)
# # 填充None:
# d_None={}
# for i in fill_None:
#     d_None[i] = 'None'
# data_df.fillna(value=d_None, inplace=True) 
# # 填充0:
# data_df[fill_0].fillna(value=0, inplace=True) 
# # 填充中位數:
# data_df[fill_median].fillna(data_df[fill_median].median(), inplace=True)

# 簡化實現:
data_df[fill_Mode] = data_df[fill_Mode].fillna(data_df[fill_Mode].mode())
data_df[fill_None] = data_df[fill_None].fillna('None')
data_df[fill_0] = data_df[fill_0].fillna(0)
data_df[fill_median] = data_df[fill_median].fillna(data_df[fill_median].median())

# 驗證
# data_df.isna().sum()

編程練習4: 特徵分析

有這麼一句話在業界普遍流傳:特徵數據決定了機器學習的上限,而模型和算法只是逼近這個上限而已。特徵工程,是整個數據分析過程當中不可缺乏的一個環節,其結果質量直接關係到模型效果和最終結論。從上面兩步中咱們獲得了「乾淨」的數據,可是data_df總共有81個特徵,咱們應當剔除那些可有可無的特徵(噪聲),使用真正關鍵的特徵來進行模型訓練。如今須要咱們對這些龐大的數據進行分析,提取出與目標最爲關聯的數據。

問題4.1:繪製'SalePrice'的直方圖,並說明該直方圖屬於什麼分佈

# 4.1 TODO:繪製要求的圖形
# plt.hist(data_df['SalePrice'], bins=50, normed=False, color=None)
# plt.xlabel('SalePrice')
# plt.show()
data_df['SalePrice'].hist(bins=50)
<matplotlib.axes._subplots.AxesSubplot at 0x22186a3e7b8>

圖片描述

回答問題4.1:'SalePrice'屬於 正偏態 分佈

import scipy
scipy.stats.skew(data_df['SalePrice'])
1.8793604459195012

結果>0,表明正偏,從計算上也說明了'SalePrice'屬於正偏態分佈。

若是特徵極其多,很難清晰的看到特徵與目標變量之間的關係,就須要利用統計知識來進行多變量分析了。咱們常使用熱圖heatmap結合corr方法來進行客觀分析,熱圖Heatmap能夠用顏色變化來反映變量之間的相關性二維矩陣或說相關性表格中的數據信息,它能夠直觀地將數據值的大小以定義的顏色深淺表示出來。這個項目,爲了簡化訓練,咱們以相關性絕對值大於0.5爲界來選取咱們須要的特徵。

# 不用修改直接運行
corrmat = data_df.corr().abs() #計算連續型特徵之間的相關係數
#將於SalePrice的相關係數大於5的特徵取出來,並按照SalePrice降序排列,而後取出對應的特徵名,保存在列表中
top_corr = corrmat[corrmat["SalePrice"]>0.5].sort_values(by = ["SalePrice"], ascending = False).index 
cm = abs(np.corrcoef(data_df[top_corr].values.T)) #注意這裏要轉置,不然變成樣本之間的相關係數,而咱們要計算的是特徵之間的相關係數
f, ax = plt.subplots(figsize=(20, 9))
sns.set(font_scale=1.3)
hm = sns.heatmap(cm, cbar=True, annot=True,
                 square=True, fmt='.2f', annot_kws={'size': 13}, 
                 yticklabels=top_corr.values, xticklabels=top_corr.values);
data_df = data_df[top_corr]

圖片描述

咱們已經從龐大的特徵羣中篩選出了最相關的特徵,也瞭解了咱們目標數據的分佈,那麼接下來,咱們從創造性方面來對咱們的特徵進行「改造」。

  • 創造性:創造性主要是說兩種狀況,一種是對現有數據的處理,好比對類別的獨熱編碼(One-hotEncoder)或者標籤編碼(LabelEncoder),數值的區間縮放,歸一化,標準化等等,另外一種就是根據某一個或多個特徵創造一個新的特徵,例如某特徵按組分類(groupby)後,或者某些特徵組合後來創造新特徵等等。

由於咱們篩選出來的特徵都爲數值類型特徵,因此咱們只作標準化的操做:這個項目是一個迴歸類型的項目,而咱們的迴歸算法對標準正態分步預測較爲準確,從咱們的目標數據能夠看出數據是一個偏態分佈,那麼咱們使用log將數據從偏態分佈轉換爲標準正態分佈,最後進行標準化。

# 不要修改,直接運行
from scipy.special import boxcox1p
from sklearn.preprocessing import StandardScaler

data_df['SalePrice'] = np.log1p(data_df['SalePrice']) # np.log1p()函數將數據從偏態分佈轉換成標準正態分佈
numeric_features = list(data_df.columns)
numeric_features.remove('SalePrice')
for feature in numeric_features:
    #all_data[feat] += 1
    data_df[feature] = boxcox1p(data_df[feature], 0.15)

scaler = StandardScaler()
scaler.fit(data_df[numeric_features])
data_df[numeric_features] = scaler.transform(data_df[numeric_features])

筆記:

  1. 對目標變量的變換np.loglp():這個函數用來在數據預處理過程對偏度較大的數據進行平滑處理,使其更加符合高斯分佈,有利於後續獲得一個更好地結果,數學公式:ln(x+1).
  2. 對數值類型的特徵變量的變換boxcox1p():對於數值類型爲數值的特徵變量,也須要進行相似的變換。經過統計每一變量的skew值(偏度/偏態值,越接近0,越符合正態分佈。大於0爲正偏態,小於0爲負偏態),對絕對值大於0.5的進行boxcox變換。可使線性迴歸模型知足線性性、獨立性、方差齊性以及正態性的同時,又不丟失信息,此種變換稱之爲Box—Cox變換。$$y=\frac{x^{\lambda}-1}{\lambda},\lambda != 0 \\ y=log(x),\lambda == 0$$Box-Cox變換後,殘差能夠更好的知足正態性、獨立性等假設前提,下降了僞迴歸的機率

第三步. 創建模型

在項目的第三步中,你須要瞭解必要的工具和技巧來讓你的模型可以進行預測。用這些工具和技巧對每個模型的表現作精確的衡量能夠極大地加強你預測的可信度。

編程練習5:定義衡量標準

若是不能對模型的訓練和測試的表現進行量化地評估,咱們就很難衡量模型的好壞。一般咱們會定義一些衡量標準,這些標準能夠經過對某些偏差或者擬合程度的計算來獲得。在這個項目中,你將經過運算決定係數 $R^2$ 來量化模型的表現。模型的決定係數是迴歸分析中十分經常使用的統計信息,常常被看成衡量模型預測能力好壞的標準。

$R^2$ 的數值範圍從0至1,表示目標變量的預測值和實際值之間的相關程度平方的百分比。一個模型的 $R^2$ 值爲0還不如直接用平均值來預測效果好;而一個 $R^2$ 值爲1的模型則能夠對目標變量進行完美的預測。從0至1之間的數值,則表示該模型中目標變量中有百分之多少可以用特徵來解釋。模型也可能出現負值的 $R^2$,這種狀況下模型所作預測有時會比直接計算目標變量的平均值差不少。

問題5.1:
在下方代碼的 performance_metric 函數中,你要實現:

  • 使用 sklearn.metrics 中的 r2_score 來計算 y_truey_predict 的 $R^2$ 值,做爲對其表現的評判。
  • 將他們的表現評分儲存到 score 變量中。
# 5.1 TODO: 引入 'r2_score'
from sklearn.metrics import r2_score
def performance_metric(y_true, y_predict):
    """ Calculates and returns the performance score between 
        true and predicted values based on the metric chosen. """
    
    # TODO: 計算 'y_true' 與 'y_predict' 的r2值
    score = r2_score(y_true, y_predict)
    
    # 返回這一分數
    return score

筆記

R^2是評價模型表現的方法之一,每一個機器學習模型的創建都要有相對應的評價指標,後面咱們會學到更多的評價指標。不過R^2其實也有不少侷限性須要注意 https://en.wikipedia.org/wiki...

skearn對於常見的模型表現衡量方法也有詳細的介紹。
http://scikit-learn.org/stabl...

問題 5.2:擬合程度

假設一個數據集有五個數據且某一模型作出下列目標變量的預測:

真實數值 預測數值
3.0 2.5
-0.5 0.0
2.0 2.1
7.0 7.8
4.2 5.3

你以爲這個模型已成功地描述了目標變量的變化嗎?若是成功,請解釋爲何,若是沒有,也請給出緣由。

提示1:運行下方的代碼,使用 performance_metric 函數來計算 y_truey_predict 的決定係數。

提示2:$R^2$ 分數是指能夠從自變量中預測的因變量的方差比例。 換一種說法:

  • $R^2$ 爲0意味着因變量不能從自變量預測。
  • $R^2$ 爲1意味着能夠從自變量預測因變量。
  • $R^2$ 在0到1之間表示因變量可預測的程度。
  • $R^2$ 爲0.40意味着 Y 中40%的方差能夠從 X 預測。
#TODO 5.2:計算這一模型的表現
score = performance_metric([3, -0.5, 2, 7, 4.2], [2.5, 0.0, 2.1, 7.8, 5.3])
print("Model has a coefficient of determination, R^2, of {:.3f}.".format(score))
Model has a coefficient of determination, R^2, of 0.923.

問題 5.2 - 回答:模型能夠描述目標變量的變化,由於R^2值爲0.923,說明自變量(預測數值)對因變量(真實數值)的解釋越好。

編程練習 6: 數據分割與重排

接下來,你須要分割波士頓房屋數據集,包括特徵與目標變量、訓練集和測試集。一般在這個過程當中,數據也會被重排,以消除數據集中因爲順序而產生的誤差。
在下面的代碼中,你須要

問題6.1:將data_df分割爲特徵和目標變量

# TODO: 6.1 

labels = data_df['SalePrice']#TODO:提取SalePrice做爲labels
features = data_df.drop(['SalePrice'], axis=1)#TODO:提取除了SalePrice之外的特徵賦值爲features

問題6.2 :

  • 使用 sklearn.model_selection 中的 train_test_split, 將 featuresprices 的數據都分紅用於訓練的數據子集和用於測試的數據子集。

    • 分割比例爲:80%的數據用於訓練,20%用於測試;
    • 選定一個數值以設定 train_test_split 中的 random_state ,這會確保結果的一致性;
  • 將分割後的訓練集與測試集分配給 X_train, X_test, y_trainy_test
# TODO: 引入 'train_test_split'
from sklearn.model_selection import train_test_split

# TODO: 打亂並分割訓練集與測試集
X_train, X_test, y_train, y_test = train_test_split(features, labels, test_size=0.2, random_state=1)

# 成功~
print("Training and testing split was successful.")
Training and testing split was successful.

問題 6.3 - 訓練及測試
將數據集按必定比例分爲訓練用的數據集和測試用的數據集對學習算法有什麼好處?

若是用模型已經見過的數據,例如部分訓練集數據進行測試,又有什麼壞處?

提示: 若是沒有數據來對模型進行測試,會出現什麼問題?

問題 6.3 - 回答:

  • 將數據集按必定比例分爲訓練用的數據集和測試用的數據集對學習算法能夠對算法進行驗證,在必定程度上避免算法過擬合;
  • 若是部分訓練數據進行測試,會使得算法的準確率不可靠
  • 若是沒有數據進行測試,沒法說明模型的優劣
  • 將數據集分紅訓練集和測試集的好處:既能夠用於訓練又能夠用於測試,並且不會相互干擾,並且能夠對訓練模型進行有效的驗證。
  • 用部分訓練集進行測試的壞處:模型就是根據訓練集得出的,使用訓練集進行測試確定會得出較好的結果,這不能判斷訓練模型的優劣。

第四步. 分析模型的表現

在項目的第四步,咱們來觀察不一樣參數下,模型在訓練集和驗證集上的表現。這裏,咱們專一於一個特定的算法(帶剪枝的決策樹DecisionTreeRegressor,但這並非這個項目的重點)和這個算法的一個參數 'max_depth'。用所有訓練集訓練,選擇不一樣'max_depth' 參數,觀察這一參數的變化如何影響模型的表現。畫出模型的表現來對於分析過程十分有益。

學習曲線

下方區域內的代碼會輸出四幅圖像,它們是一個決策樹模型在不一樣最大深度下的表現。每一條曲線都直觀得顯示了隨着訓練數據量的增長,模型學習曲線在訓練集評分和驗證集評分的變化,評分使用決定係數 $R^2$。曲線的陰影區域表明的是該曲線的不肯定性(用標準差衡量)。

運行下方區域中的代碼,並利用輸出的圖形回答下面的問題。

# Produce learning curves for varying training set sizes and maximum depths
vs.ModelLearning(features, labels)

圖片描述

問題 7 - 學習曲線

  • 選擇上述圖像中的其中一個,並給出其最大深度。
  • 隨着訓練數據量的增長,訓練集曲線的評分有怎樣的變化?驗證集曲線呢?
  • 若是有更多的訓練數據,是否能有效提高模型的表現呢?

提示:學習曲線的評分是否最終會收斂到特定的值?通常來講,你擁有的數據越多,模型表現力越好。可是,若是你的訓練和測試曲線以高於基準閾值的分數收斂,這是否有必要?基於訓練和測試曲線已經收斂的前提下,思考添加更多訓練點的優缺點。

問題 7 - 回答:當max_depth爲3時,隨着訓練數據的增長,訓練集曲線的評分下降,驗證集曲線的評分提升,可是二者會收斂到一特定數值,隨着訓練數據的增長,模型表現再也不提升,此時沒有必要再繼續增長訓練數據,由於會增長模型的訓練時間。可是,若是在過擬合的狀況下,比方說當max_depth爲6或者10時,隨着訓練數據的增長,模型表現也不斷提升,此時反而有必要增長訓練數據。

筆記
對訓練曲線和測試曲線趨勢和意義解釋的很好。這裏隨着數據的增多,max_depth不變的狀況下,模型提高的幅度也愈來愈小。
20190326142508752.png

傳統的機器學習算法(又被稱爲基於統計的機器學習)在數據量達到必定程度後,更多的數據沒法提高模型的表現。深度學習的一個優點就是它能夠把大量的數據利用起來,提高學習表現。

這裏還有更多關於學習曲線的介紹:
https://www.coursera.org/lear...
http://scikit-learn.org/stabl...
傳統的機器學習算法(又被稱爲基於統計的機器學習)在數據量達到必定程度後,更多的數據沒法提高模型的表現。深度學習的一個優點就是它能夠把大量的數據利用起來,提高學習表現。

這裏還有更多關於學習曲線的介紹:
https://www.coursera.org/lear...
http://scikit-learn.org/stabl...

複雜度曲線

下列代碼內的區域會輸出一幅圖像,它展現了一個已經通過訓練和驗證的決策樹模型在不一樣最大深度條件下的表現。這個圖形將包含兩條曲線,一個是訓練集的變化,一個是驗證集的變化。跟學習曲線類似,陰影區域表明該曲線的不肯定性,模型訓練和測試部分的評分都用的 performance_metric 函數。

運行下方區域中的代碼,根據問題5與問題6對輸出的圖形進行分析並給出你思考的結果。

vs.ModelComplexity(X_train, y_train)

圖片描述

問題 8 - 誤差(bias)與方差(variance)之間的權衡取捨

  • 當模型以最大深度 1訓練時,模型的預測是出現很大的誤差仍是出現了很大的方差?
  • 當模型以最大深度10訓練時,情形又如何呢?
  • 圖形中的哪些特徵可以支持你的結論?

提示: 高誤差表示欠擬合(模型過於簡單),而高方差表示過擬合(模型過於複雜,以致於沒法泛化)。考慮哪一種模型(深度1或10)對應着上述的狀況,並權衡誤差與方差。

問題 8 - 回答:當max_depth爲1時,模型表現出高誤差,由於訓練集和測試集的R2分數都比較低;當max_depth爲10時,模型表現出高方差,由於訓練集的R2分數較高,而驗證集的R2分數較低。

筆記

誤差-方差分解是解釋學習算法泛化性能的重要工具。這裏你須要理解三個方面:

什麼是泛化偏差(Generalization error):

  • 若是學獲得的模型是 f ,那麼用這個模型對測試數據預測的偏差即爲泛化偏差。通常所說的泛化偏差都是算法的指望泛化偏差

Bias-Variance分解:

  • 如今咱們來看題目中的方差和誤差究竟是什麼意思?經過簡單的多項式展開合併,能夠對算法的指望泛化偏差進行分解能夠獲得(具體推到過程請參考[1]、[2]) :

201903261426017.png

  • 也就是說:泛化偏差=誤差+方差+噪聲
  • 誤差度量了學習算法的指望預測和真實結果的偏離程度,即刻畫了學習算法自己的擬合能力
  • 方差度量了一樣大小的訓練集的變更所致使的學習性能的變化, 即 刻畫了數據擾動所形成的影響

Bias-Variance trade-off:

通常狀況下,誤差和方差是有衝突的,以下圖所示。爲了取得好的泛化性能,則須要誤差較小,即可以充分擬合數據,而且方差較小,即便得數據擾動產生的影響小。

  • 欠擬合:在訓練不足時,學習的擬合能力不夠強,訓練數據的擾動不足以使學習器產生顯著變化,誤差主導了泛化錯誤率。
  • 過擬合:充分訓練後, 學習器的擬合能力已很是強, 訓練數據的輕微擾動都會致使學習器發生顯著變化, 訓練數據自身的、非全局的特性被學習器學到了,方差主導了泛化錯誤率。

20190326142624808.png

更多內容請閱讀參考資料。

參考資料:

[1] «機器學習», 周志華, 2.5 節誤差與方差.

[2] Understanding the Bias-Variance Tradeoff

問題 9- 最優模型的猜想

  • 結合複雜度曲線,你認爲最大深度是多少的模型可以最好地對未見過的數據進行預測?
  • 你得出這個答案的依據是什麼?

提示:查看問題8上方的圖表,並查看模型在不一樣 depth下的驗證分數。隨着深度的增長模型的表現力會變得更好嗎?咱們在什麼狀況下得到最佳驗證分數而不會使咱們的模型過分複雜?請記住,奧卡姆剃刀:「在競爭性假設中,應該選擇假設最少的那一個。」

問題 9 - 回答:

  • 隨着深度的增長模型表現並無愈來愈好,驗證集在max_depth爲4時,模型收斂,而且訓練R2分數和驗證R2較高
  • 隨着最大深度的提高,訓練集R2分數不斷上升,驗證集R2分數並無獲得提升,反而有降低的趨勢,說明隨着模型變得複雜,模型過擬合。
  • 考慮模型的複雜度和R2分數,最大深度爲4的模型具備最好的泛化能力。

第五步. 評估模型的表現

在項目的最後一節中,你將構建一個模型,並使用 fit_model 中的優化模型去預測客戶特徵集。

問題 10- 網格搜索(Grid Search)

  • 什麼是網格搜索法?
  • 如何用它來優化模型?

提示:在解釋網格搜索算法時,首先要理解咱們爲何使用網格搜索算法,以及咱們使用它的最終目的是什麼。爲了使你的回答更具備說服力,你還能夠給出一個模型中可使用此方法進行優化參數的示例。

問題 10 - 回答: 網格搜索法是給定參數值,窮舉搜索尋找最優參數的算法。網格搜索法經過構建一個參數的字典,創造出不一樣的參數組合,輸入給分類器進行訓練,採用交叉驗證,尋找驗證分數最高的那一組參數,這一組參數就是模型的最優參數。

  • 網格搜索算法是一種窮舉搜索的算法。
  • 在全部候選的參數選擇中,經過循環遍歷,嘗試每一種可能性,表現最好的參數就是最終的結果。其原理就像是在數組裏找最大值。以有兩個參數的模型爲例,參數a有3種可能,參數b有4種可能,把全部可能性列出來,能夠表示成一個3*4的表格,其中每一個cell就是一個網格,循環過程就像是在每一個網格里遍歷、搜索,因此叫grid search。

問題 11 - 交叉驗證

  • 什麼是K折交叉驗證法(k-fold cross-validation)?
  • GridSearchCV 是如何結合交叉驗證來完成對最佳參數組合的選擇的?
  • GridSearchCV 中的'cv_results_'屬性能告訴咱們什麼?
  • 網格搜索爲何要使用K折交叉驗證?K折交叉驗證可以避免什麼問題?

提示:在解釋k-fold交叉驗證時,必定要理解'k'是什麼,和數據集是如何分紅不一樣的部分來進行訓練和測試的,以及基於'k'值運行的次數。
在考慮k-fold交叉驗證如何幫助網格搜索時,你可使用特定的數據子集來進行訓練與測試有什麼缺點,以及K折交叉驗證是如何幫助緩解這個問題。

問題 11 - 回答:

  • K折交叉驗證是隨機將訓練集劃分紅K份,依次將其中的一份做爲驗證集,其他的K-1份做爲訓練集,獲得K個模型,觀察每一個模型的表現,選擇模型表現最好的那一個,K折交叉驗證可以有效的防止過擬合;
  • GridSearchCV經過交叉驗證獲得每一個參數組合的得分,以此肯定最優的參數組合;
  • GridSearchCV 中的cv_results 屬性返回一個字典,記錄了每一組網格參數每一次訓練/驗證( K 折對應 K 次)對應的訓練結果,包括訓練/驗證時間、訓練/驗證評估分數以及相關時間和評分的統計信息;
  • 採用交叉驗證可使得網格搜索的結果可信度更高,對網格搜索來講,能夠將訓練數據能夠按必定比例分爲訓練集和驗證集,而不使用交叉驗證,可是這樣作的話,網格搜索的結果依賴於訓練集和驗證集的劃分,這樣的劃分不具備表明性。採用K折交叉驗證取平均值能夠避免樣本劃分不合理的狀況,使得結果可信度更高。

編程練習 7:訓練最優模型

在這個練習中,你將須要將所學到的內容整合,使用決策樹算法訓練一個模型。爲了得出的是一個最優模型,你須要使用網格搜索法訓練模型,以找到最佳的 'max_depth' 參數。你能夠把'max_depth' 參數理解爲決策樹算法在作出預測前,容許其對數據提出問題的數量。決策樹是監督學習算法中的一種。

在下方 fit_model 函數中,你須要作的是:

  1. 定義 'cross_validator' 變量: 使用 sklearn.model_selection 中的 KFold 建立一個交叉驗證生成器對象;
  2. 定義 'regressor' 變量: 使用 sklearn.tree 中的 DecisionTreeRegressor 建立一個決策樹的迴歸函數;
  3. 定義 'params' 變量: 爲 'max_depth' 參數創造一個字典,它的值是從1至10的數組;
  4. 定義 'scoring_fnc' 變量: 使用 sklearn.metrics 中的 make_scorer 建立一個評分函數;
    ‘performance_metric’ 做爲參數傳至這個函數中;
  5. 定義 'grid' 變量: 使用 sklearn.model_selection 中的 GridSearchCV 建立一個網格搜索對象;將變量'regressor', 'params', 'scoring_fnc''cross_validator' 做爲參數傳至這個對象構造函數中;

若是你對python函數的默認參數定義和傳遞不熟悉,能夠參考這個MIT課程的視頻

# TODO: Import 'make_scorer', 'DecisionTreeRegressor', and 'GridSearchCV'
from sklearn.model_selection import KFold
from sklearn.tree import DecisionTreeRegressor
from sklearn.metrics import make_scorer
from sklearn.model_selection import GridSearchCV

def fit_model(X, y):
    """ Performs grid search over the 'max_depth' parameter for a 
        decision tree regressor trained on the input data [X, y]. """
    
    cross_validator = KFold(n_splits=10)
    
    # TODO: Create a decision tree regressor object
    regressor = DecisionTreeRegressor(random_state=1)

    # TODO: Create a dictionary for the parameter 'max_depth' with a range from 1 to 10
    params = {'max_depth':[i for i in range(1, 11)]}

    # TODO: Transform 'performance_metric' into a scoring function using 'make_scorer' 
    scoring_fnc = make_scorer(performance_metric)

    # TODO: Create the grid search cv object --> GridSearchCV()
    # Make sure to include the right parameters in the object:
    # (estimator, param_grid, scoring, cv) which have values 'regressor', 'params', 'scoring_fnc', and 'cross_validator' respectively.
    grid = GridSearchCV(regressor, params, scoring_fnc, cv = cross_validator)

    # Fit the grid search object to the data to compute the optimal model
    grid = grid.fit(X, y)

    # Return the optimal model after fitting the data
    return grid.best_estimator_

第六步. 作出預測

當咱們用數據訓練出一個模型,它就可用於對新的數據進行預測。在咱們的例子--決策樹迴歸函數中,模型已經學會對新輸入的數據「提問」,並返回對目標變量的預測值。你能夠用這些預測來獲取未知目標變量的數據的信息,可是,輸入的新數據必須不能是已有訓練數據之中的。

問題 12 - 最優模型

最優模型的最大深度(maximum depth)是多少?此答案與你在問題 9所作的猜想是否相同?

運行下方區域內的代碼,將決策樹迴歸函數代入訓練數據的集合,以獲得最優化的模型。

# Fit the training data to the model using grid search
reg = fit_model(X_train, y_train)

# Produce the value for 'max_depth'
print("Parameter 'max_depth' is {} for the optimal model.".format(reg.get_params()['max_depth']))
Parameter 'max_depth' is 5 for the optimal model.

最終,使用咱們確認好的參數來對測試數據進行預測,完成下面的問題,來看看咱們的訓練結果如何吧

問題12.1:填入上題所確認的最優參數,查看測試結果

depth = 5
regressor = DecisionTreeRegressor(max_depth = depth)
regressor.fit(X_train, y_train)
y_pred = regressor.predict(X_test) 
score = performance_metric(y_test, y_pred)
print("The R2 score is ",score)
The R2 score is  0.7520883029841192

問題12.2:你剛剛計算了最優模型在測試集上的決定係數,你會如何評價這個結果?還有什麼地方能夠改進,以提高這一分數呢?

回答問題12.2:這個結果並非理想的,應該還須要利用決策樹的其餘參數進行網格搜索,以及使用更多的特徵;

相關文章
相關標籤/搜索