本笔记大量参考该博客,进行了少量的修改,感谢作者的付出。 【面试向】Batch Normalization | 某科学のBLOG (a-kali.github.io)

定义自己的 BN 类

我们分成 2D 和 1D 数据,分别定义对应的 BatchNrom2D 和 BatchNorm2D

首先明确 BN 在训练和测试的时候区别,在定义类需要输入的参数是 momentum, in_channels。 在写前向传播时,区分好训练和测试前提下不同的操作即可

BatchNorm 2D 的代码

注意输入的数据 input 应该是 numpy () 格式的数组。

class BatchNorm2D:
    def __init__(self, channels, momentum=0.1, eps=1e-5) -> None:
		self.momentum = momentum
        self.eps = eps
        # 滑动均值和方差
        self.running_mean = np.zeros(channels)# 均值初始化为 0
        self.running_var = np.ones(channels)  # 注意方差初始化为 1
 
        self.training = True
        
        self.beta = 0
        self.gamma = 1
    # 下面两个是更改操作模式
    def eval(self):
        self.traning = False
        
    def train(self):
        self.traning = True
 
    def forward(self, input):
        out = np.zeros(input.shape)
        B, C, H, W = input.shape
        for i in range(C):
            cur = input[:, i, :, :] # 取出当前的通道
            N = cur.size  # 元素总数量
            if self.training:
                # 计算均值和方差, 并归一化
                mean = cur.sum() / N
                var = ((cur - mean)**2).sum() / N
                cur_out = (cur - mean) / (var + self.eps) **0.5
                
                # 计算方差的无偏估计,利用方差的无偏估计并更新滑动值
                var_unbias = ((cur - mean)**2).sum() / (N - 1)
                self.running_mean[i] = (1 - self.momentum) * self.running_mean[i] + self.momentum * mean
                self.running_var[i] = (1 - self.momentum) * self.running_var[i] + self.momentum * var_unbias
            else:
                cur_out = (cur - self.running_mean[i]) / (self.running_val[i] + self.eps) **0.5
 
            # 缩放平移
            out[:, i,:,:] = self.gamma * cur_out + self.beta
        return out

BatchNorm 1D 的代码 与 2D 的代码类似,只不过 1D 时没有 channel 了,输入数据的维度是 [B, D]. B 是 batch size

class BatchNorm1D:
    def __init__(self, momentum=0.1, eps=1e-5) -> None:
        self.momentum = momentum
        self.eps = eps
 
        self.gamma = 1
        self.beta = 0
 
        self.running_var = 1
        self.running_mean = 0
 
        self.training = True
 
    def eval(self):
        self.training = False
 
    def train(self):
        self.training = True
 
    def forward(self, input):
        N = input.size
        out = np.zeros(input.shape)
        
        if self.training:
            mean = input.sum() / N
            var = ((input - mean) ** 2).sum() / N
            out = (out - mean) / (var + self.eps) ** 0.5
 
            var_unbiased = ((input - mean) ** 2).sum() / (N - 1)
            self.running_mean = (1 - self.momentum) * self.running_mean + self.momentum * mean
            self.running_var = (1 - self.momentum) * self.running_var + self.momentum * var_unbiased
        else:
            out = (out - self.running_mean) / (self.running_var + self.eps) ** 0.5
 
        return self.gamma * out + self.beta

主函数

if __name__ == '__main__':
	# 生成2D数据并进行测试
    input_data = np.random.random([2,3,5,5])
    bn2d = BatchNorm2D(input_data.shape[1])
    print(bn2d.forward(input_data))
 
    print('--------')
	
	# 生成1D数据并进行测试
    input_data = np.random.random([2,3])
    bn1d = BatchNorm1D()
    print(bn1d.forward(input_data))