欢迎来到预测波士顿房价项目!在此文件中,我们已经提供了一些示例代码,但你还需要完善更多功能才能让项目成功运行。除非有明确要求,你无需修改任何已给出的代码。习题的标题为编程练习。每一部分都会有详细的指导,需要实现的部分也会在注释中以TODO标出。请仔细阅读所有的提示!
除了实现代码外,你还必须回答一些问题。请仔细阅读每个问题,并且在问题后的**‘回答’**文字框中写出完整的答案。我们的项目审阅者将会根据你对问题的回答和撰写代码所实现的功能来进行评分。
**提示:**代码 和 Markdown单元格 可通过 Shift + Enter 快捷键运行。另外,Markdown单元格 可以通过双击进入编辑模式。
在这个项目中,你将使用我们提供的波士顿房屋信息数据来训练和测试一个模型,并对模型的性能和预测能力进行评估。我们希望可以通过该模型实现对房屋的价值预估,提高房地产经纪人的工作效率。
此项目的数据集来自kaggle原始数据,未经过任何处理。该数据集统计了2006年至2010年波士顿个人住宅销售情况,包含2900多条观测数据(其中一半是训练数据,即我们的housedata.csv
文件)。更多文档信息可以参考作者的文档(可以不看),以及项目附件data_description.txt
文件(特征描述文件,一定要看)。
运行下面区域的代码以载入一些此项目所需的Python库。如果成功返回提示语句,则说明载入成功。
# 载入此项目需要的库
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('你已经成功载入所有库!')
你已经成功载入所有库!
你的第一个编程练习是加载波士顿房价数据。我们已为你导入了 Pandas
,你需要使用这个库中的read_csv方法来执行必要的过程。
导入数据将会是你整个项目的开始
如果成功返回数据集的大小,表示数据集已载入成功。
# 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.
这个部分,你要对已有的波士顿房地产数据进行初步的观察与处理。同时,通过对数据的探索可以让你更好地理解数据。
由于这个项目的最终目标是建立一个预测房屋价值的模型,我们需要将数据集分为特征(features)和目标变量(target variable)。
'SalePrice'
,是我们希望预测的变量。'SalePrice'
外的属性都是特征,它们反应了数据点在某些方面的表现或性质。你的第二个编程练习是对波士顿房价的数据进行观察,这一步会让你掌握更多数据本身的信息。具体问题如下:
问题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 |
8 rows × 37 columns
# data_df.info() #查看dataFrame的信息
我们的数据不可能是百分百的‘干净’数据(即有用数据),总会在采集整理时有些”失误“、“冗余”,造成“脏”数据,所以我们要从数据的正确性和完整性这两个方面来清理数据。
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平方英尺的房屋(具体原因可以参考文档),当然本数据集还有其他的异常点,这里不再处理。问题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()
问题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 |
3 rows × 80 columns
# 需要填充众数的特征
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()
有这么一句话在业界广泛流传:特征数据决定了机器学习的上限,而模型和算法只是逼近这个上限而已。特征工程,是整个数据分析过程中不可缺少的一个环节,其结果质量直接关系到模型效果和最终结论。从上面两步中我们得到了“干净”的数据,但是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)
回答问题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]
我们已经从庞大的特征群中筛选出了最相关的特征,也了解了我们目标数据的分布,那么接下来,我们从创造性方面来对我们的特征进行“改造”。
因为我们筛选出来的特征都为数值类型特征,所以我们只做标准化的操作:这个项目是一个回归类型的项目,而我们的回归算法对标准正态分步预测较为准确,从我们的目标数据可以看出数据是一个偏态分布,那么我们使用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])
笔记:
在项目的第三步中,你需要了解必要的工具和技巧来让你的模型能够进行预测。用这些工具和技巧对每一个模型的表现做精确的衡量可以极大地增强你预测的可信度。
如果不能对模型的训练和测试的表现进行量化地评估,我们就很难衡量模型的好坏。通常我们会定义一些衡量标准,这些标准可以通过对某些误差或者拟合程度的计算来得到。在这个项目中,你将通过运算决定系数 R 2 R^2 R2 来量化模型的表现。模型的决定系数是回归分析中十分常用的统计信息,经常被当作衡量模型预测能力好坏的标准。
R 2 R^2 R2 的数值范围从0至1,表示目标变量的预测值和实际值之间的相关程度平方的百分比。一个模型的 R 2 R^2 R2 值为0还不如直接用平均值来预测效果好;而一个 R 2 R^2 R2 值为1的模型则可以对目标变量进行完美的预测。从0至1之间的数值,则表示该模型中目标变量中有百分之多少能够用特征来解释。模型也可能出现负值的 R 2 R^2 R2,这种情况下模型所做预测有时会比直接计算目标变量的平均值差很多。
问题5.1:
在下方代码的 performance_metric
函数中,你要实现:
sklearn.metrics
中的 r2_score
来计算 y_true
和 y_predict
的 R 2 R^2 R2 值,作为对其表现的评判。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
笔记:
R2是评价模型表现的方法之一,每个机器学习模型的建立都要有相对应的评价指标,后面我们会学到更多的评价指标。不过R2其实也有很多局限性需要注意 https://en.wikipedia.org/wiki/Coefficient_of_determination#Caveats
skearn对于常见的模型表现衡量方法也有详细的介绍。
http://scikit-learn.org/stable/modules/model_evaluation.html
问题 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_true
和 y_predict
的决定系数。
提示2: R 2 R^2 R2 分数是指可以从自变量中预测的因变量的方差比例。 换一种说法:
#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.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
, 将 features
和 prices
的数据都分成用于训练的数据子集和用于测试的数据子集。
train_test_split
中的 random_state
,这会确保结果的一致性;X_train
, X_test
, y_train
和 y_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 R^2 R2。曲线的阴影区域代表的是该曲线的不确定性(用标准差衡量)。
运行下方区域中的代码,并利用输出的图形回答下面的问题。
# Produce learning curves for varying training set sizes and maximum depths
vs.ModelLearning(features, labels)
**提示:**学习曲线的评分是否最终会收敛到特定的值?一般来说,你拥有的数据越多,模型表现力越好。但是,如果你的训练和测试曲线以高于基准阈值的分数收敛,这是否有必要?基于训练和测试曲线已经收敛的前提下,思考添加更多训练点的优缺点。
问题 7 - 回答:当max_depth为3时,随着训练数据的增加,训练集曲线的评分降低,验证集曲线的评分提高,但是两者会收敛到一特定数值,随着训练数据的增加,模型表现不再提高,此时没有必要再继续增加训练数据,因为会增加模型的训练时间。但是,如果在过拟合的情况下,比方说当max_depth为6或者10时,随着训练数据的增加,模型表现也不断提高,此时反而有必要增加训练数据。
笔记:
对训练曲线和测试曲线趋势和意义解释的很好。这里随着数据的增多,max_depth不变的情况下,模型提升的幅度也越来越小。
传统的机器学习算法(又被称为基于统计的机器学习)在数据量达到一定程度后,更多的数据无法提升模型的表现。深度学习的一个优势就是它可以把大量的数据利用起来,提升学习表现。
这里还有更多关于学习曲线的介绍:
https://www.coursera.org/learn/machine-learning/lecture/Kont7/learning-curves
http://scikit-learn.org/stable/auto_examples/model_selection/plot_learning_curve.html
传统的机器学习算法(又被称为基于统计的机器学习)在数据量达到一定程度后,更多的数据无法提升模型的表现。深度学习的一个优势就是它可以把大量的数据利用起来,提升学习表现。
这里还有更多关于学习曲线的介绍:
https://www.coursera.org/learn/machine-learning/lecture/Kont7/learning-curves
http://scikit-learn.org/stable/auto_examples/model_selection/plot_learning_curve.html
下列代码内的区域会输出一幅图像,它展示了一个已经经过训练和验证的决策树模型在不同最大深度条件下的表现。这个图形将包含两条曲线,一个是训练集的变化,一个是验证集的变化。跟学习曲线相似,阴影区域代表该曲线的不确定性,模型训练和测试部分的评分都用的 performance_metric
函数。
运行下方区域中的代码,根据问题5与问题6对输出的图形进行分析并给出你思考的结果。
vs.ModelComplexity(X_train, y_train)
提示: 高偏差表示欠拟合(模型过于简单),而高方差表示过拟合(模型过于复杂,以至于无法泛化)。考虑哪种模型(深度1或10)对应着上述的情况,并权衡偏差与方差。
问题 8 - 回答:当max_depth为1时,模型表现出高偏差,因为训练集和测试集的R2分数都比较低;当max_depth为10时,模型表现出高方差,因为训练集的R2分数较高,而验证集的R2分数较低。
笔记:
偏差-方差分解是解释学习算法泛化性能的重要工具。这里你需要理解三个方面:
什么是泛化误差(Generalization error):
Bias-Variance分解:
现在我们来看题目中的方差和偏差到底是什么意思?通过简单的多项式展开合并,可以对算法的期望泛化误差进行分解可以得到(具体推到过程请参考[1]、[2]) :
也就是说:泛化误差=偏差+方差+噪声
偏差度量了学习算法的期望预测和真实结果的偏离程度,即刻画了学习算法本身的拟合能力。
方差度量了同样大小的训练集的变动所导致的学习性能的变化, 即 刻画了数据扰动所造成的影响。
Bias-Variance trade-off:
一般情况下,偏差和方差是有冲突的,如下图所示。为了取得好的泛化性能,则需要偏差较小,即能够充分拟合数据,并且方差较小,即使得数据扰动产生的影响小。
更多内容请阅读参考资料。
参考资料:
[1] «机器学习», 周志华, 2.5 节偏差与方差.
[2] Understanding the Bias-Variance Tradeoff
提示:查看问题8上方的图表,并查看模型在不同 depth
下的验证分数。随着深度的增加模型的表现力会变得更好吗?我们在什么情况下获得最佳验证分数而不会使我们的模型过度复杂?请记住,奥卡姆剃刀:“在竞争性假设中,应该选择假设最少的那一个。”
问题 9 - 回答:
在项目的最后一节中,你将构建一个模型,并使用 fit_model
中的优化模型去预测客户特征集。
提示:在解释网格搜索算法时,首先要理解我们为什么使用网格搜索算法,以及我们使用它的最终目的是什么。为了使你的回答更具有说服力,你还可以给出一个模型中可以使用此方法进行优化参数的示例。
问题 10 - 回答: 网格搜索法是给定参数值,穷举搜索寻找最优参数的算法。网格搜索法通过构建一个参数的字典,创造出不同的参数组合,输入给分类器进行训练,采用交叉验证,寻找验证分数最高的那一组参数,这一组参数就是模型的最优参数。
'cv_results_'
属性能告诉我们什么?提示:在解释k-fold交叉验证时,一定要理解’k’是什么,和数据集是如何分成不同的部分来进行训练和测试的,以及基于’k’值运行的次数。
在考虑k-fold交叉验证如何帮助网格搜索时,你可以使用特定的数据子集来进行训练与测试有什么缺点,以及K折交叉验证是如何帮助缓解这个问题。
问题 11 - 回答:
在这个练习中,你将需要将所学到的内容整合,使用决策树算法训练一个模型。为了得出的是一个最优模型,你需要使用网格搜索法训练模型,以找到最佳的 'max_depth'
参数。你可以把'max_depth'
参数理解为决策树算法在做出预测前,允许其对数据提出问题的数量。决策树是监督学习算法中的一种。
在下方 fit_model
函数中,你需要做的是:
'cross_validator'
变量: 使用 sklearn.model_selection
中的 KFold
创建一个交叉验证生成器对象;'regressor'
变量: 使用 sklearn.tree
中的 DecisionTreeRegressor
创建一个决策树的回归函数;'params'
变量: 为 'max_depth'
参数创造一个字典,它的值是从1至10的数组;'scoring_fnc'
变量: 使用 sklearn.metrics
中的 make_scorer
创建一个评分函数;‘performance_metric’
作为参数传至这个函数中;'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_
当我们用数据训练出一个模型,它就可用于对新的数据进行预测。在我们的例子–决策树回归函数中,模型已经学会对新输入的数据“提问”,并返回对目标变量的预测值。你可以用这些预测来获取未知目标变量的数据的信息,但是,输入的新数据必须不能是已有训练数据之中的。
最优模型的最大深度(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:这个结果并不是理想的,应该还需要利用决策树的其他参数进行网格搜索,以及使用更多的特征;