欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

机器学习实战:基于Scikit-Learn和TensorFlow---第二章笔记

程序员文章站 2022-07-14 20:03:25
...

机器学习实战:基于Scikit-Learn和TensorFlow—第二章笔记

一、学习目标

以加州住房价格的数据集作为数据源,来进行构建一个完整的机器学习的项目。

二、完整的处理流程

2.1、目标问题

拿到数据集,搭建机器学习的项目,我们肯定是希望从这个数据中分析出什么结果。所以我们的目标就是:根据数据集,对一个区域的房价中位数进行预测。你肯定知道这是一个典型的监督式学习任务(因为已经给出了标记的训练示例),而且也是一个典型的回归任务(因
为你要对某个值进行预测)。更具体地说,这是一个多变量回归问题,因为系统要使用多个特征进行预测(使用到区域的人口、收入中位数等)。

2.2、性能指标

你辛苦构建完模型后,肯定要选择一个指标来测试下你的模型到底表现如何吧。回归问题的典型性能衡量指标是均方根误差(RMSE)。

公式1-1:均方根误差(RMSE)

(i=1m(h(xi)yi)2)m\sqrt{\frac{(\sum_{i=1}^m(h(x^i) - y^i)^2)}{m}}

解释下其中的符号:

  • m是你在测量RMSE时,所使用的数据集中实例的数量(例如,如果你在评估RMSE时使用的验证集里包含2000个区域,则m=2000)

  • xix^i 是数据集中,第i个实例的所有特征值的向量(标签特征除外),yiy^i是标签(也就是我们期待该实例的输出值)(例如,如果数据集的第一个区域位于经度-118.29°,纬度33.91°,居民数量为1416,平均收入为38372美元,房价中位数为156400美元(暂且忽略其他特征),那么:)

  • x1x^1 = $
    \begin{pmatrix}
    -118.29\
    33.91\
    1416\
    38372\
    \end{pmatrix}
    $

  • yiy^i = 156400

  • h是系统的预测函数,也称为一个假设

即便RMSE通常是回归任务的首选性能衡量指标,但在某些情况下,其他函数可能会更适合。例如,当有很多离群区域时,你可以考虑使用平均绝对误差(也称为平均绝对偏差,参见公式1-2)

公式1-2:平均绝对误差(MAE)

i=1mh(xi)yim\frac{\sum_{i=1}^m \vert h(x^i)-y^i \vert}{m}

均方根误差和平均绝对误差两种方法都是测量两个向量之间的距离:预测向量和目标值向量。距离或者范数的测度可能有多种。

2.3、数据分析

2.3.1、获取数据

此处假设我们已经下载好数据集(CSV格式)到本地项目文件夹中了,那么直接用Pandas加载即可。

2.3.2、分析数据

原书中进行了很详细的分析,此处只做个终结,同学们可以仔细阅读原书的思路。

  • 数据每一行代表一个区,共有10列,也就是有10个属性:longitude,latitude,housing_median_age,total_rooms,total_bed rooms,population,households,median_income,median_house_value以及ocean_proximity。

  • 数据集中包含20640个实例,注意,total_bed这个属性只有20433个非空值,这意味着有207个区域缺失这个特征。我们后面需要考虑到这一点

  • 所有属性的字段都是数字,除了ocean_proximity。它的类型是object,因此它可以是任何类型的Python对象,不过你是从CSV文件中加载了该数据,所以它必然是文本属性。通过查看前五行,你可能会注意到,该列中的值是重复的,这意味着它有可能是一个分类属性。

2.3.3、创建测试集

数据窥探偏误(data snooping bias):

在这个阶段主动搁置部分数据听起来可能有点奇怪。毕竟,你才只是简单浏览了一下数据而已,在决定用什么算法之前,当然还需要了解更多的知识,对吧?没错,但是大脑是个非常神奇的模式检测系统,也就是说它很容易过度匹配:如果是你本人来浏览测试集数据,你很可能会跌入某个看似有趣的数据模式,进而选择某个特殊的机器学习模型。然后当你再使用测试集对泛化误差率进行估算时,估计结果将会过于乐观,该系统启动后的表现将不如预期那般优秀。------引自原书

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

原书的思路是根据收入类别来进行分层抽样,可以仔细阅读原书的分析步骤。

2.3.4、寻找相关性

可以使用corr()方法轻松计算出每对属性之间的标准相关系数(也称为皮尔逊相关系数),最终发现最有潜力能够预测房价中位数的属性是收入中位数。

三、数据准备

3.1、数据清理

大部分的机器学习算法无法在缺失的特征上工作,所以我们要创建一些函数来辅助它。前面我们已经注意到total_bedrooms属性有部分值缺失,所以我们要解决它。有以下三种选择:

  • 放弃这些相应的地区

  • 放弃这个属性

  • 将缺失的值设置为某个值(0、平均数或者中位数等都可以)

**Scikit-Learn提供了一个非常容易上手的教程来处理缺失值:imputer。**使用方法如下,首先,你需要创建一个imputer实例,指定你要用属性的中位数值替换该属性的缺失值:

from sklearn.preprocessing import Imputer
imputer = Imputer(strategy="median")

3.2、处理文本和分类属性

分类属性ocean_proximity,因为它是一个文本属性,我们无法计算它的中位数值。大部分的机器学习算法都更易于跟数字打交道,所以我们先将这些文本标签转化为数字。

Scikit-Learn为这类任务提供了一个转换器LabelEncoder。但是这样会有一个问题,比如:假如有三种颜色特征:红、黄、蓝。 在利用机器学习的算法时一般需要进行向量化或者数字化。那么你可能想令 红=1,黄=2,蓝=3. 那么这样其实实现了标签编码,即给不同类别以标签。然而这意味着机器可能会学习到“红<黄<蓝”,但这并不是我们的让机器学习的本意,只是想让机器区分它们,并无大小比较之意。所以这时标签编码是不够的,需要进一步转换。因为有三种颜色状态,所以就有3个比特。即红色:1 0 0 ,黄色: 0 1 0,蓝色:0 0 1 。如此一来每两个向量之间的距离都是根号2,在向量空间距离都相等,所以这样不会出现偏序性,基本不会影响基于向量空间度量算法的效果。

Scikit-Learn提供了一个OneHotEncoder编码器,可以将整数分类值转换为独热向量。我们用它来将类别编码为独热向量。

使用LabelBinarizer类可以一次性完成两个转换(从文本类别转化为整数类别,再从整数类别转换为独热向量)

处理思路如下:

机器学习实战:基于Scikit-Learn和TensorFlow---第二章笔记

3.3、特征缩放

最重要也最需要应用到数据上的转换器,就是特征缩放。如果输入的数值属性具有非常大的比例差异,往往导致机器学习算法的性能表现不佳,当然也有极少数特例。案例中的房屋数据就是这样:房间总数的范围从6到39320,而收入中位数的范围是0到15。注意,目标值通常不需要缩放。

最小-最大缩放(又叫作归一化)很简单:将值重新缩放使其最终范围归于0到1之间。实现方法是将值减去最小值并除以最大值和最小值的差。对此,Scikit-Learn提供了一个名为MinMaxScaler的转换器。如果出于某种原因,你希望范围不是0~1,你可以通过调整超参数feature_range进行更改。

3.4、转换流水线

许多数据转换的步骤需要以正确的顺序来执行。而Scikit-Learn正好提供了Pipeline来支持这样的转换。

下面是一个数值属性的流水线例子:

from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
num_pipeline = Pipeline([
('imputer', Imputer(strategy="median")),
('attribs_adder', CombinedAttributesAdder()),
('std_scaler', StandardScaler()),
])
housing_num_tr = num_pipeline.fit_transform(housing_num)

Pipeline构造函数会通过一系列名称/估算器的配对来定义步骤的序列。除了最后一个是估算器之外,前面都必须是转换器(也就是说,必须有fit_transform()方法)。

当调用流水线的fit()方法时,会在所有转换器上按照顺序依次调用fit_transform(),将一个调用的输出作为参数传递给下一个调用方法,直到传递到最终的估算器,则只会调用fit()方法。流水线的方法与最终的估算器的方法相同。在本例中,最后一个估算器是StandardScaler,这是个转换器,因此Pipeline有transform()方法可以按顺序将所有的转换应用到数据中(如果不希望先调用fit()再调用transform(),也可以直接调用fit_transform()方法)。

四、选择和训练模型

现在,我们已经完成了前面的所有步骤,现在是时候来进行模型的训练了。

4.1、线性回归模型

训练一个线性回归模型:

lin_reg = LinearRegression()
lin_reg.fit(housing_prepared, housing_labels)

使用Scikit-Learn的mean_squared_error函数来测量整个训练集上回归模型的RMSE得出数据为:68628.413493824875

这显然不是一个好看的成绩:大多数地区的median_housing_values分布在120000到265000美元之间,所以典型的预测误差达到68628美元只能算是差强人意。这就是一个典型的模型对训练数据拟合不足的案例。

这种情况发生时,通常意味着这些特征可能无法提供足够的信息来做出更好的预测,或者是模型本身不够强大。

来训练一个DecisionTreeRegressor(决策树)。这是一个非常强大的模型,它能够从数据中找到复杂的非线性关系

tree_reg = DecisionTreeRegressor(random_state=42)
tree_reg.fit(housing_prepared, housing_labels)
housing_predictions = tree_reg.predict(housing_prepared)
tree_mse = mean_squared_error(housing_labels, housing_predictions)
tree_rmse = np.sqrt(tree_mse)
print(tree_rmse)

发现输出为0.0,难道我们训练出了一个完美的模型,先别急,我们要验证下。

4.2、使用交叉验证来更好地进行评估

使用Scikit-Learn的交叉验证功能。以下是执行K-折(K-fold)交叉验证的代码:它将训练集随机分割成10个不同的子集,每个子集称为一个折叠(fold),然后对决策树模型进行10
次训练和评估——每次挑选1个折叠进行评估,使用另外的9个折叠进行训练。产出的结果是一个包含10次评估分数的数组。

from sklearn.model_selection import cross_val_score
scores = cross_val_score(tree_reg, housing_prepared, housing_labels,scoring="neg_mean_squared_error", cv=10)
tree_rmse_scores = np.sqrt(-scores)
display_scores(tree_rmse_scores)

顺便与线性回归模型进行对比:

lin_reg = LinearRegression()
# lin_reg.fit(housing_prepared, housing_labels)
lin_scores = cross_val_score(lin_reg, housing_prepared, housing_labels,scoring="neg_mean_squared_error", cv=10)
lin_rmse_scores = np.sqrt(-lin_scores)
display_scores(lin_rmse_scores)

最终发现决策树模型确实是严重过度拟合了,以至于表现得比线性回归模型还要糟糕。

试试最后一个模型:RandomForestRegressor。:

forest_reg = RandomForestRegressor(random_state=42)
forest_reg.fit(housing_prepared, housing_labels)
housing_predictions = forest_reg.predict(housing_prepared)
forest_mse = mean_squared_error(housing_labels, housing_predictions)
forest_rmse = np.sqrt(forest_mse)
print(forest_rmse)

五、微调模型

5.1、网格搜索

用Scikit-Learn的GridSearchCV来替你进行探索。你所要做的只是告诉它你要进行实验的超参数是什么,以及需要尝试的值,它将会使用交叉验证来评估超参数值的所有可能的组合。

from sklearn.model_selection import GridSearchCV
param_grid = [
    # try 12 (3×4) combinations of hyperparameters
    {'n_estimators': [3, 10, 30], 'max_features': [2, 4, 6, 8]},
    # then try 6 (2×3) combinations with bootstrap set as False
    {'bootstrap': [False], 'n_estimators': [3, 10], 'max_features': [2, 3, 4]},
  ]

forest_reg = RandomForestRegressor(random_state=42)
# train across 5 folds, that's a total of (12+6)*5=90 rounds of training 
grid_search = GridSearchCV(forest_reg, param_grid, cv=5,scoring='neg_mean_squared_error')
grid_search.fit(housing_prepared, housing_labels)

5.2、通过测试集评估系统

经过各种尝试后,现在是时候用测试集来评估最终模型的时候了。

final_model = grid_search.best_estimator_
X_test = strat_test_set.drop("median_house_value", axis=1)
y_test = strat_test_set["median_house_value"].copy()
X_test_prepared = full_pipeline.transform(X_test)
final_predictions = final_model.predict(X_test_prepared)
final_mse = mean_squared_error(y_test, final_predictions)
final_rmse = np.sqrt(final_mse)

至此我们完成了整个构建的流程。

  • 参考资料 <<机器学习实战:基于Scikit-Learn和TensorFlow>>
  • 源码地址:https://github.com/LinZiYU1996/My_MachineLearning/tree/master/Base_On_Scikit-Learn_TensorFlow/Chapter_2/Demo_2
相关标签: 机器学习