特征缩放和转换以及自定义Transformers(Machine Learning 研习之九)

news2025/1/20 1:40:03

特征缩放和转换

您需要应用于数据的最重要的转换之一是功能扩展。除了少数例外,机器学习算法在输入数值属性具有非常不同的尺度时表现不佳。住房数据就是这种情况:房间总数约为6至39320间,而收入中位数仅为0至15间。如果没有任何缩放,大多数模型将倾向于忽略收入中位数,而更多地关注房间数。

有两种常见的方法使所有属性具有相同的尺度:最小-最大尺度和标准化。

与所有估计器一样,重要的是仅将标量拟合到训练数据:永远不要对训练集以外的任何对象使用fit()或fit_transform()。一旦你有了一个训练好的定标器,你就可以用它来变换()任何其他的集合,包括验证集、测试集和新的数据。请注意,虽然培训集值将始终缩放到指定的范围,但如果新的数据包含异常值,则这些值最终可能会缩放到该范围之外。如果要避免这种情况,只需将剪辑超参数设置为True即可。

最小-最大缩放(许多人称之为标准化)是最简单的:对于每个属性,值被移位和重新缩放,以便它们最终的范围从0到1。这是通过减去最小值并除以最小值和最大值之间的差来实现的。Scikit-Learn为此提供了一个名为MinMaxScaler的转换器。它有一个feature_range超参数,如果出于某种原因,不希望0-1(例如,神经网络在零均值输入下工作得最好,所以最好在-1到1的范围内工作)。是相当好用的:

from sklearn.preprocessing import MinMaxScaler
min_max_scaler = MinMaxScaler(feature_range=(-1, 1))
housing_num_min_max_scaled = min_max_scaler.fit_transform(housing_num)

标准化是不同的:首先它减去平均值(所以标准化值有一个零均值),然后它除以标准差(所以标准-化值的标准差等于1)。不像最小最大缩放,标准化化不会将值限制在特定的范围内。然而,标准化受到离群值的影响要小得多。例如,假设一个地区的收入中位数等于100(误打误撞),而不是通常的0-15。将最小最大值缩放到0-1范围会将这个离群值映射到1并将所有其他值压缩到0-0.15,而标准化不会受到太大影响。Scikit-Learn提供了一个名为StandardScaler的转换器用于标准化:

from sklearn.preprocessing import StandardScaler
std_scaler = StandardScaler()
housing_num_std_scaled = std_scaler.fit_transform(housing_num)

如果你想缩放一个稀疏矩阵而不首先将其转换为稠密矩阵,你可以使用一个标准缩放器,它的with_mean超参数设置为False它只会除以标准差,而不会减去均值(因为这会破坏稀疏性

当特征的分布具有重尾时(即,当远离平均值的值不是指数罕见时),最小-最大缩放和标准化都会将大多数值压缩到一个小范围内。 机器学习模型通常根本不喜欢这种情况,因此,在缩放特征之前,您应该首先对其进行变换以缩小重尾,并且如果可能的话,使分布大致对称。 例如,对于右侧有重尾部的正特征,执行此操作的常见方法是用其平方根替换特征(或将特征提高到 0 到 1 之间的幂)。 如果该特征具有非常长且重的尾部,例如幂律分布,则用其对数替换该特征可能会有所帮助。 例如,人口特征大致遵循幂律:拥有 10,000 名居民的地区的出现频率仅比拥有 1,000 名居民的地区低 10 倍,而不是呈指数级下降。 下图 显示了当你计算它的对数时这个特征看起来有多好:它非常接近高斯分布(即钟形)。

在这里插入图片描述

另一种处理重尾特征的方法是对特征进行反向转换。这意味着将其分布切割成大致相等大小的桶,并将每个特征值替换为它所属的桶的索引,就像我们创建收入猫特征一样(尽管我们只在分层抽样时使用它)。例如,您可以将每个值替换为其百分位数。使用相同大小的bucket处理会产生一个几乎均匀分布的特性,因此不需要进一步的缩放,或者您可以只除以bucket的数目来强制值到0-1的范围。

当一个特征具有多峰分布(即,有两个或两个以上清晰的峰值,称为模式)时,例housing_median_age特征,对它进行bucket ization也是有帮助的,但这次将bucket ID作为类别处理,而不是作为数值。这意味着必须对桶索引进行编码,例如使用OneHotEncoder(所以你通常不想用太多桶)。这种方法将允许回归模型更容易地为该特征值的不同范围学习不同的规则。例如,也许35年前建造的房子有一种独特的风格,已经过时了,因此它们比它们的年龄更便宜。

转换多峰分布的另一种方法是为每个模式(至少是主要模式)添加一个特征,表示住房中位年龄和该特定模式之间的相似性。相似性度量通常使用径向基函数(RBF)来计算–任何只依赖于输入值与不动点之间距离的函数。最常用的RBF是高斯RBF,其输出值随着输入值远离固定点而呈指数衰减。例如,高斯RBF相似度与房龄x和35由方程exp (-y (x-35)2)给出。超参数y(gamma)确定当x远离35时相似性度量衰减的速度。使用Scikit-Learn的rbf_kernel()函数,您可以创建一个新的高斯RBF特征来测量房屋中位年龄和35:

from sklearn.metrics.pairwise import rbf_kernel
age_simil_35 = rbf_kernel(housing[["housing_median_age"]], [[35]], gamma=0.1)

下图显示了这一新特征作为住房中位数年龄的函数(实线)。它还显示了如果使用较小的gamma值,该功能将是什么样子。如图所示,新的年龄相似性特征在35岁时达到峰值,正好在住房中位年龄分布的峰值附近:如果这个特定的年龄组与较低的价格有很好的相关性,那么这个新特征将有很好的机会发挥作用。

在这里插入图片描述

到目前为止,我们只看了输入特性,但是目标值可能也需要转换。例如,若目标分布具有较重的尾部,您可以选择将目标替换为其对数。但是,如果你这样做,回归模型现在将预测的房屋价值中位数的对数,而不是房屋价值中位数本身。如果您想要预测的房屋中值,则需要计算模型预测的指数值。

幸运的是,大多数Scikit-Learn的转换器都有一个inverse_transform()方法,这使得计算转换的逆运算变得很容易。例如,下面的代码示例显示了如何使用StandardScaler缩放标签(就像我们对输入所做的那样),然后在缩放后的标签上训练一个简单的线性回归模型,并使用它对一些新数据进行预测,然后使用经过训练的缩放器的inverse_transform()方法将这些数据转换回原始尺度。请注意,我们将标签从Pandas Series转换为DataFrame,因为标准Scaler期望2D输入。此外,为了简单起见,在本示例中,我们仅使用单个原始输入特征(中值收入)对模型进行训练:

from sklearn.linear_model import LinearRegression
target_scaler = StandardScaler()
scaled_labels = target_scaler.fit_transform(housing_labels.to_frame())
model = LinearRegression()
model.fit(housing[["median_income"]], scaled_labels)
some_new_data = housing[["median_income"]].iloc[:5] # pretend this is new data
scaled_predictions = model.predict(some_new_data)
predictions = target_scaler.inverse_transform(scaled_predictions)

这工作得很好,但更简单的选择是使用TransformedTargetRegressor。我们只需要构造它,给它回归模型和标签转换器,然后使用原始的未缩放标签将其拟合到训练集上。它将自动使用转换器缩放标签,并在缩放后的标签上训练回归模型,就像我们之前所做的那样。然后,当我们想要进行预测时,它将调用回归模型的predict()方法,并使用scaler的inverse_trans form()方法生成预测:

from sklearn.compose import TransformedTargetRegressor
model = TransformedTargetRegressor(LinearRegression(),
transformer=StandardScaler())
model.fit(housing[["median_income"]], housing_labels)
predictions = model.predict(some_new_data)

自定义Transformers

虽然Scikit-Learn提供了许多有用的转换器,但您需要编写自己的任务,如自定义转换、清理操作或组合特定属性。

对于不需要任何训练的转换,您可以只编写一个函数,该函数接受NumPy数组作为输入,并输出转换后的数组。例如,如前一节所述,通过将具有重尾分布的特征替换为它们的对数(假设特征为正数且尾部位于右侧),通常是一个好主意。让我们创建一个log-transformer,并将其应用于人口特征:

from sklearn.preprocessing import FunctionTransformer
log_transformer = FunctionTransformer(np.log, inverse_func=np.exp)
log_pop = log_transformer.transform(housing[["population"]])

inverse_func参数是可选的。它允许您指定一个逆变换函数,例如,如果您计划在
TransformedTargetRegressor中使用您的转换器。转换函数可以接受超参数作为附加参数。例如,以下是如何创建一个转换器,计算与前面相同的高斯RBF相似性度量:

rbf_transformer = FunctionTransformer(rbf_kernel,
kw_args=dict(Y=[[35.]], gamma=0.1))
age_simil_35 = rbf_transformer.transform(housing[["housing_median_age"]]

注意,RBF核没有反函数,因为在距离一个固定点的给定距离上总是有两个值(距离0除外)。还要注意rbf_kernel()不会单独处理这些特性。如果你给它传递一个有两个特征的数组,它会测量二维距离(欧几里得)来测量相似性。例如,以下是如何添加一个功能,该功能将测量每个区和旧金山之间的地理相似性:

sf_coords = 37.7749, -122.41
sf_transformer = FunctionTransformer(rbf_kernel,
kw_args=dict(Y=[sf_coords], gamma=0.1))
sf_simil = sf_transformer.transform(housing[["latitude", "longitude"]])

自定义transformers对于组合功能也很有用。例如,这里有一个FunctionTransformer,它计算输入特征0和1之间的比率:

在这里插入图片描述

FunctionTransformer非常方便,但是如果您希望您的转换器是可训练的,在fit()方法中学习一些参数,然后在transform()方法中使用它们,那该怎么办呢?为此,您需要编写一个自定义类。Scikit-Learn依赖于duck类型,所以这个类不必从任何特定的基类继承。它只需要三个方法:fit()(必须返回self)、
transform () 和fit_transform()。

只需添加TransformerMixin作为基类,就可以免费获得fit_transform():默认实现将只调用fit(),然后再调用transform()。如果将BaseEstimator添加为基类(并避免使用*args和**kwargs),您还将获得两个额外的方法:get_params ()和set_params()。这些对于自动超参数调优非常有用。

例如,这里有一个自定义 transformer,其行为与StandardScaler非常相似:

from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.utils.validation import check_array, check_is_fitted
class StandardScalerClone(BaseEstimator, TransformerMixin):
    def __init__(self, with_mean=True): # no *args or **kwargs!
    	self.with_mean = with_mean
    def fit(self, X, y=None): # y is required even though we don't use it
        X = check_array(X) # checks that X is an array with finite float values
        self.mean_ = X.mean(axis=0)
        self.scale_ = X.std(axis=0)
        self.n_features_in_ = X.shape[1] # every estimator stores this in fit()
        return self # always return self!
    def transform(self, X):
        check_is_fitted(self) # looks for learned attributes (with trailing _)
        X = check_array(X)
        assert self.n_features_in_ == X.shape[1]
        if self.with_mean:
        X = X - self.mean_
        return X / self.scal

这里有几点需要注意:

  • sklearn.utils.validation包包含几个我们可以用来验证输入的函数。为了简单起见,我们将在本书的其余部分跳过这样的测试,但是产品代码应该有这些测试。
  • Scikit-Learn管道要求fit()方法有两个参数X和y,这就是为什么我们需要y=没有参数,即使我们不使用y。
  • 所有Scikit-Learn估计器都在fit()方法中设置n_features_in_,并且它们确保传递给transform()或predict()的数据具有此数量的特征。_
  • .fit ()方法必须返回self。
  • 这个实现不是100%完成的:所有的估算器都应该在拟合中设置feature_
    names_in_ ()方法时,它们被传递一个DataFrame。此外,所有的转换器都
    应该提供get_feature_names_out()方法,以及当它们的转换可以被逆转时
    的inverse_transform()方法。更多细节请参见本章最后一个练习。

自定义转换器可以(而且经常)在其实现中使用其他估计器。例如,以下代码演示了自定义转换器,该转换器在fit()方法中使用KMeans聚类器来识别训练数据中的主要聚类,然后在transform()方法中使用rbf
kernel ()来衡量每个样本与每个聚类中心的相似程度:

from sklearn.cluster import KMeans
class ClusterSimilarity(BaseEstimator, TransformerMixin):
    def __init__(self, n_clusters=10, gamma=1.0, random_state=None):
        self.n_clusters = n_clusters
        self.gamma = gamma
        self.random_state = random_state
    def fit(self, X, y=None, sample_weight=None):
        self.kmeans_ = KMeans(self.n_clusters, random_state=self.random_state)
        self.kmeans_.fit(X, sample_weight=sample_weight)
        return self # always return self!
    def transform(self, X):
    	return rbf_kernel(X, self.kmeans_.cluster_centers_, gamma=self.gamma)
    def get_feature_names_out(self, names=None):
    	return [f"Cluster {i} similarity" for i in range(self.n_clusters)]

k-means是一种在数据中定位聚类的聚类算法。它搜索的数量由n_clusters超参数控制。训练后,可以通过cluster_centers_属性使用聚类中心。KMeans的fit()方法支持一个可选参数sample_weight,该参数允许用户指定样本的相对权重。K-means是一种随机算法,这意味着它依赖随机性来定位聚类,因此若您想要可重现的结果,则必须设置random_state参数。正如您所看到的,尽管任务很复杂,但代码相当简单。现在让我们使用这个自定义的变压器:

cluster_simil = ClusterSimilarity(n_clusters=10, gamma=1., random_state=42)
similarities = cluster_simil.fit_transform(housing[["latitude", "longitude"]],
sample_weight=housing_labels)

这段代码创建一个ClusterSimilarity转换器,将集群数设置为10.然后调用fit_transform(),输入训练集中每个区的纬度和经度,并根据每个区的房屋中值对每个区进行加权。变压器采用k-均值定位聚类,
然后测量每个区域与所有10个聚类中心之间的高斯RBF相似性。结果是一个矩阵,每个区有一行,每个集群有一列。先看前三行,四舍五入到小数点后两位:

在这里插入图片描述

下图显示了通过k-means找到的10个聚类中心。根据它们与最近的集群中心的地理相似性,对这些区域进行着色。正如您所看到的,大多数集群都位于人口密集且价格昂贵的地区。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1224528.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【总结】I/O接口中的数据线,地址线,控制线,状态线传输什么信息?

数据线 方向:双向功能:在内存、寄存器和数据缓冲寄存器进行数据交换;接口和设备的状态信息也通过数据线传给CPU(这里的状态指的是设备独有的,和状态线中的忙碌、空闲相区别);CPU对外设的控制命…

第一次组会汇报(2023/11/18)

目录 一,浅谈学习规划 二, 两个比较典型的注意力机制 ㈠SEnet ⒈结构图 ⒉机制流程讲解 ⒊源码(pytorch框架实现)及逐行解释 ⒋测试结果 ㈡CBAM ⒈结构图 ⒉机制流程讲解 ⒊源码(pytorch框架实现)…

【数据分享】2023年我国省市县三级的专精特新“小巨人”企业数量(Excel/Shp格式)

企业是经济活动的参与主体。一个城市的企业数量决定了这个城市的经济发展水平!比如一个城市的金融企业较多,那这个城市的金融产业肯定比较发达;一个城市的制造业企业较多,那这个城市的制造业肯定比较发达。 之前我们给大家分享了…

『 MySQL数据库 』数据库之表的约束

文章目录 前言 💻空属性约束(非空约束) 🔖default约束(默认值约束,缺省) 🔖列描述comment 🔖数字类型长度zerofill 🔖主键primary key 🔖📍 追加主键 📍📍 删除主键 &…

鞋业生产制造用什么ERP软件?能为企业带来哪些好处

鞋服这类商品的种类众多,同时也是我们生活当中较为常见的产品,各个制鞋企业有差异化的营销渠道和经营模式,日常生产过程存在的问题呈现多样化。 有些制鞋企业依然采用传统的管理方式,在这种模式之下,企业并不能随时掌…

97.qt qml-自定义Table之实现ctrl与shift多选

我们之前实现了:93.qt qml-自定义Table优化(新增:水平拖拽/缩放自适应/选择使能/自定义委托)-CSDN博客 实现选择使能的时候,我们只能一行行去点击选中,非常麻烦,所以本章我们实现ctrl多选与shift多选、 所以在Table控件新增两个属性: 1.实现介绍 ctrl多选实现原理:当我…

Linux命令之查看文件和权限修改操作

目录 查看文件 1. cat --- 将文件中的内容打印在输出设备 2. more --- 分页显示文件内容 3.less ---查看文件内容 4. head -- 查看文件前n行内容 5.tail -- 查看指定文件的后n行内容或实时监测文件 6. wc -- 可计算文件的字节数、字数和列数 文件搜索 1.which --- 获取…

Selenium——利用input标签上传文件

Selenium利用input标签上传文件 完整流程 打开文件上传页面选择要上传的文件点击上传按钮确认文件上传成功介绍怎么方便的获取对应元素的Xpath或者Css 简单介绍 在使用Selenium进行浏览器自动化测试时,文件上传是一个常见的需求。而 标签就是实现文件上传功能的…

Leetcode—206.反转链表【简单】

2023每日刷题(三十三) Leetcode—206.反转链表 头插法实现代码 /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/ struct ListNode* reverseList(struct ListNode* head) {if(head NULL…

MySQL 的执行原理(一)

5.1 单表访问之索引合并 我们前边说过 MySQL 在一般情况下执行一个查询时最多只会用到单个二级 索引,但存在有特殊情况,在这些特殊情况下也可能在一个查询中使用到多个二 级索引,MySQL 中这种使用到多个索引来完成一次查询的执行方法称之为&…

UML统一建模语言

UML包含3种构造块:事物、关系、图。 事物:模型中代表性成分的抽象关系:把事物结合在一起图:聚集了相关的事物 事物 结构事务:模型的静态部分,包括类、接口、协作、用例、主动类、构件、制品、结点 行为事…

CAD长方形纤维插件2D

插件介绍 CAD长方形纤维插件2D版本可用于在AutoCAD软件内生成随机分布的长方形纤维图形,生成的dwg格式模型可用于模拟二维随机分布的纤维复合材料、随机初始裂缝等,同时模型可导入COMSOL、Abaqus、ANSYS、Fluent等有限元软件内进行仿真分析计算。 插件…

torch.stack

看网上看多没讲的不是很明白,我来试试空间上的理解 # 假设是时间步T1的输出 T1 torch.tensor([[1, 2, 3],[4, 5, 6],[7, 8, 9]]) # 假设是时间步T2的输出 T2 torch.tensor([[10, 20, 30],[40, 50, 60],[70, 80, 90]])输出: print(torch.stack((T1,T2…

解决/usr/lib/libstdc++.so.6: version `GLIBCXX_3.x.x‘ not found问题

目录 1、查找缺少库版本2、动态库版本与gcc版本对应关系3、查找 libstdc.so.6.0.x 库文件4、如果libstdc.so.6.0.21库文件已存在,则按照下面的步骤创建软链接即可4.1 拷贝、软连接4.2验证新的 libstdc.so.6.0.21 库文件是否生效 5、如果libstdc.so.6.0.21库文件不存…

在做题中学习(30):字符串相加

思路: 相加时要转换成对应的数字,所以让字符数字-0 如‘9’-‘0’(ASCII)57-489 9110,会进1,把进位保存起来,只取0头插到新串里。 头插时要转换对应字符数字,所以让对应的数字‘…

【LeetCode刷题-双指针】--977.有序数组的平方

977.有序数组的平方 方法:双指针 由于数组是升序排序的,如果所有的数都是非负的,那么数组平方后,仍然保持升序,但数组中有负数,将每个数平方后,数组就会降序 需要找到数组中负数与非负数的分界…

车载开发岗位如何?Android程序员是否转行

这几年的大环境让大家都过的不安逸;社会动荡与就业问题一直困扰了不少人。在大家都认为的高薪工作程序员行业中,现在也是费力不讨好裁员风潮大部分指向互联网。 我们Android开发基本上已经感受很久了,就这就业问题很难存活。对此我们的目光都…

【LeetCode刷题-双指针】--360.有序转化数组

360.有序转化数组 方法&#xff1a;双指针 从两头计算&#xff0c;保存两端较小的值&#xff0c;高中抛物线知识&#xff0c;a>0&#xff0c;向上的抛物线&#xff0c;两端大中间小&#xff0c;从后往前存储计算结果&#xff1b;a<0&#xff0c;向下的抛物线&#xff0c…

深度模型压缩研究回顾

深度模型压缩研究回顾 作者&#xff1a;安静到无声 个人主页 目录 深度模型压缩研究回顾推荐专栏 在本节中&#xff0c;主要介绍了目前主流的深度神经网络压缩与加速方法&#xff0c;主要包括轻量化网络设计、参数量化、知识蒸馏、模型剪枝和硬件加速等&#xff0c;其中模型剪…

【开源】基于Vue.js的音乐偏好度推荐系统的设计和实现

项目编号&#xff1a; S 012 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S012&#xff0c;文末获取源码。} 项目编号&#xff1a;S012&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、系统设计2.1 功能模块设计2.1.1 音乐档案模块2.1…