使用 NumPy 进行数据归一化和缩放 – wiki基地


深入理解与实践:使用NumPy进行数据归一化与缩放

在数据科学和机器学习的旅程中,数据预处理是至关重要的一步。原始数据往往包含各种尺度、范围和分布的特征,直接使用这些数据进行模型训练可能会导致性能下降、收敛缓慢甚至无法收敛。为了解决这个问题,数据归一化(Normalization)和缩放(Scaling)技术应运而生。它们的目标是将不同特征的数据转换到相似的尺度上,从而消除或减弱量纲的影响。

本文将详细探讨数据归一化与缩放的必要性、常见技术及其背后的数学原理,并重点介绍如何利用强大的Python库 NumPy 来高效地实现这些技术。我们将通过具体的 NumPy 代码示例,展示如何对一维和二维数据进行不同类型的缩放处理。

第一章:为什么需要数据归一化与缩放?

想象一下,你正在为一个机器学习模型准备数据,其中一个特征是房屋面积(单位:平方米),范围可能是几十到几百甚至上千;另一个特征是卧室数量,范围通常是1到5或10。如果直接使用这些数据,面积这个特征的数值范围远大于卧室数量,许多机器学习算法在计算距离或梯度时,会不自觉地给予面积更大的权重,即使卧室数量在某些情况下可能是更重要的预测因素。

这种因特征数值范围差异带来的问题,是数据归一化与缩放需要解决的核心问题。具体来说,其必要性体现在以下几个方面:

  1. 改善依赖距离度量的算法性能: K近邻(KNN)、K均值聚类(K-Means)、支持向量机(SVM)等算法严重依赖于样本之间的距离计算。如果特征尺度差异过大,距离计算会受到尺度大的特征主导,导致结果不准确。
  2. 加速梯度下降算法的收敛: 线性回归、逻辑回归、神经网络等基于梯度下降的优化算法,在特征尺度差异大的情况下,损失函数的等高线会呈现狭长的椭圆形。这会导致梯度下降沿着“之”字形路径缓慢前进,收敛速度变慢。将特征缩放到相似的尺度可以使等高线更接近圆形,从而加快收敛速度。
  3. 防止模型对特定特征产生偏倚: 在某些模型中,如正则化线性模型(Lasso, Ridge),惩罚项会作用于模型权重。如果特征没有缩放,模型可能会为了减小惩罚项而压缩那些尺度较大的特征的权重,即使这些特征本身很重要。
  4. 提升模型的稳定性和鲁棒性: 缩放后的数据可以减少数值计算中的精度问题,提高模型的稳定性。对于一些对输入范围敏感的激活函数(如 Sigmoid、Tanh),缩放可以确保输入落在有效的范围内,避免梯度消失或爆炸。

简而言之,数据归一化与缩放是确保机器学习模型公平、高效地利用所有特征信息的基础步骤。

第二章:常见的数据归一化与缩放技术

数据归一化和缩放是两个紧密相关但有时略有区别的概念。广义上讲,它们都属于“特征缩放”(Feature Scaling)。狭义上:
* 归一化(Normalization) 通常指将数据缩放到一个固定的范围,最常见的是 [0, 1] 或 [-1, 1]。
* 标准化(Standardization) 通常指将数据转换为均值为 0、标准差为 1 的分布,也称为 Z-score 缩放。

以下是几种常见的缩放技术,我们将重点关注前四种,因为它们最常用且易于使用 NumPy 实现:

  1. Min-Max Scaling (最小-最大缩放): 将数据线性地缩放到一个预定的范围,通常是 [0, 1]。
  2. Standardization (Z-score 标准化): 将数据转换为均值为 0、标准差为 1 的分布。
  3. Max Absolute Scaling (最大绝对值缩放): 将数据除以特征的最大绝对值,使数据缩放到 [-1, 1] 的范围。适用于已经以 0 为中心的数据,或者数据是稀疏的。
  4. Robust Scaling (鲁棒缩放): 使用中位数和四分位距(IQR)进行缩放,对异常值(outliers)不敏感。
  5. Unit Vector Scaling (单位向量缩放/范数归一化):将每个样本(行)的特征向量缩放到 L1 或 L2 范数为 1。通常用于文本分类或聚类等领域。

我们将在后续章节详细讲解前四种技术及其 NumPy 实现。

第三章:使用 NumPy 实现 Min-Max Scaling (最小-最大缩放)

Min-Max Scaling 是一种将特征值线性地映射到指定范围 [min_range, max_range] 的方法,最常见的目标范围是 [0, 1]。

数学公式:

对于原始特征值 $x$,缩放后的值 $x’$ 的计算公式为:
$x’ = \text{min_range} + \frac{(x – \text{min}) \times (\text{max_range} – \text{min_range})}{\text{max} – \text{min}}$

当目标范围是 [0, 1] 时,公式简化为:
$x’ = \frac{x – \text{min}}{\text{max} – \text{min}}$

其中,$\text{min}$ 是特征的最小值,$\text{max}$ 是特征的最大值。

NumPy 实现步骤:

  1. 计算原始数据的最小值和最大值。
  2. 使用 NumPy 的广播(broadcasting)特性,将原始数据减去最小值。
  3. 计算最大值与最小值之差(即范围)。
  4. 将步骤2的结果除以步骤3的结果。
  5. 如果目标范围不是 [0, 1],还需要乘以目标范围的长度,并加上目标范围的最小值。

NumPy 代码示例 (目标范围 [0, 1]):

“`python
import numpy as np

创建一个 NumPy 数组作为示例数据

data = np.array([10, 20, 30, 40, 50])
print(“原始数据:”, data)

1. 计算最小值和最大值

data_min = np.min(data)
data_max = np.max(data)
print(f”原始数据的最小值: {data_min}, 最大值: {data_max}”)

检查分母是否为零(如果所有数据点都相同)

if data_max – data_min == 0:
print(“警告: 数据中的所有值都相同,无法进行 Min-Max 缩放。所有值将保持不变或变为目标范围的最小值。”)
# 在实际应用中,可能需要根据情况处理,例如所有值都变为目标范围的最小值
scaled_data = np.full_like(data, 0) # 如果目标范围是 [0, 1],则所有值变为 0
else:
# 2. 原始数据减去最小值 (NumPy 广播)
data_shifted = data – data_min

# 3. 计算范围 (max - min)
data_range = data_max - data_min

# 4. 除以范围得到缩放后的数据 (NumPy 广播)
scaled_data = data_shifted / data_range

print(“Min-Max 缩放后的数据 (范围 [0, 1]):”, scaled_data)

验证缩放后的范围

print(f”缩放后数据的最小值: {np.min(scaled_data)}, 最大值: {np.max(scaled_data)}”)

示例:缩放到范围 [-1, 1]

target_min = -1
target_max = 1
target_range = target_max – target_min

if data_max – data_min == 0:
print(“警告: 数据中的所有值都相同,无法进行 Min-Max 缩放。所有值将变为目标范围的最小值。”)
scaled_data_neg1_pos1 = np.full_like(data, target_min)
else:
# 重新计算 (或者基于 [0, 1] 的结果进行线性变换)
# 方法一:从头计算
scaled_data_neg1_pos1 = target_min + (data – data_min) * (target_range / (data_max – data_min))

# 方法二:基于 [0, 1] 结果变换
# scaled_data_neg1_pos1 = target_min + scaled_data * target_range

print(“Min-Max 缩放后的数据 (范围 [-1, 1]):”, scaled_data_neg1_pos1)
print(f”缩放后数据 ([-1, 1]) 的最小值: {np.min(scaled_data_neg1_pos1)}, 最大值: {np.max(scaled_data_neg1_pos1)}”)
“`

优点:
* 简单直观,易于理解。
* 将数据映射到固定范围,有助于某些算法(如神经网络中使用 Sigmoid 或 Tanh 激活函数)。

缺点:
* 对异常值非常敏感。单个极端值可以极大地影响最小值和最大值,从而压缩所有其他数据的范围。
* 不改变数据的分布形状。

适用场景:
当你知道数据的上下界,且数据中没有或很少有异常值时,可以使用 Min-Max 缩放。例如,图像处理中像素值缩放到 [0, 255] 或 [0, 1]。

第四章:使用 NumPy 实现 Standardization (Z-score 标准化)

Standardization (Z-score 标准化) 是指将原始数据转换为均值为 0、标准差为 1 的新数据。这种方法不将数据缩放到特定范围,而是使其符合标准正态分布的特性(如果原始数据近似正态分布的话)。

数学公式:

对于原始特征值 $x$,标准化后的值 $x”$ 的计算公式为:
$x” = \frac{x – \mu}{\sigma}$

其中,$\mu$ 是特征的均值,$\sigma$ 是特征的标准差。

NumPy 实现步骤:

  1. 计算原始数据的均值。
  2. 计算原始数据的标准差。注意,NumPy 的 np.std 默认使用 ddof=0(总体标准差),通常在机器学习中我们更倾向于使用 ddof=1(样本标准差),但这取决于具体的应用场景和库的默认行为(例如 Scikit-learn 的 StandardScaler 默认计算的是样本标准差)。本文示例中使用 ddof=0 与 NumPy 默认保持一致,但在实际 ML 项目中请注意选择。
  3. 使用 NumPy 的广播特性,将原始数据减去均值。
  4. 将步骤3的结果除以标准差。

NumPy 代码示例:

“`python
import numpy as np

创建一个 NumPy 数组作为示例数据

data = np.array([10, 20, 30, 40, 50, 100]) # 增加一个稍大的值观察效果
print(“原始数据:”, data)

1. 计算均值

data_mean = np.mean(data)
print(f”原始数据的均值: {data_mean}”)

2. 计算标准差 (使用 ddof=0,对应总体标准差)

如果需要样本标准差,使用 data_std = np.std(data, ddof=1)

data_std = np.std(data, ddof=0)
print(f”原始数据的标准差 (ddof=0): {data_std}”)

检查标准差是否为零(如果所有数据点都相同)

if data_std == 0:
print(“警告: 数据中的所有值都相同,无法进行 Z-score 标准化。所有值将变为 0。”)
scaled_data = np.full_like(data, 0.0)
else:
# 3. 原始数据减去均值 (NumPy 广播)
data_centered = data – data_mean

# 4. 除以标准差得到标准化数据 (NumPy 广播)
scaled_data = data_centered / data_std

print(“Z-score 标准化后的数据:”, scaled_data)

验证标准化后的均值和标准差

print(f”标准化后数据的均值: {np.mean(scaled_data):.4f}”) # 浮点计算可能不精确为0
print(f”标准化后数据的标准差 (ddof=0): {np.std(scaled_data, ddof=0):.4f}”) # 浮点计算可能不精确为1
“`

优点:
* 对异常值的敏感度低于 Min-Max 缩放(尽管异常值仍会影响均值和标准差)。
* 不限制数据的范围,保留了数据中的异常值信息。
* 使数据符合零均值、单位方差的特性,这对于许多基于梯度下降的算法是理想的。

缺点:
* 不将数据缩放到一个固定的范围,缩放后的数据可能超出 [0, 1] 或 [-1, 1]。

适用场景:
当数据的分布未知或近似正态分布,且存在一些异常值时,Z-score 标准化是一个不错的选择。它是许多机器学习模型的默认缩放方法。

第五章:使用 NumPy 实现 Max Absolute Scaling (最大绝对值缩放)

Max Absolute Scaling 是将每个特征的数据除以该特征的最大绝对值。这种方法使数据缩放到 [-1, 1] 的范围,并且不会移动数据的中心点(即如果数据原来是围绕 0 对称的,缩放后仍然是)。

数学公式:

对于原始特征值 $x$,缩放后的值 $x”’$ 的计算公式为:
$x”’ = \frac{x}{\text{max_abs}}$

其中,$\text{max_abs} = \max(|x|)$ 是特征的最大绝对值。

NumPy 实现步骤:

  1. 计算原始数据的最大绝对值。可以使用 np.abs() 计算每个元素的绝对值,然后使用 np.max() 找到最大值。
  2. 使用 NumPy 的广播特性,将原始数据除以最大绝对值。

NumPy 代码示例:

“`python
import numpy as np

创建一个 NumPy 数组作为示例数据,包含负数

data = np.array([-10, 5, 0, 15, -20])
print(“原始数据:”, data)

1. 计算最大绝对值

data_max_abs = np.max(np.abs(data))
print(f”原始数据的最大绝对值: {data_max_abs}”)

检查最大绝对值是否为零(如果所有数据点都是零)

if data_max_abs == 0:
print(“警告: 数据中的所有值都是零,无法进行 Max Absolute 缩放。所有值将保持为 0。”)
scaled_data = np.full_like(data, 0.0)
else:
# 2. 原始数据除以最大绝对值 (NumPy 广播)
scaled_data = data / data_max_abs

print(“Max Absolute 缩放后的数据:”, scaled_data)

验证缩放后的范围和最大绝对值

print(f”缩放后数据的最小值: {np.min(scaled_data)}”)
print(f”缩放后数据的最大值: {np.max(scaled_data)}”)
print(f”缩放后数据的最大绝对值: {np.max(np.abs(scaled_data))}”)
“`

优点:
* 将数据缩放到 [-1, 1] 范围。
* 不会破坏数据的稀疏性(即零值依然为零)。
* 适用于数据已经以 0 为中心或希望保留 0 的特殊意义的场景。

缺点:
* 对异常值敏感,类似于 Min-Max 缩放。

适用场景:
数据已经以 0 为中心,或者数据非常稀疏(包含大量零值),且不希望缩放过程破坏稀疏性时。

第六章:使用 NumPy 实现 Robust Scaling (鲁棒缩放)

Robust Scaling(鲁棒缩放)使用数据的中位数(median)和四分位距(Interquartile Range, IQR)进行缩放。IQR 是第三四分位数(75th percentile)与第一四分位数(25th percentile)的差值。

数学公式:

对于原始特征值 $x$,缩放后的值 $x””$ 的计算公式为:
$x”” = \frac{x – \text{median}}{\text{IQR}}$

其中,$\text{median}$ 是特征的中位数,$\text{IQR} = Q3 – Q1$,$Q3$ 是 75th 百分位数,$Q1$ 是 25th 百分位数。

NumPy 实现步骤:

  1. 计算原始数据的中位数。
  2. 计算原始数据的 25th 百分位数 ($Q1$) 和 75th 百分位数 ($Q3$)。
  3. 计算四分位距 ($IQR = Q3 – Q1$)。
  4. 使用 NumPy 的广播特性,将原始数据减去中位数。
  5. 将步骤4的结果除以 IQR。

NumPy 代码示例:

“`python
import numpy as np

创建一个 NumPy 数组作为示例数据,包含异常值

data = np.array([10, 20, 30, 40, 50, 1000]) # 1000 是异常值
print(“原始数据:”, data)

1. 计算中位数

data_median = np.median(data)
print(f”原始数据的中位数: {data_median}”)

2. 计算第一四分位数 (Q1) 和第三四分位数 (Q3)

q1 = np.percentile(data, 25)
q3 = np.percentile(data, 75)
print(f”原始数据的第一四分位数 (Q1): {q1}, 第三四分位数 (Q3): {q3}”)

3. 计算四分位距 (IQR)

iqr = q3 – q1
print(f”原始数据的四分位距 (IQR): {iqr}”)

检查 IQR 是否为零(如果数据分布非常集中或所有点相同)

if iqr == 0:
print(“警告: IQR 为零,无法进行 Robust 缩放。所有值将变为 0。”)
scaled_data = np.full_like(data, 0.0)
else:
# 4. 原始数据减去中位数 (NumPy 广播)
data_centered = data – data_median

# 5. 除以 IQR 得到缩放后的数据 (NumPy 广播)
scaled_data = data_centered / iqr

print(“Robust 缩放后的数据:”, scaled_data)

注意 Robust 缩放不保证特定范围或均值/标准差

验证缩放后的中位数是否接近 0

print(f”缩放后数据的中位数: {np.median(scaled_data):.4f}”)

观察缩放后的数据,异常值 1000 对应的缩放值与其他值对应的缩放值之间差距相对 Min-Max 或 Z-score 会小一些

例如,对于 Z-score,1000 的值会非常大,而在 Robust 缩放中相对温和。

“`

优点:
* 对异常值具有很强的鲁棒性,因为中位数和四分位距不像均值和标准差那样受异常值影响。

缺点:
* 不将数据缩放到特定的范围。
* 计算相对于 Min-Max 或 Z-score 稍复杂。

适用场景:
当数据中包含明显的异常值,且希望缩放方法不受其影响时,Robust Scaling 是最佳选择。

第七章:使用 NumPy 实现缩放函数的封装与二维数据处理

为了方便使用,可以将上述缩放逻辑封装成函数。更重要的是,真实的数据集通常是多维的(例如表格数据,每列是一个特征)。NumPy 在处理多维数据时非常强大,我们可以利用其 axis 参数对每列(每个特征)独立地进行缩放。

封装缩放函数:

“`python
import numpy as np

def minmax_scale_np(data, target_range=(0, 1)):
“””
使用 NumPy 对一维或二维数据进行 Min-Max 缩放。
默认目标范围 [0, 1]。
对于二维数据,按列(特征)进行缩放。
“””
data = np.asarray(data, dtype=float) # 确保是浮点类型
min_val = np.min(data, axis=0) if data.ndim > 1 else np.min(data)
max_val = np.max(data, axis=0) if data.ndim > 1 else np.max(data)

# 处理所有值都相同的情况
range_val = max_val - min_val
# 创建一个掩码,找到 range_val 为 0 的列
zero_range_mask = (range_val == 0)
# 对于 range_val 为 0 的列,设置一个非零值(例如 1),计算完成后再处理
# 这样避免除以零,但需要后续修正结果
range_val_safe = np.where(zero_range_mask, 1.0, range_val)


scaled_data_0_1 = (data - min_val) / range_val_safe

# 将原来 range_val 为 0 的列对应的 scaled_data_0_1 设置为目标范围的最小值
if data.ndim > 1:
     scaled_data_0_1[:, zero_range_mask] = target_range[0]
elif zero_range_mask: # 一维数组且 range_val == 0
     scaled_data_0_1[:] = target_range[0]


# 线性变换到目标范围
min_target, max_target = target_range
scaled_data = min_target + scaled_data_0_1 * (max_target - min_target)

return scaled_data

def standardize_np(data):
“””
使用 NumPy 对一维或二维数据进行 Z-score 标准化。
对于二维数据,按列(特征)进行标准化。
使用样本标准差 (ddof=1),与 Scikit-learn 默认行为一致。
“””
data = np.asarray(data, dtype=float) # 确保是浮点类型
mean_val = np.mean(data, axis=0) if data.ndim > 1 else np.mean(data)
std_val = np.std(data, axis=0, ddof=1) if data.ndim > 1 else np.std(data, ddof=1)

# 处理标准差为零的情况
zero_std_mask = (std_val == 0)
# 对于 std_val 为 0 的列,设置一个非零值(例如 1),计算完成后再处理
std_val_safe = np.where(zero_std_mask, 1.0, std_val)

scaled_data = (data - mean_val) / std_val_safe

# 将原来 std_val 为 0 的列对应的 scaled_data 设置为 0
if data.ndim > 1:
    scaled_data[:, zero_std_mask] = 0.0
elif zero_std_mask: # 一维数组且 std_val == 0
    scaled_data[:] = 0.0

return scaled_data

def maxabs_scale_np(data):
“””
使用 NumPy 对一维或二维数据进行 Max Absolute 缩放。
对于二维数据,按列(特征)进行缩放。
“””
data = np.asarray(data, dtype=float) # 确保是浮点类型
max_abs_val = np.max(np.abs(data), axis=0) if data.ndim > 1 else np.max(np.abs(data))

# 处理最大绝对值为零的情况
zero_max_abs_mask = (max_abs_val == 0)
 # 对于 max_abs_val 为 0 的列,设置一个非零值(例如 1),计算完成后再处理
max_abs_val_safe = np.where(zero_max_abs_mask, 1.0, max_abs_val)

scaled_data = data / max_abs_val_safe

# 将原来 max_abs_val 为 0 的列对应的 scaled_data 设置为 0
if data.ndim > 1:
    scaled_data[:, zero_max_abs_mask] = 0.0
elif zero_max_abs_mask: # 一维数组且 max_abs_val == 0
    scaled_data[:] = 0.0

return scaled_data

def robust_scale_np(data):
“””
使用 NumPy 对一维或二维数据进行 Robust 缩放。
对于二维数据,按列(特征)进行缩放。
“””
data = np.asarray(data, dtype=float) # 确保是浮点类型
median_val = np.median(data, axis=0) if data.ndim > 1 else np.median(data)
q1 = np.percentile(data, 25, axis=0) if data.ndim > 1 else np.percentile(data, 25)
q3 = np.percentile(data, 75, axis=0) if data.ndim > 1 else np.percentile(data, 75)
iqr = q3 – q1

# 处理 IQR 为零的情况
zero_iqr_mask = (iqr == 0)
# 对于 IQR 为 0 的列,设置一个非零值(例如 1),计算完成后再处理
iqr_safe = np.where(zero_iqr_mask, 1.0, iqr)

scaled_data = (data - median_val) / iqr_safe

# 将原来 IQR 为 0 的列对应的 scaled_data 设置为 0
if data.ndim > 1:
    scaled_data[:, zero_iqr_mask] = 0.0
elif zero_iqr_mask: # 一维数组且 iqr == 0
    scaled_data[:] = 0.0


return scaled_data

“`
处理二维数据 (按列缩放):

上述函数已经设计为可以处理二维 NumPy 数组。axis=0 参数在 np.min, np.max, np.mean, np.std, np.median, np.percentile 等函数中指定计算是沿着第一个轴(即列)进行的。这样,这些函数会返回一个包含每列统计量的一维数组。然后,通过 NumPy 的广播机制,这个一维统计量数组会自动扩展到原始二维数组的形状,从而实现对每一列独立进行减法或除法运算。

我们还需要处理一些边缘情况,比如某个特征列的所有值都相同。在这种情况下,Min-Max 缩放的分母 (max - min) 或 Z-score/Robust 缩放的标准差/IQR 将为零,导致除以零的错误。安全的做法是检测这种情况,并将该列的所有缩放后的值设置为目标范围的最小值(Min-Max)或 0(Standardization/Robust/MaxAbs)。上面封装的函数已经加入了这个处理。

二维数据示例:

“`python
import numpy as np

创建一个二维 NumPy 数组 (样本 x 特征)

假设有 5 个样本,每个样本有 3 个特征

特征1: 年龄 (小范围)

特征2: 收入 (中等范围,包含异常值)

特征3: 房屋面积 (大范围)

data_2d = np.array([
[25, 50000, 100],
[30, 60000, 120],
[35, 70000, 150],
[40, 80000, 200],
[50, 1000000, 500] # 1000000 是收入的异常值
])

print(“原始二维数据:\n”, data_2d)

使用封装的函数进行缩放

Min-Max 缩放

scaled_minmax_2d = minmax_scale_np(data_2d)
print(“\nMin-Max 缩放后的数据 (按列):\n”, scaled_minmax_2d)
print(“缩放后每列的最小值:”, np.min(scaled_minmax_2d, axis=0))
print(“缩放后每列的最大值:”, np.max(scaled_minmax_2d, axis=0))

Z-score 标准化

scaled_standard_2d = standardize_np(data_2d)
print(“\nZ-score 标准化后的数据 (按列):\n”, scaled_standard_2d)
print(“缩放后每列的均值:”, np.mean(scaled_standard_2d, axis=0)) # 接近 0
print(“缩放后每列的标准差 (ddof=1):”, np.std(scaled_standard_2d, axis=0, ddof=1)) # 接近 1

Max Absolute 缩放

scaled_maxabs_2d = maxabs_scale_np(data_2d)
print(“\nMax Absolute 缩放后的数据 (按列):\n”, scaled_maxabs_2d)
print(“缩放后每列的最大绝对值:”, np.max(np.abs(scaled_maxabs_2d), axis=0)) # 接近 1

Robust 缩放

scaled_robust_2d = robust_scale_np(data_2d)
print(“\nRobust 缩放后的数据 (按列):\n”, scaled_robust_2d)
print(“缩放后每列的中位数:”, np.median(scaled_robust_2d, axis=0)) # 接近 0

注意收入列 (第二列) 在 Robust 缩放中,异常值 1000000 的影响相对较小,

与 Z-score 或 Min-Max 缩放的结果进行对比可以体现鲁棒性。

“`

第八章:逆变换 (Inverse Transformation)

有时候,在模型训练完成后,我们需要将预测结果或缩放后的数据还原到原始数据的尺度,以便于解释或与其他未缩放的数据进行比较。这个过程称为逆变换(Inverse Transformation)。

逆变换的公式是缩放公式的逆运算。

Min-Max Scaling 逆变换:

$x = \text{min} + (x’ – \text{min_range}) \times \frac{\text{max} – \text{min}}{\text{max_range} – \text{min_range}}$

当目标范围是 [0, 1] 时:
$x = \text{min} + x’ \times (\text{max} – \text{min})$

Standardization 逆变换:

$x = x” \times \sigma + \mu$

Max Absolute Scaling 逆变换:

$x = x”’ \times \text{max_abs}$

Robust Scaling 逆变换:

$x = x”” \times \text{IQR} + \text{median}$

进行逆变换的关键是,你需要保存原始数据在进行缩放时计算出的统计量(最小值、最大值、均值、标准差、中位数、IQR)。

NumPy 逆变换示例:

“`python
import numpy as np

使用之前 Min-Max 缩放示例的数据和计算出的统计量

data_original = np.array([10, 20, 30, 40, 50])
scaled_minmax = np.array([0. , 0.25, 0.5 , 0.75, 1. ]) # 假设这是 Min-Max 缩放 [0, 1] 后的结果

保存原始数据的统计量

original_min = np.min(data_original)
original_max = np.max(data_original)
original_range = original_max – original_min
target_min_range = 0
target_max_range = 1
target_range = target_max_range – target_min_range

逆变换公式应用 (目标范围 [0, 1])

if original_range == 0:
# 如果原始范围是0,无法逆变换回原始值,除非原始值是唯一的
print(“警告: 原始数据范围为零,无法进行 Min-Max 逆变换。”)
inverted_data_minmax = np.full_like(scaled_minmax, original_min)
else:
inverted_data_minmax = original_min + (scaled_minmax – target_min_range) * (original_range / target_range)

print(“\nMin-Max 逆变换后的数据:”, inverted_data_minmax)

使用之前 Z-score 标准化示例的数据和计算出的统计量

data_original_std = np.array([10, 20, 30, 40, 50, 1000])

假设 scaled_standard 是 Z-score 标准化后的结果 (使用 ddof=1 计算)

我们需要重新计算均值和标准差,或者从缩放过程中保存

original_mean_std = np.mean(data_original_std)
original_std_std = np.std(data_original_std, ddof=1)

Z-score 标准化示例结果 (假设是这个值)

scaled_standard = np.array([-0.9806, -0.9456, -0.9105, -0.8755, -0.8405, 4.5528]) # 这是使用ddof=1计算出的近似值

逆变换公式应用

if original_std_std == 0:
print(“警告: 原始数据标准差为零,无法进行 Z-score 逆变换。”)
inverted_data_standard = np.full_like(scaled_standard, original_mean_std)
else:
inverted_data_standard = scaled_standard * original_std_std + original_mean_std

print(“Z-score 逆变换后的数据:”, inverted_data_standard)

打印原始数据进行对比

print(“原始 Z-score 示例数据:”, data_original_std)

“`

逆变换对于解释模型输出(特别是回归模型的预测值)非常重要。在预测阶段,你需要使用训练数据计算出的统计量来缩放新的预测输入,然后使用这些相同的统计量来对模型输出(如果模型输出是在缩放尺度上的)进行逆变换。

第九章:实践中的注意事项

  1. 训练集/测试集划分: 最重要的一点 是,你应该只使用训练数据来计算缩放所需的统计量(min, max, mean, std, median, IQR)。然后,使用这些在训练集上计算出的统计量来转换训练集和测试集。绝对不要 在整个数据集上计算统计量然后进行划分,或者在测试集上单独计算统计量,这会导致数据泄露(Data Leakage),使模型在测试集上表现得看似很好,但在真实的未知数据上失效。
    在 NumPy 中,这意味着你应该先从训练数据计算统计量,然后将这些统计量应用于训练和测试 NumPy 数组。
  2. 选择合适的缩放器: 不同的缩放方法适用于不同的数据和算法。
    • 如果数据没有异常值且需要固定范围:Min-Max Scaling。
    • 如果数据近似正态分布或算法对范围不敏感:Standardization。
    • 如果数据稀疏且中心为 0:Max Absolute Scaling。
    • 如果数据包含异常值:Robust Scaling。
      没有一种万能的缩放器,通常需要根据数据的特性和所使用的模型进行选择,甚至尝试不同的方法并通过交叉验证来评估效果。
  3. 数据类型: 在 NumPy 中进行缩放计算时,确保数据类型是浮点型(float)。整数类型可能会导致除法结果被截断。使用 np.asarray(data, dtype=float) 可以方便地将数据转换为浮点型 NumPy 数组。
  4. 处理零方差/零范围的特征: 如果某个特征的所有值都相同,其标准差、IQR 或范围将为零。尝试除以零会引发错误。如前所述,安全的处理方法是检测这种情况,并将该特征缩放后的值设置为固定值(通常是 0 或目标范围的最小值)。上面封装的函数已经包含了这个处理。
  5. Scikit-learn 的优势: 虽然 NumPy 可以实现缩放的底层逻辑,但在实际机器学习项目开发中,强烈推荐使用 Scikit-learn 提供的 sklearn.preprocessing 模块中的缩放器类(如 MinMaxScaler, StandardScaler, MaxAbsScaler, RobustScaler)。这些类提供了 fit(), transform(), fit_transform() 等方法,方便地实现了训练集上 fit 后在训练集和测试集上 transform 的流程,并且内置了对零方差等边缘情况的处理,以及支持稀疏数据等。NumPy 实现的价值在于理解缩放的原理和底层计算过程。

第十章:NumPy 与 Scikit-learn 缩放器的对比

特性 NumPy 实现 Scikit-learn Scalers (e.g., StandardScaler)
实现方式 手动编写计算统计量和应用公式的代码 提供了封装好的类,带有 fit(), transform(), fit_transform() 方法
易用性 相对较低,需要手动处理统计量计算和应用 较高,API 标准化,易于集成到机器学习流程
处理多维数据 需要手动指定 axis 参数并注意广播机制 类方法(如 transform)自动处理列式缩放
统计量存储 需要手动保存从训练数据计算的统计量以便逆变换和转换测试集 fit() 方法会自动计算并存储统计量在类的属性中
边缘情况处理 需要手动编写代码处理零标准差/范围等情况 内置了对常见边缘情况的处理
稀疏数据 对稀疏矩阵支持不好,直接计算会破坏稀疏性 部分缩放器(如 MaxAbsScaler)支持稀疏矩阵
代码量 实现一个缩放器需要较多代码 实例化一个类并调用方法,代码简洁
目的 理解底层原理,用于教学或特定场景的定制 快速、标准、健壮地应用于实际机器学习任务

总的来说,对于学习理解,NumPy 是极好的工具;对于实际项目开发,Scikit-learn 提供了更成熟、更方便、更鲁棒的解决方案。理解 NumPy 实现有助于你更好地掌握 Scikit-learn 缩放器的工作原理。

结论

数据归一化与缩放是数据预处理的关键环节,对于提升机器学习模型的性能和稳定性具有重要作用。本文详细介绍了 Min-Max Scaling、Standardization、Max Absolute Scaling 和 Robust Scaling 这几种常见的缩放技术,并深入讲解了如何利用 NumPy 这个强大的数值计算库来一步步实现它们。

通过 NumPy,我们不仅可以理解缩放背后的数学原理,还能亲自动手处理一维和多维数据。掌握了使用 NumPy 实现这些基本操作,将为进一步学习更高级的数据处理和机器学习技术打下坚实的基础。同时,我们也讨论了在实际应用中需要注意的训练/测试集划分、异常值处理等重要事项,并对比了 NumPy 实现与 Scikit-learn 库提供的专业工具的优劣。

在实际的数据科学项目中,通常会选择 Scikit-learn 等库提供的缩放器,因为它们更健壮、易于使用且功能更全面。但这不应削弱我们对底层原理的探索兴趣。NumPy 提供了实现这些原理的强大能力,让我们能够更深入地理解数据转换的本质。希望本文能帮助你透彻地掌握数据归一化与缩放的概念及其 NumPy 实现。


发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部