最近做一个抖音的数据分析的时候,在数据归一化和标准化经常看到不一样的解释,通过不断地百度+知乎+测试,算是大概明白了这个过程。简单总结一下。
标准化与归一化
其实这个问题主要的原因是这这些概念都是直接翻译过来的,Normalize和Standard等等经常会被认为是一个意思,其实还是有一定的差别的。
归一化是将样本的特征值转换到同一量纲下把数据映射到[0,1]或者[-1, 1]区间内,仅由变量的极值决定,因区间放缩法是归一化的一种。标准化是依照特征矩阵的列处理数据,转换为”标准正态分布”,注意这里标准正态分布是打了引号的。和整体样本分布相关,每个样本点都能对标准化产生影响。
为了统一,我们将均值和标准差设为常见的mu和sigma。
1.标准化的定义
$$
x’ = \frac{x-\mu}{\sigma}
$$
这是最常用的标准话的定义,通过这样的一个方式,可以将数据分布在一个均值0,方差为1的一个区间。但是不代表处理过后的数据就是正态分布,因为其实这样一个过程改变了均值和方差但是并没有改变分布。所以如果原来是服从正态分布,那么现在也是,原来是什么分布,那现在也是什么分布。他有一个好处,就是可以加快收敛,在深度学习中经常用到,所以一般在深度学习中不知道用啥那就用这个吧。证明过程我也实在是忘了,用的就是方差的性质。
2.归一化
$$
x’ = \frac{x-min(x)}{max(x)-min(x)}
$$
$$
x’ = \frac{x-\mu}{max(x)-min(x)}
$$
这个有两种表示,第一种是min-max归一化,第二种是mean归一化,这个不难理解,相当于把数据分布给压缩了,结果也是在0-1之间,但是压缩的程度完全取决于极值,这样这种方式就不够稳定。
3.Normalize
我姑且称这种方式叫做正则把,也可以叫归一化,它保留原始数据的分布,只是同时除以一个数,这个数就是向量的L1-norm值。
$$
x’ = \frac{x}{\sqrt{x_1^2+x_2^2+…+x_n^2}}
$$
这种其实也是一种常见的归一化。
在sklearn中的使用
在sklearn中,这几种都在preprocessing包中,使用也就一行代码的事,这里先引用知乎中的一段代码
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn import preprocessing
def plot(data, title):
sns.set_style('dark')
f, ax = plt.subplots()
ax.set(ylabel='frequency')
ax.set(xlabel='height(blue) / weight(green)')
ax.set(title=title)
sns.distplot(data[:, 0:1], color='blue')
sns.distplot(data[:, 1:2], color='green')
plt.savefig(title + '.png')
plt.show()
np.random.seed(42)
height = np.random.normal(loc=168, scale=5, size=1000).reshape(-1, 1)
weight = np.random.normal(loc=70, scale=10, size=1000).reshape(-1, 1)
original_data = np.concatenate((height, weight), axis=1)
plot(original_data, 'Original')
standard_scaler_data = preprocessing.StandardScaler().fit_transform(original_data)
plot(standard_scaler_data, 'StandardScaler')
min_max_scaler_data = preprocessing.MinMaxScaler().fit_transform(original_data)
plot(min_max_scaler_data, 'MinMaxScaler')
max_abs_scaler_data = preprocessing.MaxAbsScaler().fit_transform(original_data)
plot(max_abs_scaler_data, 'MaxAbsScaler')
normalizer_data = preprocessing.Normalizer().fit_transform(original_data)
plot(normalizer_data, 'Normalizer')
robust_scaler_data = preprocessing.RobustScaler().fit_transform(original_data)
plot(robust_scaler_data, 'RobustScaler')
链接:https://www.zhihu.com/question/20467170/answer/839255695.
要注意的一点是,如果给定的是一个矩阵,除了normalize,其他的都是默认对列向量进行归一化或标准化,但是normalize是对行进行标准化,这个很有意思。
from sklearn import preprocessing
arr = np.array([
[1, 2, 2],
[1, 1, 3],
[0, 1, 2]
])
scarlar = preprocessing.StandardScaler()
print(scarlar.fit_transform(arr))
scarlar = preprocessing.Normalizer()
print(scarlar.fit_transform(arr))
输出:
[[ 0.70710678 1.41421356 -0.70710678]
[ 0.70710678 -0.70710678 1.41421356]
[-1.41421356 -0.70710678 -0.70710678]]
[[0.33333333 0.66666667 0.66666667]
[0.30151134 0.30151134 0.90453403]
[0. 0.4472136 0.89442719]]
可以简单的带进去算一下,就很清晰了。
初始化
还有一个比较重要的一点,很多数据需要进行初始化,比如normal等等,这些初始化是从给定的分布中堆积选择数据。举个例子,初始化一个服从正态分布N(1, 3)的100x100维度的矩阵,并不是说这些数据每一列或者每一行恰好服从均值为1方差为3的正态分布,而是矩阵中所有的值都是从这个分布中随机选的值。但是一般数据量大一些的时候如50x100等其实已经很接近设定的这个分布了。如果3x3或者2x4这种一般会均值方差会比较抖动。
import os
import torch
import random
import numpy as np
import torch.nn as nn
from common_tools import set_seed
set_seed(1) # 设置随机种子
class MLP(nn.Module):
def __init__(self, neural_num, layers):
super(MLP, self).__init__()
self.linears = nn.ModuleList([nn.Linear(neural_num, neural_num, bias=False) for i in range(layers)])
self.neural_num = neural_num
def forward(self, x):
for (i, linear) in enumerate(self.linears):
x = linear(x)
# x = torch.relu(x)
print("layer:{}, std:{}".format(i, x.std()))
if torch.isnan(x.std()):
print("output is nan in {} layers".format(i))
break
return x
def initialize(self):
for m in self.modules():
if isinstance(m, nn.Linear):
# 使用这个会造成梯度消失或爆炸,方差在精度内无法表示
nn.init.normal_(m.weight.data) # normal: mean=0, std=1
# nn.init.normal_(m.weight.data, std=np.sqrt(1/self.neural_num))
# a = np.sqrt(6 / (self.neural_num + self.neural_num))
#
# tanh_gain = nn.init.calculate_gain('tanh')
# a *= tanh_gain
#
# nn.init.uniform_(m.weight.data, -a, a)
# nn.init.xavier_uniform_(m.weight.data, gain=tanh_gain)
# nn.init.normal_(m.weight.data, std=np.sqrt(2 / self.neural_num))
# nn.init.kaiming_normal_(m.weight.data)
wc = torch.ones((3, 4))
ans = nn.init.normal_(wc, std=1, mean=0)
ans2 = nn.init.xavier_normal(wc)
print(ans[0].std())
print(ans2[0].std())