• VSCODE调试控制台的使用——以pytorch下神经网络的加载模型前向传播为例


    前言

    最近在做模型的量化与量化后模型在arm平台上的迁移工作,为了保证量化后的模型在python下与在arm下运行的一致性(相同输入下,不同环境下的输出完全一致),需要对python下模型前向传播的每个操作的激活值、权重、输入的量化参数(scale与zero_point)、权重的量化参数(scale与zero_point)都能查看,在这个查看的过程中,发现VSCODE帮了大忙,因此记录一下VSCODE调试控制台的使用,便于自己与他人后续参考使用。

    查看加载模型的参数

    我在保存模型时,仅保存了模型的参数信息,而非整个模型的结构信息:

    torch.save(model_object.state_dict(), 'params.pth')  
    
    • 1

    加载模型的参数信息,则需:

    state_dict = model_object.load_state_dict(torch.load('params.pth')) 
    
    • 1

    state_dict为OrderedDict类型,加载后,在左侧的变量栏可以看到state_dict包含的一些key, value
    在这里插入图片描述

    对于模型比较大的,在左侧栏可能看不全state_dict包含的所有key,value,可以通过遍历查看state_dict中的key值

    for k,v in stat_dict.items():
            # if `v` is `torch.Tensor`, then save it into dict: 
            print(k)
    
    • 1
    • 2
    • 3

    查看或修改state_dict中的值

    这里以查看并修改模型中某一层的激活scale值、权重scale值为例
    查看并修改激活scale的值
    在调试控制台,可以通过state_dict的key值对其进行查看,并修改其中的值
    在这里插入图片描述
    查看修改权重scale的值
    在这里插入图片描述
    通过stat_dict['stage3.1.branch2.0.weight']可以查看到stage3.1.branch2.0的权重是一个Tensor,这里是量化后的模型,可以看到tensor中还有scale, zero_point等属性,但是直接使用stat_dict['stage3.1.branch2.0.weight'].scale是无法查看到scale的值的,也就无法通过这种方式对其进行修改。查看与修改应使用stat_dict['stage3.1.branch2.0.weight'].q_per_channel_scales(),如下图:
    在这里插入图片描述

    对模型的前向传播进行跟踪

    现在的网络大都不是简单的只有conv,fc这样的平直的结构,二是有一些其他的基本单元,如,Resnet的残差块,MobileNet中的瓶颈层等等。这种基本单元的构成会使得调试的时候,深入查看某个基本单元的某一层的值有点麻烦。以shufflenetv2的基本单元InvertedResidual为例。branch1为空,或depthwise conv + pointwise conv,branch2为pointwise conv + depthwise conv + pointwise conv。

    class InvertedResidual(nn.Module):
        def __init__(self, inp, oup, stride):
            super(InvertedResidual, self).__init__()
    
            if not (1 <= stride <= 3):
                raise ValueError('illegal stride value')
            self.stride = stride
    
            branch_features = oup // 2
            assert (self.stride != 1) or (inp == branch_features << 1)
    
            if self.stride > 1:
                self.branch1 = nn.Sequential(
                    self.depthwise_conv(inp, inp, kernel_size=3, stride=self.stride, padding=1),
                    nn.BatchNorm2d(inp),
                    nn.Conv2d(inp, branch_features, kernel_size=1, stride=1, padding=0, bias=False),
                    nn.BatchNorm2d(branch_features),
                    nn.ReLU(inplace=True),
                )
            else:
                self.branch1 = nn.Sequential()
    
            self.branch2 = nn.Sequential(
                nn.Conv2d(inp if (self.stride > 1) else branch_features,
                          branch_features, kernel_size=1, stride=1, padding=0, bias=False),
                nn.BatchNorm2d(branch_features),
                nn.ReLU(inplace=True),
                self.depthwise_conv(branch_features, branch_features, kernel_size=3, stride=self.stride, padding=1),
                nn.BatchNorm2d(branch_features),
                nn.Conv2d(branch_features, branch_features, kernel_size=1, stride=1, padding=0, bias=False),
                nn.BatchNorm2d(branch_features),
                nn.ReLU(inplace=True),
            )
    
        @staticmethod
        def depthwise_conv(i, o, kernel_size, stride=1, padding=0, bias=False):
            return nn.Conv2d(i, o, kernel_size, stride, padding, bias=bias, groups=i)
    
        def forward(self, x):
            if self.stride == 1:
                x1, x2 = x.chunk(2, dim=1)
                out = torch.cat((x1, self.branch2(x2)), dim=1)
            else:
                out = torch.cat((self.branch1(x), self.branch2(x)), dim=1)  # 可在此处加断点
    
            out = channel_shuffle(out, 2)
    
            return out
    
    • 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
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    查看基本单元中某一层权重的值

    调试时,在前向传播里添加断点,在InvertedResidual中branch1,branch2是作为一个整体前向传播的,如果想要具体查看内部某一层的执行情况,可以在调试控制台中实现。
    在这里插入图片描述
    上图是branch1和branch2的结构,这里量化的时候CONV与BN进行了融合,因此CONV变成了QuantizedConv2d,BN变为了Identity()。如果想要查看branch1.0.weight则可以在调试控制台中使用self.branch1[0].state_dict()['weight']进行查看
    在这里插入图片描述
    使用self.branch1[0].state_dict()['weight'].int_repr()可以查看该层量化后的权重值
    在这里插入图片描述

    查看基本单元中某一层前向传播的值

    以branch2.4的输出为例,可以通过以下代码得到brach2.4的输出
    在这里插入图片描述
    进一步,可以通过以下代码查看该层量化后的值
    在这里插入图片描述

  • 相关阅读:
    vue多层嵌套子路由不显示
    第九届大唐杯国赛获奖名单
    H5游戏开发-搭建开发环境
    【面试题】 vue高频面试知识点汇总【2022寒冬版】
    Python编写的人工智能美颜系统
    SpringBoot 关于异步与事务一起使用的问题
    Python-入门-列表(四)
    leetcode:1323. 6 和 9 组成的最大数字(python3解法)
    Linux22 --- 网络为什么要分层、使用tcp协议实现两个进程间通信的功能、IP地址转换函数
    技术对接51
  • 原文地址:https://blog.csdn.net/qq_22763299/article/details/126986990