标准差为零的归一化

Normalisation with a zero in the standard deviation

我正在尝试使用以下代码

在 python 中对数据集进行居中和规范化
mean = np.mean(train, axis=0)
std = np.std(train, axis=0)
norm_train = (train - mean) / std

问题是我得到了除以零的错误。数据集中的两个值最终的标准差为零。数据集的形状为 (3750, 55)。我的统计技能不是那么强,所以我不确定如何克服这个问题。有什么建议吗?

由于 standard deviation 是通过对均值的 平方 偏差求和计算得出的,因此只有当 a 的所有值都为变量相同(都等于平均值​​)。在这种情况下,这些变量没有判别力,因此可以将它们从分析中删除。它们无法改进任何分类、聚类或回归任务。许多实现会为您完成或抛出有关矩阵计算的错误。

对于该功能,您只需将 0 std 替换为 1。这基本上意味着该特征的所有数据点的缩放值将为零。这是有道理的,因为这意味着特征值不会偏离平均值一点点(因为值是常数,常数就是平均值。)

仅供参考-这就是 sklearn 的作用! https://github.com/scikit-learn/scikit-learn/blob/7389dbac82d362f296dc2746f10e43ffa1615660/sklearn/preprocessing/data.py#L70

一个标准是包含一个防止被零除的 epsilon 变量。从理论上讲,不需要它,因为进行此类计算在逻辑上没有意义。实际上,机器只是计算器,除以零会变成 NaN 或 +/-Inf。

简而言之,像这样定义你的函数:

def z_norm(arr, epsilon=1e-100):
    return (arr-arr.mean())/(arr.std()+epsilon)  

这假定一个一维数组,但很容易更改为 row-wise 或 column-wise 二维数组的计算。

Epsilon 是故意添加到计算中的错误,以防止创建 NaN 或 Inf。在 Inf 的情况下,你仍然会得到非常大的数字,但以后的计算不会传播 Inf 并且可能仍然保留一些意义。

1/(1 x 10^100) 的值非常小,不会对您的结果产生太大影响。如果你愿意,你可以降低到 1e-300 左右,但你可能会在进一步计算后达到最低精度值。请注意您使用的精度以及它可以处理的最小精度。我用的是 float64.

2021-11-03更新:添加测试代码。此 epsilon 的 objective 是为了最大限度地减少损坏并消除数据管道中出现随机 NaN 的可能性。将 epsilon 设置为正值可解决此问题。

for arr in [
        np.array([0,0]),
        np.array([1e-300,1e-300]),
        np.array([1,1]),
        np.array([1,2])
    ]:
    for epi in [1e-100,0,1e100]:
        stdev = arr.std()
        mean = arr.mean()
        result = z_norm(arr, epsilon=epi)
        print(f' z_norm(np.array({str(arr):<21}),{epi:<7}) ### stdev={stdev}; mean={mean:<6}; becomes --> {str(result):<19} (float-64) --> Truncate to 32 bits. =', result.astype(np.float32))

z_norm(np.array([0 0]                ),1e-100 ) ### stdev=0.0; mean=0.0   ; becomes --> [0. 0.]             (float-64) --> Truncate to 32 bits. = [0. 0.]
z_norm(np.array([0 0]                ),0      ) ### stdev=0.0; mean=0.0   ; becomes --> [nan nan]           (float-64) --> Truncate to 32 bits. = [nan nan]
z_norm(np.array([0 0]                ),1e+100 ) ### stdev=0.0; mean=0.0   ; becomes --> [0. 0.]             (float-64) --> Truncate to 32 bits. = [0. 0.]
z_norm(np.array([1.e-300 1.e-300]    ),1e-100 ) ### stdev=0.0; mean=1e-300; becomes --> [0. 0.]             (float-64) --> Truncate to 32 bits. = [0. 0.]
z_norm(np.array([1.e-300 1.e-300]    ),0      ) ### stdev=0.0; mean=1e-300; becomes --> [nan nan]           (float-64) --> Truncate to 32 bits. = [nan nan]
z_norm(np.array([1.e-300 1.e-300]    ),1e+100 ) ### stdev=0.0; mean=1e-300; becomes --> [0. 0.]             (float-64) --> Truncate to 32 bits. = [0. 0.]
z_norm(np.array([1 1]                ),1e-100 ) ### stdev=0.0; mean=1.0   ; becomes --> [0. 0.]             (float-64) --> Truncate to 32 bits. = [0. 0.]
z_norm(np.array([1 1]                ),0      ) ### stdev=0.0; mean=1.0   ; becomes --> [nan nan]           (float-64) --> Truncate to 32 bits. = [nan nan]
z_norm(np.array([1 1]                ),1e+100 ) ### stdev=0.0; mean=1.0   ; becomes --> [0. 0.]             (float-64) --> Truncate to 32 bits. = [0. 0.]
z_norm(np.array([1 2]                ),1e-100 ) ### stdev=0.5; mean=1.5   ; becomes --> [-1.  1.]           (float-64) --> Truncate to 32 bits. = [-1.  1.]
z_norm(np.array([1 2]                ),0      ) ### stdev=0.5; mean=1.5   ; becomes --> [-1.  1.]           (float-64) --> Truncate to 32 bits. = [-1.  1.]
z_norm(np.array([1 2]                ),1e+100 ) ### stdev=0.5; mean=1.5   ; becomes --> [-5.e-101  5.e-101] (float-64) --> Truncate to 32 bits. = [-0.  0.]

回到它的定义,z_score 背后的想法是根据标准差给出元素与样本均值之间的距离。如果所有元素都相同,则意味着它们与均值的距离为 0,因此 zscore 为标准差的 0 倍,因为所有数据点都处于均值。标准除法是将距离与数据分散相关联的一种方式。视觉上很容易理解,得出这样的结论:https://en.wikipedia.org/wiki/Standard_score#/media/File:The_Normal_Distribution.svg