发布于2026-07-20 阅读(0)
扫一扫,手机访问
在实际项目中,常会遇到需要手动实现特征缩放的情况。今天就来聊聊如何用NumPy实现MinMaxScaler,顺便把那些容易踩的坑一一说清楚。

直接套用公式 (x - x.min()) / (x.max() - x.min()) 就行,但有几个坑得提前说明,比如分母为零、单值数组、多维数组轴向处理,这三类问题一旦碰上,结果就全偏了。
最容易踩的坑有两个:一个是 ZeroDivisionError: float division by zero,当某列所有值都相等(也就是极差为0)时就会触发;另一个是 RuntimeWarning: invalid value encountered in true_divide,虽然不报错,但结果里悄悄混进了 nan 或 inf,数据已经被污染了。
x_min = x.min() 和 x_max = x.max() 算标量极值就行。axis=0,也就是 x.min(axis=0) 和 x.max(axis=0)。range_val = np.where(x_max == x_min, 1.0, x_max - x_min)。并不是为了造轮子,而是有些场景确实绕不开。比如环境里没有scikit-learn,或者要嵌入轻量级部署(比如MicroPython的兼容层),又或者需要和JAX/TensorFlow的张量流无缝衔接,这时候纯NumPy实现反而更可控。
从实际跑过的数据来看,NumPy手写版在小到中等规模数据上,反而会比sklearn的对应版本快10%~20%。原因很简单,省去了对象初始化、参数校验和fit-transform分离这些开销。不过代价也有,就是缺少了 feature_range 参数的灵活缩放,比如想缩放到[-1, 1]就得自己调整公式:(x - x_min) / (x_max - x_min) * (new_max - new_min) + new_min。
x_min 和 x_max,不能只存变换后的数组。feature_range=(0, 1),纯NumPy实现默认也是[0,1],这里不需要额外参数。.min() 和 .max() 默认会忽略NaN,但万一数组里本身就有NaN,最好先用 np.nanmin() 和 np.nanmax(),配合 np.isnan() 的掩码做一次清理。填 0(默认)——按列缩放,每个特征独立归一化;填 1——按行缩放,每个样本的所有特征被拉到[0,1]区间。绝大多数机器学习场景都要求前者。
常见的误用场景是:把 shape 为 (n_samples, n_features) 的数据误填了 axis=1,结果每个样本内部的特征关系被破坏,原始量纲关系彻底丢失,模型训练一下就失效了。
np.allclose(X_scaled.min(axis=0), 0) 和 np.allclose(X_scaled.max(axis=0), 1),如果都返回 True,说明没问题。(1, n_features)),仍然用 axis=0,否则 min(axis=1) 会返回一个标量,没法广播。keepdims=True,比如 x_min = x.min(axis=0, keepdims=True),避免维度塌缩导致减法出错。逆运算公式是 x_original = x_scaled * (x_max - x_min) + x_min,但前提是必须保留原始的 x_min 和 x_max。它们不是常数,而是随数据动态计算出的数组,尤其多维时,可能是一个长度为n_features的向量。
容易被忽略的一点是:如果训练时用了 np.nanmin 处理缺失值,但预测时没做同样的清洗,x_min 和 x_max 的维度可能不匹配,直接运算会触发 ValueError: operands could not be broadcast together。
return X_scaled, {'min': x_min, 'max': x_max}。min 和 max,不能重新计算——测试集的极值不等于训练集的极值。x_min(而不是0),否则还原值会恒等于 x_min,而不是原始值。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8