MinMaxScaler,最大最小归一化 from sklearn.preprocessing import MinMaxScale (x - min) / (max - min) 将数据映射到[0,1] ,近似于同比映射 |
StandardScaler from sklearn.preprocessing import StandardScaler (x - mean) / std 这实际上将数据拉向 均值为0 标准差为1的 标准正态分布 import torch from torch import nn from sklearn.preprocessing import StandardScaler data = [[0, 0], [0, 0], [1, 1], [1, 1]] scaler = StandardScaler() scaler.fit(data) scaler.transform(data) array([[-1., -1.], [-1., -1.], [ 1., 1.], [ 1., 1.]]) scaler.mean_ array([0.5, 0.5]) scaler.var_ array([0.25, 0.25]) (2-0.5)/0.5 = 3 scaler.transform([[2, 2]]) array([[3., 3.]]) |
在深度学习训练的过程中,数据以0为中心,在[-1,1]之间时,比较容易收敛 转化为标准正态分布的处理,虽然不是所有数据, 但绝大多数还是映射到了[-1,1]之间 这符合了深度学习多层网络训练的需求,大概过程如下: 数据预处理时,做了归一化,在[-1,1]之间, 经过一次神经网络,数据分布就漂一些, 然后标准归一化处理一下,让数据向标准正态分布靠拢一些, 再经过一次神经网络,数据分布再漂一些, 然后标准归一化处理一下,让数据再向标准正态分布靠拢一些, ... ... ... 深度学习中的归一化 同样的计算逻辑,将其封装为一层 每次神经网络处理,数据分布就会跑偏一些, 然后归一处理一下,它就往标准正态分布靠拢一些, 在理论上, 在理论上,会好那么一点点... 问题是,归一处理会不会影响数据中包含的规律, 确切地说,会不会影响数据到指定标签之间的规律? 这个经过多次,或者说很多人的很多次试验证明, 不影响,或者影响可以忽略不计 计算机擅长计算[-1,1]且以0为中心分布的数据,这样的数据不容易分散, 过大容易梯度爆炸, 过小容易数据都消失了, - 数据流过N个网络最终到达标签所在的网络层, - 结果还没到呢,数据就全是0或者几乎不变了, - 或者是一次训练一次改变,结果还没到预期效果呢,数据就全是0了,相当于流不到标签那里就断流了 归一化能很大程序上缓解这个问题 |
- 将数据分布拉到标准正态分布 - 加速收敛 - 一定程度上将数据集中到[-1,1]之间,捎带有统一量纲的作用 |
归一化的位置 归一化是为了解决数据分布偏移的问题,通常是经过几次神经网络计算之后 , 比如transformer的每一层中,有以下几步: 1 求补码, 2 编码并按特征维度拆分, 3 矩阵相乘,即线性变换 4 做softmax, 5 求得分, 6 再矩阵相乘,即线性变换 7 层归一化 8 全连接, 9 激活函数处理, 10 全连接 1-7 是一个逻辑,这一通计算后数据分布肯定偏移了,第8步加一个归一化 - 当然了,如果网络简单,可以尝试不要归一化 - 意思是,如果不加归一化的效果会更好,那么就不要归一化 在传统神经网络中,BN在卷积之后,再之后是relu - 如果是这样,BN将数据拉到以0为中心,relu直接抹掉小于0的数据,相当于抹去一半的正态分布 - 但要注意的是,批次数据半不是前半部分为负,后半部分为正,而是正负混合的 - 被抹去的一半负数据,是零散分布于批次数据中的 - 因为经过relu处理,矩阵会零零散散一多出一些0数据 |
Batch Normalization论文标题解读
这是论文的标题: Batch Normalization: Accelerating Deep Network Training byReducing Internal Covariate Shift Reducing Internal Covariate Shift 减少内部协变量偏移 加速收敛及减少数据分布的偏移 accelerating英/əkˈseləreɪtɪŋ/ 美 /əkˈseləreɪtɪŋ/ v.(使)加速;加快 数据是有分布的,或者说不管它有没有,统一将数据看作或者默认它是符合正态分布的 数据分布主要有两个衡量指标:分布中心以及其离散程序 深度学习训练不像机器学习那样一次训练就定型,或者网络有很多的层, 多次训练,每次微调模型参数... 再用新的模型参数与数据计算,得到新的分布 一次次训练后,新的分布与原有数据有分布就不一样的,或者说漂移了,这就是论文标题提到的 Internal Covariate Shift 这种分布的改变,过大会导致梯度爆炸, 过小梯度消失,数据很小后,改变量也随之就小...
BN与归一化
本质上BN与归一化算法思路一样,细节上有些差异:BN增加了可学习参数 这个细节设计的来源: - 数据分布改变了,你说这不适合,你想拉回来 - 那拉回来多少合适? - 你就这样要求所有数据回到标准正态分布是否太断武断,是否是一刀切行为? - 如果一刀切了,有些激活函数,比如ReLU直接把小于0的全扔了,这是否合适呢 - 有的神经网络没有把ReLU直接放BN的后面,或者干脆就没用BN,效果也不差 - 你所认为的不适合,只是你认为的,还是实现也的确如此? 解决方法 - 到底是否适合,拉到什么程度,设计成可学习的,由模型的参数通过学习决定 也就是常说的为神经网络赋予某种能力, - 功能给你了,有需要再用,实际不需要时,参数自动学习为0,相当于不用 - 用的时候用几分力,用到什么程度,由实际情况决定
BN核心逻辑
输入是一列的数据中的一个批次, 1 一个x对应一个y 2 求均值 3 求方差 5 映射到标准正态分布,其中分母为了防止为0,加一个微小的数 6 yi = a*xi + b, - a,b是可学习参数, - 如果a=std,b=mean的话,y那么yi就是最开始的xi - 如果a,b是其他的值的,y就是以正态分布为基础,根据数据规律漂移后的结果
图像处理用批归一化的多, 序列处理用层归一化的多
机器学习没有批次的概念,一个批次就是全体数据集 深度学习中有批次的概念,所有的计算都是以批次形式计算的 但有时会用到均值,标准差这些变量,同时要寻找的规律,实现是整个数据集的规律,怎么办? 先求批次的,然后逆推整个数据集的
import torch from torch import nn # The mean and standard-deviation are calculated per-dimension over nn.BatchNorm2d() y = \frac{x - \mathrm{E}[x]}{ \sqrt{\mathrm{Var}[x] + \epsilon}} * \gamma + \beta
参数num_features:特征数,比如像素的3色,单词的embedding_dim
nn.BatchNorm2d(num_features=3) 如果是2维关系表,对一列做归一化, - 如果是RGB3色的话,就是对[H,W]的数值做归一化 按维度的话,就是对一个维度的特征做归一化
不管原来是什么shape,统一转化为[B,C,L],然后对L做归一化
第一个维度是批次 第二个维度是特征 第三个维度是原特征图展平形成的向量/列表 对于图像[B,C,H,W] -- 将[H,W]展平求归一化 对于文件[B,C,L] -- 直接对L求归一化 归一化:减 均值 ,再除以标准差
取特征维度数据
X表示RGB三色的批次图像数据 X[B,C,H,W] 取第1个特征 X[:,0,:,:] 取第2个特征 X[:,1,:,:] 取第3个特征 X[:,2,:,:]
预测时使用的是训练时批次的均值和标准差
训练是批次的,预测通常是单个样本 单个样本的均值与标准差 浮动过大,不合适用 批次训练时,记录每个批次的均值与标准差,并以此评估整个数据集的均值与标准差 预测使用的是,训练时预测的整个数据集的 均值与标准差 这种思想是伟大的... 即保证了预测与训练的计算过程是一样的,又避免了预测时单个样本均值与标准差浮动带来的波动问题
为什么图像多使用BN(以下仅个人猜测,如有...纯属...)
图像处理用批归一化的多, - 因为图像特征数不多, - 基本就1-灰色,3-RGB,4-RGB+一个透明度, - 特征图[H,W]的元素数据是很多的 - 核心的信息更多在特征图[H,W]中,而不是RGB特征维度中 序列处理用层归一化的多 - 序列中一个单词的维度通常在100-300之间 - 单词个数,即句子长度可能是8,32,64...不等,一句话20个单词就算多了 - 核心的信息更多在单词的维度中,所以才不是像图像那样对特征图做归一化
The mean and standard-deviation are calculated per-dimension over
deviation英/ˌdiːviˈeɪʃn/美/ˌdiːviˈeɪʃn/ n.偏差; dimension英/dɪˈmenʃn/ 美/daɪˈmenʃn/ n.方面;范围;维(构成空间的因素);侧面 The mean and standard-deviation are calculated per-dimension over 核心在于“dimension”-特征维度 均值和标准差是在 特征维度 上计算的
nn.BatchNorm要求特征维度在第2个位置上,即索引为1的维度上
也不是BatchNorm的要求,实际上是torch框架的要求 在torch框架中, 文本等一维数据的shape通常为[Batch_size,特征维度(单词向量维数,embedding_dim),特征shape(单词个数)] 图像等2维数据[Batch_size,C,W,H],特征维数也是在第2个位置上 BatchNorm在torch.nn下面,自然就遵守这个约定,将特征维度放在了第2个位置上
针对一个特征层上的数据分布进行归一化,相当于对库表某列做归一化
import torch from torch import nn torch.manual_seed(73) batch_size = 1 num_features=2 x=torch.linspace(start=1,end=6,steps=6).reshape(batch_size,num_features,3) x tensor([[[1., 2., 3.], [4., 5., 6.]]]) bn = nn.BatchNorm1d(num_features=num_features) bn(x) tensor([[[-1.2247e+00, 0.0000e+00, 1.2247e+00], [-1.2247e+00, -1.1921e-07, 1.2247e+00]]], grad_fn=NativeBatchNormBackward0)
文本处理举例
import torch from torch import nn data = [[[1,2,3,4,5],[1,2,3,4,5],[1,20,30,400,500]]] embedding_dim = 5 data = torch.tensor(data).float() data.shape torch.Size([1, 3, 5])
批归一化:特征层必须在第2个维度,公式完全一致
层归一化,归一的是每个元素(比如每个单词)的特征数据,,相当于对库表某行做归一化 批归一化,归一的是每个特征层上的数据分布(所有数据在某个特征维度上的数据),相当于对库表某列做归一化
# 1D要求3维[B,C,L],2D要求4维[B,C,W,H] data = [[[1,2,3,4,5],[1,2,3,4,5],[1,2,3,4,5]]] data = torch.tensor(data).float() data = data.permute(0,2,1) # 这里不要用reshape,这里是交换维度 bn = nn.BatchNorm1d(num_features=5) # 每个特征层上的数据分布 data tensor([[[1., 1., 1.], [2., 2., 2.], [3., 3., 3.], [4., 4., 4.], [5., 5., 5.]]]) bn(data) tensor([[[0., 0., 0.], [0., 0., 0.], [0., 0., 0.], [0., 0., 0.], [0., 0., 0.]]], grad_fn=NativeBatchNormBackward0)
文本处理举例,1个批次,3个单词,每个单词5个维度
import torch from torch import nn data = [[[1,2,3,4,5],[1,2,3,4,5],[1,20,30,400,500]]] embedding_dim = 5 data = torch.tensor(data).float() data.shape torch.Size([1, 3, 5])
层归一化:标准化 + 可学习参数
# normalized_shape必须是data.shape的最后一维,将这个维度的数据分布拉到正态分布 # 如果是文本,[batch_size,seq_len,embedding_dim],相当于对每个单词做了归一化 layer_norm = nn.LayerNorm(normalized_shape=embedding_dim) #400,500相对1是一个很大的数,归一化后,接近于1, # 这是要将每个元素的数据分布拉到标准正态分布了,有加速收敛的作用 layer_norm(data) tensor([[[-1.4142, -0.7071, 0.0000, 0.7071, 1.4142], [-1.4142, -0.7071, 0.0000, 0.7071, 1.4142], [-0.8813, -0.7928, -0.7463, 0.9773, 1.4431]]], grad_fn=NativeLayerNormBackward0)
The mean and standard-deviation are calculated over the last `D` dimensions, where `D` is the dimension of :attr:`normalized_shape`. For example, if :attr:`normalized_shape` is ``(3, 5)`` (a 2-dimensional shape), the mean and standard-deviation are computed over the last 2 dimensions of the input (i.e. ``input.mean((-2, -1))``). :math:`\gamma` and :math:`\beta` are learnable affine transform parameters of :attr:`normalized_shape` if :attr:`elementwise_affine` is ``True``. The standard-deviation is calculated via the biased estimator, equivalent to `torch.var(input, unbiased=False)`. Shape: - Input: :math:`(N, *)` - Output: :math:`(N, *)` (same shape as input)
NLP Example
# NLP Example batch, sentence_length, embedding_dim = 20, 5, 10 embedding = torch.randn(batch, sentence_length, embedding_dim) layer_norm = nn.LayerNorm(embedding_dim) # Activate module layer_norm(embedding)
参数 elementwise_affine
note:: Unlike Batch Normalization and Instance Normalization, which applies scalar scale and bias for each entire channel/plane with the :attr:`affine` option, Layer Normalization applies per-element scale and bias with :attr:`elementwise_affine`. elementwise_affine: a boolean value that when set to ``True``, this module has learnable per-element affine parameters initialized to ones (for weights) and zeros (for biases). Default: ``True``. 批归一化是一个特征层(相当于数据处理时列的维度)一个可学习参数加一个偏置 层归一化是每个元素(相当于数据处理时行的维度,一行多列为一个元素)都有一个可学习参数加一个偏置 elementwise_affine为True时,初始化参数所有值为1,默认为True
import torch from torch import nn def batch_norm(data): data = torch.tensor(data).float() print(data.shape) #torch.Size([3, 5]) # 1D要求3维[B,C,L],2D要求4维[B,C,W,H] data = data.unsqueeze(1) #目录仍然是一行一个样本 print(data.shape) # torch.Size([3, 1, 5]) #相当于取data[:,i]作归一化 bn = nn.BatchNorm1d(num_features=data.shape[1]) return bn(data) #常见2维数表,一行对应一个样本 data = [[1,2,3,4,5],[1,2,3,4,5],[1,2,3,4,5]] batch_norm(data) torch.Size([3, 5]) torch.Size([3, 1, 5]) tensor([[[-1.4142e+00, -7.0710e-01, 1.1921e-07, 7.0711e-01, 1.4142e+00]], [[-1.4142e+00, -7.0710e-01, 1.1921e-07, 7.0711e-01, 1.4142e+00]], [[-1.4142e+00, -7.0710e-01, 1.1921e-07, 7.0711e-01, 1.4142e+00]]], grad_fn=NativeBatchNormBackward0) data = [[5,2,3,4,5],[1,2,3,4,5],[1,2,3,4,5]] batch_norm(data) torch.Size([3, 5]) torch.Size([3, 1, 5]) tensor([[[ 1.2480, -0.9120, -0.1920, 0.5280, 1.2480]], [[-1.6320, -0.9120, -0.1920, 0.5280, 1.2480]], [[-1.6320, -0.9120, -0.1920, 0.5280, 1.2480]]], grad_fn=NativeBatchNormBackward0) 一个维度上修改一个数,影响了所有维度上的数据 批归一化针对的是整个批次的数据 |
import torch from torch import nn def layer_norm(data): data = torch.tensor(data).float() print(data.shape) #torch.Size([3, 5]) # 1D要求3维[B,C,L],2D要求4维[B,C,W,H] data = data.unsqueeze(1) #目录仍然是一行一个样本 print(data.shape) # torch.Size([3, 1, 5]) #相当于取data[:,i]作归一化,不同于BN,这里按维度进行了分组,要求需要做归一化的维度放在数据的最后一维 norm = nn.LayerNorm(normalized_shape=data.shape[2]) return norm(data) #常见2维数表,一行对应一个样本 data = [[1,2,3,4,5],[1,2,3,4,5],[1,2,3,4,5]] layer_norm(data) torch.Size([3, 5]) torch.Size([3, 1, 5]) tensor([[[-1.4142, -0.7071, 0.0000, 0.7071, 1.4142]], [[-1.4142, -0.7071, 0.0000, 0.7071, 1.4142]], [[-1.4142, -0.7071, 0.0000, 0.7071, 1.4142]]], grad_fn=NativeLayerNormBackward0) #常见2维数表,一行对应一个样本 data = [[5,2,3,4,5],[1,2,3,4,5],[1,2,3,4,5]] layer_norm(data) torch.Size([3, 5]) torch.Size([3, 1, 5]) tensor([[[ 1.0290, -1.5435, -0.6860, 0.1715, 1.0290]], [[-1.4142, -0.7071, 0.0000, 0.7071, 1.4142]], [[-1.4142, -0.7071, 0.0000, 0.7071, 1.4142]]], grad_fn=NativeLayerNormBackward0) 层归一化针对一个维度,某个维度上的数据变化,不会影响其他维度上的数据 |
相同点 [B,C,feature_shape] [B,C,L] 都是对[B,C]的处理,处理方式都是拉到标准正态分布 不同点 层归化一化比批归一化多一次分组 - 批归一化是按C分组,一个C,即一个特征维度上的所有数据取出来做一个归一化 - 层归一化,是按[C,L]分组,先按C分组,然后再按L分组 输入参数不同 - 批归一化,处理的维度确定为第2维C,只需要输入C的维数num_features - 层归一化,确定为[B,C,L],[B,C,H,W]为B,C之后的维度,但由于不确定是有多少维,所以要输入shape:normalized_shape import torch data = torch.randn(2,3,4,5) norm = nn.LayerNorm(normalized_shape=[4,5]) norm(data).shape #torch.Size([2, 3, 4, 5]) 层归一化 虽然可以对多个维度进行归一,但必须是data.shape最后的维度 - 可以是最后一维,也可以是最后2维 |
|
没见过这种方法,目前看仅个人推测,非官方观点,仅供参考,如有雷同,纯属巧合;
警告,如果你初步接触 统计学,对正统(教材)的基本概念没有深刻的理解,请不要看下面的内容!!! 向量单位化 = 向量/向量的模 向量的模就是 向量的欧氏距离,向量的大小,L2距离, 是各个元素的平方之和再开方, 强调向量本身的长度,向量/向量本身的长 才归于1 而方差呢,方差有两个重点: 1. 每个元素 - 分布的均值,强调分布的中心 2. 平方求和,再除以样本个数,是个平均值 如此, 方差强调的是 整个分布 相对分布中心 的离散程度, 标准差就是整个分布相对分布中心的离散长度(距离或大小) 每个元素-分布均值=每个元素离分布中心的长度(距离或大小) 二者相除,就得到一个分布离散比 从物理学的角度看,就是,要比较两个事物的不同,可以看看两个事物的密度... 方差的侧重点,并不是统一量纲, 而向量单位化则是让任何一个向量100%变化模为1的单位向量,100%的统一量纲 此时向量之间的差异,完全由其夹角体现, 夹角所体现出来的差异性, 个人感觉(就是本人目前没有能力也没时间用数学严格推导证明(就是一疲惫不堪的上班族...)), 要比 纯距离的 分布离散比 有更强的表达能力,带有更多的信息...
批归一化或层归一化LayerNorm(LayerNorm中的layer就是指特征层), 它并不是将矩阵中向量的模归于1,而是 将分布 按数据的 每个特征层都拉向一个正态分布
import torch def entorpy(x,label): """两个概率分布越接近其交叉熵越小 """ # 转tensor label = torch.tensor(label) x1 = torch.tensor(x) # 熵计算 # log0是负无穷, # torch.log(torch.tensor(0.)) = -inf # 为了避免出现此情况,为向量加上一个极小的数,通常是1e-6 x1 = x1 + 1e-6 entorpy = -label*torch.log(x1) return entorpy.sum() label = [0,0,1] x1 = [0.7,0.1,0.2] print(entorpy(x1,label)) # tensor(1.6094) label = [0,0,1] x1 = [0.02,0.06,0.92] print(entorpy(x1,label)) # tensor(0.0834) label = [0,0,1] x1 = [0,0.0001,0.9999] print(entorpy(x1,label)) # tensor(9.9008e-05)
模型输出分布与标签分布
交叉熵是衡量两个分布之间的差距 在AI中通常使用one-hot编码对标签进行编码,比如 [ [0, 0, 1] [0, 1, 0] [1, 0, 0] ] 对于某个样本,比如[0, 0, 1] 中的三个元素,每个元素的位置代表的是一个类别 ,[0, 0, 1] 则表示第3个类别 从概率的角度讲, [0, 0, 1]是第1类别的概率是0 [0, 0, 1]是第2类别的概率是0 [0, 0, 1]是第3类别的概率是1, 同时0+0+1 = 1,满足概率所有事物可能之和为1的条件 所以[0,0,1]是标签的分布,同时也是其概率分布 模型输出 对于第3个类别,模型输出[0.1, 0.3, 0.7]则表示该样本 在第1类别的输出是0.1,它的目标是0 在第2类别的输出是0.3,它的目标是0 在第3类别的输出是0.7,它的目标是1 或者是干扰因素多,或者是模型不够好,或者是还没有优化到极限, 总之,它现在输出了[0.1, 0.3, 0.7] 它是模型输出的分布,但不是其概率分布 0.1+0.3+ 0.7=1.1
模型输出转概率
import torch # 转概率 label = torch.tensor([0,0,1]) x1 = torch.tensor([0.1,0.3,0.7]) x1 = torch.exp(x1)/torch.exp(x1).sum() + 1e-6 print(x1) # tensor([0.2473, 0.3021, 0.4506])
模型输出是转成概率了,但有两个问题: 1. 对于第3个类别,模型在通过参数调整试图逼近标签,到0.7了,结果转了概率后变成了0.4506,离标签更远了 2. 标签由于是one-hot编码,[0,0,1]前两类是0,算了相当于没算,因为0乘以其他数还是0 工程上优化方向思考: 1. 针对概率变小的问题,a=x-min(x),a/max(a),可使最大索引位置的数值化为1,更符合分类问题的处理方式 2. 不做概率转化,只计算标签[0,0,1]中第3类位置上的数据, 即只计算标签为1的索引位置上的数据, 这里标签永远是1,模型输出则是0.7,|1-0.7|=0.3 计算代价最小,关键是这么做有实际效果吗? 经实践验证是有的,并且效果不差
神经网络】神经元ReLU、Leaky ReLU、PReLU和RReLU的比较