torch.nn ------ 標准化歸一化層


torch.nn ------ 標准化歸一化層

作者:elfin   參考資料來源:torch.nn.BatchNorm


標准化結構圖

  • BN是所有樣本關於某個信道的標准化
  • LN是關於某個樣本的所有特征標准化
  • IN是關於某個樣本在某個信道上標准化
  • GN是某個樣本關於信道分組的標准化

Top---Bottom

一、BatchNorm1d

對 2D 或 3D 輸入(具有可選附加通道維度的小批量 1D 輸入)應用批量歸一化。歸一化的公式為:

\[y = \frac{x - \mathrm{E}[x]}{\sqrt{\mathrm{Var}[x] + \epsilon}} * \gamma + \beta \]

​ 這里的\(x\)是張量的一個元素,\(\gamma\)\(\beta\)分別是標准化處理后的數據放縮、偏置(平移)參數,這兩個參數需要學習,為什么不直接使用\(\gamma=1,\beta=0\)呢?因為這里數據的分布本身是有差異性的,我們通過標准化強行將數據放縮到0均值,使其接近標准正態分布,同一個偽逆過程讓模型自學習其均值方差,以獲取更好的性能,如果強制給定處理后的均值方差,理論上也是可以的,但是這里的人為信息偏置引入是很強的,但又沒有一個好的科學解釋,實驗表明讓模型學習是一個很好的選擇!

詳情參考鏈接:https://zhuanlan.zhihu.com/p/441573901

​ BN層雖然原理是一樣的,但是有1d、2d、3d的區別,這里我們先看1d的標准化(歸一化)處理。

注:我習慣稱標准化,但大部分人都稱歸一化,雖然名稱有本質區別,但是在這里我們不做區分

參數:

  • num_features: 輸入大小\(C\),處理的數據維度為\(\left(N,C,L\right)\)或為\(\left(N,C\right)\)
  • eps: 給分母添加的數,防止分母為0,默認1e-5
  • momentum: 移動平均算法(EMA)的動量參數,默認0.1,可以設置為None
  • affine: 布爾值,設置是否可學習的仿射參數
  • track_runing_stats: 是否跟蹤數據的均值方差信息,默認為True。如果不記錄,推理時我們減均值除標准差就不能處理,只能基於數據的批量均值與方差進行運算,但是此時數據往往樣本量只有1

標准化層BN是不改變輸入數據的shape的!

  • 輸入\(\left(N,C,L\right)\)或為\(\left(N,C\right)\)
  • 輸出\(\left(N,C,L\right)\)或為\(\left(N,C\right)\)

案例:

In[1]: elfin = nn.BatchNorm1d(2, affine=False)
In[2]: data = torch.tensor([[1,2], [3,4], [5,6], [7,8], [9,10]], dtype=torch.float32)
In[3]: elfin(data)
Out[3]: 
tensor([[-1.4142, -1.4142],
        [-0.7071, -0.7071],
        [ 0.0000,  0.0000],
        [ 0.7071,  0.7071],
        [ 1.4142,  1.4142]])
In[4]: list(elfin.parameters())
Out[4]: []
In[5]: elfin = nn.BatchNorm1d(2)
In[6]: list(elfin.parameters())
Out[6]: 
[Parameter containing:
 tensor([1., 1.], requires_grad=True),
 Parameter containing:
 tensor([0., 0.], requires_grad=True)]
In[7]: data = torch.tensor([[[1,2], [3,4]], [[7,8], [9,10]]], dtype=torch.float32)
In[8]: data
Out[8]: 
tensor([[[ 1.,  2.],
         [ 3.,  4.]],
        [[ 7.,  8.],
         [ 9., 10.]]])
In[9]: elfin = nn.BatchNorm1d(2, affine=False)
In[10]: elfin(data)
Out[10]: 
tensor([[[-1.1508, -0.8220],
         [-1.1508, -0.8220]],
        [[ 0.8220,  1.1508],
         [ 0.8220,  1.1508]]])

對於\(\left(N,C\right)\)我們很容易從上面的案例看出來它是在\(N\)個元素中進行標准化的。對於\(\left(N,C,L\right)\)是不是在\(N \times L\)個元素上進行標准化呢?

In[11]: data[:,0,:]
Out[11]:
tensor([[1., 2.],
        [7., 8.]])
# 明顯標准化處理后的結果為:-1.1508,-0.8220, 0.8220, 1.1508
# 注:均值4.5,標准差3.0413812651491097

經過證明猜想是對的!


Top---Bottom

二、BatchNorm2d

​ 對4D數據進行標准化(歸一化),輸入數據形式為\((N,C,H,W)\),輸出也是\((N,C,H,W)\)。計算公式見BatchNorm1d

參數說明:

  • num_features: 輸入大小\(C\),處理的數據維度為\(\left(N,C,H,W\right)\)
  • eps: 給分母添加的數,防止分母為0,默認1e-5
  • momentum: 移動平均算法(EMA)的動量參數,默認0.1,可以設置為None
  • affine: 布爾值,設置是否可學習的仿射參數
  • track_runing_stats: 是否跟蹤數據的均值方差信息,默認為True。如果不記錄,推理時我們減均值除標准差就不能處理,只能基於數據的批量均值與方差進行運算,但是此時數據往往樣本量只有1
  • device: 設備
  • dtype: 數據類型

動量參數說明

\[\hat{x}_\text{new} = (1 - \text{momentum}) \times \hat{x} + \text{momentum} \times x_t \]

這里實際上是進行移動平均(加權求期望)。

案例:

>>> m = nn.BatchNorm2d(2)
>>> data = torch.randn(2,2,3,2)
>>> data[:,0,:,:]
tensor([[[ 0.2732,  0.7230],
         [ 0.1640, -0.7623],
         [-1.5510, -2.1041]],
        [[-0.3899,  0.7928],
         [-1.2680,  0.1955],
         [ 1.4165, -1.0685]]])
>>> m(data)[:,0,:,:]
tensor([[[ 0.5589,  0.9988],
         [ 0.4520, -0.4539],
         [-1.2252, -1.7661]],
        [[-0.0897,  1.0670],
         [-0.9484,  0.4829],
         [ 1.6770, -0.7533]]], grad_fn=<SliceBackward>)
>>> list(m.parameters())
[Parameter containing:
 tensor([1., 1.], requires_grad=True),
 Parameter containing:
 tensor([0., 0.], requires_grad=True)]

均值:torch.std_mean(data[:,0,:,:])=(tensor(1.0680), tensor(-0.2982));如果根據計算的均值方差計算BN后的數據會有較大差異,這里的原因主要是方差、標准差的計算方式,統計學上我們是除以\(n-1\)(std_mean是使用torch.sqrt(torch.sum((data[:,0,:,:]+0.2982)**2) / 11)=1.0680),BN是除以\(n\),它是使用(torch.sqrt(torch.sum((data[:,0,:,:]+0.2982)**2) / 12)=1.0225),使用1.0225去計算就能得到m(data)的計算結果!

經過實驗和1d的接口類似,我們只在通道維度上選擇某一個時刻,取出的數據進行標准化操作!


Top---Bottom

三、BatchNorm3d

和BatchNorm2d類似,參考BatchNorm2d。


Top---Bottom

四、LazyBatchNorm1d~3d

延遲初始化weight、bias、running_mean、running_var參數。不需要指定通道維度,可以自己根據通道維數進行設定num_features參數!


Top---Bottom

五、GroupNorm

分組批量標准化,對每個在通道維度上分組進行標准化,BatchNorm默認是在所有通道上,相當於每個通道都是一組!

參數說明:

  • num_group: 通道維度分組數量
  • num_channels: 通道的維度
  • eps: 給分母添加的數,防止分母為0,默認1e-5
  • affine: 布爾值,設置是否可學習的仿射參數

案例:

>>> m = nn.GroupNorm(2,4)
>>> data = torch.randn(2,4,2,2)
# 驗證是否在所有樣本上分組
>>> torch.mean(data[:, :2, :, :])
tensor(-0.3137)
>>> torch.sqrt(torch.sum((data[:, :2,:,:]+0.3137)**2) / 16)
tensor(0.8521)
>>> data[0, 0, 0, 0]
tensor(-0.6422)
>>> m(data)[0,0,0,0]
tensor(-0.0309, grad_fn=<SelectBackward>)
>>> (-0.6422+0.3137) / 0.8521
-0.3855181316746861
# 驗證是否BN模式,在每個信道上進行標准化(因為參數對數量與信道數量一樣)
>>> torch.mean(data[:, 0, :, :])
tensor(-0.2070)
>>> torch.sqrt(torch.sum((data[:, 0,:,:]+0.2070)**2) / 8)
tensor(0.6600)
>>> (data[0,0,0,0]+0.207) / 0.66
tensor(-0.6595)
# 驗證是在一個樣本某組信道上進行標准化
>>> torch.mean(data[0, :2, :, :])
tensor(-0.6166)
>>> torch.sqrt(torch.sum((data[0, :2,:,:]+0.6166)**2) / 8)
tensor(0.8293)
>>> (data[0,0,0,0]+0.6166) / 0.8293
tensor(-0.0309)

根據案例我們知道GN是在某個樣本的一組信道上進行所有特征的標准化!

GN標准化是何凱明團隊(2018)提出的BN替代品,它解決了在小批量時的BN不穩定問題,所以在小批量時,LN也能有較好的錯誤率!


Top---Bottom

六、SyncBatchNorm並行標准化

SyncBatchNorm只會發生在訓練時,推理時我們並不會使用SyncBatchNorm!所以我們在些模型的時候可以直接寫BN,並行運算時,可以進行轉換torch.nn.SyncBatchNorm.convert_sync_batchnorm(),經過這個接口的轉換后再交給DDP進行封裝!

# With Learnable Parameters
m = nn.SyncBatchNorm(100)
# creating process group (optional)
# ranks is a list of int identifying rank ids.
ranks = list(range(8))
r1, r2 = ranks[:4], ranks[4:]
# Note: every rank calls into new_group for every
# process group created, even if that rank is not
# part of the group.
process_groups = [torch.distributed.new_group(pids) for pids in [r1, r2]]
process_group = process_groups[0 if dist.get_rank() <= 3 else 1]
# Without Learnable Parameters
m = nn.BatchNorm3d(100, affine=False, process_group=process_group)
input = torch.randn(20, 100, 35, 45, 10)
output = m(input)

# network is nn.BatchNorm layer
sync_bn_network = nn.SyncBatchNorm.convert_sync_batchnorm(network, process_group)
# only single gpu per process is currently supported
ddp_sync_bn_network = torch.nn.parallel.DistributedDataParallel(
                        sync_bn_network,
                        device_ids=[args.local_rank],
                        output_device=args.local_rank)

參數說明

  • process_group: 進程分組,默認是將所有進程處理的數據聚合進行BN操作,設置這個參數之后就會變成在組內進行操作BN
  • 其他參數見BatchNorm2d

關於distributed設置進程間通信和torch.multiprocessing創建進程完美會在后面單獨講解!這里只需要淺顯的理解即可!


Top---Bottom

七、InstanceNorm?d與LazyInstanceNorm?d

InstanceNorm是在某個樣本的某個信道上進行標准化,而LazyInstanceNorm與LazyBN是一樣的道理!

八、LayerNorm

NLP最常用的標准化層LayerNorm是將一個樣本所有的特征進行標准化
normalized_shape參數指定的是要進行標准化的維度,和BN的參數剛好相反,如數據維度為\((B,C,N)\),normalized_shape=[C,N],則是每一個樣本所有特征進行標准化,如果是normalized_shape=N,則只在最后一個維度上進行標准化,這里normalized_shape必須指定最后幾個維度不能是中間維度(不包含最后一個維度的情況)!對於圖像數據可以參考案例。

官方案例

# 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)
# Image Example
N, C, H, W = 20, 5, 10, 10
input = torch.randn(N, C, H, W)
# Normalize over the last three dimensions (i.e. the channel and spatial dimensions)
# as shown in the image below
layer_norm = nn.LayerNorm([C, H, W])
output = layer_norm(input)

Top---Bottom

九、局部標准化LRN

LRN局部響應標准化,有兩種LRN實現,一種是在通道上進行某個窗口大小為size的鄰域內進行標准化,另一種是在某個通道對應的特征圖上大小為size的鄰域內進行標准化!

具體參考文章:https://blog.csdn.net/weixin_46221946/article/details/122729460


Top---Bottom

完!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM