机器学习项目:boston_housing

59次阅读

共计 19598 个字符,预计需要花费 49 分钟才能阅读完成。

机器学习工程师纳米学位
模型评价与验证
项目 1: 预测波士顿房价
欢迎来到预测波士顿房价项目!在此文件中,我们已经提供了一些示例代码,但你还需要完善更多功能才能让项目成功运行。除非有明确要求,你无需修改任何已给出的代码。习题的标题为编程练习。每一部分都会有详细的指导,需要实现的部分也会在注释中以 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(‘ 你已经成功载入所有库!’)
你已经成功载入所有库!

编程练习 1:加载数据
你的第一个编程练习是加载波士顿房价数据。我们已为你导入了 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:观察数据
你的第二个编程练习是对波士顿房价的数据进行观察, 这一步会让你掌握更多数据本身的信息。具体问题如下:
问题 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()

问题 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])
笔记:

对目标变量的变换 np.loglp():这个函数用来在数据预处理过程对偏度较大的数据进行平滑处理,使其更加符合高斯分布,有利于后续得到一个更好地结果,数学公式:ln(x+1).

对数值类型的特征变量的变换 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_true 和 y_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_true 和 y_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,将 features 和 prices 的数据都分成用于训练的数据子集和用于测试的数据子集。

分割比例为:80% 的数据用于训练,20% 用于测试;
选定一个数值以设定 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$。曲线的阴影区域代表的是该曲线的不确定性(用标准差衡量)。
运行下方区域中的代码,并利用输出的图形回答下面的问题。
# 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 不变的情况下,模型提升的幅度也越来越小。
传统的机器学习算法(又被称为基于统计的机器学习)在数据量达到一定程度后,更多的数据无法提升模型的表现。深度学习的一个优势就是它可以把大量的数据利用起来,提升学习表现。
这里还有更多关于学习曲线的介绍: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]) :

也就是说:泛化误差 = 偏差 + 方差 + 噪声

偏差度量了学习算法的期望预测和真实结果的偏离程度,即刻画了学习算法本身的拟合能力。

方差度量了同样大小的训练集的变动所导致的学习性能的变化, 即 刻画了数据扰动所造成的影响。

Bias-Variance trade-off:
一般情况下,偏差和方差是有冲突的,如下图所示。为了取得好的泛化性能,则需要偏差较小,即能够充分拟合数据,并且方差较小,即使得数据扰动产生的影响小。

欠拟合:在训练不足时,学习的拟合能力不够强,训练数据的扰动不足以使学习器产生显著变化,偏差主导了泛化错误率。

过拟合:充分训练后, 学习器的拟合能力已非常强, 训练数据的轻微扰动都会导致学习器发生显著变化, 训练数据自身的、非全局的特性被学习器学到了,方差主导了泛化错误率。

更多内容请阅读参考资料。
参考资料:
[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 函数中,你需要做的是:

定义 ‘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_
第六步. 做出预测
当我们用数据训练出一个模型,它就可用于对新的数据进行预测。在我们的例子 – 决策树回归函数中,模型已经学会对新输入的数据“提问”,并返回对目标变量的预测值。你可以用这些预测来获取未知目标变量的数据的信息,但是,输入的新数据必须不能是已有训练数据之中的。
问题 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:这个结果并不是理想的,应该还需要利用决策树的其他参数进行网格搜索,以及使用更多的特征;

正文完
 0

机器学习项目:boston_housing

65次阅读

共计 19970 个字符,预计需要花费 50 分钟才能阅读完成。

机器学习工程师纳米学位
模型评价与验证
项目 1: 预测波士顿房价
欢迎来到预测波士顿房价项目!在此文件中,我们已经提供了一些示例代码,但你还需要完善更多功能才能让项目成功运行。除非有明确要求,你无需修改任何已给出的代码。习题的标题为编程练习。每一部分都会有详细的指导,需要实现的部分也会在注释中以 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(‘ 你已经成功载入所有库!’)
你已经成功载入所有库!

编程练习 1:加载数据
你的第一个编程练习是加载波士顿房价数据。我们已为你导入了 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:观察数据
你的第二个编程练习是对波士顿房价的数据进行观察, 这一步会让你掌握更多数据本身的信息。具体问题如下:
问题 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])
<div><style scoped>
.dataframe tbody tr th:only-of-type {
vertical-align: middle;
}

.dataframe tbody tr th {
vertical-align: top;
}

.dataframe thead th {
text-align: right;
}
</style>

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

<p>8 rows × 37 columns</p></div>
# 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()

问题 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
<div><style scoped>
.dataframe tbody tr th:only-of-type {
vertical-align: middle;
}

.dataframe tbody tr th {
vertical-align: top;
}

.dataframe thead th {
text-align: right;
}
</style>

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

<p>3 rows × 80 columns</p></div>
# 需要填充众数的特征
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])
笔记:

对目标变量的变换 np.loglp():这个函数用来在数据预处理过程对偏度较大的数据进行平滑处理,使其更加符合高斯分布,有利于后续得到一个更好地结果,数学公式:ln(x+1).

对数值类型的特征变量的变换 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_true 和 y_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_true 和 y_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,将 features 和 prices 的数据都分成用于训练的数据子集和用于测试的数据子集。

分割比例为:80% 的数据用于训练,20% 用于测试;
选定一个数值以设定 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$。曲线的阴影区域代表的是该曲线的不确定性(用标准差衡量)。
运行下方区域中的代码,并利用输出的图形回答下面的问题。
# 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 不变的情况下,模型提升的幅度也越来越小。
传统的机器学习算法(又被称为基于统计的机器学习)在数据量达到一定程度后,更多的数据无法提升模型的表现。深度学习的一个优势就是它可以把大量的数据利用起来,提升学习表现。
这里还有更多关于学习曲线的介绍: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]) :

也就是说:泛化误差 = 偏差 + 方差 + 噪声

偏差度量了学习算法的期望预测和真实结果的偏离程度,即刻画了学习算法本身的拟合能力。

方差度量了同样大小的训练集的变动所导致的学习性能的变化, 即 刻画了数据扰动所造成的影响。

Bias-Variance trade-off:
一般情况下,偏差和方差是有冲突的,如下图所示。为了取得好的泛化性能,则需要偏差较小,即能够充分拟合数据,并且方差较小,即使得数据扰动产生的影响小。

欠拟合:在训练不足时,学习的拟合能力不够强,训练数据的扰动不足以使学习器产生显著变化,偏差主导了泛化错误率。

过拟合:充分训练后, 学习器的拟合能力已非常强, 训练数据的轻微扰动都会导致学习器发生显著变化, 训练数据自身的、非全局的特性被学习器学到了,方差主导了泛化错误率。

更多内容请阅读参考资料。
参考资料:
[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 函数中,你需要做的是:

定义 ‘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_
第六步. 做出预测
当我们用数据训练出一个模型,它就可用于对新的数据进行预测。在我们的例子 – 决策树回归函数中,模型已经学会对新输入的数据“提问”,并返回对目标变量的预测值。你可以用这些预测来获取未知目标变量的数据的信息,但是,输入的新数据必须不能是已有训练数据之中的。
问题 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:这个结果并不是理想的,应该还需要利用决策树的其他参数进行网格搜索,以及使用更多的特征;

正文完
 0