网站首页 > 技术文章 正文
在机器学习的特征工程中对特征数据进行处理时经常会遇到“归一化”处理,那到底什么是归一化处理?归一化处理有什么作用?它的数学原理是什么?本文将分别从理论及实战角度给予说明。
什么是归一化?
归一化是一种数据处理方式,能将数据经过处理后限制在某个固定范围内。
归一化存在两种形式,一种是在通常情况下,将数处理为 [0, 1] 之间的小数,其目的是为了在随后的数据处理过程中更便捷。例如,在图像处理中,就会将图像从 [0, 255] 归一化到 [0, 1]之间,这样既不会改变图像本身的信息储存,又可加速后续的网络处理。其他情况下,也可将数据处理到 [-1, 1] 之间,或其他的固定范围内。
另一种是通过归一化将有量纲表达式变成无量纲表达式。那么什么是量纲,又为什么需要将有量纲转化为无量纲呢?具体举一个例子。当我们在做对房价的预测时,收集到的数据中,如房屋的面积、房间的数量、到地铁站的距离、住宅附近的空气质量等,都是量纲,而他们对应的量纲单位分别为平方米、个数、米、AQI等。这些量纲单位的不同,导致数据之间不具有可比性。同时,对于不同的量纲,数据的数量级大小也是不同的,比如房屋到地铁站的距离可以是上千米,而房屋的房间数量一般只有几个。经过归一化处理后,不仅可以消除量纲的影响,也可将各数据归一化至同一量级,从而解决数据间的可比性问题。
为什么要归一化?
归一化可以将有量纲转化为无量纲,同时将数据归一化至同一量级,解决数据间的可比性问题。在回归模型中,自变量的量纲不一致会导致回归系数无法解读或错误解读。在KNN、Kmeans等需要进行距离计算的算法中,量纲的量级不同可能会导致拥有较大量级的特征在进行距离计算时占主导地位,从而影响学习结果。
数据归一化后,寻求最优解的过程会变得平缓,可以更快速的收敛到最优解。
为什么归一化能提高求解最优解的速度?
之前我们提到一个对房价进行预测的例子,假设自变量只有房子到地铁站的距离 x1和房子内房间的个数 x2,因变量为房价,预测公式和损失函数分别为:
在未归一化时,房子到地铁站的距离的取值在 0~5000 之间,而房间个数的取值范围仅为0~10。假设 x1=1000 ,x2= 3 ,x_{1} = 1000,那么损失函数的公式可以写为:
可将该损失函数寻求最优解过程可视化为下图:
左图的红色椭圆代表归一化前的损失函数等高线,蓝色线段代表梯度的更新,箭头的方向代表梯度更新的方向。寻求最优解的过程就是梯度更新的过程,其更新方向与登高线垂直。由于x1 和 x2 的量级相差过大,损失函数的等高线呈现为一个瘦窄的椭圆。因此如图(左)所示,瘦窄的椭圆形会使得梯度下降过程呈之字形呈现,导致梯度下降速度缓慢。
当数据经过归一化后,损失函数的公式可以写为:
我们可以看到,经过归一化后的数据属于同一量级,损失函数的等高线呈现为一个矮胖的椭圆形(如图(右)所示),求解最优解过程变得更加迅速且平缓,因此可以在通过梯度下降进行求解时获得更快的收敛。
代码验证
以上是理论和数学说明,为了对整个过程有更直观的了解,我们按照老规矩上代码:
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import SGDRegressor
# Create a simple linear regression dataset with one feature having a much larger scale than the other
# np.random.seed(0)
# X = 2 * np.random.rand(100, 2)
# y = 4 + 3 * X[:, 0] + 0.01 * X[:, 1] + np.random.randn(100)
np.random.seed(0)
X = 2 * np.pi * np.random.rand(100, 1)
y = np.sin(X) + 0.1 * np.random.randn(100, 1)
y = y.ravel()
# SGDRegressor without feature scaling
sgd_reg_no_scaling = SGDRegressor(max_iter=1000, tol=1e-5, eta0=0.01)
sgd_reg_no_scaling.fit(X, y)
# SGDRegressor with feature scaling
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
sgd_reg_scaling = SGDRegressor(max_iter=1000, tol=1e-3, eta0=0.01)
sgd_reg_scaling.fit(X_scaled, y)
# Get the number of iterations and loss values (No Scaling)
iterations_no_scaling = []
loss_values_no_scaling = []
for i in range(1000):
sgd_reg_no_scaling.partial_fit(X, y) # Perform one iteration
loss = np.mean((sgd_reg_no_scaling.predict(X) - y) ** 2)
loss_values_no_scaling.append(loss)
iterations_no_scaling.append(i)
# Get the number of iterations and loss values (Scaling)
iterations_scaling = []
loss_values_scaling = []
for i in range(1000):
sgd_reg_scaling.partial_fit(X_scaled, y) # Perform one iteration
loss = np.mean((sgd_reg_scaling.predict(X_scaled) - y) ** 2)
loss_values_scaling.append(loss)
iterations_scaling.append(i)
# Plot the loss curves
plt.figure(figsize=(12, 6))
plt.subplot(1, 2, 1)
plt.plot(iterations_no_scaling, loss_values_no_scaling, linewidth=2, label='No Scaling')
plt.xlabel('Iterations')
plt.ylabel('Loss')
plt.title('Loss vs. Iterations (No Scaling)')
plt.grid(True)
plt.legend()
plt.subplot(1, 2, 2)
plt.plot(iterations_scaling, loss_values_scaling, linewidth=2, label='Scaling')
plt.xlabel('Iterations')
plt.ylabel('Loss')
plt.title('Loss vs. Iterations (Scaling)')
plt.grid(True)
plt.legend()
plt.tight_layout()
plt.show()
再看一下运行结果。从运行结果可以很直观地看出,为经过归一化处理的回归会不断震荡,回归迭代的次数很多,而经过了归一化处理的回归很快就到使得loss达到了很小值。
下面针对代码做更深入的说明以方便对归一化处理的过程有深入了解。
这段代码的主要作用是演示特征归一化(Feature Scaling)对随机梯度下降回归(SGDRegressor)的训练效果的影响。它通过以下步骤完成:
- 创建一个简单的非线性回归数据集,其中包括一个特征(X)和一个目标变量(y)。这个数据集的特点是,目标变量(y)的值是通过一个正弦函数生成的,带有一些随机噪声。
- 创建两个SGDRegressor模型:一个使用未进行特征归一化的数据(sgd_reg_no_scaling),另一个使用经过特征归一化的数据(sgd_reg_scaling)。
- 对每个模型进行随机梯度下降的训练,分别记录每次迭代的损失值。
- 绘制损失曲线以可视化不同训练情况下损失随迭代次数的变化。
具体解释:
- 未进行特征归一化的模型 (sgd_reg_no_scaling) 将在原始数据上进行训练,包括一个特征(X),该特征的尺度较大(在这里是正弦函数的输入)。这会导致梯度下降在不同特征上的步长不均匀,可能需要更多的迭代次数才能找到最优解。
- 经过特征归一化的模型 (sgd_reg_scaling) 在经过归一化处理的数据上进行训练。特征归一化确保了特征的尺度一致,梯度下降在各个特征上的步长更均匀,因此通常可以更快地收敛到最优解。
最后,通过绘制损失曲线,你可以观察到两种情况下损失随着迭代次数的变化。归一化的情况下,损失通常更快地下降,说明模型更快地接近最优解,而未归一化的情况下,损失下降较慢,需要更多的迭代次数。这突出了特征归一化在优化算法中的重要性,可以帮助模型更快地收敛到最优解。
猜你喜欢
- 2024-09-25 果断收藏!python数据分析入门学习笔记(下)
- 2024-09-25 「机器学习」支持向量机分类 支持向量机 知乎
- 2024-09-25 数据可视化之箱线图详细介绍 箱线图绘制步骤
- 2024-09-25 简单的统计学:如何用Python计算扑克概率
- 2024-09-25 Python进行数据预处理 python如何做数据处理
- 2024-09-25 Distribution is all you need:这里有12种做ML不可不知的分布
- 2024-09-25 如何使用 Qdrant DB 创建基于向量的电影推荐系统?
- 2024-09-25 如何可视化卷积网络分类图像时关注的焦点
- 2024-09-25 感知机:教程,实现和可视示例 感知机定义
- 2024-09-25 深度残差网络+自适应参数化ReLU(调参记录23)Cifar10~95.47%
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- oraclesql优化 (66)
- 类的加载机制 (75)
- feignclient (62)
- 一致性hash算法 (71)
- dockfile (66)
- 锁机制 (57)
- javaresponse (60)
- 查看hive版本 (59)
- phpworkerman (57)
- spark算子 (58)
- vue双向绑定的原理 (68)
- springbootget请求 (58)
- docker网络三种模式 (67)
- spring控制反转 (71)
- data:image/jpeg (69)
- base64 (69)
- java分页 (64)
- kibanadocker (60)
- qabstracttablemodel (62)
- java生成pdf文件 (69)
- deletelater (62)
- com.aspose.words (58)
- android.mk (62)
- qopengl (73)
- epoch_millis (61)
本文暂时没有评论,来添加一个吧(●'◡'●)