机器学习项目: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:这个结果并非理想的,应该还须要利用决策树的其余参数进行网格搜索,以及使用更多的特征;

相关文章
相关标签/搜索