标准偏差为零的归一化
Posted
技术标签:
【中文标题】标准偏差为零的归一化【英文标题】:Normalisation with a zero in the standard deviation 【发布时间】:2016-07-28 22:07:00 【问题描述】:我正在尝试使用以下代码在 python 中对数据集进行居中和规范化
mean = np.mean(train, axis=0)
std = np.std(train, axis=0)
norm_train = (train - mean) / std
问题是我得到了一个零错误的划分。数据集中的两个值最终具有零标准。数据集的形状为 (3750, 55)。我的统计技能不是那么强,所以我不知道如何克服这一点。有什么建议吗?
【问题讨论】:
两个值是指两个变量/特征?如果它们的标准偏差为零,则意味着所有值都相同,因此它们对于任何类型的分析基本上都是无用的。如果你应该保留它们,考虑到所有其他变量的均值均为 0,你也可以将它们转换为零。 我所说的两个值的意思是 np.std(trainData, axis=0)[28] = 0 和 np.std(trainData, axis=0)[49] = 0 是明确的.我刚刚又看了一下数据,我可以看到 trainData[:, 28] 和 trainData[:, 49] 都是零。那么你是在建议我将它们从数据集中删除吗? 是否应该删除它们取决于您想对它们做什么。但是您不能将它们除以标准偏差。因为他们没有。 我们的目标是训练和 k-mans 分类器。在 std 的结果中将 thou 个特定索引的零值高估为 1 以使除法后的剩余值仅为 0 是否有效? 既然这叫train,你能不能也检查一下测试数据集,如果它们也都是零?如果是这样的话,它们就没有辨别力,所以我会说删除它们是安全的(大多数算法都会删除它们,或者无论如何都不能使用它们)。 【参考方案1】:由于standard deviation 是通过对均值的平方 偏差求和来计算的,因此只有当变量的所有值都相同(全部相等)时,零标准偏差才有可能平均而言)。在这种情况下,这些变量没有区分能力,因此可以从分析中删除。它们无法改进任何分类、聚类或回归任务。许多实现会为您执行此操作,否则会引发有关矩阵计算的错误。
【讨论】:
【参考方案2】:一个标准是包含一个防止被零除的 epsilon 变量。理论上,它是不需要的,因为进行这样的计算在逻辑上没有意义。实际上,机器只是计算器,除以零变成 NaN 或 +/-Inf。
简而言之,像这样定义你的函数:
def z_norm(arr, epsilon=1e-100):
return (arr-arr.mean())/(arr.std()+epsilon)
这假定为一维数组,但很容易更改为二维数组的按行或按列计算。
Epsilon 是故意添加到计算中的错误,以防止创建 NaN 或 Inf。在 Inf 的情况下,您仍然会得到非常大的数字,但以后的计算不会传播 Inf 并且可能仍然保留一些含义。
1/(1 x 10^100) 的值非常小,不会对结果产生太大影响。如果需要,您可以降至 1e-300 左右,但在进一步计算后您可能会遇到最低精度值。请注意您使用的精度和它可以处理的最小精度。我使用的是 float64。
2021-11-03 更新:添加测试代码。此 epsilon 的目标是最大程度地减少损坏并消除数据管道中随机 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.]
【讨论】:
1e-100
的 epsilon 非常小,导致该术语急剧上升。你可能想要更多像1e100
。
我添加了测试代码和说明。【参考方案3】:
您可以将该功能的 0 std 替换为 1。这基本上意味着该特征的所有数据点的缩放值将为零。这是有道理的,因为这意味着特征值不会偏离平均值(因为值是常数,常数就是平均值。)
仅供参考-这就是 sklearn 所做的! https://github.com/scikit-learn/scikit-learn/blob/7389dbac82d362f296dc2746f10e43ffa1615660/sklearn/preprocessing/data.py#L70
【讨论】:
【参考方案4】:回到它的定义,z_score 背后的想法是根据标准差给出元素与样本均值之间的距离。如果所有元素都相同,则意味着它们与平均值的距离为 0,因此 zscore 是标准偏差的 0 倍,因为所有数据点都处于平均值。标准除法的除法是将距离与数据的分散联系起来的一种方式。视觉上很容易理解,得出这样的结论:https://en.wikipedia.org/wiki/Standard_score#/media/File:The_Normal_Distribution.svg
【讨论】:
以上是关于标准偏差为零的归一化的主要内容,如果未能解决你的问题,请参考以下文章