欢迎来到机器学习工程师纳米学位的第一个项目!在此文件中,有些示例代码已经提供给你,但你还需要实现更多的功能来让项目成功运行。除非有明确要求,你无须修改任何已给出的代码。以'练习'开始的标题表示接下来的内容中有需要你必须实现的功能。每一部分都会有详细的指导,需要实现的部分也会在注释中以'TODO'标出。请仔细阅读所有的提示!
除了实现代码外,你还必须回答一些与项目和实现有关的问题。每一个需要你回答的问题都会以'问题 X'为标题。请仔细阅读每个问题,并且在问题后的'回答'文字框中写出完整的答案。你的项目将会根据你对问题的回答和撰写代码所实现的功能来进行评分。
提示:Code 和 Markdown 区域可通过 Shift + Enter 快捷键运行。此外,Markdown可以通过双击进入编辑模式。
在这个项目中,你将利用马萨诸塞州波士顿郊区的房屋信息数据训练和测试一个模型,并对模型的性能和预测能力进行测试。通过该数据训练后的好的模型可以被用来对房屋做特定预测---尤其是对房屋的价值。对于房地产经纪等人的日常工作来说,这样的预测模型被证明非常有价值。
此项目的数据集来自UCI机器学习知识库。波士顿房屋这些数据于1978年开始统计,共506个数据点,涵盖了麻省波士顿不同郊区房屋14种特征的信息。本项目对原始数据集做了以下处理:
'MEDV'
值为50.0的数据点被移除。 这很可能是由于这些数据点包含遗失或看不到的值。'RM'
值为8.78. 这是一个异常值,已经被移除。'RM'
, 'LSTAT'
,'PTRATIO'
以及'MEDV'
特征是必要的,其余不相关特征已经被移除。'MEDV'
特征的值已经过必要的数学转换,可以反映35年来市场的通货膨胀效应。运行下面区域的代码以载入波士顿房屋数据集,以及一些此项目所需的Python库。如果成功返回数据集的大小,表示数据集已载入成功。
# Import libraries necessary for this project
# 载入此项目所需要的库
import numpy as np
import pandas as pd
import visuals as vs # Supplementary code
from sklearn.cross_validation import ShuffleSplit
# Pretty display for notebooks
# 让结果在notebook中显示
%matplotlib inline
# Load the Boston housing dataset
# 载入波士顿房屋的数据集
data = pd.read_csv('housing.csv')
prices = data['MEDV']
features = data.drop('MEDV', axis = 1)
# Success
# 完成
print "Boston housing dataset has {} data points with {} variables each.".format(*data.shape)
在项目的第一个部分,你会对波士顿房地产数据进行初步的观察并给出你的分析。通过对数据的探索来熟悉数据可以让你更好地理解和解释你的结果。
由于这个项目的最终目标是建立一个预测房屋价值的模型,我们需要将数据集分为特征(features)和目标变量(target variable)。特征 'RM'
, 'LSTAT'
,和 'PTRATIO'
,给我们提供了每个数据点的数量相关的信息。目标变量:'MEDV'
,是我们希望预测的变量。他们分别被存在features
和prices
两个变量名中。
你的第一个编程练习是计算有关波士顿房价的描述统计数据。我们已为你导入了numpy
,你需要使用这个库来执行必要的计算。这些统计数据对于分析模型的预测结果非常重要的。
在下面的代码中,你要做的是:
prices
中的'MEDV'
的最小值、最大值、均值、中值和标准差;# TODO: Minimum price of the data
#目标:计算价值的最小值
minimum_price = np.min(prices)
# TODO: Maximum price of the data
#目标:计算价值的最大值
maximum_price = np.max(prices)
# TODO: Mean price of the data
#目标:计算价值的平均值
mean_price = np.mean(prices)
# TODO: Median price of the data
#目标:计算价值的中值
median_price = np.median(prices)
# TODO: Standard deviation of prices of the data
#目标:计算价值的标准差
std_price = np.std(prices)
# Show the calculated statistics
#目标:输出计算的结果
print "Statistics for Boston housing dataset:\n"
print "Minimum price: ${:,.2f}".format(minimum_price)
print "Maximum price: ${:,.2f}".format(maximum_price)
print "Mean price: ${:,.2f}".format(mean_price)
print "Median price ${:,.2f}".format(median_price)
print "Standard deviation of prices: ${:,.2f}".format(std_price)
如前文所述,本项目中我们关注的是其中三个值:'RM'
、'LSTAT'
和'PTRATIO'
,对每一个数据点:
'RM'
是该地区中每个房屋的平均房间数量;'LSTAT'
是指该地区有多少百分比的房东属于是低收入阶层(有工作但收入微薄);'PTRATIO'
是该地区的中学和小学里,学生和老师的数目比(学生/老师
)。凭直觉,上述三个特征中对每一个来说,你认为增大该特征的数值,'MEDV'
的值会是增大还是减小呢?每一个答案都需要你给出理由。
提示:你预期一个'RM'
值是6的房屋跟'RM'
值是7的房屋相比,价值更高还是更低呢?
回答: 当然是 RM 高的房子价格高。
在项目的第二部分中,你需要了解必要的工具和技巧来让你的模型进行预测。用这些工具和技巧对每一个模型的表现做精确的衡量可以极大地增强你预测的信心。
如果不能对模型的训练和测试的表现进行量化地评估,我们就很难衡量模型的好坏。通常我们会定义一些衡量标准,这些标准可以通过对某些误差或者拟合程度的计算来得到。在这个项目中,你将通过运算决定系数R2 来量化模型的表现。模型的决定系数是回归分析中十分常用的统计信息,经常被当作衡量模型预测能力好坏的标准。
R2的数值范围从0至1,表示目标变量的预测值和实际值之间的相关程度平方的百分比。一个模型的R2 值为0还不如直接用平均值来预测效果好;而一个R2 值为1的模型则可以对目标变量进行完美的预测。从0至1之间的数值,则表示该模型中目标变量中有百分之多少能够用特征来解释。_模型也可能出现负值的R2,这种情况下模型所做预测有时会比直接计算目标变量的平均值差很多。
在下方代码的 performance_metric
函数中,你要实现:
sklearn.metrics
中的 r2_score
来计算 y_true
和 y_predict
的R2值,作为对其表现的评判。score
变量中。# TODO: Import '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: Calculate the performance score between 'y_true' and 'y_predict'
score = r2_score(y_true, y_predict)
# Return the score
return score
假设一个数据集有五个数据且一个模型做出下列目标变量的预测:
真实数值 | 预测数值 |
---|---|
3.0 | 2.5 |
-0.5 | 0.0 |
2.0 | 2.1 |
7.0 | 7.8 |
4.2 | 5.3 |
你会觉得这个模型已成功地描述了目标变量的变化吗?如果成功,请解释为什么,如果没有,也请给出原因。
运行下方的代码,使用performance_metric
函数来计算模型的决定系数。
# Calculate the performance of this model
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)
回答: 已经可以说成功预测了,因为它有0.923的 $R^2$ 分数,很接近于1。
接下来,你需要把波士顿房屋数据集分成训练和测试两个子集。通常在这个过程中,数据也会被重新排序,以消除数据集中由于排序而产生的偏差。 在下面的代码中,你需要:
sklearn.cross_validation
中的 train_test_split
, 将features
和prices
的数据都分成用于训练的数据子集和用于测试的数据子集。train_test_split
中的 random_state
,这会确保结果的一致性;X_train
,X_test
,y_train
,和y_test
。# TODO: Import 'train_test_split'
from sklearn.cross_validation import train_test_split
# TODO: Shuffle and split the data into training and testing subsets
X_train, X_test, y_train, y_test = train_test_split(features, prices,
train_size=0.8, random_state=123)
# Success
print "Training and testing split was successful."
答案: 如果没有数据进行测试,就无法对模型的预测能力做出评估。
模型在训练集上表现良好只能说明它具有记忆力,只有它在从未训练过的数据上仍然保持良好的预期效果时,我们才能说它是有效的模型。
在项目的第三部分,我们来看一下几个模型针对不同的数据集在学习和测试上的表现。另外,你需要专注于一个特定的算法,用全部训练集训练时,提高它的'max_depth'
参数,观察这一参数的变化如何影响模型的表现。把你模型的表现画出来对于分析过程十分有益。可视化可以让我们看到一些单看结果看不到的行为。
下方区域内的代码会输出四幅图像,它们是一个决策树模型在不同最大深度下的表现。每一条曲线都直观的显示了随着训练数据量的增加,模型学习曲线的训练评分和测试评分的变化。注意,曲线的阴影区域代表的是该曲线的不确定性(用标准差衡量)。这个模型的训练和测试部分都使用决定系数R2来评分。
运行下方区域中的代码,并利用输出的图形回答下面的问题。
# Produce learning curves for varying training set sizes and maximum depths
vs.ModelLearning(features, prices)
选择上述图像中的其中一个,并给出其最大深度。随着训练数据量的增加,训练曲线的评分有怎样的变化?测试曲线呢?如果有更多的训练数据,是否能有效提升模型的表现呢? 提示:学习曲线的评分是否最终会收敛到特定的值?
答案:
我选择最大深度为3的决策树的学习曲线。
Q: 随着训练数据量的增加,训练曲线的评分有怎样的变化?
A: 训练曲线的分数逐渐降低。
Q: 测试曲线呢?
A: 测试曲线的分数逐渐上升。
Q: 如果有更多的训练数据,是否能有效提升模型的表现呢?
A: 不能提升模型的表现,因为后期即使增加100个训练数据也不能提高模型的$R^2$分数。
Q: 学习曲线的评分是否最终会收敛到特定的值?
A: 学习曲线的评分最终都能收敛到特定的值。
下列代码内的区域会输出一幅图像,它展示了一个已经经过训练和验证的决策树模型在不同最大深度条件下的表现。这个图形将包含两条曲线,一个是训练的变化,一个是测试的变化。跟学习曲线相似,阴影区域代表该曲线的不确定性,模型训练和测试部分的评分都用的 performance_metric
函数。
运行下方区域中的代码,并利用输出的图形并回答下面的两个问题。
vs.ModelComplexity(X_train, y_train)
当模型以最大深度 1训练时,模型的预测是出现很大的偏差还是出现了很大的方差?当模型以最大深度10训练时,情形又如何呢?图形中的哪些特征能够支持你的结论?
提示: 你如何得知模型是否出现了偏差很大或者方差很大的问题?
答案:
Q: 当模型以最大深度 1训练时,模型的预测是出现很大的偏差还是出现了很大的方差?
A: 很大的偏差。
Q: 当模型以最大深度10训练时,情形又如何呢?
A: 很大的方差。
Q: 图形中的哪些特征能够支持你的结论?
A: 当决策树的深度为1时,训练分数和测试分数都非常低,也就是说它处在一种欠拟合的状态,属于高偏差。
当决策树的深度为10时,虽然它的训练分数很高,但是它的测试分数比之前决策树深度稍低的时候的分数还要低,因此它处在过拟合的状态,属于高方差。
你认为最大深度是多少的模型能够最好地对未见过的数据进行预测?为什么你会得出了这个答案?
答案: 我会以最大深度为4的决策树去进行预测,因为它的测试分数最高。
什么是网格搜索法?如何用它来优化学习算法?
回答:
Q: 什么是网格搜索法?
A: 网格搜索算法是一种能将所有可选的参数排列组合,然后逐个去学习,求得最高分数的模型的一个算法。
Q: 如何用它来优化学习算法?
A: 我们可以通过它来选择最好的模型。
什么是K折交叉验证法(k-fold cross-validation)?优化模型时,使用这种方法对网格搜索有什么好处?
提示: 跟为何需要一组训练集的原因差不多,网格搜索时如果不使用交叉验证会有什么问题?
答案:
Q: 什么是K折交叉验证法(k-fold cross-validation)?
A: K折交叉验证就是把数据集分成k份,然后循环k次,每次使用第k个数据集当测试集,其余k-1个数据集做训练集,然后将训练结果做一个平均。
Q: 优化模型时,使用这种方法对网格搜索有什么好处?
A: 使用 K 折交叉验证能尽可能避免训练集随机分配的偏差导致模型准确度之间的差异,同时还能验证模型的稳健性。
在最后一个练习中,你将需要将所学到的内容整合,使用决策树演算法训练一个模型。为了保证你得出的是一个最优模型,你需要使用网格搜索法训练模型,以找到最佳的 'max_depth'
参数。你可以把'max_depth'
参数理解为决策树算法在做出预测前,允许其对数据提出问题的数量。决策树是监督学习算法中的一种。
此外,你会发现你的实现使用的是 ShuffleSplit()
。它也是交叉验证的一种方式(见变量 'cv_sets'
)。虽然这不是问题8中描述的 K-Fold 交叉验证,这个教程验证方法也很有用!这里 ShuffleSplit()
会创造十个混洗过的集合,每个集合中20%('test_size'
)的数据会被用作验证集。当你在实现的时候,想一想这跟 K-Fold 交叉验证有哪些相同点,哪些不同点?
在下方 fit_model
函数中,你需要做的是:
sklearn.tree
中的 DecisionTreeRegressor
创建一个决策树的回归函数;'regressor'
变量中;'max_depth'
创造一个字典,它的值是从1至10的数组,并储存到 'params'
变量中;sklearn.metrics
中的 make_scorer
创建一个评分函数;performance_metric
作为参数传至这个函数中;'scoring_fnc'
变量中;sklearn.grid_search
中的 GridSearchCV
创建一个网格搜索对象;'regressor'
, 'params'
, 'scoring_fnc'
, 和 'cv_sets'
作为参数传至这个对象中;GridSearchCV
存到 'grid'
变量中。如果有同学对python函数如何传递多个参数不熟悉,可以参考这个MIT课程的视频。
# TODO: Import 'make_scorer', 'DecisionTreeRegressor', and 'GridSearchCV'
from sklearn.tree import DecisionTreeRegressor
from sklearn.metrics import make_scorer
from sklearn.grid_search 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]. """
# Create cross-validation sets from the training data
cv_sets = ShuffleSplit(X.shape[0], n_iter = 10, test_size = 0.20, random_state = 0)
# TODO: Create a decision tree regressor object
regressor = DecisionTreeRegressor(random_state=123)
# TODO: Create a dictionary for the parameter 'max_depth' with a range from 1 to 10
params = {'max_depth': range(1, 10)}
# TODO: Transform 'performance_metric' into a scoring function using 'make_scorer'
scoring_fnc = make_scorer(performance_metric)
# TODO: Create the grid search object
grid = GridSearchCV(regressor, params, scoring_fnc, cv=cv_sets)
# 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_
当我们用数据训练出一个模型,它现在就可用于对新的数据进行预测。在决策树回归函数中,模型已经学会对新输入的数据提问,并返回对目标变量的预测值。你可以用这个预测来获取数据未知目标变量的信息,这些数据必须是不包含在训练数据之内的。
# 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'])
Answer: 最优参数为4,与我的猜测相同。
想像你是一个在波士顿地区的房屋经纪人,并期待使用此模型以帮助你的客户评估他们想出售的房屋。你已经从你的三个客户收集到以下的资讯:
特征 | 客戶 1 | 客戶 2 | 客戶 3 |
---|---|---|---|
房屋内房间总数 | 5 间房间 | 4 间房间 | 8 间房间 |
社区贫困指数(%被认为是贫困阶层) | 17% | 32% | 3% |
邻近学校的学生-老师比例 | 15:1 | 22:1 | 12:1 |
你会建议每位客户的房屋销售的价格为多少?从房屋特征的数值判断,这样的价格合理吗?
提示:用你在分析数据部分计算出来的统计信息来帮助你证明你的答案。
运行下列的代码区域,使用你优化的模型来为每位客户的房屋价值做出预测。
# Produce a matrix for client data
client_data = [[5, 17, 15], # Client 1
[4, 32, 22], # Client 2
[8, 3, 12]] # Client 3
# Show predictions
for i, price in enumerate(reg.predict(client_data)):
print "Predicted selling price for Client {}'s home: ${:,.2f}".format(i+1, price)
答案: 我会建议客户按照上面的建议去销售,从房间数量,贫困指数,师生比例来看,建议的价格是合理的。
一个最优的模型不一定是一个健壮模型。有的时候模型会过于复杂或者过于简单,以致于难以泛化新增添的数据;有的时候模型采用的学习算法并不适用于特定的数据结构;有的时候样本本身可能有太多噪点或样本过少,使得模型无法准确地预测目标变量。这些情况下我们会说模型是欠拟合的。执行下方区域中的代码,采用不同的训练和测试集执行 fit_model
函数10次。注意观察对一个特定的客户来说,预测是如何随训练数据的变化而变化的。
vs.PredictTrials(features, prices, fit_model, client_data)
简单地讨论一下你建构的模型能否在现实世界中使用?
提示: 回答几个问题:
答案:
(本题结果不影响项目是否通过)通过上面的实践,相信你对机器学习的一些常用概念有了很好的领悟和掌握。但利用70年代的波士顿房价数据进行建模的确对我们来说意义不是太大。现在你可以把你上面所学应用到北京房价数据集中bj_housing.csv
。
免责声明:考虑到北京房价受到宏观经济、政策调整等众多因素的直接影响,预测结果仅供参考。
这个数据集的特征有:
目标变量:
你可以参考上面学到的内容,拿这个数据集来练习数据分割与重排、定义衡量标准、训练模型、评价模型表现、使用交叉验证对参数进行调优并选出最佳参数,比较两者的差别,最终得出最佳模型对验证集的预测分数。
### 你的代码
data = pd.read_csv('bj_housing.csv')
data.head()
X = data.drop('Value', axis = 1)
y = data['Value']
X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.8, random_state=123)
print len(X_train), len(X_test)
$R^2$大概在0.6左右。
vs.ModelComplexity(X_train, y_train)
结果是n_estimators=29时效果最好。
from sklearn.ensemble import ExtraTreesRegressor
cv_sets = ShuffleSplit(X_train.shape[0], n_iter=10, test_size=0.20, random_state=0)
scoring_fnc = make_scorer(performance_metric)
params = {'n_estimators': range(20, 40, 3)}
grid = GridSearchCV(ExtraTreesRegressor(random_state=0), params, scoring_fnc, cv=cv_sets)
grid.fit(X_train, y_train)
print grid.best_score_
reg = grid.best_estimator_
reg.get_params()
可以看到$R^2$的平均值是0.7315,$R^2$的标准差也只有0.0551,效果还行,模型稳定。
from sklearn.model_selection import KFold, cross_val_score
kfold = KFold(n_splits=10)
scores = cross_val_score(reg, X, y, cv=kfold)
print 'mean: %.4f, std: %.4f' % (scores.mean(), scores.std())
reg.score(X_test, y_test)
import random
indexes = random.sample(X_test.index, 1000)
df = X_test.loc[indexes].copy()
# df = X_test.copy()
df.loc[:,'Prediction'] = reg.predict(df)
df.loc[:,'Real'] = y_test.loc[indexes]
df.head()
import seaborn as sns
l = 500
sns.jointplot(x=df["Prediction"], y=df["Real"], xlim=l, ylim=l)
你成功的用新的数据集构建了模型了吗?他能对测试数据进行验证吗?它的表现是否符合你的预期?交叉验证是否有助于提升你模型的表现?
答案:
Q: 你成功的用新的数据集构建了模型了吗?
A: 是的。
Q: 他能对测试数据进行验证吗?
A: 能。
Q: 它的表现是否符合你的预期?
A: 还行,有0.746的R^2了。
Q: 交叉验证是否有助于提升你模型的表现?
A: 它只能帮我检验模型表现,不能提升表现。
如果你是从零开始构建机器学习的代码会让你一时觉得无从下手。这时不要着急,你要做的只是查看之前写的代码,把每一行都看明白,然后逐步构建你的模型。当中遇到什么问题也可以在我们论坛寻找答案。也许你会发现你所构建的模型的表现并没有达到你的预期,这说明机器学习并非是一项简单的任务,构建一个表现良好的模型需要长时间的研究和测试。这也是我们接下来的课程中会逐渐学到的。