侧边栏壁纸
博主头像
秋码记录

一个游离于山间之上的Java爱好者 | A Java lover living in the mountains

  • 累计撰写 124 篇文章
  • 累计创建 261 个标签
  • 累计创建 42 个分类

为机器学习算法准备数据(Machine Learning 研习之八)

本文还是同样建立在前两篇的基础之上的!

属性组合实验

希望前面的部分能让您了解探索数据并获得洞察力的几种方法。您发现了一些数据怪癖,您可能希望在将数据提供给机器学习算法之前对其进行清理,并且发现了属性之间有趣的相关性,特别是与目标属性 之间的相关性。您还注意到一些属性具有向右倾斜的分布,因此您可能需要转换它们(例如,通过计算它们的对数或平方根)。当然,你的里程会因每个项目而有很大的不同,但大致的想法是相似的。

在为机器学习算法准备数据之前,您可能需要做的最后一件事是尝试各种属性组合。例如,如果你不知道一个地区有多少住户,那么这个地区的房间总数就不是很有用。你真正想要的是每个家庭的房间数量。同样,卧室总数本身也不是很有用:你可能想对比一下房间的数量。每个家庭的人口似乎也是一个有趣的属性组合。创建这些新属性如下:

housing["rooms_per_house"] = housing["total_rooms"] / housing["households"]
housing["bedrooms_ratio"] = housing["total_bedrooms"] / housing["total_rooms"]
housing["people_per_house"] = housing["population"] / housing["households"]

然后你再看一遍相关矩阵:

!新的bedrooms_ratio属性与房屋中值的相关性要比与房间或卧室总数的相关性大得多。显然,卧室/房间比率较低的房子往往更贵。每个家庭的房间数量也比一个地区的房间总数更能说明问题-很明显,房 子越大,就越贵。

这一轮的探索不需要绝对彻底;关键是从正确的角度出发,并迅速获得见解,这将帮助您获得第一个相当好的原型。但是这是一个迭代的过程:一旦你建立并运行了一个原型,你就可以分析它的输出以获得更多的见解,然后再回到这个探索步骤。

为机器学习算法准备数据

是时候为您的机器学习算法准备数据了。你应该为此编写函数,而不是手工操作,这有几个很好的理由:

  • 这将允许您在任何数据集上轻松重现这些转换(例如,下次获得新数据集时)。
  • 您将逐步构建一个转换函数库,以便在未来的项目中重用。
  • 您可以在实时系统中使用这些函数来转换新数据,然后再将其输入到您的算法中。
  • 这将使您能够轻松地尝试各种转换,并查看哪种转换组合效果最好。

但首先,恢复到一个干净的训练集(通过再次复制strat_train_set)。您还应该将预测变量和标签分开,因为您不一定希望对预测变量和目标值应用相同的转换(请注意,drop()创建数据的副本,并且不影响strat_train_set):

housing = strat_train_set.drop("median_house_value", axis=1)
housing_labels = strat_train_set["median_house_value"].copy()

清除数据

大多数机器学习算法无法处理缺失的功能,因此您需要处理这些功能。例如,您之前注意到total_bedrooms属性有一些缺失值。你有三个选项可以解决这个问题:

  1. 去掉相应的区。

  2. 去掉整个属性。

  3. 将缺失值设置为某个值(零、均值、中位数等)。这就是所谓的归罪。

您可以使用PandasDataFrame的dropna () 、drop () 和fillna ()方法轻松完成这些任务:

housing.dropna(subset=["total_bedrooms"], inplace=True) # option 1
housing.drop("total_bedrooms", axis=1) # option 2
median = housing["total_bedrooms"].median() # option 3
housing["total_bedrooms"].fillna(median, inplace=True)

您决定使用选项3,因为它的破坏性最小,但是您将使用一个方便的Scikit-Learn类:Simplelmputer,而不是前面的代码。这样做的好处是,它将存储每个特征的中值:这将使得它不仅可以估算训练集上的缺失值,还可以估算验证集、测试集和输入到模型的任何新数据上的缺失值。要使用它,首先需要创建一个Simplelmputer实例,指定要将每个属性的缺失值替换为该属性的中位数:

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

由于中位数只能在数值属性上计算,因此您需要创建一个仅具有数值属性的数据副本(这将排除文本属性ocean_proximity):

housing_num = housing.select_dtypes(include=[np.number])

现在,您可以使用fit()方法将补缺器实例拟合到训练数据:

imputer.fit(housing_num)

估算器只是计算每个属性的中位数,并将结果存储在它的statistics_instance变量中。只有total_bedrooms属性有缺失值,但您无法确定系统上线后的新数据中不会有任何缺失值,因此更安全的做法是将补缺器应用于所有数值属性:

现在,您可以使用这个"训练过的"估算器通过用学习到的中位数替换缺失值来转换训练集:

X = imputer.transform(housing_num)

缺失的值也可以替换为平均值(strategy=“mean”),或替换为最频繁的值(strategy=“most_frequent”),或替换为常值(strategy=“constant”, fill_value=…)。后两种策略支持非数值数据。

sklear.impute软件包中还有更强大的imputer(都仅用于数值特性):

  • KNNImputer将每个缺失值替换为该功能的k-近邻值的平均 值。距离是基于所有可用的功能。
  • Iterativelmputer为每个特征训练回归模型,以根据所有其他可用 特征预测缺失值。然后,它会根据更新的数据再次训练模型,并 多次重复该过程,在每次迭代时改进模型和替换值。

Scikit-Learn转换器输出NumPy数组(或有时SciPy稀疏矩阵),即使它们被输入熊猫数据帧。“因此,inputer.Transform(Home_Num)的输出是NumPy数组:X既没有列名,也没有索引。幸运的是,在DataFrame中包装X并从宿主num中恢复列名和索引并不难:

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

处理文本和分类属性

到目前为止,我们只处理了数字属性,但您的数据也可能包含文本属性。在这个数据集中,只有一个:ocean_proximity属性。让我们看看它的值的前几个实例:

它不是任意的文本:有有限数量的可能值,每个值代表一个类别。所以这个属性是一个分类属性。大多数机器学习算法更喜欢与数字打交道,所以让我们将这些类别从文本转换为数字。为此,我们可以使用Scikit-Learn的OrdinalEncoder类:

from sklearn.preprocessing import OrdinalEncoder
ordinal_encoder = OrdinalEncoder()
housing_cat_encoded = ordinal_encoder.fit_transform(housing_cat)

housing_cat_encoded中的前几个编码值是这样的:

您可以使用categories_instance变量获取类别列表。它是一个列表,包含每个分类属性的一维类别数组(在本例中,列表包含单个数组,因为只有一个分类属性):

这种表示法的一个问题是,ML算法将假设两个附近的值比两个遥远的值更相似。这在某些情况下可能是没有问题的(例如,对于已排序的类别(如“坏”、“平均”、“好”和“优秀”),但显然海洋邻近栏的情况并非如此(例如,类别0和4显然比类别0和1更相似)。要解决这个问题,一个常见的解决方案是为每个类别创建一个二进制属性:一个属性在类别为“<1H海洋”时等于1(否则为0),另一个属性在“内陆”时等于1(否则为0),依此类推。这称为单热编码,因为只有一个属性将等于1(热),而其他属性将等于0(冷)。新属性有时被称为伪属性。Scikit-Learn提供了一个OneHotEncoder类来将分类值转换为单热向量:

from sklearn.preprocessing import OneHotEncoder
cat_encoder = OneHotEncoder()
housing_cat_1hot = cat_encoder.fit_transform(housing_cat)

默认情况下,OneHotEncoder的输出是SciPy稀疏矩阵,而不是NumPyarray:

稀疏矩阵是大多数包含零的矩阵的一种非常有效的表示形式。实际上,它内部只存储非零值及其位置。当一个分类属性有数百或数千个类别时,单热编码会产生一个非常大的矩阵,其中除了每行只有一个1之外,其余都是0。在这种情况下,稀疏矩阵正是您所需要的:它将节省大量内存并加快计算速度。你可以使用一个稀疏矩阵,就像一个普通的2D数组,12但是如果你想把它转换成一个(密集的)NumPy数组,只需要调用toarray()方法:

或者,您可以在创建OneHotEncoder时设置sparse=False,在这种情况下,transform()方法将直接返回一个常规(密集)NumPy数组。

与OrdinalEncoder一样,您可以使用编码器的categories_instance变量获取类别列表:

Pandas有一个名为get_dummies()的函数,它也将每个分类特征转换为单热点表示, 每个类别有一个二进制特征:

它看起来很好很简单,那么为什么不使用它来代替OneHotEncoder呢?OneHotEncoder的优点是它能记住训练的类别。这一点非常重要,因为一旦您的模型投入生产,就应该提供与训练期间完全相同的功能:不多也不少。看看我们的训练好的cat_encoder在转换相同的df_test时输出(使用transform(),而不是fit_transform ()):

看到区别了吗get_dummies()只看到两个类别,所以它输出两列,而OneHotEncoder按照正确的顺序为每个学习到的类别输出一列。而且,如果您给get_dummies()提供一个包含未知类别DataFrame(例如,“《2HOPEN”),那么它将很高兴地为其生成一列:

但OneHotEncoder更聪明:它将检测未知类别并引发异常。如果你愿意,你可以将handle_unknown超参数设置为"ignore",在这种情况下,它将用零表示未知类别:

使用DataFrame拟合任何Scikit-Learn估计器时,估计器将列名存储在feature_names_in_attribute中。Scikit-Learn然后确保任何DataFrame在此之后被馈送到该估算器(例如要转换()或预测())具有相同的列名。Transformers还提供get_feature_names_out ()方法,您可以使用该方法围绕Transformers的输出构建DataFrame:

端到端的机器学习项目之探索数据(Machine Learning 研习之七)
« 上一篇 2023-10-24
特征缩放和转换以及自定义Transformers(Machine Learning 研习之九)
下一篇 » 2023-11-18

相关推荐

  • 端到端的机器学习项目之探索数据(Machine Learning 研习之七) 2023-10-24 19:30:00 +0800 CST
    端到端的机器学习项目之探索数据(Machine Learning 研习之七) 本篇其实是承接上一篇内容,之所以没在上一篇将它写完,那是有原因的,毕竟,本着学习的态度,篇幅不应过长,方能使你有学习的欲望! 探索数据 首先,确保你已经把测试放在一边,你只是在探索训练集。此外,如果训练集非常大,您可能希望对探索进行采样设置,使操作在勘探阶段变得容易和快速。在这种情况下,培训集非常小,所以您可以直接处理完整集。由于你要试验完整训练集的各种变换,你应该制作一份原始的所以你可以在之后恢复它: housing = strat_train_set.copy() 可视化地理数据 因为数据集包括地理信息(纬度和经度),所以创建一个所有地区的散点图来可视化数据是一个好主意(见下图): housing.plot(kind="scatter", x="longitude", y="latitude", grid=True) plt.show() 这看起来很像加州,但除此之外,很难看到任何特别的模式。将alpha选项设置为0.2可以更容易地显示数据点密度高的位置(见下图): housing.plot(kind="scatter", x="longitude", y="latitude", grid=True, alpha=0.2) plt.show() 现在的情况要好得多:你可以清楚地看到高密度地区,即海湾地区、洛杉矶和圣地亚哥周围,再加上中央山谷(特别是萨克拉门托和弗雷斯诺)的一长排高密度地区。 我们的大脑非常善于发现图片中的模式,但你可能需要玩弄可视化参数,使模式脱颖而出。 接下来,你再看看房价(见下图)。每个圆圈的半径代表该地区的人口(选项s),颜色代表价格(选项c)。这里使用了一个名为jet的预定义颜色映射(选项cmap),其范围从蓝色(低值)到红色(高价):s housing.plot(kind="scatter", x="longitude", y="latitude", grid=True, s=housing["population"] / 100, label="population", c="median_house_value", cmap="jet", colorbar=True, legend=True, sharex=False, figsize=(10, 7)) plt.show() 这张图告诉你,房价与地理位置(例如,靠近大海)和人口密度密切相关,你可能已经知道了。聚类算法对于检测主集群和添加测量与集群中心的邻近性的新特性应该是有用的。海洋邻近属性也可能是有用的,尽管在北加州沿海地区的房价不是太高,所以这不是一个简单的规则。 寻找相关性 由于数据集不是太大,你可以很容易地使用corr()方法计算每对属性之间的标准相关系数(也称为皮尔逊相关系数): corr_matrix = housing.corr() 现在您可以查看每个属性与房屋中值的相关性: 相关系数从-1到1。当它接近1时,意味着有很强的正相关关系,例如,当收入中位数上升时,房屋价值中位数往往会上升。当系数接近-1时,意味着有很强的负相关性;你可以看到纬度和房屋价值中间值之间有一个很小的负相关性(即往北走,房价有轻微的下降趋势)。最后,系数接近0意味着不存在线性相关关系。 检查属性之间相关性的另一种方法是使用Pandas scatter_matrix ()函数,该函数将每个数值属性与其他每个数值属性进行绘图。由于现在有11个数字属性,您将得到112=121幅不适合页面的地块,所以您决定将重点放在一些看起来与住房中值最相关的有前途的属性上(见下图): 如果Pandas将每个变量相对于自身绘制,那么主对角线将充满直线,这将不是很有用。因此,Pandas显示每个属性的直方图(其他选项可用;请参阅Pandas文档以获取更多详细信息)。 查看相关散点图,似乎最有希望预测房屋价值中位数的属性是收入中位数,所以你放大他们的散点图(见下图): housing.plot(kind="scatter", x="median_income", y="median_house_value", alpha=0.1, grid=True) plt.show() 这个情节揭示了一些事情。首先,相关性确实很强;你可以清楚地看到上升的趋势,而且点也不太分散。第二,你之前注意到的价格上限在500,000美元的水平线上明显可见。但这个情节也揭示了其他不那么明显的直线:一条水平线在45万美元左右,另一条大约35万美元左右,也许还有一条大约28万美元,还有几条低于这一点。您可能希望尝试删除相应的区域,以防止您的算法学习重现这些数据怪癖。 警告: 相关系数只测量线性相关性(“当x上升时,y通常上升/下降”)。它可能会完全忽略非线性关系(例如,“当x接近0时,y通常会上升”)。图2-16显示了各种数据集及其相关系数。请注意,尽管它们的轴线显然不是独立的,但底部行的所有图都具有等于0的相关系数:这些都是非线性关系的例子。另外,第二行显示了相关系数等于1或-1的示例;注意,这与斜率无关。例如,以英寸为单位的身高与以英尺或纳米为单位的身高的相关系数为1。
  • 端到端的机器学习项目(Machine Learning 研习之六) 2023-10-14 17:02:17 +0800 CST
  • 机机器学习的测试和验证(Machine Learning 研习之五) 2023-08-27 16:32:17 +0800 CST
  • 机器学习的主要挑战和任务(Machine Learning 研习之四) 2023-08-13 15:12:38 +0800 CST
  • 对于大量数据集的解决方案便是在线学习或是增量学习(Machine Learning 研习之三) 2023-07-28 21:12:38 +0800 CST