欢迎来到机器学习纳米学位的第一个实战项目!在这个 notebook 中,我们已经为你提供了一些代码模板,你将需要补充函数来顺利完成项目。除非有明确要求,你无须修改任何已给出的代码。以 “实现” 为开头的部分意味着你需要在之后的大括号中提供额外的函数。每一部分都会有详细的指导,需要实现的部分也会以 “TODO” 标出。请仔细阅读所有的提示!
除了实现代码外,你还必须回答一些与项目和实现有关的问题。每一个需要你回答的问题都会以 “问题 X” 为标题。请仔细阅读每个问题,并且在以 “回答:” 开头的文字框中写出完整的答案。你的项目将会根据你的回答和实现来进行评分。
请注意: Code 和 Markdown 区域可通过 Shift + Enter 快捷键运行。此外,Markdown可以通过双击进入编辑模式。
在这个项目中,你将利用马萨诸塞州波士顿郊区的房屋信息数据来训练和测试一个模型,并对模型的性能和预测能力进行评估。使用该数据训练出适配的模型可用于对房屋进行特定预测,尤其是预测房屋的价值。对于房地产经纪等人的日常工作来说,这样的预测模型非常有价值。
此项目的数据集来自 UCI 机器学习代码库。波士顿房屋数据于1978年开始统计,共506个数据点,涵盖了波士顿郊区房屋的14种特征信息。本项目对原始数据集做了以下处理:
'MEDV'
值为 50.0。由于这些数据点包含遗失或被删去的值,因此已被移除。RM
值为 8.78。这是一个异常值,因此已被移除。'RM'
,'LSTAT'
,'PTRATIO'
和 'MEDV'
。其余不相关的特征已被移除。'MEDV'
已根据 35 年来的市场通货膨胀状况进行了加倍。运行下面的代码以加载波士顿房屋数据集,以及一些此项目所需的 Python 库。如果成功返回数据集的大小,表示数据集已载入成功。
In [ ]:
# Import libraries necessary for this project
import numpy as np
import pandas as pd
from sklearn.cross_validation import ShuffleSplit
# Import supplementary visualizations code visuals.py
import visuals as vs
# Pretty display for notebooks
%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))
在该项目的第一部分,你将对波士顿的房地产数据进行初步观察和分析。你可以先探索和熟悉数据,这样可以更好地理解和解释你的结果。
由于本项目的最终目标是建立一个预测房屋价值的模型,因此我们需要将数据集分为特征和目标变量。特征,即'RM'
,'LSTAT'
和 'PTRATIO'
为我们提供了每个数据点的数量信息。目标变量,即 'MEDV'
是我们准备预测的变量。它们被分别存储在 features
和 prices
中。
你的第一个编程实现是计算有关波士顿房价的描述统计数据。我们已为你导入了 numpy
,你需要使用这个库来执行必要的计算。这些统计数据对于分析模型的预测结果十分重要。
在下面的代码中,你将需要实现以下内容:
prices
中的 'MEDV'
的最小值、最大值、均值、中值和标准差。
In [ ]:
# TODO: Minimum price of the data
minimum_price = None
# TODO: Maximum price of the data
maximum_price = None
# TODO: Mean price of the data
mean_price = None
# TODO: Median price of the data
median_price = None
# TODO: Standard deviation of prices of the data
std_price = None
# Show the calculated statistics
print("Statistics for Boston housing dataset:\n")
print("Minimum price: ${}".format(minimum_price))
print("Maximum price: ${}".format(maximum_price))
print("Mean price: ${}".format(mean_price))
print("Median price ${}".format(median_price))
print("Standard deviation of prices: ${}".format(std_price))
在前面我们提到过,波士顿房屋信息的数据组中,我们用到三个特征,即 'RM'
,'LSTAT'
和'PTRATIO'
。对于每个数据点(街区):
'RM'
指该街区房屋的平均房间数量。'LSTAT'
指该街区屋主为”低收入阶层“(收入微薄)的比例。'PTRATIO'
指该街区的小学和中学中学生与老师的比例。根据你的直觉,对于上面的三个特征而言,你认为增大某特征的数值, 'MEDV'
值是会增大还是减小?请给出理由。
提示: 该问题可以使用下面的例子来解释:
'RM'
值(房间数量)为 6 的房子与 'RM'
为 7 的房子相比,哪一个价值更高?'LSTAT'
值(低收入阶级比例)为 15 的街区与 'LSTAT'
值为 20 的街区相比,哪一个房价更高?'PTRATIO'
值(学生和教师的比例)为 10 的街区与 'PTRATIO'
值为15的街区相比,哪一个房价更高?回答:
在本项目的第二部分,你将会了解必要的工具和技术来让模型进行预测。通过使用这些工具和技术,你可以精确评估每个模型的性能,来更好地加强预测结果的准确性。
如果不能对模型的训练和测试的表现进行量化评估,我们很难衡量模型的好坏。通常我们会定义一些衡量标准,这些标准可以通过对某些误差或者拟合程度的计算来得到。在这个项目中,你将通过运算决定系数 R2 来量化模型的表现。模型的决定系数是回归分析中常用的统计信息,经常被当作衡量模型预测能力好坏的标准。
R2 的数值位于 0 和 1 之间,它表示目标变量的预测值和实际值相关程度平方的百分比。当一个模型的 R2 值为 0 时,其预测效果还不如使用目标变量的均值进行预测,而 R2 值为 1 的模型能完美预测目标变量。当数值位于 0 至 1 之间时,则表示该模型中目标变量能以多大百分比用该特征表示。模型中的 R2 也有可能出现负值,在这种情况下,模型做出的预测会比直接预测均值差很多。
在下方代码块的 performance_metric
函数中,你需要实现:
sklearn.metrics
中的 r2_score
来进行 y_true
和 `y_predict 之间的性能计算。score
变量中。
In [ ]:
# TODO: 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 = None
# Return the score
return score
In [ ]:
# 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))
提示:R2 分数是从自变量中预测的因变量的方差比例。换句话说:
回答:
在这个实现中,你需要把波士顿房屋数据集分成训练和测试两个子集。通常在这个过程中,数据也会被重排列,以消除数据集中由于顺序而产生的偏差。
在下面的代码中,你需要实现以下内容:
sklearn.cross_validation
中的 train_test_split
来将 features
和 prices
分割成训练集和测试集,并重新排列。train_test_split
设置 random_state
。这将保证结果的一致性。X_train
,X_test
,y_train
和 y_test
。
In [ ]:
# TODO: Import 'train_test_split'
# TODO: Shuffle and split the data into training and testing subsets
X_train, X_test, y_train, y_test = (None, None, None, None)
# Success
print("Training and testing split was successful.")
提示: 思考一下数据分割会如何造成过拟合和欠拟合。
回答:
在本项目的第三部分,你将学习不同训练数据的子集中,几个模型的学习和测试性能。此外,你还会关注一个特定的算法,该算法的 'max_depth'
参数在整个训练集中呈上升趋势,通过这个算法,你将观察到模型的复杂度对性能的影响。根据不同标准绘制模型的性能曲线可以帮助我们进行分析,比如能让我们看到一些单从结果无法判断的行为。
下方区域内的代码会输出四幅图像,它们是一个决策树模型在不同最大深度下的表现。每一张图显示了随着训练集的增加,训练和测试模型的学习曲线的变化情况。请注意,学习曲线中的阴影部分代表了该曲线的不确定性(标准差)。该模型在训练集和测试集中的性能使用决定系数 R2 进行评分。
运行下面的代码,并根据这些图像回答问题。
In [ ]:
# Produce learning curves for varying training set sizes and maximum depths
vs.ModelLearning(features, prices)
提示:学习曲线最终会汇集到一个特定的分数吗?一般来说,数据越多越好。但如果你的训练和测试曲线在一个特定分数汇集,并且超过了你的基准,这是否必要?
根据训练和测试曲线是否汇聚,思考增加训练点的优缺点。
答案:
下列代码将输出一幅图像,它表示一个使用训练数据进行过训练和验证的决策树模型在不同最大深度下的表现。该图形将包含两条复杂度曲线,一个是训练集的变化,一个是测试集的变化。跟学习曲线相似,阴影区域代表该曲线的不确定性,模型训练和测试部分的评分都使用 performance_metric
函数。
运行下方代码,并根据此图表回答下面的两个问题(Q5 和 Q6)。
In [ ]:
vs.ModelComplexity(X_train, y_train)
提示: 高偏差是欠拟合的表示(模型不够复杂,无法察觉数据中的微妙变化),而高方差是过拟合的标志(模型中数据过于庞大,无法一般化)。思考两个模型(深度为 1 和 10)中哪一个与偏差或方差一致。
回答:
提示:结合问题 5 中的图像,查看模型在不同深度下的验证分数。当深度更高时,分数也更高吗?我们什么时候能在避免使模型过度复杂化的情况下获得最佳分数?请记住,Occams Razor 曾说过“在多个假说中,我们应该选择假设最少的哪一个。”
回答:
在本项目的最后一部分,你将构建一个模型,并使用 fit_model
中的优化模型来预测客户的特征集。
提示:在解释网格搜索技术时,请说明为何使用这一技术,“网格”代表什么,该方法的目标又是什么?你还可以举例说明如何使用这个方法来优化模型中的参数。
答案:
什么是 K 折交叉验证训练技术?
在优化模型时,该技术会给网格搜索带来哪些益处?
提示:在解释 K 折交叉验证技术时,请说明“k”代表什么?如何根据“k”值将数据集分成训练集和测试集,以及确定运行次数。
在说明 k 折交叉验证给网格搜索带来的好处时,请思考网格搜索的主要劣势,它取决于使用特定的数据集子集来进行训练或测试,以及 k 折交叉验证如何改善这一点。你可以参考这个文件。
答案:
在最终的实现中,你需要整合学习的内容,并使用决策树算法来训练一个模型。为了保证得到最优模型,你需要使用网格搜索方法来训练模型,并为这个决策树优化 'max_depth'
参数。你可以将 'max_depth'
参数视作在做出预测前,该决策树被允许针对数据提出的问题。决策树是监督学习算法的一部分。
此外,你还会发现你的实现使用 ShuffleSplit()
作为交叉验证的另一种形式(查看 'cv_sets'
变量)。尽管这并不是你在 问题 8 中描述的 k 折交叉验证技术,这种交叉验证方法同样十分有用!下方的 ShuffleSplit()
实现将创建 10('n_splits'
) 个重新排列的数据集,针对每个数据集,20%('test_size'
)的数据将作为“验证集”。在完成你的实现时,请思考该方法与 k 折验证技术的不同点和相似之处。
请注意,在 scikit-learn 的 0.17 和 0.18 版本中,ShuffleSplit 的参数也有所不同。
对于下方代码块中的 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'
变量。
In [ ]:
# TODO: Import 'make_scorer', 'DecisionTreeRegressor', and '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
# sklearn version 0.18: ShuffleSplit(n_splits=10, test_size=0.1, train_size=None, random_state=None)
# sklearn versiin 0.17: ShuffleSplit(n, n_iter=10, test_size=0.1, train_size=None, random_state=None)
cv_sets = ShuffleSplit(X.shape[0], n_iter = 10, test_size = 0.20, random_state = 0)
# TODO: Create a decision tree regressor object
regressor = None
# TODO: Create a dictionary for the parameter 'max_depth' with a range from 1 to 10
params = {}
# TODO: Transform 'performance_metric' into a scoring function using 'make_scorer'
scoring_fnc = None
# 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 'cv_sets' respectively.
grid = None
# 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_
In [ ]:
# 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']))
提示: 该答案来自上方代码的输出。
回答:
假设你是波士顿地区的房屋经纪人,并希望使用此模型帮助你的客户评估他们想出售的房屋。你已经从三个客户那里收集到以下的信息:
特征 | 客户 1 | 客户 2 | 客户 3 |
---|---|---|---|
房屋内的房间总数 | 5 间 | 4 间 | 8 间 |
社区贫困指数(%) | 17% | 32% | 3% |
附近学校的学生-教师比例 | 15-to-1 | 22-to-1 | 12-to-1 |
提示: 使用你在数据分析部分计算的数据,来验证你的回答。对于这三个客户,客户 3 的房屋最大,附近有最优秀的公立学校,同时贫困指数最低。而客户 2 的房屋最小,所在街区贫困指数相对较高,附近的公立学校也十分一般。
请运行下面的代码,使用你的优化模型来预测每位客户的房屋价值。
In [ ]:
# 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))
In [ ]:
vs.PredictTrials(features, prices, fit_model, client_data)
提示: 根据上方代码计算出的价格范围,回答以下问题:
答案:
请注意:在完成了上方所有的代码实现和问题之后,你将这个 iPython Notebook 导出为 HTML 文件来完成这个项目。你可以通过上方的菜单导出文件,或转到 File -> Download as -> HTML (.html)。请将完成的文件和这个 notebook 一起提交审阅。