pytorch中BatchNorm的在eval模型下的使用与验证

结论:

1. 前提在train模式下,nn.BatchNormxd中的参数track_running_stats必须为True, 这样才能保存训练时的统计参数: running_mean,running_var; 以及训练时的学习参数: weight,bias(其中weight,bias是缩放系数,在训练时weigth和bias是可训练的参数) 。

2. nn.BatchNormxd在eval模式下计算过程如下:

  • 使用在训练模式下保存的统计参数running_mean,running_var作为mean和var
  • 使用在训练模型下保存的学习参数weight,bias
  • 计算公式(MyBatchnorm1d的forward方法)

    其中: \large \mu_B是running_mean, \large \sigma_B^2是running_var, \large \gamma是weight, \large \beta是bias 

     代码笔记:

    1. 给模型添加可训练参数的方法:用torch.nn.Parameter()定义的参数是可训练的参数

    self.weight = torch.nn.Parameter(torch.ones(num_features).float())
    self.bias = torch.nn.Parameter(torch.zeros(num_features).float())

    2. 给模型添加不可训练的参数的方法:

            #register_buffer相当于requires_grad=False的Parameter,所以两种方法都可以
            #方法一:
            self.register_buffer('running_mean',torch.zeros(num_features))
            self.register_buffer('running_var',torch.zeros(num_features))
            self.register_buffer('num_batches_tracked',torch.tensor(0))
            #方法二:
            # self.running_mean = torch.nn.Parameter(torch.zeros(num_features),requires_grad=False)
            # self.running_var = torch.nn.Parameter(torch.ones(num_features),requires_grad=False)
            # self.num_batches_tracked = torch.nn.Parameter(torch.tensor(0),requires_grad=False)

    3. 模型参数名称和个数相同的情况下,两个模型可以参数互加载(比如加载预训练模型的参数)

    4. 模型参数名称和个数不一致的情况下,也可以加载,但需要手动进行参数对应

    参考:https://blog.csdn.net/FY_2018/article/details/119961226

第一步:模型训练,并保存模型参数

定义模型SimpleModel进行训练,训练是一个简单的训练,主要是为保存参数,便于后面的验证

# -*- coding: utf-8 -*-
import torch
from torch.autograd import Variable
import torch.optim as optim

batchsize = 16
rawdata = torch.randn([64,3])
y = Variable(torch.FloatTensor([4,5]))
dataset = [Variable(rawdata[curpos:curpos+batchsize]) for curpos in range(0,len(rawdata),batchsize)]

class SimpleModel(torch.nn.Module):
    def __init__(self):
        super(SimpleModel,self).__init__()
        linear1_features = 5
        self.linear1 = torch.nn.Linear(3,linear1_features)
        self.relu = torch.nn.ReLU()
        self.linear2 = torch.nn.Linear(linear1_features,2)
        #设计时batchnorm放在linear1后面,所以这里用linear1的输出维度
        self.batch_norm = torch.nn.BatchNorm1d(linear1_features)  #标准库中的Barchnorm,track_running_stats默认为True
        # self.batch_norm = torch.nn.BatchNorm1d(linear1_features,track_running_stats=True)  #标准库中的Barchnorm

    def forward(self,x):
        x = self.linear1(x)
        x = self.relu(x)
        x = self.batch_norm(x)
        x = self.linear2(x)
        return x


model = SimpleModel()
print(list(model.parameters()))
#查看模型的初始参数
print(model.state_dict().keys())
# for i, j in model.named_parameters():
for i, j in model.state_dict().items():
    print('++++',i)
    print('\t',j)

train_demo = 1
if train_demo == 1:
    loss_fn = torch.nn.MSELoss(size_average=False)
    optimizer = optim.SGD(model.parameters(),lr=0.001,momentum=0.9)
    model.train()
    for t,x in enumerate(dataset):
        y_pred = model(x)
        loss = loss_fn(y_pred,y)
        print(t,loss.data)
        model.zero_grad()
        loss.backward()
        optimizer.step()

    #查看训练后的模型参数
    print('##################The trained Model parameters###############')
    print(model.state_dict().keys())
    # for i, j in model.named_parameters():
    for i,j in model.state_dict().items():
        print('++++',i)
        print('\t',j)
    #保存模型参数
    state = {'model':model.state_dict()}
    torch.save(state,'test_batchnorm.pth')

第二步:自定义模型,按自己的理解定义BatchNorm

1. 按自己理解的Batchnorm计算过程定义MyBatchnorm1d

2. 创建模型DebugSimpleModel,与SimpleModel的唯一区别就是batchnorm用的是自定义的MyBatchnorm1d

3. 加载预训练模型SimpleModel的参数到DebugSimpleModel中

4. 验证相同的输入是否具有相同的输出,如果输出相同,那么说明自定义的batchnorm是正确的(与标准库的batchnorm算法一样)

'''
自定义Batchnorm,目的是通过自己的理解来验证标准Batchnorm在eval模式下batchnorm的计算过程.
结论:如果自定义模型的输出与标准模型的输出结果一样,就可以证明自己的理解是对的,自定义的Batchnorm与标准模型的算法一样

前提在train模式下,nn.BatchNorm中的参数track_running_stats必须为True,
这样才能保存训练时的统计参数:
    running_mean,running_var;
以及训练时的学习参数:
    weight,bias(其中weight,bias是缩放系数,在训练时weigth和bias是可训练的参数)
    
Batchnorm在eval模式下batchnorm的计算过程如下:
1. 使用在训练模式下保存的统计参数running_mean,running_var作为mean和var
2. 使用在训练模型下保存的学习参数weight,bias
3. 计算公式就是MyBatchnorm1d的forward方法
'''

class MyBatchnorm1d(torch.nn.Module):
    def __init__(self,num_features):
        super(MyBatchnorm1d,self).__init__()
        self.weight = torch.nn.Parameter(torch.ones(num_features).float())
        self.bias = torch.nn.Parameter(torch.zeros(num_features).float())
        #register_buffer相当于requires_grad=False的Parameter,所以两种方法都可以
        #方法一:
        self.register_buffer('running_mean',torch.zeros(num_features))
        self.register_buffer('running_var',torch.zeros(num_features))
        self.register_buffer('num_batches_tracked',torch.tensor(0))
        #方法二:
        # self.running_mean = torch.nn.Parameter(torch.zeros(num_features),requires_grad=False)
        # self.running_var = torch.nn.Parameter(torch.ones(num_features),requires_grad=False)
        # self.num_batches_tracked = torch.nn.Parameter(torch.tensor(0),requires_grad=False)

    def forward(self,x):
        eps = 1e-5
        x_normalized = (x - self.running_mean) / torch.sqrt(self.running_var + eps)
        results = self.weight * x_normalized + self.bias
        return results

class DebugSimpleModel(torch.nn.Module):
    def __init__(self):
        super(DebugSimpleModel,self).__init__()
        linear1_features = 5
        self.linear1 = torch.nn.Linear(3,linear1_features)
        self.relu = torch.nn.ReLU()
        self.linear2 = torch.nn.Linear(linear1_features,2)
        self.batch_norm = MyBatchnorm1d(linear1_features)  #使用自定义的Batchnorm

    def forward(self,x):
        x = self.linear1(x)
        x = self.relu(x)
        x = self.batch_norm(x)
        x = self.linear2(x)
        return x

'''
#自定义模型实例化
实验方法:
1. 加载预训练模型的参数作为自定义模型的参数
2. 在eval模式下输出模型的结果与标准模型进行对比
'''


debug_demo = 1
if debug_demo == 1:
    debug_model = DebugSimpleModel()
    #因为自定义的模型参数与标准模型的参数完全一样,所以把标准模型作为预训练的模型(即可以加载标准模型的训练后的参数作为自己的参数)
    debug_model.load_state_dict(torch.load('test_batchnorm.pth')['model'])
    debug_model.eval()

    #查看加载的参数情况是否正确(与标准模型训练后的参数保持一致)
    # print(debug_model.state_dict().keys())
    # # for i, j in test_model.named_parameters():
    # for i,j in debug_model.state_dict().items():
    #     print('++++',i)
    #     print('\t',j)
    print('++++++++++++ Mymodel Output ++++++++++++++')
    for t,x in enumerate(dataset):
        y_pred = debug_model(x)
        print(y_pred)

'''
标准模型的实例化与输出结果,与自定义模型的输出进行对比,看是否一致。
如果一致说明自定义模型的batchnorm算法是正确的,与标准库的Batchnorm算法一样
'''
test_demo = 1
if test_demo == 1:
    test_model = SimpleModel()
    test_model.load_state_dict(torch.load('test_batchnorm.pth')['model'])
    test_model.eval()
    print('\n++++++++++  Norm output  ++++++++++++++++')
    for t,x in enumerate(dataset):
        y_pred = test_model(x)
        print(y_pred)

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是一个使用PyTorch实现的简单的UNet模型。 首先,需要安装PyTorch库。可以使用以下命令进行安装: ``` pip install torch ``` 然后,我们可以定义一个UNet模型。 ```python import torch import torch.nn as nn class DoubleConv(nn.Module): def __init__(self, in_channels, out_channels): super(DoubleConv, self).__init__() self.conv = nn.Sequential( nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1), nn.BatchNorm2d(out_channels), nn.ReLU(inplace=True), nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=1), nn.BatchNorm2d(out_channels), nn.ReLU(inplace=True) ) def forward(self, x): return self.conv(x) class Up(nn.Module): def __init__(self, in_channels, out_channels): super(Up, self).__init__() self.up = nn.ConvTranspose2d(in_channels, in_channels // 2, kernel_size=2, stride=2) self.conv = DoubleConv(in_channels, out_channels) def forward(self, x1, x2): x1 = self.up(x1) diffX = x2.size()[2] - x1.size()[2] diffY = x2.size()[3] - x1.size()[3] x1 = nn.functional.pad(x1, (diffX // 2, diffX - diffX // 2, diffY // 2, diffY - diffY // 2)) x = torch.cat([x2, x1], dim=1) return self.conv(x) class UNet(nn.Module): def __init__(self, n_channels, n_classes): super(UNet, self).__init__() self.inc = DoubleConv(n_channels, 64) self.down1 = nn.Sequential(nn.MaxPool2d(2), DoubleConv(64, 128)) self.down2 = nn.Sequential(nn.MaxPool2d(2), DoubleConv(128, 256)) self.down3 = nn.Sequential(nn.MaxPool2d(2), DoubleConv(256, 512)) self.down4 = nn.Sequential(nn.MaxPool2d(2), DoubleConv(512, 1024)) self.up1 = Up(1024, 512) self.up2 = Up(512, 256) self.up3 = Up(256, 128) self.up4 = Up(128, 64) self.outc = nn.Conv2d(64, n_classes, kernel_size=1) def forward(self, x): x1 = self.inc(x) x2 = self.down1(x1) x3 = self.down2(x2) x4 = self.down3(x3) x5 = self.down4(x4) x = self.up1(x5, x4) x = self.up2(x, x3) x = self.up3(x, x2) x = self.up4(x, x1) return self.outc(x) ``` 在这个模型,我们首先定义了一个双卷积层(DoubleConv)和一个上采样层(Up),然后将它们组合起来构建了一个UNet模型。UNet模型用于图像分割,将输入图像分割成多个部分,每个部分都对应着一个特定的标签。UNet模型的结构类似于自编码器,由一个下采样器和一个上采样器组成。下采样器用于提取特征,上采样器用于将特征图恢复到原始图像大小,并将特征图与下采样器对应的特征图进行特征融合。 接下来,我们可以定义一个函数来训练这个模型。 ```python def train(model, train_loader, val_loader, criterion, optimizer, n_epochs=10): for epoch in range(n_epochs): train_loss = 0 val_loss = 0 model.train() for batch_idx, (data, target) in enumerate(train_loader): optimizer.zero_grad() output = model(data) loss = criterion(output, target) loss.backward() optimizer.step() train_loss += loss.item() model.eval() with torch.no_grad(): for batch_idx, (data, target) in enumerate(val_loader): output = model(data) loss = criterion(output, target) val_loss += loss.item() train_loss /= len(train_loader.dataset) val_loss /= len(val_loader.dataset) print('Epoch: {} Train Loss: {:.6f} Val Loss: {:.6f}'.format( epoch + 1, train_loss, val_loss)) ``` 在训练函数,我们首先循环训练数据集,计算损失并更新模型参数。然后我们循环验证数据集,计算损失并输出训练和验证损失。 接下来,我们可以定义一个函数来测试这个模型。 ```python def test(model, test_loader): model.eval() test_loss = 0 correct = 0 with torch.no_grad(): for data, target in test_loader: output = model(data) test_loss += criterion(output, target).item() pred = output.argmax(dim=1, keepdim=True) correct += pred.eq(target.view_as(pred)).sum().item() test_loss /= len(test_loader.dataset) print('Test Loss: {:.6f} Test Accuracy: {}/{} ({:.0f}%)\n'.format( test_loss, correct, len(test_loader.dataset), 100. * correct / len(test_loader.dataset))) ``` 在测试函数,我们首先将模型设置为评估模式,然后循环测试数据集,计算损失并输出测试精度。 最后,我们可以定义一个函数来进行训练和测试的循环。 ```python def train_and_test(model, train_loader, val_loader, test_loader, criterion, optimizer, n_epochs=10): for epoch in range(n_epochs): train(model, train_loader, val_loader, criterion, optimizer, n_epochs) test(model, test_loader) model = UNet(n_channels=3, n_classes=2) optimizer = torch.optim.Adam(model.parameters(), lr=0.001) criterion = nn.CrossEntropyLoss() train_and_test(model, train_loader, val_loader, test_loader, criterion, optimizer, n_epochs=10) ``` 在这个函数,我们首先定义了一些超参数,包括训练轮数、优化器和损失函数。然后我们循环训练和测试模型,并在每个epoch结束后输出测试结果。 这就是一个简单的基于PyTorch的UNet模型。当然,这里只是给出了一个简单的实现,还可以进行更多的优化和改进,例如使用更复杂的模型使用预训练模型等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值