实战-机器学习基础流程与多变量线性回归

814 阅读23分钟

端到端的机器学习项目

内容来源于开源项目:hands1ml.apachecn.org/#/docs/2

数据集可从上述项目中获取

使用机器学习方法解决问题的主要步骤:

  1. 项目概述。
  2. 获取数据。
  3. 发现并可视化数据,发现规律。
  4. 为机器学习算法准备数据。
  5. 选择模型,进行训练。
  6. 微调模型。
  7. 给出解决方案。
  8. 部署、监控、维护系统。

项目概览

首先要明确项目问题,你的第一个任务是利用加州普查数据,建立一个加州房价模型。这个数据包含每个街区组的人口、收入中位数、房价中位数等指标。

街区组是美国调查局发布样本数据的最小地理单位(一个街区通常有 600 到 3000 人)。我们将其简称为“街区”。

你的模型要利用这个数据进行学习,然后根据其它指标,预测任何街区的的房价中位数。

划定问题

流水线

一系列的数据处理组件被称为数据流水线。流水线在机器学习系统中很常见,因为有许多数据要处理和转换。

组件通常是异步运行的。每个组件吸纳进大量数据,进行处理,然后将数据传输到另一个数据容器中,而后流水线中的另一个组件收入这个数据,然后输出,这个过程依次进行下去。每个组件都是独立的:组件间的接口只是数据容器。这样可以让系统更便于理解(记住数据流的图),不同的项目组可以关注于不同的组件。进而,如果一个组件失效了,下游的组件使用失效组件最后生产的数据,通常可以正常运行(一段时间)。这样就使整个架构相当健壮。

另一方面,如果没有监控,失效的组件会在不被注意的情况下运行一段时间。数据会受到污染,整个系统的性能就会下降

image.png

定义问题

定义机器学习的问题非常重要。建立模型可能不是最终目标。要如何使用、并从模型受益?这非常重要,因为它决定了如何划定问题,要选择什么算法,评估模型性能的指标是什么,要花多少精力进行微调。

先看看现在的解决方案效果如何。老板通常会给一个参考性能,以及如何解决问题。老板说,现在街区的房价是靠专家手工估计的,专家队伍收集最新的关于一个区的信息(不包括房价中位数),他们使用复杂的规则进行估计。这种方法费钱费时间,而且估计结果不理想,误差率大概有 15%。

OK,有了这些信息,你就可以开始设计系统了。首先,你需要划定问题:监督或非监督,还是强化学习?这是个分类任务、回归任务,还是其它的?要使用批量学习还是线上学习?

很明显,这是一个典型的监督学习任务,因为你要使用的是有标签的训练样本(每个实例都有预定的产出,即街区的房价中位数)。并且,这是一个典型的回归任务,因为你要预测一个值。讲的更细些,这是一个多变量回归问题,因为系统要使用多个变量进行预测(要使用街区的人口,收入中位数等等)。

选择性能指标/损失函数

选择性能指标。回归问题的典型指标是均方根误差(RMSE)。均方根误差测量的是系统预测误差的标准差。例如,RMSE 等于 50000,意味着,68% 的系统预测值位于实际值的 50000 美元以内,95% 的预测值位于实际值的 100000 美元以内(一个特征通常都符合高斯分布,即满足 “68-95-99.7”规则:大约 68% 的值落在内,95% 的值落在内,99.7% 的值落在内,这里的σ等于 50000)。

image.png

常见的机器学习符号:

  • m是测量 RMSE 的数据集中的实例数量。 例如,如果用一个含有 2000 个街区的验证集求 RMSE,则m = 2000

  • x^(i)是数据集第i个实例的所有特征值(不包含标签)的向量,y^(i)是它的标签(这个实例的输出值)。

    例如,如果数据集中的第一个街区位于经度 –118.29°,纬度 33.91°,有 1416 名居民,收入中位数是 38372 美元,房价中位数是 156400 美元(忽略掉其它的特征),则有:

    x(1)=(118.2933.91141638372)y(1)=(156400)x^{(1)}=\begin{pmatrix} -118.29 \\ 33.91 \\ 1416 \\ 38372\end{pmatrix}, y^{(1)}=\begin{pmatrix}156400\end{pmatrix}
  • h是系统的预测函数,也称为假设(hypothesis)。当系统收到一个实例的特征向量x^(i),就会输出这个实例的一个预测值y_hat = h(x^(i))y_hat读作y-hat)。

    例如,如果系统预测第一区的房价中位数是 158400 美元,则y_hat^(1) = h(x^(1)) = 158400。预测误差是 y_hat^(1) - y^(1) = 2000

  • RMSE(X,h)是使用假设h在样本集上测量的损失函数。

我们使用小写斜体表示标量值(例如my^(i))和函数名(例如h),小写粗体表示向量(例如x^(i)),大写粗体表示矩阵(例如X)。

虽然大多数时候 RMSE 是回归任务可靠的性能指标,在有些情况下,你可能需要另外的函数。例如,假设存在许多异常的街区。此时,你可能需要使用平均绝对误差(Mean Absolute Error,也称作平均绝对偏差)

MAE(X,h)=1mi=1mh(xi)yiMAE(X,h)=\frac{1}{m}\sum_{i=1}^m|h(x^i)-y^i|

RMSE 和 MAE 都是测量预测值和目标值两个向量距离的方法。有多种测量距离的方法,或范数:

  • 计算对应欧几里得范数的平方和的根(RMSE):这个距离介绍过。它也称作ℓ2范数,标记为||·||₂(或只是||·||)。
  • 计算对应于ℓ1(标记为||·||₁)范数的绝对值和(MAE)。有时,也称其为曼哈顿范数,因为它测量了城市中的两点,沿着矩形的边行走的距离。
  • 更一般的,包含n个元素的向量vℓk范数(K 阶闵氏范数),定义成

  • 范数的指数越高,就越关注大的值而忽略小的值。这就是为什么 RMSE 比 MAE 对异常值更敏感。但是当异常值是指数分布的(类似正态曲线),RMSE 就会表现很好。

获取数据

数据集的处理占整个工作的60%以上,从公开数据集中下载好数据并使用pycharm+jupyter创建机器学习项目

加载数据集

使用pandas加载数据集并显示

import pandas as pd
HOUSING_PATH="./dataset/housing/housing.csv"
#加载数据集
def load_housing_data(housing_path=HOUSING_PATH):
    return pd.read_csv(housing_path)

housing=load_housing_data()
housing.head()

分析数据特征

每一行都表示一个街区。共有 10 个属性(截图中可以看到 6 个):经度、维度、房屋年龄中位数、总房间数、总卧室数、人口数、家庭数、收入中位数、房屋价值中位数、离大海距离。

info()方法可以快速查看数据的描述,特别是总行数、每个属性的类型和非空值的数量

数据集中共有 20640 个实例,按照机器学习的标准这个数据量很小,但是非常适合入门。我们注意到总卧室数只有 20433 个非空值,这意味着有 207 个街区缺少这个值。将在后面对它进行处理。

所有的属性都是数值的,除了离大海距离这项。它的类型是对象,因此可以包含任意 Python 对象,但是因为该项是从 CSV 文件加载的,所以必然是文本类型。在刚才查看数据前五项时,你可能注意到那一列的值是重复的,意味着它可能是一项表示类别的属性。可以使用value_counts()方法查看该项中都有哪些类别,每个类别中都包含有多少个街区

>>> housing["ocean_proximity"].value_counts()
<1H OCEAN     9136
INLAND        6551
NEAR OCEAN    2658
NEAR BAY      2290
ISLAND           5
Name: ocean_proximity, dtype: int64

再来看其它字段。describe()方法展示了数值属性的概括

countmeanminmax几行的意思很明显了。注意,空值被忽略了(所以,卧室总数是 20433 而不是 20640)。std是标准差(揭示数值的分散度)。25%、50%、75% 展示了对应的分位数:每个分位数指明小于这个值,且指定分组的百分比。例如,25% 的街区的房屋年龄中位数小于 18,而 50% 的小于 29,75% 的小于 37。这些值通常称为第 25 个百分位数(或第一个四分位数),中位数,第 75 个百分位数(第三个四分位数)。

**另一种快速了解数据类型的方法是画出每个数值属性的柱状图。柱状图(的纵轴)展示了特定范围的实例的个数。**你还可以一次给一个属性画图,或对完整数据集调用hist()方法,后者会画出每个数值属性的柱状图。例如,你可以看到略微超过 800 个街区的median_house_value值差不多等于 500000 美元。

#绘图
%matplotlib inline
import matplotlib.pyplot as plt
housing.hist(bins=50,figsize=(20,15))
plt.show()

hist()方法依赖于 Matplotlib,后者依赖于用户指定的图形后端以打印到屏幕上。因此在画图之前,你要指定 Matplotlib 要使用的后端。最简单的方法是使用 Jupyter 的魔术命令%matplotlib inline。它会告诉 Jupyter 设定好 Matplotlib,以使用 Jupyter 自己的后端。绘图就会在笔记本中渲染了。注意在 Jupyter 中调用show()不是必要的,因为代码框执行后 Jupyter 会自动展示图像。

还需要注意的是数据可能经过了预处理,并不是完全真是的数据,如本例中过高收入中位数的会变为 15(实际为 15.0001),过低的会变为 5(实际为 0.4999)。

房屋年龄中位数和房屋价值中位数也被设了上限。后者可能是个严重的问题,因为它是你的目标属性(你的标签)。你的机器学习算法可能学习到价格不会超出这个界限。你需要与下游团队核实,这是否会成为问题。如果他们告诉你他们需要明确的预测值,即使超过 500000 美元,你则有两个选项:

  1. 对于设了上限的标签,重新收集合适的标签;
  2. 将这些街区从训练集移除(也从测试集移除,因为若房价超出 500000 美元,你的系统就会被差评)。

数据探索和可视化、发现规律

如果训练集非常大,你可能需要再采样一个探索集,保证操作方便快速。在我们的案例中,数据集很小,所以可以在全集上直接工作。创建一个副本,以免损伤训练集:

housing_train_copy=train_set.copy()

这一步可以看做是分析数据,进行特征提取

数据可视化

因为存在地理信息(纬度和经度),创建一个所有街区的散点图来数据可视化是一个不错的主意,将alpha设为 0.1,可以更容易看出数据点的密度

image.png

通常来讲,人类的大脑非常善于发现图片中的规律,但是需要调整可视化参数使规律显现出来。

现在来看房价。每个圈的半径表示街区的人口(选项s),颜色代表价格(选项c)。我们用预先定义的名为jet的颜色图(选项cmap),它的范围是从蓝色(低价)到红色(高价)(色彩图):

image.png

这种色彩图在论文展示的时候挺有用的

这张图说明房价和位置(比如,靠海)和人口密度联系密切,这点你可能早就知道。可以使用聚类算法来检测主要的聚集,用一个新的特征值测量聚集中心的距离。尽管北加州海岸区域的房价不是非常高,但离大海距离属性也可能很有用,所以这不是用一个简单的规则就可以定义的问题。

查找关联

因为数据集并不是非常大,你可以很容易地使用corr()方法计算出每对属性间的标准相关系数(standard correlation coefficient,也称作皮尔逊相关系数):

corr_matrix=housing.corr()
corr_matrix["median_house_value"].sort_values(ascending=False)
//-----
median_house_value    1.000000
median_income         0.688075
income_cat            0.643892
total_rooms           0.134153
housing_median_age    0.105623
households            0.065843
total_bedrooms        0.049686
population           -0.024650
longitude            -0.045967
latitude             -0.144160
Name: median_house_value, dtype: float64

也就是数据相关性度量指标,如距离度量,余弦距离,支持度与置信度...

相关系数的范围是 -1 到 1。当接近 1 时,意味强正相关;例如,当收入中位数增加时,房价中位数也会增加。当相关系数接近 -1 时,意味强负相关;你可以看到,纬度和房价中位数有轻微的负相关性(即,越往北,房价越可能降低)。最后,相关系数接近 0,意味没有线性相关性。

相关系数只测量线性关系(如果x上升,y则上升或下降)。相关系数可能会完全忽略非线性关系(例如,如果x接近 0,则y值会变高)。

另一种检测属性间相关系数的方法是使用 Pandas 的scatter_matrix函数,它能画出每个数值属性对每个其它数值属性的图。因为现在共有 11 个数值属性,你可以得到11 ** 2 = 121张图。最有希望用来预测房价中位数的属性是收入中位数,重点绘图查看

image.png

这张图说明了几点。**首先,相关性非常高;可以清晰地看到向上的趋势,并且数据点不是非常分散。第二,我们之前看到的最高价,清晰地呈现为一条位于 500000 美元的水平线。**这张图也呈现了一些不是那么明显的直线:一条位于 450000 美元的直线,一条位于 350000 美元的直线,一条在 280000 美元的线,和一些更靠下的线。你可能希望去除对应的街区,以防止算法重复这些巧合。

特征分析/特征提取

进行属性组合试验,对于原始数据集进行分析,查找关联性,提取特征,创建新的属性

尝试多种属性组合。例如,如果你不知道某个街区有多少户,该街区的总房间数就没什么用。你真正需要的是每户有几个房间。相似的,总卧室数也不重要:你可能需要将其与房间数进行比较。每户的人口数也是一个有趣的属性组合。创建这些新的属性:

housing["rooms_per_household"] = housing["total_rooms"]/housing["households"]
housing["bedrooms_per_room"] = housing["total_bedrooms"]/housing["total_rooms"]
housing["population_per_household"]=housing["population"]/housing["households"]
corr_matrix=housing.corr()
corr_matrix["median_house_value"].sort_values(ascending=False)
//
median_house_value          1.000000
median_income               0.688075
income_cat                  0.643892
rooms_per_household         0.151948
total_rooms                 0.134153
housing_median_age          0.105623
households                  0.065843
total_bedrooms              0.049686
population_per_household   -0.023737
population                 -0.024650
longitude                  -0.045967
latitude                   -0.144160
bedrooms_per_room          -0.255880
Name: median_house_value, dtype: float64

数据预处理

现在来为机器学习算法准备数据。不要手工来做,你需要写一些函数:

  • 函数可以让你在任何数据集上(比如,你下一次获取的是一个新的数据集)方便地进行重复数据转换。
  • 你能慢慢建立一个转换函数库,可以在未来的项目中复用。
  • 在将数据传给算法之前,你可以在实时系统中使用这些函数。
  • 这可以让你方便地尝试多种数据转换,查看哪些转换方法结合起来效果最好。

但是,还是先回到干净的训练集(通过再次复制train_set),将预测量和标签分开,因为我们不想对预测量和目标值应用相同的转换(注意drop()创建了一份数据的备份,而不影响strat_train_set):

数据清洗

大多机器学习算法不能处理缺失的特征,因此先创建一些函数来处理特征缺失的问题。前面,你应该注意到了属性total_bedrooms有一些缺失值。有三个解决选项:

  • 去掉对应的街区;
  • 去掉整个属性;
  • 进行赋值(0、平均值、中位数等等)。

DataFramedropna()drop(),和fillna()方法,可以方便地实现:

housing.dropna(subset=["total_bedrooms"])    # 选项 1
housing.drop("total_bedrooms", axis=1)       # 选项 2
median = housing["total_bedrooms"].median()
housing["total_bedrooms"].fillna(median)     # 选项 3复制ErrorOK!

如果选择选项 3,你需要计算训练集的中位数,用中位数填充训练集的缺失值,不要忘记保存该中位数。后面用测试集评估系统时,需要替换测试集中的缺失值,也可以用来实时替换新数据中的缺失值。

Scikit-Learn 提供了一个方便的类来处理缺失值:SimpleImputer。下面是其使用方法:1.首先,需要创建一个Imputer实例,指定用某属性的中位数来替换该属性所有的缺失值:

from sklearn.impute import SimpleImputer
imputer = SimpleImputer(strategy="median")

因为只有数值属性才能算出中位数,我们需要创建一份不包括文本属性ocean_proximity的数据副本:

#使用中位数代替,去掉文本属性
housing_num=housing.drop("ocean_proximity",axis=1)

2.现在,就可以用fit()方法将imputer实例拟合到训练数据:

imputer.fit(housing_num)复制ErrorOK!

imputer计算出了每个属性的中位数,并将结果保存在了实例变量statistics_中。虽然此时只有属性total_bedrooms存在缺失值,但我们不能确定在以后的新的数据中会不会有其他属性也存在缺失值,所以安全的做法是将imputer应用到每个数值。

拟合数据就是一种把现有数据透过数学方法来代入一条数式的表示方式。科学和工程问题可以通过诸如采样、实验等方法获得若干离散的数据,根据这些数据,我们往往希望得到一个连续的函数(也就是曲线)或者更加密集的离散方程与已知数据相吻合,这过程就叫做拟合(fitting)。

3. 现在,你就可以使用这个“训练过的”imputer来对训练集进行转换transform,将缺失值替换为中位数:

X = imputer.transform(housing_num)

结果是一个包含转换后特征的普通的 Numpy 数组。如果你想将其放回到 PandasDataFrame中,也很简单:

housing_tr = pd.DataFrame(X, columns=housing_num.columns)

image.png

处理文本数据

将文本数据转换为类别数据/数值型

前面,我们丢弃了类别属性ocean_proximity,因为它是一个文本属性,不能计算出中位数。大多数机器学习算法更喜欢和数字打交道,所以需要把这些文本标签转换为数字。这里先不转换,直接删除文本属性。

自定义转换器

尽管 Scikit-Learn 提供了许多有用的转换器,你还是需要自己动手写转换器执行任务,比如自定义的清理操作,或属性组合。你需要让自制的转换器与 Scikit-Learn 组件(比如流水线)无缝衔接工作,因为 Scikit-Learn 是依赖鸭子类型的(而不是继承),你所需要做的是创建一个类并执行三个方法:fit()(返回self),transform(),和fit_transform()。通过添加TransformerMixin作为基类,可以很容易地得到最后一个。另外,如果你添加BaseEstimator作为基类(且构造器中避免使用*args**kargs),你就能得到两个额外的方法(get_params() set_params()),二者可以方便地进行超参数自动微调。

特征缩放

数据要做的最重要的转换之一是特征缩放。除了个别情况,当输入的数值属性量度不同时,机器学习算法的性能都不会好。这个规律也适用于房产数据:总房间数分布范围是 6 到 39320,而收入中位数只分布在 0 到 15。注意通常情况下我们不需要对目标值进行缩放。

有两种常见的方法可以让所有的属性有相同的量度:线性函数归一化(Min-Max scaling)和标准化(standardization)。

线性函数归一化(许多人称其为归一化(normalization))很简单:值被转变、重新缩放,直到范围变成 0 到 1。我们通过减去最小值,然后再除以最大值与最小值的差值,来进行归一化。Scikit-Learn 提供了一个转换器MinMaxScaler来实现这个功能。它有一个超参数feature_range,可以让你改变范围,如果不希望范围是 0 到 1。

标准化就很不同:首先减去平均值(所以标准化值的平均值总是 0),然后除以方差,使得到的分布具有单位方差。与归一化不同,标准化不会限定值到某个特定的范围,这对某些算法可能构成问题(比如,神经网络常需要输入值得范围是 0 到 1)。但是,标准化受到异常值的影响很小。例如,假设一个街区的收入中位数由于某种错误变成了 100,归一化会将其它范围是 0 到 15 的值变为 0-0.15,但是标准化不会受什么影响。Scikit-Learn 提供了一个转换器StandardScaler来进行标准化。

警告:与所有的转换一样,缩放器只能向训练集拟合,而不是向完整的数据集(包括测试集)。只有这样,你才能用缩放器转换训练集和测试集(和新数据)。

from sklearn.preprocessing import StandardScaler

std_scaler=StandardScaler()
XX=std_scaler.fit_transform(housing_num)
XX

创建测试集

人类的大脑是一个神奇的发现规律的系统,这意味着大脑非常容易发生过拟合:如果你查看了测试集,就会不经意地按照测试集中的规律来选择某个特定的机器学习模型。再当你使用测试集来评估误差率时,就会导致评估过于乐观,而实际部署的系统表现就会差。这称为数据透视偏差。

理论上,创建测试集很简单:只要随机挑选一些实例,一般是数据集的 20%,放到一边:

**通常的解决办法是使用每个实例的 ID 来判定这个实例是否应该放入测试集(假设每个实例都有唯一并且不变的 ID)。例如,你可以计算出每个实例 ID 的哈希值,只保留其最后一个字节,如果该值小于等于 51(约为 256 的 20%),就将其放入测试集。**这样可以保证在多次运行中,测试集保持不变,即使更新了数据集。新的测试集会包含新实例中的 20%,但不会有之前位于训练集的实例。

如果使用行索引作为唯一识别码,你需要保证新数据都放到现有数据的尾部,且没有行被删除。如果做不到,则可以用最稳定的特征来创建唯一识别码。例如,一个区的维度和经度在几百万年之内是不变的,所以可以将两者结合成一个 ID

Scikit-Learn 提供了一些函数,可以用多种方式将数据集分割成多个子集。最简单的函数是train_test_split

# 切分测试集和训练集,测试集比例0.2
from sklearn.model_selection import train_test_split
train_set,test_set=train_test_split(housing,test_size=0.2,random_state=42)

上述使用随机采样,当你的数据集很大时(尤其是和属性数相比),这通常可行;但如果数据集不大,就会有采样偏差的风险。当一个调查公司想要对 1000 个人进行调查,它们不是在电话亭里随机选 1000 个人出来。调查公司要保证这 1000 个人对人群整体有代表性。例如,美国人口的 51.3% 是女性,48.7% 是男性。所以在美国,严谨的调查需要保证样本也是这个比例:513 名女性,487 名男性。**这称作分层采样(stratified sampling):将人群分成均匀的子分组,称为分层,从每个分层去取合适数量的实例,以保证测试集对总人数有代表性。**如果调查公司采用纯随机采样,会有 12% 的概率导致采样偏差:女性人数少于 49%,或多于 54%。不管发生那种情况,调查结果都会严重偏差。

假设专家告诉你,收入中位数是预测房价中位数非常重要的属性。你可能想要保证测试集可以代表整体数据集中的多种收入分类。因为收入中位数是一个连续的数值属性,你首先需要创建一个收入类别属性。

大多数的收入中位数的值聚集在 2-5(万美元),但是一些收入中位数会超过 6。数据集中的每个分层都要有足够的实例位于你的数据中,这点很重要。否则,对分层重要性的评估就会有偏差。这意味着,你不能有过多的分层,且每个分层都要足够大。后面的代码通过将收入中位数除以 1.5(以限制收入分类的数量),创建了一个收入类别属性,用ceil对值舍入(以产生离散的分类),然后将所有大于 5 的分类归入到分类 5。

新建属性是数据预处理时的常用方法,在这里可以根据新建属性进行分层抽样,保证训练效果更好

import numpy as np
housing['income_cat']=np.ceil(housing['median_income'] / 1.5)
housing['income_cat'].where(housing['income_cat'] < 5,5.0,inplace=True)
housing.head()

现在,就可以根据收入分类,进行分层采样。可以使用 Scikit-Learn 的StratifiedShuffleSplit类:

from sklearn.model_selection import StratifiedShuffleSplit

split=StratifiedShuffleSplit(n_splits=1,test_size=0.2,random_state=42)
train_set=pd.DataFrame()
test_set=pd.DataFrame()
for train_index,test_index in split.split(housing,housing['income_cat']):
    train_set=housing.loc[train_index]
    test_set=housing.loc[test_index]
# 验证分层抽样比例
print(housing['income_cat'].value_counts()/len(housing))
# 删除自定义属性
for set in (train_set,test_set):
    set.drop(['income_cat'],axis=1,inplace=True)
print(train_set.head())
>>> housing["income_cat"].value_counts() / len(housing)
3.0    0.350581
2.0    0.318847
4.0    0.176308
5.0    0.114438
1.0    0.039826
Name: income_cat, dtype: float64

现在,分层采样测试集的收入分类比例与总数据集几乎相同,而随机采样数据集偏差严重。

用了大量时间来生成测试集的原因是:测试集通常被忽略,但实际是机器学习非常重要的一部分。还有,生成测试集过程中的许多思路对于后面的交叉验证讨论是非常有帮助的。

选择并训练模型

在训练集上训练和评估

因为是监督学习,所以先把训练的属性和标签分开

train_set_attr=train_set.drop(["median_house_value"],axis=1)
train_set_label=train_set["median_house_value"]
test_set_attr=test_set.drop(["median_house_value"],axis=1)
test_set_label=test_set["median_house_value"]

先来训练一个线性回归模型:

from sklearn.linear_model import LinearRegression
lin_reg=LinearRegression()
lin_reg.fit(train_set_attr,train_set_label)

之后用模型进行预测,计算损失函数

from sklearn.metrics import mean_squared_error
housing_predictions=lin_reg.predict(test_set_attr)
lin_mse=mean_squared_error(test_set_label,housing_predictions)
line_rmse=np.sqrt(lin_mse)
line_rmse
# 73368.96605703988

OK,有总比没有强,但显然结果并不好:大多数街区的median_housing_values位于 120000 到 265000 美元之间,因此预测误差 73368美元不能让人满意。

在使用测试集进行预测之前,可以使用交叉验证法对训练集进行多次训练,模型会选择训练最好的参数作为最终模型

K-Fold交叉验证

最基本的方法被称之为,k-折交叉验证 。 k-折交叉验证将训练集划分为 k 个较小的集合。 每一个 k 折都会遵循下面的过程:

  • k-1 份训练集子集作为 training data (训练集)训练模型,
  • 将剩余的 1 份训练集子集用于模型验证(也就是把它当做一个测试集来计算模型的性能指标,例如准确率)。

k-折交叉验证得出的性能指标是循环计算中每个值的平均值。 该方法虽然计算代价很高,但是它不会浪费太多的数据(如固定任意测试集的情况一样), 在处理样本数据集较少的问题(例如,逆向推理)时比较有优势。

from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import cross_val_score
forest_reg=RandomForestRegressor()
scores=cross_val_score(forest_reg,train_set_attr,train_set_label,scoring="neg_mean_squared_error",cv=3)
print(scores)
forest_reg.fit(train_set_attr,train_set_label)

效果如果比原来的还差,说明发生了过拟合

分别使用线性回归、决策树、随机森林预测

总结

数据科学与数据分析,统计学习方法应用,通常来说,步骤大体一致

获取数据,分析数据、特征提取、特征选择、数据清洗、创建测试集与训练集、选择模型,交叉验证检验,模型预测

但是通常来说,模型的预测效果并不会很好,需要不断的对数据进行分析并且调参,“炼丹”

源码notebook:

github.com/ageron/hand…

github.com/LSLWind/Mac…