惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - Franky
N
Netflix TechBlog - Medium
宝玉的分享
宝玉的分享
Google DeepMind News
Google DeepMind News
腾讯CDC
G
Google Developers Blog
Martin Fowler
Martin Fowler
Microsoft Security Blog
Microsoft Security Blog
Recent Announcements
Recent Announcements
爱范儿
爱范儿
Engineering at Meta
Engineering at Meta
Microsoft Azure Blog
Microsoft Azure Blog
A
About on SuperTechFans
aimingoo的专栏
aimingoo的专栏
有赞技术团队
有赞技术团队
Jina AI
Jina AI
人人都是产品经理
人人都是产品经理
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
M
MIT News - Artificial intelligence
罗磊的独立博客
博客园 - 三生石上(FineUI控件)
美团技术团队
WordPress大学
WordPress大学
阮一峰的网络日志
阮一峰的网络日志

博客园 - Dsp Tian

MMDiT 骨干网络详解 DiT (Diffusion Transformer) 骨干网络详解 Flow Matching 原理与 MNIST 条件生成实践 Claude Code 自动推送测试 ssh端口转发 【Python】使用uv虚拟环境 解决ModuleNotFoundError: No module named 'pkg_resources' 配置Nginx反向代理 【Python】大模型工具调用 Claude Code配置Qwen3-Coder OpenCode + Oh My OpenCode配置Qwen3-Coder 【Python】vllm部署调用Qwen3-VL make指定安装目录 解决colcon编译卡死 【Python】调用C++ 深度学习(Grad-CAM) 深度学习(CVAE) 深度学习(DBBNet重参数化) 深度学习(视觉注意力SeNet/CbmaNet/SkNet/EcaNet) 深度学习(RepVGG重参数化) 深度学习(修改onnx文件batchsize) 【Python】生成git仓库贡献热力图 深度学习(onnx量化) 深度学习(pytorch量化) cmake构建后执行命令
深度学习(ACNet重参数化)
Dsp Tian · 2025-08-30 · via 博客园 - Dsp Tian
import torch
import torch.nn as nn

class AcNetBlock(nn.Module):
    def __init__(self, channels, deploy):
        super(AcNetBlock, self).__init__()

        self.deploy = deploy
        self.channels = channels

        self.conv3x3 = nn.Conv2d(channels, channels, kernel_size=3, stride=1, padding=1, bias=True)
        self.bn3x3 = nn.BatchNorm2d(channels)

        self.conv3x1 = nn.Conv2d(channels, channels, kernel_size=(3,1), stride=1, padding=(1,0), bias=True)
        self.bn3x1 = nn.BatchNorm2d(channels)

        self.conv1x3 = nn.Conv2d(channels, channels, kernel_size=(1,3), stride=1, padding=(0,1), bias=True)
        self.bn1x3 = nn.BatchNorm2d(channels)

        if deploy == False:
            self.conv3x3.weight.data = torch.randn(channels, channels, 3, 3)
            self.conv3x3.bias.data = torch.randn(channels)
            self.bn3x3.weight.data = torch.randn(channels)
            self.bn3x3.bias.data = torch.randn(channels)

            self.conv3x1.weight.data = torch.randn(channels, channels, 3, 1)
            self.conv3x1.bias.data = torch.randn(channels)
            self.bn3x1.weight.data = torch.randn(channels)
            self.bn3x1.bias.data = torch.randn(channels)

            self.conv1x3.weight.data = torch.randn(channels, channels, 1, 3)
            self.conv1x3.bias.data = torch.randn(channels)
            self.bn1x3.weight.data = torch.randn(channels)
            self.bn1x3.bias.data = torch.randn(channels)

        # Fusion conv
        self.fusion_conv = nn.Conv2d(channels, channels, kernel_size=3, stride=1, padding=1, bias=True)
        self.relu = nn.ReLU(inplace=True)

    def forward(self, x):
        if self.deploy == False:
            x1 = self.conv3x3(x)
            x1 = self.bn3x3(x1)

            x2 = self.conv3x1(x)
            x2 = self.bn3x1(x2)

            x3 = self.conv1x3(x)
            x3 = self.bn1x3(x3)

            x = x1 + x2 + x3
        else:
            x = self.fusion_conv(x)

        return self.relu(x)
    
    def reparam3x3(self):
        conv_w = self.conv3x3.weight
        conv_b = self.conv3x3.bias

        bn_w = self.bn3x3.weight
        bn_b = self.bn3x3.bias 

        bn_w = bn_w.div(torch.sqrt(self.bn3x3.eps + self.bn3x3.running_var))

        fusion_w = torch.mm(torch.diag(bn_w), conv_w.view(self.channels, -1)).view(self.channels,self.channels,3,3)
        fusion_b = bn_w * (conv_b - self.bn3x3.running_mean) + bn_b

        print(fusion_w.shape,fusion_b.shape)
        return fusion_w, fusion_b

    def reparam3x1(self):
        conv_w = self.conv3x1.weight
        conv_b = self.conv3x1.bias

        bn_w = self.bn3x1.weight
        bn_b = self.bn3x1.bias 

        bn_w = bn_w.div(torch.sqrt(self.bn3x1.eps + self.bn3x1.running_var))

        fusion_w = torch.mm(torch.diag(bn_w), conv_w.view(self.channels, -1)).view(self.channels,self.channels,3,1)
        w = torch.zeros(self.channels, self.channels, 3, 3)
        w[:,:,:,1] = fusion_w.squeeze(3)

        fusion_b = bn_w * (conv_b - self.bn3x1.running_mean) + bn_b

        print(w.shape,fusion_b.shape)
        return w, fusion_b

    def reparam1x3(self):
        conv_w = self.conv1x3.weight
        conv_b = self.conv1x3.bias

        bn_w = self.bn1x3.weight
        bn_b = self.bn1x3.bias 

        bn_w = bn_w.div(torch.sqrt(self.bn1x3.eps + self.bn1x3.running_var))

        fusion_w = torch.mm(torch.diag(bn_w), conv_w.view(self.channels, -1)).view(self.channels,self.channels,1,3)
        w = torch.zeros(self.channels, self.channels, 3, 3)
        w[:,:,1,:] = fusion_w.squeeze(2)

        fusion_b = bn_w * (conv_b - self.bn1x3.running_mean) + bn_b

        print(w.shape,fusion_b.shape)
        return w, fusion_b

    def reparam(self):
        w_3x3, b_3x3 = self.reparam3x3()
        w_3x1, b_3x1 = self.reparam3x1()
        w_1x3, b_1x3 = self.reparam1x3()

        self.fusion_conv.weight.data = (w_3x3 + w_3x1 + w_1x3).clone()
        self.fusion_conv.bias.data = (b_3x3 +b_3x1 + b_1x3).clone()   
    

x = torch.randn(1, 20, 224, 224)  

net1 = AcNetBlock(20, False)
torch.save(net1.state_dict(), "acnet.pth")
net1.eval()   
y1 = net1(x)

net2 = AcNetBlock(20, True)
net2.load_state_dict(torch.load("acnet.pth"))
net2.reparam()  
net2.eval()   
y2 = net2(x)

print(y1.shape,y2.shape)
print(torch.allclose(y1, y2, atol=1e-4))

torch.onnx.export(net1, x, "acnet.onnx", input_names=['input'], output_names=['output'])
torch.onnx.export(net2, x, "acnet_deploy.onnx", input_names=['input'], output_names=['output'])