《机器学习实战:基于Scikit-LearnKeras和TensorFlow第2版》-学习笔记
Posted 新四石路打卤面
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《机器学习实战:基于Scikit-LearnKeras和TensorFlow第2版》-学习笔记相关的知识,希望对你有一定的参考价值。
第二章 端到端的机器学习项目
· Hands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow, 2nd Edition, by Aurélien Géron (O’Reilly). Copyright 2019 Aurélien Géron, 978-1-492-03264-9.
· 环境:Anaconda(Python 3.8) + Pycharm
· 学习时间:2022.03.29~2022.03.31
· 预计阅读时间:1小时
本章将介绍一个端到端的项目案例。假设你是一个房地产公司最近新雇用的数据科学家,以下是你将会经历的主要步骤:
1.观察大局。
2.获得数据。
3.从数据探索和可视化中获得洞见。
4.机器学习算法的数据准备。
5.选择并训练模型。
6.微调模型。
7.展示解决方案。
8.启动、监控和维护系统。
项目案例纯属虚构,目的仅仅是为了说明机器学习项目的主要步骤,而不是为了了解房地产业务。
文章目录
- 第二章 端到端的机器学习项目
2.1 使用真实数据
本章我们从StatLib库中选择了加州住房价格的数据集。该数据集基于1990年加州人口普查的数据。
出于教学目的,我们还特意添加了一个分类属性,并且移除了一些特征。
原 始 数 据 集 由 R.Kelley Pace 和 Ronald Barry 提 供 , “Sparse Spatial Autoregressions”,Statistics&Probability Letters 33,no.3(1997):291–297。
2.2 机器学习项目清单
该清单可以指导你完成机器学习项目。主要有8个步骤:
1.框出问题并看整体。
2.获取数据。
3.研究数据以获得深刻见解。
4.准备数据以便更好地将潜在的数据模式提供给机器学习算法。
5.探索许多不同的模型,并列出最佳模型。
6.微调你的模型,并将它们组合成一个很好地解决方案。
7.演示你的解决方案。
8.启动、监视和维护你的系统。
2.3 观察大局
2.3.1 数据流水线
一个序列的数据处理组件称为一个数据流水线。
流水线在机器学习系统中非常普遍,因为需要大量的数据操作和数据转化才能应用。组件通常是异步运行的。每个组件拉取大量的数据,然后进行处理,
再将结果传输给另一个数据仓库。一段时间之后,流水线中的下一个组件会拉取前面的数据,并给出自己的输出,以此类推。每个组件都很独立:
组件和组件之间的连接只有数据仓库。这使得整个系统非常简单易懂(在数据流图表的帮助下),不同团队可以专注于不同的组件。
如果某个组件发生故障,它的下游组件还能使用前面的最后一个输出继续正常运行(至少一段时间),所以使得整体架构鲁棒性较强。
2.3.2 框架问题
你现在可以开始设计系统了。首先,你需要回答框架问题:是有监督学习、无监督学习还是强化学习?是分类任务、回归任务还是其他任务?
应该使用批量学习还是在线学习技术?在继续阅读之前,请先暂停一会儿,尝试回答一下这些问题。
有监督、回归、批量学习
2.3.3 选择性能指标
下一步是选择性能指标。回归问题的典型性能指标是均方根误差(RMSE)。它给出了系统通常会在预测中产生多大误差,对于较大的误差,权重较高。
2.3.4 获取数据及数据观察
首先导入需要用到的库:
import os
import tarfile
import urllib
import pandas as pd
读取数据:
# 文件地址
csv_path = "D:\\\\Py-project\\\\Python Learning\\\\Hands-On Machine Learning\\\\2.End to End Project\\\\housing.csv"
# 读取文件
housing = pd.read_csv(csv_path)
# 查看文件基本信息
housing.head() # 查看头五条数据
housing.tail() # 查看末五条数据
housing.info() # 快速获取数据集的简单描述,特别是总行数、每个属性的类型和非空值的数量
# 所有属性的字段都是数字,除了ocean_proximity。它的类型是object,因此它可以是任何类型的Python对象。不过从CSV中加载该数据,所以它是文本属性。
housing["ocean_proximity"].value_counts() # 频次统计. 可以查看分类属性,有多少种分类存在,每种类别下分别有多少个区域
housing.describe() # 显示数值属性的摘要
对于非数值型 Series 对象, describe() (opens new window)返回值的总数、唯一值数量、出现次数最多的值及出现的次数。
count、mean、min以及max行的意思很清楚。需要注意的是,这里的空值会被忽略(因此本例中,total_bedrooms的count是20 433而不是20 640)。
std行显示的是标准差(用来测量数值的离散程度)。25%、50%和75%行显示相应的百分位数:百分位数表示一组观测值中给定百分比的观测值都低于该值。
快速查看数据的直方图:
import matplotlib
import matplotlib.pyplot as plt
plt.style.use('seaborn-white')
matplotlib.rcParams["font.sans-serif"] = ["SimHei"] # 指定字体为SimHei,用于显示中文,如果Ariel,中文会乱码
matplotlib.rcParams["axes.unicode_minus"] = False # 用来正常显示负号
housing.hist(bins=50, figsize=(20, 15)) # 绘制每个数值属性的直方图
# 直方图用来显示给定值范围(横轴)的实例数量(纵轴)。你可以一次绘制一个属性,也可以在整个数据集上调用hist()方法,绘制每个属性的直方图。
plt.savefig('①直方图.png')
2.3.5 创建测试集
在进一步查看数据之前,你需要先创建一个测试集
在这个阶段主动搁置部分数据听起来可能有些奇怪。毕竟,你才只简单浏览了一下数据而已,在决定用什么算法之前,当然还需要了解更多的知识,对吧?
没错,但是大脑是个非常神奇的模式检测系统,也就是说,它很容易过拟合:如果是你本人来浏览测试集数据,很可能会跌入某个看似有趣的测试数据模式,
进而选择某个特殊的机器学习模型。然后当你再使用测试集对泛化误差率进行估算时,估计结果将会过于乐观,该系统启动后的表现将不如预期那般优秀。
这称为数据窥探偏误(data snooping bias)。
理论上,创建测试集非常简单,只需要随机选择一些实例,通常是数据集的20%(如果数据集很大,比例将更小),然后将它们放在一边:
但为了即使在更新数据集之后也有一个稳定的训练测试分割,常见地解决方案是每个实例都使用一个标识符来决定是否进入测试集
(假定每个实例都有一个唯一且不变的标识符)。例如,你可以计算每个实例标识符的哈希值,如果这个哈希值小于或等于最大哈希值的20%,
则将该实例放入测试集。这样可以确保测试集在多个运行里都是一致的,即便更新数据集也仍然一致。
新实例的20%将被放入新的测试集,而之前训练集中的实例也不会被放入新测试集。
from zlib import crc32
import numpy as np
def test_set_check(identifier, test_ratio):
return crc32(np.int64(identifier)) & 0xffffffff < test_ratio * 2 ** 32
不幸的是,housing数据集没有标识符列。最简单的解决方法是使用行索引作为ID:
housing_with_id = housing.reset_index() # 添加一列‘index’索引列
如果使用行索引作为唯一标识符,你需要确保在数据集的末尾添加新数据,并且不会删除任何行。
如果不能保证这一点,那么你可以尝试使用某个最稳定的特征来创建唯一标识符。
例如,一个区域的经纬度肯定几百万年都不会变,你可以将它们组合成如下的ID
housing_with_id[“id”] = housing[“longitude”] * 1000 + housing[“latitude”] # 添加一列‘index’索引列
Scikit-Learn提供了一些函数,可以通过多种方式将数据集分成多个子集。
最简单的函数是train_test_split()。首先,它也有random_state参数,让你可以像之前提到过的那样设置随机生成器种子;
random_state是为了保证程序每次运行都分割一样的训练集和测试集。其次,你可以把行数相同的多个数据集一次性发送给它,它会根据相同的索引将其拆分
from sklearn.model_selection import train_test_split
train_set, test_set = train_test_split(housing, test_size=0.2, random_state=42) # 划分数据集,test_size是测试集的百分比大小
要预测房价中位数,收入中位数是一个非常重要的属性。于是你希望确保在收入属性上,测试集能够代表整个数据集中各种不同类型的收入。
由于收入中位数是一个连续的数值属性,所以你得先创建一个收入类别的属性。
用pd.cut()来创建5个收入类别属性的(用1~5来做标签),0~1.5是类别1,1.5~3是类别2,以此类推:
housing["income_cat"] = pd.cut(housing["median_income"], bins=[0.0, 1.5, 3.0, 4.5, 6.0, np.inf], labels=[1, 2, 3, 4, 5]) # np.inf是无穷大
重新抽样划分数据集:
现在,你可以根据收入类别进行分层抽样了。使用Scikit-Learn的StratifiedShuffleSplit类(分层随机切分交叉验证器):
from sklearn.model_selection import StratifiedShuffleSplit
split = StratifiedShuffleSplit(n_splits=1, test_size=0.2, random_state=42) # 设置分组抽样切分数据集的参数
n_splits重新打乱和切分迭代的次数。test_size和train_size的大小只用设置1个,另一个默认是其补集。
for train_index, test_index in split.split(housing, housing["income_cat"]): # 生成索引以将数据分为训练集和测试集。
strat_train_set = housing.loc[train_index]
strat_test_set = housing.loc[test_index]
# 查看分层结果
print(strat_test_set["income_cat"].value_counts() / len(strat_test_set))
print(strat_train_set["income_cat"].value_counts() / len(strat_train_set))
# 现在你可以删除income_cat属性,将数据恢复原样了:
for set_ in (strat_train_set, strat_test_set):
set_.drop("income_cat", axis=1, inplace=True)
2.4 从数据探索和可视化中获得洞见
到现在为止,我们还只是在快速浏览数据,从而对手头上正在处理的数据类型形成一个大致的了解。本阶段的目标是再深入一点。
首先,把测试集放在一边,你能探索的只有训练集。此外,如果训练集非常庞大,你可以抽样一个探索集,这样后面的操作更简单快捷一些。
让我们先创建一个副本,这样可以随便尝试而不损害训练集:
housing = strat_train_set.copy()
2.4.1 将地理数据可视化
由于存在地理位置信息(经度和纬度),因此建立一个各区域的分布图以便于可视化数据是一个很好的想法
housing.plot(kind="scatter", x="longitude", y="latitude", alpha=0.1) # kind指定图形类型,x,y定数据,alpha突出高密度数据点位置
plt.savefig('②地位位置.png')
现在加入房价信息和人口信息。每个圆的半径大小代表了每个区域的人口数量(选项s),颜色代表价格(选项c)。
我们使用一个名叫jet的预定义颜色表(选项cmap)来进行可视化,颜色范围从蓝(低)到红(高)
housing.plot(kind="scatter", x="longitude", y="latitude", alpha=0.4,
s=housing["population"] / 100, label="population", figsize=(10, 7),
c="median_house_value", cmap=plt.get_cmap("jet"), colorbar=True)
plt.legend()
plt.savefig('③地理位置+房价+人口.png')
s指定圆的大小与人口数量相关,label标注,c指定圆的颜色与房价高低相关,cmap指定颜色表,colorbar=True/False开启或关闭色条显示。
2.4.2 寻找相关性
corr_matrix = housing.corr() # corr()方法计算每对属性之间的标准相关系数(默认pearson相关)
# 也可以用kendall或spearman,eg: corr_matrix = housing.corr(method='spearman/kendall')
print(corr_matrix)
print(corr_matrix["median_house_value"].sort_values(ascending=False)) # 单独现实1列的相关性,sort_values是用来排序的
还有一种方法可以检测属性之间的相关性,就是使用pandas的scatter_matrix函数,它会绘制出每个数值属性相对于其他数值属性的相关性。
现在我们有11个数值属性,可以得到121个图像,篇幅原因无法完全展示,这里仅关注那些与房价中位数属性最相关的,可算作是最有潜力的属性
from pandas.plotting import scatter_matrix
attributes = ["median_house_value", "median_income", "total_rooms", "housing_median_age"]
scatter_matrix(housing[attributes], figsize=(12, 8)) # 指定绘制部分数据,可加入“diagonal='kde'”让直方图变成密度分布图
plt.savefig('④相关性散点图.png')
最有潜力能够预测房价中位数的属性是收入中位数,所以我们单独来看看其相关性的散点图
housing.plot(kind="scatter", x="median_income", y="median_house_value", alpha=0.1)
plt.savefig('⑤房价中位数与收入中位数相关性散点图.png')
2.4.3 试验不同属性的组合
在准备给机器学习算法输入数据之前,你要做的最后一件事应该是尝试各种属性的组合。
例如,如果你不知道一个区域有多少个家庭,那么知道一个区域的“房间总数”也没什么用。你真正想要知道的是一个家庭的房间数量。
同样,单看“卧室总数”这个属性本身也没什么意义,你可能想拿它和“房间总数”来对比,或者拿来同“每个家庭的人口数”这个属性组合似乎也挺有意思。
我们来试着创建这些新属性:
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).to_excel(R"⑥新相关性矩阵.xlsx")
显然,卧室/房间比例更低的房屋往往价格更贵。同样,“每个家庭的房间数量”也比“房间总数”更具信息量——房屋越大,价格越贵。
这一轮的探索不一定要多么彻底,关键是迈开第一步,快速获得洞见,这将有助于你获得非常棒的第一个原型。
这也是一个不断迭代的过程:一旦你的原型产生并且开始运行,你可以分析它的输出以获得更多洞见,然后再次回到这个探索步骤。
2.5 机器学习算法的数据准备
这里你应该编写函数来执行,而不是手动操作,原因如下:
-
你可以在任何数据集上轻松重现这些转换(例如,获得更新的数据集之后)。
-
你可以逐渐建立起一个转换函数的函数库,可以在以后的项目中重用。
-
你可以在实时系统中使用这些函数来转换新数据,再输入给算法。
-
你可以轻松尝试多种转换方式,查看哪种转换的组合效果最佳。
但是现在,让我们先回到一个干净的训练集(再次复制strat_train_set),然后将预测器和标签分开,因为这里我们不一定对它们使用相同的转换方式
(需要注意drop()会创建一个数据副本,但是不影响strat_train_set)
housing = strat_train_set.drop("median_house_value", axis=1) # drop将预测值列和标签分开
housing_labels = strat_train_set["median_house_value"].copy()
2.5.1 数据清理
大部分的机器学习算法无法在缺失的特征上工作,所以我们要创建一些函数来辅助它。
前面我们已经注意到total_bedrooms属性有部分值缺失,所以我们要解决它。有以下三种选择:
-
放弃这些相应的区域。
-
放弃整个属性。
-
将缺失的值设置为某个值(0、平均数或者中位数等)。
通过DataFrame的dropna()、drop()和fillna()方法,可以轻松完成这些操作:
housing.dropna(subset=["total_bedrooms"]) # option 1
housing.drop("total_bedrooms", axis=1) # option 2
median = housing["total_bedrooms"].median()
housing["total_bedrooms"].fillna(median, inplace=True) # option 3
Scikit-Learn提供了一个非常容易上手的类来处理缺失值:SimpleImputer。使用方法如下:
# 首先,你需要创建一个SimpleImputer实例,指定你要用属性的中位数值替换该属性的缺失值:
from sklearn.impute import SimpleImputer
imputer = SimpleImputer(strategy="median")
# 由于中位数值只能在数值属性上计算,所以我们需要创建一个没有文本属性ocean_proximity的数据副本:
housing_num = housing.drop("ocean_proximity", axis=1)
# 使用fit()方法将imputer实例适配到训练数据,使用transform()方法将imputer应用到housing_num:
# fit_transform()是两者的联合优化方法。(适用于sklearn的大多数方法)
x = imputer.fit_transform(housing_num)
PS:这里imputer仅仅只是计算了每个属性的中位数值,并将结果存储在其实例变量statistics_中。虽然只有total_bedrooms这个属性存在缺失值,
但是我们无法确认系统启动之后新数据中是否一定不存在任何缺失值,所以稳妥起见,还是将imputer应用于所有的数值属性:
现在x是一个包含转换后特征的NumPy数组(不适用与pandas的dataframe)。如果你想将它放回pandas DataFrame,也很简单:
housing_tr = pd.DataFrame(x, columns=housing_num.columns, index=housing_num.index)
2.5.2 处理文本和分类属性
到目前为止,我们只处理数值属性,但现在让我们看一下文本属性。在此数据集中,只有一个:ocean_proximity属性。
housing_cat = housing[["ocean_proximity"]]
它不是任意文本,而是有限个可能的取值,每个值代表一个类别。因此,此属性是分类属性。
大多数机器学习算法更喜欢使用数字,因此让我们将这些类别从文本转到数字。为此,我们可以使用Scikit-Learn的OrdinalEncoder类
from sklearn.preprocessing import OrdinalEncoder
ordinal_encoder = OrdinalEncoder()
housing_cat_encoded = ordinal_encoder.fit_transform(housing_cat)
print('使用Categories_实例变量获取类别列表: ', ordinal_encoder.categories_)
print(housing_cat_encoded[:10])
这种表征方式产生的一个问题是,机器学习算法会认为两个相近的值比两个离得较远的值更为相似一些。
在某些情况下这是对的(对一些有序类别,像“坏”“平均”“好”“优秀”),但是,对ocean_proximity而言情况并非如此
(例如,类别0和类别4之间就比类别0和类别1之间的相似度更高)。
为了解决这个问题,常见的解决方案是给每个类别创建一个二进制的属性:
当类别是“<1H OCEAN”时,一个属性为1(其他为0),当类别是“INLAND”时,另一个属性为1(其他为0),以此类推。
这就是独热编码(one hot),因为只有一个属性为1(热),其他均为0(冷)。新的属性有时候称为哑(dummy)属性。
Scikit-Learn提供了一个OneHotEncoder编码器,可以将整数类别值转换为独热向量。我们用它来将类别编码为独热向量。
from sklearn.preprocessing import OneHotEncoder
cat_encoder = OneHotEncoder()
housing_cat_1hot = cat_encoder.fit_transform(housing_cat)
print('观察生成的SciPy稀疏矩阵:\\n', housing_cat_1hot)
这里的输出是一个SciPy稀疏矩阵,而不是一个NumPy数组。
当你有成千上万个类别属性时,这个函数会非常有用。因为在独热编码完成之后,我们会得到一个几千列的矩阵,并且全是0,每行仅有一个1。
占用大量内存来存储0是一件非常浪费的事情,因此稀疏矩阵选择仅存储非零元素的位置。而你依旧可以像使用一个普通的二维数组那样来使用它,
当然如果你实在想把它转换成一个(密集的)NumPy数组,只需要调用toarray()方法即可:
housing_cat_1hot_array = housing_cat_1hot.toarray()
print('---begin---\\n', housing_cat_1hot_array)
print('再次使用Categories_实例变量获取类别列表: ', cat_encoder.categories_)
如果类别属性具有大量可能的类别(例如,国家代码、专业、物种),那么独热编码会导致大量的输入特征,这可能会减慢训练并降低性能。
如果发生这种情况,你可能想要用相关的数字特征代替类别输入。
例如,你可以用与海洋的距离来替换ocean_proximity特征(类似地,可以用该国家的人口和人均GDP来代替国家代码)。
或者,你可以用可学习的低维向量(称为嵌入)来替换每个类别。每个类别的表征可以在训练期间学习。这是表征学习的示例
2.5.3 自定义转换器
虽然Scikit-Learn提供了许多有用的转换器(比如上面的SimpleImputer、OrdinalEncoder),
但是你仍然需要为一些诸如自定义清理操作或组合特定属性等任务编写自己的转换器。
你当然希望让自己的转换器与Scikit-Learn自身的功能(比如流水线)无缝衔接,而由于Scikit-Learn依赖于鸭子类型的编译,而不是继承,
所以你所需要的只是创建一个类,然后应用以下三种方法:fit()(返回self)、transform()、fit_transform()。
你可以通过添加TransformerMixin作为基类,直接得到最后一种方法。同时,如果添加BaseEstimator作为基类(并在构造函数中避免*args和**kargs)
你还能额外获得两种非常有用的自动调整超参数的方法(get_params()和set_params(),包含在BaseEstimator类中)。
注:大概意思就是,用sklearn的类组建自己的新类。
from sklearn.base import BaseEstimator, TransformerMixin
rooms_ix, bedrooms_ix, population_ix, households_ix = 3, 4, 5, 6
class CombinedAttributesAdder(BaseEstimator, TransformerMixin):
def __init__(self, add_bedrooms_per_room=True): # no *args or **kargs
self.add_bedrooms_per_room = add_bedrooms_per_room
def fit(self, x, y=None):
return self # nothing else to do
def transform(self, x):
rooms_per_household = x[:, rooms_ix] / x[:, households_ix]
population_per_household = x[:, population_ix] / x[:, households_ix]
if self.add_bedrooms_per_room:
bedrooms_per_room = x[:, bedrooms_ix] / x[:, rooms_ix]
return np.c_[x, rooms_per_household, population_per_household, bedrooms_per_room]
else:
return np.c_[x, rooms_per_household, population_per_household]
attr_adder = CombinedAttributesAdder(add_bedrooms_per_room=False)
housing_extra_attribs = attr_adder.transform(housing.values)
2.5.4 特征缩放
特征缩放即把数据都放在同一个尺度下。
最重要也最需要应用到数据上的转换就是特征缩放。如果输入的数值属性具有非常大的比例差异,往往会导致机器学习算法的性能表现不佳,当然也有极少数特例
案例中的房屋数据就是这样:房间总数的范围从6~39 320,而收入中位数的范围是0~15。
注意,目标值通常不需要缩放。
同比例缩放所有属性的两种常用方法是最小-最大缩放和标准化。
最小-最大缩放(又叫作归一化):
将值重新缩放使其最终范围归于0~1之间。实现方法是将值减去最小值并除以最大值和最小值的差。
对此,Scikit-Learn提供了一个名为MinMaxScaler的转换器。如果出于某种原因,你希望范围不是0~1,那么可以通过调整超参数feature_range进行更改。
housing_try_by_myself = strat_train_set.drop('ocean_proximity', axis=1) # 创建一个自己用的训练数据集
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler(feature_range=(0, 2)) # feature_range可用来设置归一化的范围区域,不设置默认是(0,1)
housing_try_by_myself = scaler.fit_transform(housing_try_by_myself)
print('\\nhello myself: \\n', housing_try_by_myself)
标准化:
首先减去平均值(所以标准化值的均值总是零),然后除以方差,从而使得结果的分布具备单位方差。不同于最小-最大缩放的是,标准化不将值绑定到特定范围,
对某些算法而言,这可能是个问题(例如,神经网络期望的输入值范围通常是0~1)。但是标准化的方法受异常值的影响更小。
例如,假设某个地区的平均收入为100(错误数据),最小-最大缩放会将所有其他值从0~15降到0~0.15,而标准化则不会受到很大影响。
Scikit-Learn提供了一个标准化的转换器StandardScaler。
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
housing_try_by_myself = scaler.fit_transform(housing_try_by_myself)
print('\\nhello myself 2 : \\n', housing_try_by_myself)
重要的是,跟所有转换一样,缩放器仅用来拟合训练集,而不是完整的数据集(包括测试集)。只有这样,才能使用它们来转换训练集和测试集(和新数据)。
2.5.5 转换流水线
转换流水线: 带有最终估计器的转换管道 → 按流程处理数据。
正如你所见,许多数据转换的步骤需要以正确的顺序来执行。而Scikit-Learn正好提供了Pipeline类来支持这样的转换。下面是一个数值属性的流水线示例:
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
num_pipeline = Pipeline([
('imputer', SimpleImputer(strategy="median")),
('attribs_adder', CombinedAttributesAdder()),
('std_scaler', StandardScaler())])
housing_num_tr = num_pipeline.fit_transform(housing_num)
Pipeline构造函数会通过一系列名称/估算器的配对来定义步骤序列。除了最后一个是估算器之外,前面都必须是转换器(也就是说,必须有fit_transform()方法)。至于命名可以随意,你喜欢就好(只要它们是独一无二的,不含双下划线),它们稍后在超参数调整中会有用
到目前为止,我们分别处理了类别列和数值列。拥有一个能够处理所有列的转换器会更方便,将适当的转换应用于每个列。
在0.20版中,Scikit-Learn为此引入了ColumnTransformer,好消息是它与pandas DataFrames一起使用时效果很好。
让我们用它来将所有转换应用到房屋数据:
from sklearn.compose import ColumnTransformer
# ColumnTransformer将转换器应用于数组或pandas的DataFrame的列
# 该估计器允许独立地转换输入的不同列或列子集,并将每个转换器生成的特征连接起来形成一个单一的特征空间。
# 这对于异构或柱状数据非常有用,可以将多个特征提取机制或转换组合成单个转换器。
num_attribs = list(housing_num)
cat_attribs = ["ocean_proximity"]
full_pipeline = ColumnTransformer([("num", num_pipeline, num_attribs), ("cat", OneHotEncoder(), cat_attribs)])
housing_prepared = full_pipeline.fit_transform(housing)
构造函数需要一个元组列表,其中每个元组都包含一个名字、一个转换器,以及一个该转换器能够应用的列名字(或索引)的列表。
在此示例中,我们指定数值列使用之前定义的num_pipeline进行转换,类别列使用OneHotEncoder进行转换。
最后,我们将ColumnTransformer应用到房屋数据:它将每个转换器应用于适当的列,并沿第二个轴合并输出(转换器必须返回相同数量的行)。
请注意,OneHotEncoder返回一个稀疏矩阵,而num_pipeline返回一个密集矩阵。
当稀疏矩阵和密集矩阵混合在一起时,ColumnTransformer会估算最终矩阵的密度
以上是关于《机器学习实战:基于Scikit-LearnKeras和TensorFlow第2版》-学习笔记的主要内容,如果未能解决你的问题,请参考以下文章
☀️机器学习实战☀️基于 YOLO网络 的人脸识别 |(文末送机器学习书籍~)
机器学习实战3:基于朴素贝叶斯实现单词拼写修正器(附Python代码)