当前位置:   article > 正文

torch.mean和torch.var的个人能理解,以及通俗理解BatchNorm1d的计算原理

torch.var

看图通俗理解BatchNorm1d的作用原理,其本质E(x)和D(x)的计算原理

一、举例的数据格式可视化

假设我们的数据是(3,2,8)的一维序列格式的数据,表示3个Batch、每个Batch有2个Channel、每个Channel的Sequence为8。这是一维数据处理中十分常见的数据格式(B,C,N)。如下图所示:
在这里插入图片描述
上图中不同颜色区域标识不同的Batch,每个Batch均是(2,8)的格式。以下代码举例的维度均是(2,2,4)

二、 理解BatchNorm1d的计算公式

torch.nn.BatchNorm1d(num_features, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True, device=None, dtype=None)

参数:
-> num_features(int)– 特征或通道C的数量.
-> ϵ \epsilon ϵ - eps(float)– 为数值稳定性添加到分母上的值。默认值:1e-5.
-> momentum(float)– 用于running_mean和running_var计算的值。对于累积移动平均线,可以设置为“无”。默认值:0.1
-> affine(bool)– 一个布尔值,当设置为True时,该模块具有可学习的仿射参数。默认值:True (对应于公式中的 γ 、 β \gamma 、 \beta γβ)
-> track_running_stats(bool)–一个布尔值,当设置为True时,此模块跟踪运行平均值和方差;当设置为False时,该模块不跟踪此类统计信息,并将统计信息缓冲区running_mean和running_var初始化为None。当这些缓冲区为None时,此模块始终使用批处理统计信息。无论是训练模式还是评估模式。默认值:True

y = x − E [ x ] V a r [ x ] + ϵ ∗ γ + β y = \frac{ x - E[x] }{ \sqrt{Var[x] + \epsilon} } * \gamma + \beta y=Var[x]+ϵ xE[x]γ+β
上面公式中, x x x是我们输入的数据, E [ x ] E[x] E[x]则是我们需要计算的期望, V a r [ x ] Var[x] Var[x]是计算的方差, ϵ \epsilon ϵ是分母的稳定因子, γ 、 β \gamma 、\beta γβ 是放射参数(可参考y=ax+b形式的变换)。

现在我们有个问题就是,我们不知道公式中的期望和方差怎么计算的?

三、torch.mean()和torch.var()的理解

在这部分结束后相信大家都能清楚这来个torch的函数的计算方式!!!

3.1 torch.mean()

在我们查阅pytorch的官方文档后,我们会发现torch.mean的用法有两种,如下所示:

torch.mean(input, *, dtype=None) → Tensor
torch.mean(input, dim, keepdim=False, *, dtype=None, out=None) → Tensor

对于第一种用法,我们则是直接计算所有值的平均值,如下:


>>>a = torch.randn((2,2,4))
>>>a
tensor([[[ 1.1548,  0.5501, -0.5606,  0.8808],
         [-1.6358, -1.2661,  1.0681, -1.7350]],
        [[-0.1020, -1.1071, -1.9635,  0.2054],
         [-1.5953,  1.0970,  0.7805, -0.2438]]])
>>>torch.mean(a)
tensor(-0.2795)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

对于第二种用法则添加了很多的参数,包括keepdim、dim等参数:

–> dim(int或int元组)– 要消减的一个或多个维度。
–> keepdim(bool)– 输出张量是否保留了dim,是否和原始的x的维度相同,要和dim参数一起使用。

>>>a = torch.randn((2,2,4))
>>>a
tensor([[[ 1.1548,  0.5501, -0.5606,  0.8808],
         [-1.6358, -1.2661,  1.0681, -1.7350]],
         
        [[-0.1020, -1.1071, -1.9635,  0.2054],
         [-1.5953,  1.0970,  0.7805, -0.2438]]])
>>>torch.mean(a, dim=0, keepdim=True)
tensor([[[ 0.5264, -0.2785, -1.2621,  0.5431],
         [-1.6155, -0.0846,  0.9243, -0.9894]]])
>>>torch.mean(a, dim=0)
tensor([[ 0.5264, -0.2785, -1.2621,  0.5431],
        [-1.6155, -0.0846,  0.9243, -0.9894]])
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

疑惑:dim的作用是什么,如何计算来的均值?

在上面我们说过dim的作用是选择我们需要消减的那个维度,在上面的计算中我们将dim=0传入函数执行,意思就是我们不要维度0了,就是不要batchsize这个维度。
注意上面添加了keepdim和没有添加keepdim的两个计算结果,我么得到的结果是一样的,但是两者结果的维度却不相同,上面的结果任然是是哪个维度,但是下面的结果却是两个维度。(如果看不出来几个维度的,可以使用shape打印出来看看)。

那dim=0时,均值的计算原理是啥?

我们可以简单看一下数值,可以发现,是每个batch的对应位置上数值的和的平均。这么说可能很迷糊,或许我们这次记住了,下次又忘了,那怎么办呢?请回忆一下再文章开头展示的图片,就是下面那张图片。下面我将用图形的方式介绍的给大家更清晰的认识。
在这里插入图片描述
我们假定在一个空间中,数据的分布方式就是上面的那种方式。那为啥是上面那种方式呢?我也不知道
如何通俗理解这个消减维度呢,dim=0表示消减第0个维度,那我们应该尽可能的让这个图形看到尽可能少的Batch,对吧!当我们只看到1个batch的时候,是不是可以用squeeze将这个维度消灭掉,因为维度维值为1的不影响数据的分布。
再次看向上面的这张图,我们给这张图加上各个方向的一个方向标识,以便我们后续的进一步理解。

  1. 对于dim=0而言,为了消减维度0,即尽可能少的看到batch,那么在这个空间里面,我们可以从上往下看,便只能看到一个batch,这便是我们的目的。那么,我们在从上往下看时,看到的各自重叠的部分,便是各自需要计算均值的部分。对重叠的部分进行求和,在取均值,便得到了均值。
    在这里插入图片描述
>>>a = torch.randn((2,2,4))
>>>a
tensor([[[ 1.1548,  0.5501, -0.5606,  0.8808],
         [-1.6358, -1.2661,  1.0681, -1.7350]],
         
        [[-0.1020, -1.1071, -1.9635,  0.2054],
         [-1.5953,  1.0970,  0.7805, -0.2438]]])
>>>torch.mean(a, dim=0, keepdim=True)
tensor([[[ 0.5264, -0.2785, -1.2621,  0.5431],
         [-1.6155, -0.0846,  0.9243, -0.9894]]])
>>>torch.mean(a, dim=0)
tensor([[ 0.5264, -0.2785, -1.2621,  0.5431],
        [-1.6155, -0.0846,  0.9243, -0.9894]])
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  1. 对于dim=1而言,为了消减维度1,即尽可能少的看到channel,那么在这个空间里面,我们可以从右往左看,便只能看到一个channel。同理,我们在从右往左看时,看到的各自重叠的部分,便是各自需要计算均值的部分。对重叠的部分进行求和,在取均值,便得到了均值。
    在这里插入图片描述
>>>a
tensor([[[ 1.1548,  0.5501, -0.5606,  0.8808],
         [-1.6358, -1.2661,  1.0681, -1.7350]],
         
        [[-0.1020, -1.1071, -1.9635,  0.2054],
         [-1.5953,  1.0970,  0.7805, -0.2438]]])
         
>>>torch.mean(a, dim=1)
tensor([[-0.2405, -0.3580,  0.2538, -0.4271],
        [-0.8486, -0.0050, -0.5915, -0.0192]])

>>>torch.mean(a, dim=1, keepdim=True)
tensor([[[-0.2405, -0.3580,  0.2538, -0.4271]],
        [[-0.8486, -0.0050, -0.5915, -0.0192]]])
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  1. 对于dim=2而言,为了消减维度2,即尽可能少的看到squence,那么在这个空间里面,我们可以从前往后看,便只能看到一个sequence。同理,我们在从右往左看时,看到的各自重叠的部分,便是各自需要计算均值的部分。对重叠的部分进行求和,在取均值,便得到了均值。
    在这里插入图片描述
>>>a
tensor([[[ 1.1548,  0.5501, -0.5606,  0.8808],
         [-1.6358, -1.2661,  1.0681, -1.7350]],
         
        [[-0.1020, -1.1071, -1.9635,  0.2054],
         [-1.5953,  1.0970,  0.7805, -0.2438]]])

>>>torch.mean(a, dim=2, keepdim=True)
tensor([[[ 0.5063],
         [-0.8922]],
        [[-0.7418],
         [ 0.0096]]])
         
>>>torch.mean(a, dim=2)
tensor([[ 0.5063, -0.8922],
        [-0.7418,  0.0096]])
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

dim=(0,1)该怎么理解呢?

我们第一反应是消减维度0和1,但是我们该如何一次性将这两个维度消灭?
实际上,这个方式可以分解成先消减维度0,在消减维度1。 看代码:

>>>a
tensor([[[ 1.1548,  0.5501, -0.5606,  0.8808],
         [-1.6358, -1.2661,  1.0681, -1.7350]],
         
        [[-0.1020, -1.1071, -1.9635,  0.2054],
         [-1.5953,  1.0970,  0.7805, -0.2438]]])
         
>>>torch.mean(a, dim=(0,1), keepdim=True)
tensor([[[-0.5446, -0.1815, -0.1689, -0.2231]]])

>>>b = torch.mean(a, dim=0, keepdim=True)
>>>torch.mean(b, dim=1, keepdim=True)
tensor([[[-0.5446, -0.1815, -0.1689, -0.2232]]])

# dim=(1,0)与dim=(0,1)结果相同
>>>torch.mean(a, dim=(1,0), keepdim=True)
tensor([[[-0.5446, -0.1815, -0.1689, -0.2231]]])
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

可以发现我们可以通过先将维度0消减,在对维度1消减,记得带上keepdim=True去验证。
看图理解,从下图我们可以知道,最后得到的就是最右边地下灰色的数据,是经过两次计算均值得到的,但是想象里好的话,我们也可以经过一次计算的到;此外我们也可以先消减维度1,再消减维度0,得到的结果是一样的。看图理解应该不难。
在这里插入图片描述

同理dim=(1,2)、dim=(0,3)等等,相信大家也能用同样的方法计算出来了。

3.2 torch.var

想必经过上面图形的解释,大家应该能够非常清楚各种方式的均值计算。对于var的计算方式,也是同理。

torch.var(input, dim=None, *, correction=1, keepdim=False, out=None) → Tensor
参数:
-> input(张量)– 输入张量。
-> dim(int或int元组,可选)– 要消减的一个或多个维度。如果“无”,则所有尺寸都将消减。
关键字参数:
-> correction(int)- 样本大小和样本自由度之间的差异。默认为贝塞尔校正,correction=1。
在2.0版本中更改:以前此参数被称为unbiased,是布尔值,True对应correction=1,False对应correction=0。这里表示是否采用无偏估计。
-> keepdim(bool)– 输出张量是否保留了dim。
-> out(张量,可选)– 输出张量。

>>>a
tensor([[[ 1.1548*,  0.5501, -0.5606,  0.8808],
         [-1.6358, -1.2661,  1.0681, -1.7350]],
         
        [[-0.1020*, -1.1071, -1.9635,  0.2054],
         [-1.5953,  1.0970,  0.7805, -0.2438]]])
>>>torch.mean(a, dim=0, keepdim=True)
tensor([[[ 0.5264*, -0.2785, -1.2621,  0.5431],
         [-1.6155, -0.0846,  0.9243, -0.9894]]])
         
# 用笔验算一下,发现是和下面一样的
>>>torch.var(a, dim=0, keepdim=True)
tensor([[[7.8977e-01*, 1.3732e+00, 9.8406e-01, 2.2808e-01],
         [8.2013e-04, 2.7921e+00, 4.1357e-02, 1.1118e+00]]])
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

这里默认correction=1,是无偏估计,求样本方差。参加运算的用*表示。计算公式:
S 2 = 1 n − 1 ∑ i = 1 n ( X i − X ‾ ) 2 S^2 = \frac{1}{n-1} \sum_{i=1}^{n} {(X_i - \overline{X})^2} S2=n11i=1n(XiX)2
举例:
7.8977 e − 01 = ( 1.1548 − 0.5264 ) 2 + ( − 0.1020 − 0.5264 ) 2 2 − 1 7.8977e-01 = \frac {(1.1548 - 0.5264)^2 + (-0.1020 - 0.5264)^2} {2-1} 7.8977e01=21(1.15480.5264)2+(0.10200.5264)2

其他的就不一一举例了,请自行参悟。

四、BatchNorm1d的计算方式

y = x − E [ x ] V a r [ x ] + ϵ ∗ γ + β y = \frac{ x - E[x] }{ \sqrt{Var[x] + \epsilon} } * \gamma + \beta y=Var[x]+ϵ xE[x]γ+β
这个公式里面, V a r [ x ] Var[x] Var[x]默认的是有偏估计。有偏估计(总体方差)的计算公式如下:
σ 2 = 1 N ∑ i = 1 N ( X i − μ ) \sigma^2 = \frac{1}{N} \sum_{i=1}^{N} {(X_i - \mu)} σ2=N1i=1N(Xiμ)

>>>a
tensor([[[ 1.1548*,  0.5501, -0.5606,  0.8808],
         [-1.6358, -1.2661,  1.0681, -1.7350]],
         
        [[-0.1020*, -1.1071, -1.9635,  0.2054],
         [-1.5953,  1.0970,  0.7805, -0.2438]]])

>>>mean = torch.mean(a, dim=(0,2), keepdim=True)
>>>mean
tensor([[[-0.1178],
         [-0.4413]]])

>>>var = torch.var(a, dim=(0,2), keepdim=True, unbiased=False)
>>>var
tensor([[[0.9686],
         [1.4111]]])
         
# 自己计算的结果
>>>(a-mean)*(var+1e-5).rsqrt()
tensor([[[ 1.2930,  0.6786, -0.4500,  1.0146],
         [-1.0055, -0.6943,  1.2706, -1.0891]],
        [[ 0.0160, -1.0052, -1.8754,  0.3284],
         [-0.9715,  1.2950,  1.0285,  0.1663]]])
         
# 定义一个BatchNorm1d
>>>bn = torch.nn.BatchNorm1d(num_features=2, momentum=0,affine=False, track_running_stats=False)
>>>bn(a)
tensor([[[ 1.2930,  0.6786, -0.4500,  1.0146],
         [-1.0055, -0.6943,  1.2706, -1.0891]],
        [[ 0.0160, -1.0052, -1.8754,  0.3284],
         [-0.9715,  1.2950,  1.0285,  0.1663]]])
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

可以发现两者的结果一样。希望可以通过这种方式帮助你理解BatchNorm1d的原理,理解BatchNorm1d作用在哪些特征上面。
在这里插入图片描述

上图最右边的图中,只有最后面的灰色是得到的最终结果。或许换个方向,从图形的角度,我们可以更加清楚BatchNorm1d是在干什么,理解BatchNorm1d的归一化的目的是什么,有什么作用,能对神经网络起到什么作用。很明显的作用就是,在所有数据的分布基本一致时,这样做是可以加快神经网络收敛的,似乎可以更好的提取大部分数据共有的特征。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/606371
推荐阅读
相关标签
  

闽ICP备14008679号