飞桨源码MobileNetV3分类模型对齐Pytorch-省事版

P粉084495128
发布: 2025-07-31 15:15:31
原创
728人浏览过
本文记录了飞桨复现MobileNetV3分类模型的过程。因飞桨框架及相关工具无现成实现,作者基于PaddleClas套件代码修改,将脚本改写成notebook代码。复现中解决了飞桨比PyTorch多一层卷积、SE模块通道数不对齐、下采样与空洞卷积差异等问题,最终实现前向对齐,为相关学习提供参考。

☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepSeek R1 模型☜☜☜

飞桨源码mobilenetv3分类模型对齐pytorch-省事版 - php中文网

飞桨源码MobileNetV3分类模型对齐Pytorch-省事版

在我准备复现的时候,发现整个AIStudio中都没有几个MobileNetV3复现项目,大部分都是PaddleX或者PaddleClas等工具和套件的使用。

缘起

前期在复现RobustVideoMatting模型的时候,发现其骨干网络可以选ResNet50和MobileNetV3,在实现了resNet50骨干后,本想就这么算了,但是总感觉不完美,于是投入到复现MobileNetV3的工作中。

缘中

本想省事,拿现成的代码,但是整个AIStudio中都没有找到现成的代码可以抄(划掉,应该是可以参考),于是尝试了如下几种方案和思路:

  1. 用X2Paddle直接转换Torch代码,很遗憾,这个我用着真不习惯,我想闭着眼睛一键转换成功,可惜没有得逞。
  2. 手工一个API一个API的复现,这真不符合我的人设,实在没这个耐心
  3. 找到Paddle的实现代码,小改一下对齐。这个符合我的人设,最后就是参考PaddleClas里面的MobileNetV3代码,略微改动一下(其实不是)就搞定了。

对了,整个复现过程本着实用原则,是至上而下进行的,哪里没有跟Pytorch对齐就改哪里。步骤和方法不同于科班式的自下而上方式。

缘末

道路是曲折的,未来是光明的。复现过程中间有几个小坑,最终成功复现出来,心里还是有点小激动呢!

仅以此文,留档学习。

撸起袖子,试试复现MobilNetV3

作为一个飞桨人,拿到复现任务的时候,第一反应就是:看看飞桨里有没有现成的!

非常可惜,飞桨官网框架中只有paddle.vision.mobilenet_v2,而没有版本3.

非常可惜+1,X2Paddle中也没有现成的MobilNetV3代码。

非常可惜+2,AIStudio中竟然也没有MobilNetV3的实现项目,大部分是应用项目。

非常幸运飞桨还有庞大的套件库,不出意外的在PaddleClas套件中找到了飞桨版本的MobilNetV3实现,所以我们成功的实现了第一步:

找到一份飞桨代码实现

后面一步就是

在飞桨实现的基础上,修改代码以使模型跟torch版本对齐。

为了调试方便,我们要

将PaddleClas里的脚本代码改写成notebook代码

改写的时候需要把相关的PaddleClas库里的代码都放上来,以便可以在不导入PaddleClas库的时候可以用。

此步是个力气活,就用传说中的Ctrl+C Ctrl+V大法即可。为了项目美观,这里就不贴出长长的代码了,所有代码放在mobilenetv3.py文件中,后面演示的时候调用其中的代码。

测试一下代码是否可以正常执行

In [ ]
import paddlefrom mobilenetv3error import MobileNetV3_large_x1_0
model = MobileNetV3_large_x1_0()
a = paddle.ones([1,3,224,224])
out = model(a)print("out shape", out.shape)print ("test MobileNetV3_large_x1_0 ok")
登录后复制
   

执行测试通过!

这就是从上到下复现的好处,整套程序代码在第一步就能顺利执行!

飞桨和torch模型结构对比

对比飞桨和torch的模型结构,在飞桨和torch执行如下命令,后面就是用眼睛看就行了!

In [ ]
model = MobileNetV3_large_x1_0()
a = paddle.ones([1,3,224,224])
out = model(a)for i in model.state_dict():    print(i,model.state_dict()[i].shape)
登录后复制
   

找不同开始啦!结果一对比就看出不同,第一层一样,第二层就不一样。飞桨比torch多了一层:blocks.0.expand_conv.conv.weight [16, 16, 1, 1],后面几层大约都一样,没问题。

飞桨
blocks.0.expand_conv.conv.weight [16, 16, 1, 1]blocks.0.expand_conv.bn.weight [16]blocks.0.expand_conv.bn.bias [16]blocks.0.expand_conv.bn._mean [16]blocks.0.expand_conv.bn._variance [16]blocks.0.bottleneck_conv.conv.weight [16, 1, 3, 3]blocks.0.bottleneck_conv.bn.weight [16]blocks.0.bottleneck_conv.bn.bias [16]blocks.0.bottleneck_conv.bn._mean [16]blocks.0.bottleneck_conv.bn._variance [16]blocks.0.linear_conv.conv.weight [16, 16, 1, 1]blocks.0.linear_conv.bn.weight [16]blocks.0.linear_conv.bn.bias [16]blocks.0.linear_conv.bn._mean [16]blocks.0.linear_conv.bn._variance [16]torch
features.1.block.0.0.weight torch.Size([16, 1, 3, 3])

features.1.block.0.1.weight torch.Size([16])
features.1.block.0.1.bias torch.Size([16])
features.1.block.0.1.running_mean torch.Size([16])
features.1.block.0.1.running_var torch.Size([16])
features.1.block.0.1.num_batches_tracked torch.Size([])

features.1.block.1.0.weight torch.Size([16, 16, 1, 1])

features.1.block.1.1.weight torch.Size([16])
features.1.block.1.1.bias torch.Size([16])
features.1.block.1.1.running_mean torch.Size([16])
features.1.block.1.1.running_var torch.Size([16])
features.1.block.1.1.num_batches_tracked torch.Size([])
登录后复制
       

既然有不同,就到代码中去找。发现两者的不同,在飞桨class ResidualUnit(TheseusLayer) 中, 第一层x = self.expand_conv(x) 而在torch中,有判断语句

        if cnf.expanded_channels != cnf.input_channels:
            layers.append(ConvBNActivation(cnf.input_channels, cnf.expanded_channels, kernel_size=1,                                           norm_layer=norm_layer, activation_layer=activation_layer))
登录后复制
       

也就是如果输入和输出通道数一样的话,torch就省略掉一层ConvBNActivation层。而飞桨没有省略。

解决的方法就是像torch一样加上判断,另外由于飞桨和torch代码实现不同,飞桨除了在forward中加上判断,还要在初始化中加上判断

        if self.in_c != self.mid_c:
            self.expand_conv = ConvBNLayer(                in_c=in_c,                out_c=mid_c,                filter_size=1,                stride=1,                padding=0,                if_act=True,                act=act)
登录后复制
       

如果不在初始化中加上判断,那么尽管在前向计算的时候没有该层,但是在看模型结构的时候却能看到,容易造成困扰。 因为这个事情看着很灵异,还专门发帖求助了:灵异事件,MobileNetV3模型修改去掉一层结果去不掉

整体结构对齐后,再进行细节对比

飞桨和torch模型shape对比

发现mid_se么有对齐。比如飞桨 Block.3.mid_se.conv1.weight 和torch blocks.4.SqueezeExcitation.Conv2d,飞桨是18, torch是24

blocks.3.mid_se.conv1.weight [18, 72, 1, 1]blocks.3.mid_se.conv1.bias [18]blocks.3.mid_se.conv2.weight [72, 18, 1, 1]blocks.3.mid_se.conv2.bias [72](2): SqueezeExcitation(
          (fc1): Conv2d(72, 24, kernel_size=(1, 1), stride=(1, 1))
          (relu): ReLU(inplace=True)
          (fc2): Conv2d(24, 72, kernel_size=(1, 1), stride=(1, 1))
登录后复制
       

原来是在SEModule这里,torch使用了_make_divisible,保证为8 的倍数。前期看torch代码的时候,也看到了,但是没有注意。 飞桨默认这里没有使用_make_divisible,导致conv shape对不齐。

将SEModule模块里的中间通道用_make_divisible处理成8的倍数,飞桨和torch的shape终于对齐了。

class SEModule(TheseusLayer):
    def __init__(self, channel, reduction=4):
        super().__init__()
        squeeze_channels = _make_divisible(channel // reduction)
登录后复制
       

由于在RobustVideoMatting模型中,mobilenetv3模型的最后几层没有使用,所以没有再花精力去对齐它们。

这样就复现完成了!

其实不是。

在后续RobustVideoMatting模型测试中,发现飞桨和torch的数据处理并没有对齐

飞桨的是:
MobileNetV3LargeEncoder ok ! 输入数据shape:[2, 2, 3, 224, 224] 输出数据长度:4[2, 2, 16, 112, 112][2, 2, 24, 56, 56][2, 2, 40, 28, 28][2, 2, 960, 7, 7]torch的是:
MobileNetV3LargeEncoder ok ! 输入数据shape:torch.Size([2, 2, 3, 224, 224]) 输出数据长度:4torch.Size([2, 2, 16, 112, 112])
torch.Size([2, 2, 24, 56, 56])
torch.Size([2, 2, 40, 28, 28])
torch.Size([2, 2, 960, 14, 14])
登录后复制
       

仔细对比,发现是飞桨的x = self.blocks12 这一步,也进行了下采样 。 看来要去看mobilenetv3的代码,对比一下shape了。

飞桨的12对应toch的13

torch 13 
(13): InvertedResidual(
      (block): Sequential(
        (0): ConvBNActivation(
          (0): Conv2d(112, 672, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (1): BatchNorm2d(672, eps=0.001, momentum=0.01, affine=True, track_running_stats=True)
          (2): Hardswish()
        )
        (1): ConvBNActivation(
          (0): Conv2d(672, 672, kernel_size=(5, 5), stride=(1, 1), padding=(4, 4), dilation=(2, 2), groups=672, bias=False)
          (1): BatchNorm2d(672, eps=0.001, momentum=0.01, affine=True, track_running_stats=True)
          (2): Hardswish()
        )
        (2): SqueezeExcitation(
          (fc1): Conv2d(672, 168, kernel_size=(1, 1), stride=(1, 1))
          (relu): ReLU(inplace=True)
          (fc2): Conv2d(168, 672, kernel_size=(1, 1), stride=(1, 1))
        )
        (3): ConvBNActivation(
          (0): Conv2d(672, 160, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (1): BatchNorm2d(160, eps=0.001, momentum=0.01, affine=True, track_running_stats=True)
          (2): Identity()
        )
      )
    )
    
   飞桨12
       (12): ResidualUnit(
      (expand_conv): ConvBNLayer(
        (conv): Conv2D(112, 672, kernel_size=[1, 1], data_format=NCHW)
        (bn): BatchNorm()
        (act): Hardswish()
      )
      (bottleneck_conv): ConvBNLayer(
        (conv): Conv2D(672, 672, kernel_size=[5, 5], stride=[2, 2], padding=2, groups=672, data_format=NCHW)
        (bn): BatchNorm()
        (act): Hardswish()
      )
      (mid_se): SEModule(
        (avg_pool): AdaptiveAvgPool2D(output_size=1)
        (conv1): Conv2D(672, 168, kernel_size=[1, 1], data_format=NCHW)
        (relu): ReLU()
        (conv2): Conv2D(168, 672, kernel_size=[1, 1], data_format=NCHW)
        (hardsigmoid): Hardsigmoid()
      )
      (linear_conv): ConvBNLayer(
        (conv): Conv2D(672, 160, kernel_size=[1, 1], data_format=NCHW)
        (bn): BatchNorm()
      )
    )
登录后复制
       

也就是飞桨使用了步长为2的卷积,torch使用了步长为1,dilation为2的空洞卷积。

In [ ]
from mobilenetv3error import MobileNetV3LargeEncoderimport paddle
model = MobileNetV3LargeEncoder()
a = paddle.randn([2, 3, 224, 224])# print(a)tmp = model(a)print(f"输入数据shape:{a.shape} 输出数据长度:{len(tmp)} ")for i in tmp :    print(i.shape)
登录后复制
   

针对上面找到的不同卷积,仔细阅读MobileNetV3飞桨代码和torch代码,找不同。发现飞桨的代码中,模型配置为:

飞桨:    "large": [        # k, exp, c, se, act, s
        [3, 16, 16, False, "relu", 1],
        [3, 64, 24, False, "relu", 2],
        [3, 72, 24, False, "relu", 1],
        [5, 72, 40, True, "relu", 2],
        [5, 120, 40, True, "relu", 1],
        [5, 120, 40, True, "relu", 1],
        [3, 240, 80, False, "hardswish", 2],
        [3, 200, 80, False, "hardswish", 1],
        [3, 184, 80, False, "hardswish", 1],
        [3, 184, 80, False, "hardswish", 1],
        [3, 480, 112, True, "hardswish", 1],
        [3, 672, 112, True, "hardswish", 1],
        [5, 672, 160, True, "hardswish", 2],
        [5, 960, 160, True, "hardswish", 1],
        [5, 960, 160, True, "hardswish", 1],
    ],

torch的配置为:
        inverted_residual_setting = [
            bneck_conf(16, 3, 16, 16, False, "RE", 1, 1),
            bneck_conf(16, 3, 64, 24, False, "RE", 2, 1),  # C1
            bneck_conf(24, 3, 72, 24, False, "RE", 1, 1),
            bneck_conf(24, 5, 72, 40, True, "RE", 2, 1),  # C2
            bneck_conf(40, 5, 120, 40, True, "RE", 1, 1),
            bneck_conf(40, 5, 120, 40, True, "RE", 1, 1),
            bneck_conf(40, 3, 240, 80, False, "HS", 2, 1),  # C3
            bneck_conf(80, 3, 200, 80, False, "HS", 1, 1),
            bneck_conf(80, 3, 184, 80, False, "HS", 1, 1),
            bneck_conf(80, 3, 184, 80, False, "HS", 1, 1),
            bneck_conf(80, 3, 480, 112, True, "HS", 1, 1),
            bneck_conf(112, 3, 672, 112, True, "HS", 1, 1),
            bneck_conf(112, 5, 672, 160 // reduce_divider, True, "HS", 2, dilation),  # C4
            bneck_conf(160 // reduce_divider, 5, 960 // reduce_divider, 160 // reduce_divider, True, "HS", 1, dilation),
            bneck_conf(160 // reduce_divider, 5, 960 // reduce_divider, 160 // reduce_divider, True, "HS", 1, dilation),
        ]
登录后复制
       

torch比飞桨的参数多,在倒数第三层,飞桨的步长step值为2,这样导致在conv层多下采样了一次。仔细思考怎样才能像torch那样处理,感觉还是需要像torch那样多一列信息,于是增加dilation空洞卷积参数

文心大模型
文心大模型

百度飞桨-文心大模型 ERNIE 3.0 文本理解与创作

文心大模型56
查看详情 文心大模型
    "large": [        # k, exp, c, se, act, s
        [3, 16, 16, False, "relu", 1, 1],
        [3, 64, 24, False, "relu", 2, 1],
        [3, 72, 24, False, "relu", 1, 1],
        [5, 72, 40, True, "relu", 2, 1],
        [5, 120, 40, True, "relu", 1, 1],
        [5, 120, 40, True, "relu", 1, 1],
        [3, 240, 80, False, "hardswish", 2, 1],
        [3, 200, 80, False, "hardswish", 1, 1],
        [3, 184, 80, False, "hardswish", 1, 1],
        [3, 184, 80, False, "hardswish", 1, 1],
        [3, 480, 112, True, "hardswish", 1, 1],
        [3, 672, 112, True, "hardswish", 1, 1],
        [5, 672, 160, True, "hardswish", 2, 2,],
        [5, 960, 160, True, "hardswish", 1, 2,],
        [5, 960, 160, True, "hardswish", 1, 2,],
    ],
登录后复制
       

并修改代码,添加dilation的识别:

原代码:
self.blocks = nn.Sequential(* [
            ResidualUnit(                in_c=_make_divisible(self.inplanes * self.scale if i == 0 else self.cfg[i - 1][2] * self.scale),                mid_c=_make_divisible(self.scale * exp),                out_c=_make_divisible(self.scale * c),                filter_size=k,                stride=s,                use_se=se,                act=act) for i, (k, exp, c, se, act, s) in enumerate(self.cfg)
        ])

修改为:
self.blocks = nn.Sequential(* [
            ResidualUnit(                in_c=_make_divisible(self.inplanes * self.scale if i == 0 else self.cfg[i - 1][2] * self.scale),                mid_c=_make_divisible(self.scale * exp),                out_c=_make_divisible(self.scale * c),                filter_size=k,                stride=s,                use_se=se,                act=act,                dilation=d) for i, (k, exp, c, se, act, s, d) in enumerate(self.cfg)
        ])
登录后复制
       

修改完成之后,再看看输出shape,终于跟torch对齐了。同时也解决了其它空洞卷积的对齐问题。

In [ ]
from mobilenetv3 import MobileNetV3LargeEncoderimport paddle
model = MobileNetV3LargeEncoder()
a = paddle.randn([2, 3, 224, 224])
tmp = model(a)print(f"输入数据shape:{a.shape} 输出数据长度:{len(tmp)} ")for i in tmp :    print(i.shape)
登录后复制
   

当然现在还没有严格的测试训练对齐,因为项目中只需要前向对齐,所以复现工作到了这里就告一段落了。 最后看一下模型详细信息,跟torch进行最后一次比对。

In [ ]
import numpy as np
a = paddle.randn([1, 3, 224, 224])
paddle.summary(model, input=a)for i in model.state_dict():    print(i,model.state_dict()[i].shape)
登录后复制
   

总结

本次复现MobileNetV3,其实算是抄了一遍,然后再根据torch的输出shape等信息,对代码进行微调对齐。

复现方法为自上而下,直接找到最类似的飞桨代码,然后修改。提前不用看论文,不过在修改调试过程中,自然而然的就会开始探索MobileNetV3的模型结构,在修改代码中学习构建模型的知识。

整个过程波澜起伏,好几次都想放弃,幸运的是没有放弃,才终于有了这个记录过程的项目。

因为这个项目是为RobustVideoMatting模型服务的,所以在测试和调试中,会关联使用一部分RobustVideoMatting的代码,比如后面调试中碰到的MobileNetV3LargeEncoder部分。

与其它复现不同的是,此复现没有修改网络各层的名字,这样导致最终模型参数存盘文件的key值,飞桨和torch是不一样的。解决的方法是按照顺序写入,而不是按照名字写入。这样对模型参数的存储顺序也是有要求的,所以才会碰到大家普通复现不会碰到的一些问题,比如模型初始化的顺序问题。

In [ ]
<br/>
登录后复制
   

调试信息

看看torch的相关代码,然后进行复现处理 。

from torchvision.models.mobilenetv3 import MobileNetV3, InvertedResidualConfig

torch的mobilenet代码:https://github.com/pytorch/vision/blob/main/torchvision/models/mobilenetv3.py 首先尝试了x2paddle转换,转换失败后用飞桨PaddleClas里面找到的mobilenetv3的代码进行对齐。

飞桨比torch多了一层conv

发现模型第二层,飞桨比torch多了一层1*1conv 。查找两者代码的差别。这里碰到了较多的问题。

看代码,我就没明白为什么飞桨比torch多了一层conv

关键torch代码里反应不出来啊,按照代码应该跟飞桨一样才对啊,那层被吃了? torch咋会少了一层1*1 conv卷积呢? 在没明白torch为啥少了一层conv 卷积的情况下,想使用del语句删除那一层,结果失败

  • 首先想用del 命令直接删掉这层卷积,但是在试了命令之后,发现对主层很容易删除,但是子层没法删除。

仔细看torch的源代码,发现

找到少一层conv的原因了,是这句导致的:

        # expand
        if cnf.expanded_channels != cnf.input_channels:
            layers.append(
                ConvNormActivation(                    cnf.input_channels,                    cnf.expanded_channels,
                    kernel_size=1,
                    norm_layer=norm_layer,
                    activation_layer=activation_layer,
                )
            )
登录后复制
       

如果输入通道数和中间层通道数一样,则可以省略掉一层3*3conv层。 针对这句,在飞桨语句里加入了这样一句:

        if self.in_c != self.mid_c:
#             print("加入一个conv")
            x = self.expand_conv(x)
登录后复制
       

结果发现加入这句还是没有去掉3*3conv层。而且能看到打印语句证明其确实生效了。 原来是因为初始化里有这一层的缘故,具体解决方法放到下面的debug里面了。

飞桨和torch模型参数shape没对齐

发现是mid_se么有对齐。比如飞桨 Block.3.mid_se.conv1.weight 和torch blocks.4.SqueezeExcitation.Conv2d

blocks.3.mid_se.conv1.weight [18, 72, 1, 1]blocks.3.mid_se.conv1.bias [18]blocks.3.mid_se.conv2.weight [72, 18, 1, 1]blocks.3.mid_se.conv2.bias [72](2): SqueezeExcitation(
          (fc1): Conv2d(72, 24, kernel_size=(1, 1), stride=(1, 1))
          (relu): ReLU(inplace=True)
          (fc2): Conv2d(24, 72, kernel_size=(1, 1), stride=(1, 1))
登录后复制
       

原来是在SEModule这里,torch使用了_make_divisible,保证为8 的倍数。前期看torch代码的时候,也看到了,但是没有注意。 飞桨默认这里没有使用_make_divisible,导致conv对不齐。

class SEModule(TheseusLayer):
    def __init__(self, channel, reduction=4):
        super().__init__()
        squeeze_channels = _make_divisible(channel // reduction)
登录后复制
   

灵异事件出现了

上午调试好,省略掉一层3 *3卷积层, 下午再重新进入,首先上午的修改内容荡然无存(在缓存页面的时候,特别容易出现用老内容冲掉新内容的情况,所以一定要养成用完关闭页面的好习惯。),其次按照上午的思路,竟然没法去掉那层3 *3卷积,那句代码用print语句看已经生效了,但是就是没有去掉卷积。

自己看代码torch里面是根据输入和中间层的通道数不一样 ,判断是否写这一层卷积。 最终手写判断语句,但是那一层3*3 conv卷积就是去不掉。

太灵异了。

灵异事件解决

发了求助贴:https://aistudio.baidu.com/paddle/forum/topic/show/993808 后来是自己发现:在__init__里面内容和顺序影响最终的顺序,天啊,跟预想的不一样啊。

最终就是在__init__和forward里面都用了判断语句,终于把模型跟torch对齐了。

当然最后几层没有再去对齐,因为在需要使用的地方没有用到最后几层。

后续MobileNetV3LargeEncoder中normalize报错

---> 12         x = normalize(x, [0.485, 0.456, 0.406], [0.229, 0.224, 0.225])     13         x = self.conv(x)     14         x = self.blocks[0](x)

/opt/conda/lib/python3.6/site-packages/paddle/vision/transforms/functional.py in normalize(img, mean, std, data_format, to_rgb)    670 
    671     if _is_tensor_image(img):
--> 672         return F_t.normalize(img, mean, std, data_format)    673     else:    674         if _is_pil_image(img):

/opt/conda/lib/python3.6/site-packages/paddle/vision/transforms/functional_tensor.py in normalize(img, mean, std, data_format)    101 
    102     """
--> 103     _assert_image_tensor(img, data_format)    104 
    105     mean = paddle.to_tensor(mean, place=img.place)

/opt/conda/lib/python3.6/site-packages/paddle/vision/transforms/functional_tensor.py in _assert_image_tensor(img, data_format)     33         raise RuntimeError(     34             'not support [type={}, ndim={}, data_format={}] paddle image'.
---> 35             format(type(img), img.ndim, data_format))     36 
     37 RuntimeError: not support [type=<class 'paddle.Tensor'>, ndim=4, data_format=CHW] paddle image
登录后复制
       

不知是否维度太大导致的,把测试数据由4D降低到3D,报错

ValueError: (InvalidArgument) The input of Op(Conv) should be a 4-D or 5-D Tensor. But received: input's dimension is 3, input's shape is [3, 244, 244].
  [Hint: Expected in_dims.size() == 4 || in_dims.size() == 5 == true, but received in_dims.size() == 4 || in_dims.size() == 5:0 != true:1.] (at /paddle/paddle/fluid/operators/conv_op.cc:74)
  [operator < conv2d > error]
登录后复制
       

于是查找normalize的手册,先暂时修改成: 传入numpy数据,并在normalize之后,转换成tensor类型。

后来碰到flatten报错,也是这个问题

<ipython-input-77-9920eb18ba6c> in forward_time_series(self, x)
     49     def forward_time_series(self, x):     50         B, T = x.shape[:2]---> 51         features = self.forward_single_frame(x.flatten(0, 1))     52 #         features = [f.unflatten(0, (B, T)) for f in features]
     53 #         # features = [f.unflatten(0, (B, T)) for f in features]TypeError: flatten() takes at most 1 argument (2 given)
登录后复制
       

参数用列表,顺便把unflatten也改正。 不对,这里跟以前resnet一样啊,怎么就报错呢?

看来是normalize的问题,只好在内部,普通处理的时候用tensor,normalize的时候用numpy ,先能运行下去。

最终的解决方法,是根据定义,重新写了normalize函数

def normalize(x, mean, std):    mean = paddle.to_tensor(mean).reshape((-1, 1, 1))    std = paddle.to_tensor(std).reshape((-1, 1, 1))
    out = (x -mean)/std
    return out
登录后复制
       

报错name 'MobileNetV3LargeEncoder' is not defined

在测试model = MattingNetwork('mobilenetv3') 时报错:

---> 26             self.backbone = MobileNetV3LargeEncoder(pretrained_backbone)     27             self.aspp = LRASPP(960, 128)     28             self.decoder = RecurrentDecoder([16, 24, 40, 128], [80, 40, 32, 16])

NameError: name 'MobileNetV3LargeEncoder' is not defined
登录后复制
       

很难理解,已经定义了啊!

在这里也有这个报错

Traceback (most recent call last):  File "model.py", line 8, in <module>    from .mobilenetv3 import MobileNetV3LargeEncoderModuleNotFoundError: No module named '__main__.mobilenetv3'; '__main__' is not a packageroot@e7cf009009d45011ec08bb20f3128d160513-task1-0:/code/paddlerobustvideomatting/model#
登录后复制
       

不明白为什么调不进去。resnet就没问题。

是不是应该用from model.mobilenetv3 import MobileNetV3LargeEncoder ?大概是

对比mobilenetv3largeencoder 发现shape没有对齐

飞桨的是:
MobileNetV3LargeEncoder ok ! 输入数据shape:[2, 2, 3, 224, 224] 输出数据长度:4[2, 2, 16, 112, 112][2, 2, 24, 56, 56][2, 2, 40, 28, 28][2, 2, 960, 7, 7]torch的是:
MobileNetV3LargeEncoder ok ! 输入数据shape:torch.Size([2, 2, 3, 224, 224]) 输出数据长度:4torch.Size([2, 2, 16, 112, 112])
torch.Size([2, 2, 24, 56, 56])
torch.Size([2, 2, 40, 28, 28])
torch.Size([2, 2, 960, 14, 14])
登录后复制
       

仔细对比,发现是飞桨的x = self.blocks12 这一步,也进行了下采样 。 看来要去看mobilenetv3的代码,对比一下shape了。

飞桨的12对应toch的13

torch 13 
(13): InvertedResidual(
      (block): Sequential(
        (0): ConvBNActivation(
          (0): Conv2d(112, 672, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (1): BatchNorm2d(672, eps=0.001, momentum=0.01, affine=True, track_running_stats=True)
          (2): Hardswish()
        )
        (1): ConvBNActivation(
          (0): Conv2d(672, 672, kernel_size=(5, 5), stride=(1, 1), padding=(4, 4), dilation=(2, 2), groups=672, bias=False)
          (1): BatchNorm2d(672, eps=0.001, momentum=0.01, affine=True, track_running_stats=True)
          (2): Hardswish()
        )
        (2): SqueezeExcitation(
          (fc1): Conv2d(672, 168, kernel_size=(1, 1), stride=(1, 1))
          (relu): ReLU(inplace=True)
          (fc2): Conv2d(168, 672, kernel_size=(1, 1), stride=(1, 1))
        )
        (3): ConvBNActivation(
          (0): Conv2d(672, 160, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (1): BatchNorm2d(160, eps=0.001, momentum=0.01, affine=True, track_running_stats=True)
          (2): Identity()
        )
      )
    )
    
   飞桨12
       (12): ResidualUnit(
      (expand_conv): ConvBNLayer(
        (conv): Conv2D(112, 672, kernel_size=[1, 1], data_format=NCHW)
        (bn): BatchNorm()
        (act): Hardswish()
      )
      (bottleneck_conv): ConvBNLayer(
        (conv): Conv2D(672, 672, kernel_size=[5, 5], stride=[2, 2], padding=2, groups=672, data_format=NCHW)
        (bn): BatchNorm()
        (act): Hardswish()
      )
      (mid_se): SEModule(
        (avg_pool): AdaptiveAvgPool2D(output_size=1)
        (conv1): Conv2D(672, 168, kernel_size=[1, 1], data_format=NCHW)
        (relu): ReLU()
        (conv2): Conv2D(168, 672, kernel_size=[1, 1], data_format=NCHW)
        (hardsigmoid): Hardsigmoid()
      )
      (linear_conv): ConvBNLayer(
        (conv): Conv2D(672, 160, kernel_size=[1, 1], data_format=NCHW)
        (bn): BatchNorm()
      )
    )
登录后复制
   

以上就是飞桨源码MobileNetV3分类模型对齐Pytorch-省事版的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习
PHP中文网抖音号
发现有趣的

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号