轻量级超分网络:Edge-oriented Convolution Block for Real-timeMM21_ECBSR 和 eSR

本文主要是介绍轻量级超分网络:Edge-oriented Convolution Block for Real-timeMM21_ECBSR 和 eSR,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • ECBSR(Edge-oriented Convolution Block for Real-timeMM21_ECBSR)
    • 1. 作者目的是开发一个高效的适合移动端的超分网络。
    • 2. 作者决定使用plain net ,但是效果不好,因此利用重参数化方法,丰富特征表示。
    • 3. re-parameterization for efficient inference
    • 4. 结果
  • edge-SR
    • 1.转置卷积上采样 和 pixel shuffle的区别
    • 2.pooling or downsample 可能有aliasing artifacts
    • 3.单层网络eSR-MAX
    • 4.eSR-TM, eSR-TR, eSR-CNN

ECBSR(Edge-oriented Convolution Block for Real-timeMM21_ECBSR)

1. 作者目的是开发一个高效的适合移动端的超分网络。

多分支结构,以及dense connections 可以丰富特征提取和表示, 虽然不会引入太多 FLOPs, 但是会牺牲并行化速度,以及受到DDR 低带宽的影响。

另外一些 delite conv等其他卷积方法也有被提出来提高 网络性能,但是在GPU,NPU上可能没有被很好的优化。

因此作者计划 使用平坦 的网络结构 和 常规的卷积方法。

2. 作者决定使用plain net ,但是效果不好,因此利用重参数化方法,丰富特征表示。

主要结构如下图所示,

  1. 一个单独的conv-3x3

  2. conv-1x1 + conv-3x3: expanding-and-squeezing

  3. conv-1x1 + sobelx

  4. conv-1x1 + sobely(图中和代码不一致)

    在这里插入图片描述

  5. conv-1x1 + laplasian 显示提取图像的边缘特征

    在这里插入图片描述

训练的时候网络右五个分支组成,在inference的时候可以利用re-parameteize技术合并为一个conv-3x3,这样推理的速度和效率都得到提高,精度基本上没有损失。

在这里插入图片描述

3. re-parameterization for efficient inference

整体网络结构:ecb模块 和 一个pixel shuffle

## parameters for ecbsr
scale: 2
colors: 1
m_ecbsr: 4
c_ecbsr: 16
idt_ecbsr: 0
act_type: 'prelu'
pretrain: null1 + 4 个 conv
1 个 pixel shuffle
class ECBSR(nn.Module):def __init__(self, module_nums, channel_nums, with_idt, act_type, scale, colors):super(ECBSR, self).__init__()self.module_nums = module_numsself.channel_nums = channel_numsself.scale = scaleself.colors = colorsself.with_idt = with_idtself.act_type = act_typeself.backbone = Noneself.upsampler = Nonebackbone = []backbone += [ECB(self.colors, self.channel_nums, depth_multiplier=2.0, act_type=self.act_type, with_idt = self.with_idt)]for i in range(self.module_nums):backbone += [ECB(self.channel_nums, self.channel_nums, depth_multiplier=2.0, act_type=self.act_type, with_idt = self.with_idt)]backbone += [ECB(self.channel_nums, self.colors*self.scale*self.scale, depth_multiplier=2.0, act_type='linear', with_idt = self.with_idt)]self.backbone = nn.Sequential(*backbone)self.upsampler = nn.PixelShuffle(self.scale)def forward(self, x):y = self.backbone(x) + xy = self.upsampler(y)return y

ecb模块:包括五个卷积分支的定义

class ECB(nn.Module):def __init__(self, inp_planes, out_planes, depth_multiplier, act_type='prelu', with_idt = False):super(ECB, self).__init__()self.depth_multiplier = depth_multiplierself.inp_planes = inp_planesself.out_planes = out_planesself.act_type = act_typeif with_idt and (self.inp_planes == self.out_planes):self.with_idt = Trueelse:self.with_idt = Falseself.conv3x3 = torch.nn.Conv2d(self.inp_planes, self.out_planes, kernel_size=3, padding=1)self.conv1x1_3x3 = SeqConv3x3('conv1x1-conv3x3', self.inp_planes, self.out_planes, self.depth_multiplier)self.conv1x1_sbx = SeqConv3x3('conv1x1-sobelx', self.inp_planes, self.out_planes, -1)self.conv1x1_sby = SeqConv3x3('conv1x1-sobely', self.inp_planes, self.out_planes, -1)self.conv1x1_lpl = SeqConv3x3('conv1x1-laplacian', self.inp_planes, self.out_planes, -1)if self.act_type == 'prelu':self.act = nn.PReLU(num_parameters=self.out_planes)elif self.act_type == 'relu':self.act = nn.ReLU(inplace=True)elif self.act_type == 'rrelu':self.act = nn.RReLU(lower=-0.05, upper=0.05)elif self.act_type == 'softplus':self.act = nn.Softplus()elif self.act_type == 'linear':passelse:raise ValueError('The type of activation if not support!')def forward(self, x):if self.training:y = self.conv3x3(x) + \self.conv1x1_3x3(x) + \self.conv1x1_sbx(x) + \self.conv1x1_sby(x) + \self.conv1x1_lpl(x)if self.with_idt:y += xelse:RK, RB = self.rep_params()y = F.conv2d(input=x, weight=RK, bias=RB, stride=1, padding=1) if self.act_type != 'linear':y = self.act(y)return ydef rep_params(self):K0, B0 = self.conv3x3.weight, self.conv3x3.biasK1, B1 = self.conv1x1_3x3.rep_params()K2, B2 = self.conv1x1_sbx.rep_params()K3, B3 = self.conv1x1_sby.rep_params()K4, B4 = self.conv1x1_lpl.rep_params()RK, RB = (K0+K1+K2+K3+K4), (B0+B1+B2+B3+B4)if self.with_idt:device = RK.get_device()if device < 0:device = NoneK_idt = torch.zeros(self.out_planes, self.out_planes, 3, 3, device=device)for i in range(self.out_planes):K_idt[i, i, 1, 1] = 1.0B_idt = 0.0RK, RB = RK + K_idt, RB + B_idtreturn RK, RB

关于重参数化具体实现

class SeqConv3x3(nn.Module):def __init__(self, seq_type, inp_planes, out_planes, depth_multiplier):super(SeqConv3x3, self).__init__()self.type = seq_typeself.inp_planes = inp_planesself.out_planes = out_planesif self.type == 'conv1x1-conv3x3':self.mid_planes = int(out_planes * depth_multiplier)conv0 = torch.nn.Conv2d(self.inp_planes, self.mid_planes, kernel_size=1, padding=0)self.k0 = conv0.weightself.b0 = conv0.biasconv1 = torch.nn.Conv2d(self.mid_planes, self.out_planes, kernel_size=3)self.k1 = conv1.weightself.b1 = conv1.biaselif self.type == 'conv1x1-sobelx':conv0 = torch.nn.Conv2d(self.inp_planes, self.out_planes, kernel_size=1, padding=0)self.k0 = conv0.weightself.b0 = conv0.bias# init scale & biasscale = torch.randn(size=(self.out_planes, 1, 1, 1)) * 1e-3self.scale = nn.Parameter(scale)# bias = 0.0# bias = [bias for c in range(self.out_planes)]# bias = torch.FloatTensor(bias)bias = torch.randn(self.out_planes) * 1e-3bias = torch.reshape(bias, (self.out_planes,))self.bias = nn.Parameter(bias)# init maskself.mask = torch.zeros((self.out_planes, 1, 3, 3), dtype=torch.float32)for i in range(self.out_planes):self.mask[i, 0, 0, 0] = 1.0self.mask[i, 0, 1, 0] = 2.0self.mask[i, 0, 2, 0] = 1.0self.mask[i, 0, 0, 2] = -1.0self.mask[i, 0, 1, 2] = -2.0self.mask[i, 0, 2, 2] = -1.0self.mask = nn.Parameter(data=self.mask, requires_grad=False)elif self.type == 'conv1x1-sobely':conv0 = torch.nn.Conv2d(self.inp_planes, self.out_planes, kernel_size=1, padding=0)self.k0 = conv0.weightself.b0 = conv0.bias# init scale & biasscale = torch.randn(size=(self.out_planes, 1, 1, 1)) * 1e-3self.scale = nn.Parameter(torch.FloatTensor(scale))# bias = 0.0# bias = [bias for c in range(self.out_planes)]# bias = torch.FloatTensor(bias)bias = torch.randn(self.out_planes) * 1e-3bias = torch.reshape(bias, (self.out_planes,))self.bias = nn.Parameter(torch.FloatTensor(bias))# init maskself.mask = torch.zeros((self.out_planes, 1, 3, 3), dtype=torch.float32)for i in range(self.out_planes):self.mask[i, 0, 0, 0] = 1.0self.mask[i, 0, 0, 1] = 2.0self.mask[i, 0, 0, 2] = 1.0self.mask[i, 0, 2, 0] = -1.0self.mask[i, 0, 2, 1] = -2.0self.mask[i, 0, 2, 2] = -1.0self.mask = nn.Parameter(data=self.mask, requires_grad=False)elif self.type == 'conv1x1-laplacian':conv0 = torch.nn.Conv2d(self.inp_planes, self.out_planes, kernel_size=1, padding=0)self.k0 = conv0.weightself.b0 = conv0.bias# init scale & biasscale = torch.randn(size=(self.out_planes, 1, 1, 1)) * 1e-3self.scale = nn.Parameter(torch.FloatTensor(scale))# bias = 0.0# bias = [bias for c in range(self.out_planes)]# bias = torch.FloatTensor(bias)bias = torch.randn(self.out_planes) * 1e-3bias = torch.reshape(bias, (self.out_planes,))self.bias = nn.Parameter(torch.FloatTensor(bias))# init maskself.mask = torch.zeros((self.out_planes, 1, 3, 3), dtype=torch.float32)for i in range(self.out_planes):self.mask[i, 0, 0, 1] = 1.0self.mask[i, 0, 1, 0] = 1.0self.mask[i, 0, 1, 2] = 1.0self.mask[i, 0, 2, 1] = 1.0self.mask[i, 0, 1, 1] = -4.0self.mask = nn.Parameter(data=self.mask, requires_grad=False)else:raise ValueError('the type of seqconv is not supported!')def forward(self, x):if self.type == 'conv1x1-conv3x3':# conv-1x1y0 = F.conv2d(input=x, weight=self.k0, bias=self.b0, stride=1)# explicitly padding with biasy0 = F.pad(y0, (1, 1, 1, 1), 'constant', 0)b0_pad = self.b0.view(1, -1, 1, 1)y0[:, :, 0:1, :] = b0_pady0[:, :, -1:, :] = b0_pady0[:, :, :, 0:1] = b0_pady0[:, :, :, -1:] = b0_pad# conv-3x3y1 = F.conv2d(input=y0, weight=self.k1, bias=self.b1, stride=1)else:y0 = F.conv2d(input=x, weight=self.k0, bias=self.b0, stride=1)# explicitly padding with biasy0 = F.pad(y0, (1, 1, 1, 1), 'constant', 0)b0_pad = self.b0.view(1, -1, 1, 1)y0[:, :, 0:1, :] = b0_pady0[:, :, -1:, :] = b0_pady0[:, :, :, 0:1] = b0_pady0[:, :, :, -1:] = b0_pad# conv-3x3y1 = F.conv2d(input=y0, weight=self.scale * self.mask, bias=self.bias, stride=1, groups=self.out_planes)return y1def rep_params(self):device = self.k0.get_device()if device < 0:device = Noneif self.type == 'conv1x1-conv3x3':# re-param conv kernelRK = F.conv2d(input=self.k1, weight=self.k0.permute(1, 0, 2, 3))# re-param conv biasRB = torch.ones(1, self.mid_planes, 3, 3, device=device) * self.b0.view(1, -1, 1, 1)RB = F.conv2d(input=RB, weight=self.k1).view(-1,) + self.b1else:tmp = self.scale * self.maskk1 = torch.zeros((self.out_planes, self.out_planes, 3, 3), device=device)for i in range(self.out_planes):k1[i, i, :, :] = tmp[i, 0, :, :]b1 = self.bias# re-param conv kernelRK = F.conv2d(input=k1, weight=self.k0.permute(1, 0, 2, 3))# re-param conv biasRB = torch.ones(1, self.out_planes, 3, 3, device=device) * self.b0.view(1, -1, 1, 1)RB = F.conv2d(input=RB, weight=k1).view(-1,) + b1return RK, RB

4. 结果

在这里插入图片描述

edge-SR

1.转置卷积上采样 和 pixel shuffle的区别

在这里插入图片描述

2.pooling or downsample 可能有aliasing artifacts

using an anti–aliasing low–pass filter and then downsamples the image.

This process is implemented in tensor processing frameworks with strided convolutional
layers where the kernel or weight parameters correspond to the low–pass filter coefficients.

3.单层网络eSR-MAX

一个卷积,一个pixel shuffle, 一个max
卷积输出的通道数: sxsxchannel

out_channels=self.stride[0]*self.stride[1]*self.channels,

在这里插入图片描述

4.eSR-TM, eSR-TR, eSR-CNN

直接看代码更好理解:

class edgeSR_TM(nn.Module):def __init__(self, model_id):self.model_id = model_idsuper().__init__()assert self.model_id.startswith('eSR-TM_')parse = self.model_id.split('_')self.channels = int([s for s in parse if s.startswith('C')][0][1:])self.kernel_size = (int([s for s in parse if s.startswith('K')][0][1:]), ) * 2self.stride = (int([s for s in parse if s.startswith('s')][0][1:]), ) * 2self.pixel_shuffle = nn.PixelShuffle(self.stride[0])self.softmax = nn.Softmax(dim=1)self.filter = nn.Conv2d(in_channels=1,out_channels=2*self.stride[0]*self.stride[1]*self.channels,kernel_size=self.kernel_size,stride=1,padding=((self.kernel_size[0]-1)//2,(self.kernel_size[1]-1)//2),groups=1,bias=False,dilation=1)nn.init.xavier_normal_(self.filter.weight, gain=1.)self.filter.weight.data[:, 0, self.kernel_size[0]//2, self.kernel_size[0]//2] = 1.def forward(self, input):filtered = self.pixel_shuffle(self.filter(input))value, key = torch.split(filtered, [self.channels, self.channels], dim=1)return torch.sum(value * self.softmax(key),dim=1, keepdim=True)class edgeSR_TR(nn.Module):def __init__(self, model_id):self.model_id = model_idsuper().__init__()assert self.model_id.startswith('eSR-TR_')parse = self.model_id.split('_')self.channels = int([s for s in parse if s.startswith('C')][0][1:])self.kernel_size = (int([s for s in parse if s.startswith('K')][0][1:]), ) * 2self.stride = (int([s for s in parse if s.startswith('s')][0][1:]), ) * 2self.pixel_shuffle = nn.PixelShuffle(self.stride[0])self.softmax = nn.Softmax(dim=1)self.filter = nn.Conv2d(in_channels=1,out_channels=3*self.stride[0]*self.stride[1]*self.channels,kernel_size=self.kernel_size,stride=1,padding=((self.kernel_size[0]-1)//2,(self.kernel_size[1]-1)//2),groups=1,bias=False,dilation=1)nn.init.xavier_normal_(self.filter.weight, gain=1.)self.filter.weight.data[:, 0, self.kernel_size[0]//2, self.kernel_size[0]//2] = 1.def forward(self, input):filtered = self.pixel_shuffle(self.filter(input))value, query, key = torch.split(filtered, [self.channels, self.channels, self.channels], dim=1)return torch.sum(value * self.softmax(query*key),dim=1, keepdim=True)

这篇关于轻量级超分网络:Edge-oriented Convolution Block for Real-timeMM21_ECBSR 和 eSR的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/233773

相关文章

使用Python自建轻量级的HTTP调试工具

《使用Python自建轻量级的HTTP调试工具》这篇文章主要为大家详细介绍了如何使用Python自建一个轻量级的HTTP调试工具,文中的示例代码讲解详细,感兴趣的小伙伴可以参考一下... 目录一、为什么需要自建工具二、核心功能设计三、技术选型四、分步实现五、进阶优化技巧六、使用示例七、性能对比八、扩展方向建

Linux系统配置NAT网络模式的详细步骤(附图文)

《Linux系统配置NAT网络模式的详细步骤(附图文)》本文详细指导如何在VMware环境下配置NAT网络模式,包括设置主机和虚拟机的IP地址、网关,以及针对Linux和Windows系统的具体步骤,... 目录一、配置NAT网络模式二、设置虚拟机交换机网关2.1 打开虚拟机2.2 管理员授权2.3 设置子

揭秘Python Socket网络编程的7种硬核用法

《揭秘PythonSocket网络编程的7种硬核用法》Socket不仅能做聊天室,还能干一大堆硬核操作,这篇文章就带大家看看Python网络编程的7种超实用玩法,感兴趣的小伙伴可以跟随小编一起... 目录1.端口扫描器:探测开放端口2.简易 HTTP 服务器:10 秒搭个网页3.局域网游戏:多人联机对战4.

SpringBoot使用OkHttp完成高效网络请求详解

《SpringBoot使用OkHttp完成高效网络请求详解》OkHttp是一个高效的HTTP客户端,支持同步和异步请求,且具备自动处理cookie、缓存和连接池等高级功能,下面我们来看看SpringB... 目录一、OkHttp 简介二、在 Spring Boot 中集成 OkHttp三、封装 OkHttp

Linux系统之主机网络配置方式

《Linux系统之主机网络配置方式》:本文主要介绍Linux系统之主机网络配置方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、查看主机的网络参数1、查看主机名2、查看IP地址3、查看网关4、查看DNS二、配置网卡1、修改网卡配置文件2、nmcli工具【通用

使用Python高效获取网络数据的操作指南

《使用Python高效获取网络数据的操作指南》网络爬虫是一种自动化程序,用于访问和提取网站上的数据,Python是进行网络爬虫开发的理想语言,拥有丰富的库和工具,使得编写和维护爬虫变得简单高效,本文将... 目录网络爬虫的基本概念常用库介绍安装库Requests和BeautifulSoup爬虫开发发送请求解

如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别详解

《如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别详解》:本文主要介绍如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别的相关资料,描述了如何使用海康威视设备网络SD... 目录前言开发流程问题和解决方案dll库加载不到的问题老旧版本sdk不兼容的问题关键实现流程总结前言作为

SSID究竟是什么? WiFi网络名称及工作方式解析

《SSID究竟是什么?WiFi网络名称及工作方式解析》SID可以看作是无线网络的名称,类似于有线网络中的网络名称或者路由器的名称,在无线网络中,设备通过SSID来识别和连接到特定的无线网络... 当提到 Wi-Fi 网络时,就避不开「SSID」这个术语。简单来说,SSID 就是 Wi-Fi 网络的名称。比如

Java实现任务管理器性能网络监控数据的方法详解

《Java实现任务管理器性能网络监控数据的方法详解》在现代操作系统中,任务管理器是一个非常重要的工具,用于监控和管理计算机的运行状态,包括CPU使用率、内存占用等,对于开发者和系统管理员来说,了解这些... 目录引言一、背景知识二、准备工作1. Maven依赖2. Gradle依赖三、代码实现四、代码详解五

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor