First,新年到了!感谢CSDN一路相伴,成为技术交流的温馨港湾。值此蛇年新春,祝平台人气蒸蒸日上,活动精彩纷呈,助力更多开发者突破技术瓶颈,在新的一年创造无限可能,新年快乐!
文章目录
- 特征缩放:数据归一化
- 一、 概述
- 1.1 简介
- 1.2 优势
- 1.2.1 优化收敛模型
- 1.2.2 无量纲化
- 二、min-max normalization
- 2.1 正常区间
- 2.2 线性拟合
- 2.3 其余区间
- 2.4 问题
- 2.4 解决方法
- 三、Z-score normalization
- 四、Decimal Scaling Normalization
- 五、RobustScaler
- 六、第三方库实现
特征缩放:数据归一化
一、 概述
1.1 简介
看本文之前,最好先看拟合损失函数-CSDN博客
在数据预处理中,数据的归一化和标准化是极为关键的特征缩放方法。实际应用里,不同评价指标常常带有不同的量纲与量纲单位,这会干扰数据分析的结果。为了消除这些指标间的量纲差异,让数据具有可比性,进行数据归一化或标准化处理就显得尤为重要。经过处理后,原始数据中的各个指标被调整到同一数量级,这为后续的综合对比评价提供了有利条件,能够更准确、有效地挖掘数据价值,助力数据分析与决策。
1.2 优势
1.2.1 优化收敛模型
python">import numpy as np
import matplotlib.pyplot as plt# 生成数据
x1 = np.random.rand(100) * 10 # 生成0到10的随机数
x2 = np.random.rand(100) * 1000 # 生成0到300的随机数
y_ori = 15 + 20 * x1 + 0.1 * x2 + np.random.rand(100) * 5 # 生成数据,制作扰动
X = np.array([np.ones_like(x1), x1, x2]).T # 矩阵中每一行都对应const, x1, x2 : b, w1, w2
def mse_loss(param: np.ndarray, X: np.ndarray, y: np.ndarray):y_pre = X.dot(param)return np.mean((y - y_pre) ** 2) / 2w_temp1, w_temp2 = np.linspace(-10, 50, 100), np.linspace(-0.3, 0.5, 100)
w1, w2 = np.meshgrid(w_temp1, w_temp2) # 生成矩阵坐标系
loss = np.zeros_like(w1)
for i in range(loss.shape[0]):for j in range(loss.shape[1]):param = np.array([0, w1[i][j], w2[i][j]])loss[i][j] = mse_loss(param, X, y_ori)# 绘制等高线图
plt.figure(figsize=(8, 6))
contour = plt.contour(w1, w2, loss, levels=50, cmap='viridis') # 绘制图形
plt.colorbar(contour)
plt.xlabel("Var 1 (w1)")
plt.ylabel("Var 2 (w2)")
plt.title("Contour Plot of Loss Function")
plt.show()
在这里,自变量 x 1 x_{1} x1, x 2 x_{2} x2其相差的数量级较大,发现等高线为椭圆形,则其进行梯度下降法所需要经历的路径更长,并且拟合后的数据稳定性较差,产生的误差较大。即, 0 < w 1 < 40 , − 0.3 < w 2 < 0.5 0<w_{1}<40,-0.3<w_{2}<0.5 0<w1<40,−0.3<w2<0.5,在梯度下降的过程中,不是很好定义学习率 α \alpha α。
而,使用归一化后的数据进行的拟合,数据的稳定性较高,这里我们将 x 1 , x 2 x_{1},x_{2} x1,x2变量都进行数据的归一化,使用[max-min normalization](### 1.2 max-min normalization)的方法,来进行数据归一化:
这里定义匿名函数进行归一化:
min_max_normal = lambda data: (data-np.min(data))/(np.max(data)-np.min(data))
python">import numpy as np
import matplotlib.pyplot as plt# 生成数据
x1 = np.random.rand(100) * 10 # 生成0到10的随机数
x2 = np.random.rand(100) * 1000 # 生成0到300的随机数
y_ori = 15 + 20 * x1 + 0.1 * x2 + np.random.rand(100) * 5 # 生成数据,制作扰动### 数据的归一化
min_max_normal = lambda data: (data-np.min(data))/(np.max(data)-np.min(data))
x1_normal = min_max_normal(x1) * 100
x2_normal = min_max_normal(x2) * 100X_scaled = np.array([np.ones_like(x1_normal), x1_normal, x2_normal]).T # 矩阵中每一行都对应const, x1, x2 : b, w1, w2
def mse_loss(param: np.ndarray, X: np.ndarray, y: np.ndarray):y_pre = X.dot(param)return np.mean((y - y_pre) ** 2) / 2w_temp1, w_temp2 = np.linspace(-2, 5, 100), np.linspace(-2, 5, 100)
w1, w2 = np.meshgrid(w_temp1, w_temp2) # 生成矩阵坐标系
loss = np.zeros_like(w1)
for i in range(loss.shape[0]):for j in range(loss.shape[1]):param = np.array([0, w1[i][j], w2[i][j]])loss[i][j] = mse_loss(param, X_scaled, y_ori)# 绘制等高线图
plt.figure(figsize=(8, 6))
contour = plt.contour(w1, w2, loss, levels=50, cmap='viridis') # 绘制图形
plt.colorbar(contour)
plt.xlabel("Var 1 (w1)")
plt.ylabel("Var 2 (w2)")
plt.title("Contour Plot of Loss Function")
plt.show()
可以看出, w 1 , w 2 w_{1},w_{2} w1,w2的范围在 [ − 5 , 5 ] [-5,5] [−5,5]之间,即区间直接缩小了。并且,其也可以避免数值问题,否则可能会出现Runge现象
1.2.2 无量纲化
将数据映射到 0 到 1 的区间内,使得数据的取值范围仅与数据的最小值和最大值有关,而与原始的量纲无关。
假设有一组表示长度的数据 [ 10 c m , 20 c m , 30 c m , 40 c m , 50 c m ] [10cm,20cm,30cm,40cm,50cm] [10cm,20cm,30cm,40cm,50cm],使用最值归一化, x m i n = 10 c m , x m a x = 50 c m x_{min}=10cm,x_{max}=50cm xmin=10cm,xmax=50cm,对于 20 c m 20cm 20cm这个数据,归一化后为 20 − 10 50 − 10 = 0.25 \frac{20-10}{50-10}=0.25 50−1020−10=0.25。此时,数据的量纲“ c m cm cm”被去除,数据变成了无量纲的相对值。
min-max normalization、Z-score normalization和Decimal Scaling Normalization是数据预处理中常用的三种数据规范化方法。
二、min-max normalization
归一化一般是将数据映射到指定的范围,用于去除不同维度数据的量纲以及量纲单位。
2.1 正常区间
常见的映射范围有 [ 0 , 1 ] 和 [ − 1 , 1 ] [0, 1] 和 [-1, 1] [0,1]和[−1,1] ,最常见的归一化方法就是 Min-Max 归一化。也称为离差标准化,是对原始数据的线性变换,使结果值映射到 [ 0 , 1 ] [0,1] [0,1]之间。转换函数如下:
x n o r m a l = x − x m i n x m a x − x m i n x_{normal}=\frac{x-x_{min}}{x_{max}-x_{min}} xnormal=xmax−xminx−xmin
其中,max为样本数据的最大值,min为样本数据的最小值。这种归一化方法比较适用在数值比较集中的情况。但是,如果max和min不稳定,很容易是的归一化结果不稳定,使得后续使用效果也不稳定,实际使用中可以用经验常量值来替代max和min。而当有新数据加入时,可能导致max和min的变化,需要重新定义。
2.2 线性拟合
线性回归模型的一般形式为:
h ( θ ) = θ 0 + θ 1 x θ 0 :截距,也称偏移量 θ 1 :斜率 x :特征 h ( θ ) :目标值 \begin{array}{l} h(\theta)=\theta_{0}+\theta_{1}x\\ \theta_{0}:截距,也称偏移量 \\ \theta_{1}:斜率 \\ x:特征 \\ h(\theta):目标值 \end{array} h(θ)=θ0+θ1xθ0:截距,也称偏移量θ1:斜率x:特征h(θ):目标值
归一化处理,假设我们对特征 x x x进行归一化处理,使其值在 [ 0 , 1 ] [0,1] [0,1]之间。归一化公式为:
x n o r m a l = x − x m i n x m a x − x m i n x_{normal}=\frac{x-x_{min}}{x_{max}-x_{min}} xnormal=xmax−xminx−xmin
将归一化后的特征 x n o r m a l x_{normal} xnormal代入线性回归模型:
h ( θ ) = θ 0 + θ 1 ( x − x m i n x m a x − x m i n ) h(\theta) = \theta_{0}+\theta_{1}(\frac{x-x_{min}}{x_{max}-x_{min}}) h(θ)=θ0+θ1(xmax−xminx−xmin)
重新整理得,
h ( θ ) = θ 0 + θ 1 ( x x m a x − x m i n ) − θ 1 ( x m i n x m a x − x m i n ) h(\theta)=\theta_{0}+\theta_{1}(\frac{x}{x_{max}-x_{min}})-\theta_{1}(\frac{x_{min}}{x_{max}-x_{min}}) h(θ)=θ0+θ1(xmax−xminx)−θ1(xmax−xminxmin)
因此,为了简化表示,我们可以重新定义参数:
θ 1 ′ = θ 1 x m a x − x m i n θ 0 ′ = θ 0 − θ 1 ( x m i n x m a x − x m i n ) 即,可表示为: y = θ 0 ′ + θ 1 ′ x \begin{array}{l} \theta_{1}^{'}=\frac{\theta_{1}}{x_{max}-x_{min}} \\ \theta_{0}^{'}=\theta_{0}-\theta_{1}(\frac{x_{min}}{x_{max}-x_{min}}) \\ 即,可表示为:y=\theta_{0}^{'}+\theta_{1}^{'}x \end{array}{} θ1′=xmax−xminθ1θ0′=θ0−θ1(xmax−xminxmin)即,可表示为:y=θ0′+θ1′x
python">import numpy as np
import matplotlib.pyplot as plt# 生成随机数据
X = np.random.rand(100, 1) # 生成100个随机数作为特征
y = 2 * X + 1 + 0.1 * np.random.randn(100, 1) # 生成目标值,添加一些噪声# 使用numpy实现归一化
X_min = np.min(X) # 计算特征的最小值
X_max = np.max(X) # 计算特征的最大值
X_scaled = (X - X_min) / (X_max - X_min) # 进行归一化处理,使数据在0到1之间# 创建线性回归模型
def linear_regression(X, y):# 计算权重和偏置,使用矩阵运算来求解X_b = np.c_[np.ones((X.shape[0], 1)), X] # 添加偏置项theta_best = np.linalg.inv(X_b.T.dot(X_b)).dot(X_b.T).dot(y) # 正规方程求解return theta_best# 训练模型
theta_best = linear_regression(X_scaled, y) # 使用归一化后的数据训练模型# 使用归一化后的模型进行预测
X_plot = np.linspace(X_min, X_max, 100)
x_plot_scaled = (X_plot - X_min) / (X_max - X_min)
X_plot_b = np.c_[np.ones((x_plot_scaled.shape[0], 1)), x_plot_scaled] # 添加偏置项
y_plot_pred = X_plot_b.dot(theta_best) # 使用模型进行预测# 绘制结果
plt.scatter(X, y, color='blue', label='real') # 绘制实际数据点
plt.plot(X_plot, y_plot_pred, color='red', label='predict') # 绘制预测结果线
plt.xlabel('Feature') # 设置x轴标签
plt.ylabel('Target') # 设置y轴标签
plt.legend() # 显示图例
plt.show() # 显示图形
2.3 其余区间
如果使用最小值最大值归一法来进行数据的拟合,我们可以进行额外运算来达到我们需要的区间:
如,我们需要将我们的数据放缩到 [ − 1 , 1 ] [-1,1] [−1,1]区间,则使用下列公式:
X s c a l e = 2 × ( X − X m i n ) X m a x − X m i n − 1 X_{scale} = \frac{2\times(X-X_{min})}{X_{max}-X_{min}}-1 Xscale=Xmax−Xmin2×(X−Xmin)−1
我们需要将数据放缩到 [ a , b ] [a,b] [a,b]区间,使用下列公式:
X s c a l e = a + ( X − X m i n ) ( b − a ) X m a x − X m i n X_{scale}=a+\frac{(X-X_{min})(b-a)}{X_{max}-X_{min}} Xscale=a+Xmax−Xmin(X−Xmin)(b−a)
2.4 问题
当新数据的值大于训练数据的最大值时,归一化处理确实会面临一些问题。这是因为归一化是基于训练数据的最小值和最大值进行的,如果新数据超出了这个范围,直接使用训练数据的最小值和最大值进行归一化可能会导致新数据的归一化值超出 [0, 1] 区间,从而失去归一。
解决方法:
- 重置归一化系数:如果新数据的值超出了训练数据的范围,可以重新计算归一化参数,即使用新数据的最大值和最小值来重新归一化数据。但这会改变训练数据的归一化方式,可能导致模型性能下降。
- 使用滚动归一化算法,在预测新数据时,可以使用滚动归一化的方法。即每次预测时,只使用最近的 n 个数据点来计算归一化参数,然后对新数据进行归一化和预测。
- 除了最小-最大归一化,还可以使用其他归一化方法,如 Z-score 标准化,它不受数据范围的影响,对异常值更鲁棒。
2.4 解决方法
这里调用第三方的接口
python">import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 显示中文
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import MinMaxScaler# 生成随机数据
np.random.seed(0)
X_train = np.random.rand(100, 1) * 10 # 训练数据范围 [0, 10]
y_train = 2 * X_train + 1 + np.random.randn(100, 1) # 添加噪声# 新数据,部分值超出训练数据范围
X_new = np.array([[11], [12], [13], [0.5], [1.5]]) # 新数据范围 [0.5, 13]# 滚动归一化
def rolling_normalize(X_train, X_new):# 将训练数据和新数据合并,计算归一化参数,并且使用合并后的数据范围对训练数据和新数据进行归一化处理。combined = np.vstack((X_train, X_new)) # 将训练数据和新数据合并scaler = MinMaxScaler()scaler.fit(combined) # 使用合并后的数据计算归一化参数X_train_scaled = scaler.transform(X_train) # 归一化训练数据X_new_scaled = scaler.transform(X_new) # 归一化新数据return X_train_scaled, X_new_scaled, scaler# 对数据进行滚动归一化
X_train_scaled, X_new_scaled, scaler = rolling_normalize(X_train, X_new)# 创建线性回归模型
model = LinearRegression()
model.fit(X_train_scaled, y_train) # 使用归一化后的训练数据训练模型# 预测新数据
y_new_pred = model.predict(X_new_scaled)# 绘制结果
plt.figure(figsize=(10, 6))# 绘制训练数据点
plt.scatter(X_train, y_train, color='blue', label='训练数据')# 绘制拟合线
X_plot = np.linspace(0, 13, 100).reshape(-1, 1) # 生成用于绘图的 x 值,范围 [0, 13]
X_plot_scaled = scaler.transform(X_plot) # 对绘图数据进行归一化
y_plot = model.predict(X_plot_scaled) # 预测绘图数据的 y 值
plt.plot(X_plot, y_plot, color='red', label='拟合线')# 绘制新数据的预测点
plt.scatter(X_new, y_new_pred, color='green', label='新数据预测')plt.xlabel('特征 X')
plt.ylabel('目标值 y')
plt.title('线性拟合与新数据预测(滚动归一化)')
plt.legend()
plt.show()# 打印新数据的预测结果
print("新数据的预测结果:")
print(y_new_pred)
三、Z-score normalization
标准差标准化公式为:
X n o r m = X − μ σ μ :均值, m e a n σ :方差, s t d \begin{array}{l} X_{norm}=\frac{X-\mu}{\sigma } \\ \mu:均值,mean \\ \sigma:方差,std \end{array}{} Xnorm=σX−μμ:均值,meanσ:方差,std
这个方法可以将数据调整为均值为0,标准差为1的分布。
Python实现方法:
python">import numpy as npdef z_score_normalization(data):"""Z-Score 标准化函数:param data: 需要标准化的数据,可以是列表或 NumPy 数组:return: 标准化后的数据"""data = np.array(data) # 确保数据是 NumPy 数组mean = np.mean(data) # 计算均值std = np.std(data) # 计算标准差z_score = (data - mean) / std # 应用 Z-Score 标准化公式return z_score# 示例数据
data = np.array([1, 2, 3, 4, 5])
normalized_data = z_score_normalization(data)print("原始数据:", data)
print("标准化后的数据:", normalized_data)
注意事项:
- 数据分布假设:Z-Score 标准化假设数据服从正态分布。如果数据分布不符合这一假设,可能会导致误判。
- 异常值处理:在存在异常值的情况下,Z-Score 标准化可以间接通过中心化避免异常值和极端值的影响。
四、Decimal Scaling Normalization
Decimal Scaling Normalization 是一种数据归一化技术,通过移动小数点的位置来缩放数据值,使得所有数据的绝对值小于1。这种方法特别适用于处理具有不同数量级的数据。
实现方式:
python">import numpy as npdef decimal_scaling_normalization(data: np.ndarray):"""实现 Decimal Scaling Normalization:param data: 需要归一化的数据,可以是列表或 NumPy 数组:return: 归一化后的数据"""max_abs_value = np.max(np.abs(data)) # 找到数据中绝对值最大的数j = len(str(int(max_abs_value))) # 计算最大绝对值的位数scaling_factor = 10 ** j # 计算缩放因子normalized_data = data / scaling_factor # 应用缩放return normalized_data# 示例数据
data = np.array([1234, 5678, 91011, -23456])
normalized_data = decimal_scaling_normalization(data)print("原始数据:", data)
print("归一化后的数据:", normalized_data)
五、RobustScaler
RobustScaler是一种数据预处理方法,用于对数据进行缩放和中心化处理,特别适用于包含异常值或离群点的数据集。它通过使用中位数和四分位数范围(Interquartile Range, IQR)来替代传统的均值和标准差,从而提高对异常值的鲁棒性。
实现公式:
x ′ = x − m e d i a n I Q R I Q R = Q 3 − Q 1 m e d i a n :中位数 \begin{array}{l} \\ x^{'}=\frac{x-median}{IQR} \\ IQR=Q_{3}-Q_{1} \\ median:中位数 \end{array} x′=IQRx−medianIQR=Q3−Q1median:中位数
四分数范围(IQR):第3个四分位数(Q3)与第1个四分位数(Q1)之间的差值。
实现方式:
python">import numpy as npdef robust_scaler(data: np.ndarray, quantile_range=(25.0, 75.0)):"""实现 RobustScaler 的函数:param data: 需要归一化的数据,可以是列表或 NumPy 数组:param quantile_range: 四分位数范围,默认为 (25.0, 75.0),即 IQR:return: 归一化后的数据"""q_25, q_75 = np.percentile(data, quantile_range, axis=0) # 计算第25和第75百分位数median = np.median(data, axis=0) # 计算中位数iqr = q_75 - q_25 # 计算 IQR# 避免除以零的情况,否则误差很大iqr[iqr == 0] = 1# 应用 RobustScaler 公式scaled_data = (data - median) / iqrreturn scaled_data# 示例数据
data = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [100, 100, 100]]) # 包含异常值100# 使用自定义的 RobustScaler 函数
scaled_data = robust_scaler(data)print("原始数据:\n", data)
print("归一化后的数据:\n", scaled_data)
使用场景:
- 数据中存在显著的离群值(outliers),而这些离群值对标准化过程不应有过多影响。
- 数据不服从正态分布,且存在偏态分布的情况。
- 需要对数据进行鲁棒性预处理,以减少异常值对模型训练的影响。
六、第三方库实现
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei'] # 显示中文
from sklearn.preprocessing import MinMaxScaler, StandardScaler, RobustScaler# 生成随机数据
np.random.seed(0)
X = np.random.rand(100, 1) * 100 # 生成100个随机数作为特征# 最小-最大归一化
min_max_scaler = MinMaxScaler()
X_min_max_scaled = min_max_scaler.fit_transform(X)
"""
# 相当于:
min_max_scaler.fit(X) # 使用合并后的数据计算归一化参数
X_min_max_scaled = min_max_scaler.transform(X) # # 归一化训练数据
"""# Z-score 标准化
standard_scaler = StandardScaler()
X_standard_scaled = standard_scaler.fit_transform(X)# RobustScaler
robust_scaler = RobustScaler()
X_robust_scaled = robust_scaler.fit_transform(X)# 绘制结果
fig, axs = plt.subplots(2, 2, figsize=(12, 4))axs[0, 0].hist(X, bins=20, color='blue', alpha=0.7)
axs[0, 0].set_title('原始数据')axs[0, 1].hist(X_min_max_scaled, bins=20, color='green', alpha=0.7)
axs[0, 1].set_title('最小-最大归一化')axs[1, 0].hist(X_standard_scaled, bins=20, color='red', alpha=0.7)
axs[1, 0].set_title('Z-score 标准化')axs[1, 1].hist(X_robust_scaled, bins=20, color='red', alpha=0.7)
axs[1, 1].set_title('RobustScaler 放缩')plt.tight_layout()plt.show()