利用nn.BatchNorm构建带BN的神经网络

该博客探讨了在PyTorch中如何使用批量归一化(BatchNormalization)进行网络构建,并展示了其在模型训练中的应用。通过对比实验,说明了BatchNormalization的位置对模型效果的影响,以及在不同Batch_size下,模型的收敛速度和性能变化。同时强调了综合调参的重要性,尤其是在复杂模型和大数据量的情况下,BatchNormalization能带来更好的效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

网络构建:

用写好的fit函数进行模型训练

综合调参:

Bach Normalization与Batch_size综合调参


网络构建:

在构建网络中,我们会定义好一些选择哪种激活函数和是否需要进行BatchNorm以及是在激活函数之前进行BatchNorm还是之后进行,其它为正常网络构建过程:

import torch.nn as nn
import torch

class net_class1(nn.Module):
    def __init__(self,act_fun=torch.relu,in_features=2,n_hidden=4,out_features=1,bias=True,BN_model=None,momentum=0.1):
        super(net_class1, self).__init__()
        self.linear1 = nn.Linear(in_features,n_hidden,bias=bias)
        self.bn1 = nn.BatchNorm1d(n_hidden,momentum=momentum)
        self.linear2 = nn.Linear(n_hidden,out_features,bias=bias)
        self.act_fun = act_fun
        self.BN_model = BN_model

    def forward(self,x):
        if self.BN_model == 'pre':
            z1 = self.bn1(self.linear1(x))
            f1 = self.act_fun(z1)
            out = self.linear2(f1)
        elif self.BN_model == 'post':
            z1 = self.linear1(x)
            f1 = self.act_fun(z1)
            out = self.linear2(self.bn1(f1))
        else:
            z1 = self.linear1(x)
            f1 = self.act_fun(z1)
            out = self.linear2(f1)
        return out

""""""

无论bn层在隐藏层之前还是之后,对实际结果不会有影响

用写好的fit函数进行模型训练

from  MyPython.test.study.MyTorchUtils import  MyTorchUtils

"""fit model"""
utils = MyTorchUtils()
torch.manual_seed(420)

# create data
features,labls = utils.tensorDataGenRe(bag=2,w=[2,-1],bias=False)

# split data
train_loader,test_loader = utils.split_loader(features,labls)

# instantiation nn , enter training mode
relu_model1_norm = net_class1(BN_model='pre')
relu_model1_norm.train()

lr = 0.3
utils.fit(net=relu_model1_norm,
          criterion=nn.MSELoss(),
          optimizer=optim.SGD(relu_model1_norm.parameters(),lr=lr),
          batchdata=train_loader,
          epochs=20,
          cla=False)

print([*relu_model1_norm.modules()][2].weight)
print([*relu_model1_norm.modules()][2].bias)

print([*relu_model1_norm.modules()][2].running_mean)
print([*relu_model1_norm.modules()][2].running_var)

# enter testing mdoe
relu_model1_norm.eval()
print(utils.mse_cla(train_loader, relu_model1_norm))
print(utils.mse_cla(test_loader, relu_model1_norm))

print([*relu_model1_norm.modules()][2].weight)
print([*relu_model1_norm.modules()][2].bias)

print([*relu_model1_norm.modules()][2].running_mean)
print([*relu_model1_norm.modules()][2].running_var)

注意的是,单独无脑的加入BN层并不一定会提升模型的效果,这里加入一段对比代码:

def bn_sigmoid():
    torch.manual_seed(929)

    sigmoid_model1 = net_class1(act_fun=torch.sigmoid)
    sigmoid_model_norm = net_class1(act_fun=torch.sigmoid,BN_model='pre')

    model_1 = [sigmoid_model1,sigmoid_model_norm]
    name_1 = ['simoid_model1','sigmoid_model1_norm']

    lr = 0.03
    num_epochs = 40

    train_1,test_1 = utils.model_comparison(model_1=model_1,
                                            name_1=name_1,
                                            train_data=train_loader,
                                            test_data=test_loader,
                                            num_epochs=num_epochs,
                                            criterion=nn.MSELoss(),
                                            optimizer=optim.SGD,
                                            lr=lr,
                                            cla=False,
                                            eva=utils.mse_cla)
    for i,name in enumerate(name_1):
        plt.plot(list(range(num_epochs)),train_1[i],label=name)
    plt.legend(loc=1)
    plt.show()

在上面模型中加入bn层,模型效果反而会变差,此时需要和别的参数进行联合调参,结果:

 

综合调参:

Bach Normalization与Batch_size综合调参

我们首先要明白的一点是,要让模型得到最好的效果其实就是bn层计算出的均值和方差越接近真实的均值和方差,而当面对的数据量(也就是Batch_size)太少的时候,我们用小批数据去估计大的整体就会出现较大的偏差,影响模型准确率。

调大数据数量来对比:

def bn_sigmoid2():
    utils = MyTorchUtils()

    torch.manual_seed(420)

    features,labls = utils.tensorDataGenRe(bag=2,w=[2,-1],bias=False)

    train_loader,test_loader = utils.split_loader(features,labls,batch_size=50)

    sigmoid_model1 = net_class1(act_fun=torch.sigmoid)
    sigmoid_model_norm = net_class1(act_fun=torch.sigmoid,BN_model='pre')

    model_1 = [sigmoid_model1,sigmoid_model_norm]
    name_1 = ['simoid_model1','sigmoid_model1_norm']

    lr = 0.03
    num_epochs = 40

    train_1, test_1 = utils.model_comparison(model_1=model_1,
                                             name_1=name_1,
                                             train_data=train_loader,
                                             test_data=test_loader,
                                             num_epochs=num_epochs,
                                             criterion=nn.MSELoss(),
                                             optimizer=optim.SGD,
                                             lr=lr,
                                             cla=False,
                                             eva=utils.mse_cla)

    for i,name in enumerate(name_1):
        plt.plot(list(range(num_epochs)),train_1[i],label=name)
    plt.legend(loc=1)
    plt.show()

对比上段代码,更改了batch_size,结果如下:

 对比可以发现,调整batch_size后,带bn层的有更快的收敛速度和效果

这里相同的原理,除了提高batch_size外,还可以通过降低momentum参数来实现

复杂模型上Batch_normalization的表现

bn主要的优化手段是通过调整线性层的梯度,使整个模型的梯度达到平稳的状态,来获得更好的效果,所以,在一定范围内,bn方法对于复杂模型和复杂数据会更加有效,很多简单模型可以不用(会增加计算量,所以上述中我们的简单模型优化效果可能不是很明显.

# 构建卷积神经网络结构 # 当前版本为卷积核大小5 * 5的版本 class CNN(nn.Module): def __init__(self): super(CNN, self).__init__() self.conv1 = nn.Conv2d(5, 16, 3, padding='same') self.bn1 = nn.BatchNorm2d(16) self.conv2 = nn.Conv2d(16, 16, 3, padding=1) self.bn2 = nn.BatchNorm2d(16) self.conv3 = nn.Conv2d(16, 32, 3, padding=1) self.bn3 = nn.BatchNorm2d(32) self.conv4 = nn.Conv2d(32, 64, 3, padding=1) self.bn4 = nn.BatchNorm2d(64) self.conv5 = nn.Conv2d(64, 128, 3, padding=1) self.bn5 = nn.BatchNorm2d(128) self.conv6 = nn.Conv2d(128, 128, 3, padding=1) self.bn6 = nn.BatchNorm2d(128) self.conv_t6 = nn.ConvTranspose2d(128, 64, 3, padding=1) self.bn_t6 = nn.BatchNorm2d(64) self.conv_t5 = nn.ConvTranspose2d(64, 32, 3, padding=1) self.bn_t5 = nn.BatchNorm2d(32) self.conv_t4 = nn.ConvTranspose2d(32, 16, 3, padding=1) self.bn_t4 = nn.BatchNorm2d(16) self.conv_t3 = nn.ConvTranspose2d(16, 16, 3, padding=1) self.bn_t3 = nn.BatchNorm2d(16) self.conv_t2 = nn.ConvTranspose2d(16, 8, 3, padding=1) self.bn_t2 = nn.BatchNorm2d(8) self.conv_1 = nn.Conv2d(8, 2, 3, padding='same') self.bn_1 = nn.BatchNorm2d(2) self.tan_h = nn.Tanh() def forward(self, x): x1 = self.tan_h(self.bn1(self.conv1(x))) x2 = self.tan_h(self.bn2(self.conv2(x1)))**2 x3 = self.tan_h(self.bn3(self.conv3(x2)))**2 x4 = self.tan_h(self.bn4(self.conv4(x3)))**2 x5 = self.tan_h(self.bn5(self.conv5(x4)))**2 x6 = self.tan_h(self.bn6(self.conv6(x5)))**2 x_t6 = self.tan_h(self.bn_t6(self.conv_t6(x6)))**2 x_t5 = self.tan_h(self.bn_t5(self.conv_t5(x_t6)))**2 x_t4 = self.tan_h(self.bn_t4(self.conv_t4(x_t5)))**2 x_t3 = self.tan_h(self.bn_t3(self.conv_t3(x_t4))) ** 2 x_t2 = self.tan_h(self.bn_t2(self.conv_t2(x_t3))) ** 2 x_1 = self.tan_h(self.bn_1(self.conv_1(x_t2))) return x_1 # 读取模型 需要提前定义对应的类 model = torch.load("model1.pt") # 定义损失函数和优化器 criterion = nn.MSELoss() optimizer = optim.ASGD(model.parameters(), lr=0.01) 详细说明该神经网络的结构,功能以及为什么要选择这个
05-23
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Tomorrow'sThinker

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值