【Block总结】WTConv,小波变换(Wavelet Transform)来扩展卷积神经网络(CNN)的感受野

news/2025/1/19 5:11:47/

论文解读:Wavelet Convolutions for Large Receptive Fields

论文信息

  • 标题: Wavelet Convolutions for Large Receptive Fields
  • 作者: Shahaf E. Finder, Roy Amoyal, Eran Treister, Oren Freifeld
  • 提交日期: 2024年7月8日
  • arXiv链接: Wavelet Convolutions for Large Receptive Fields
  • Github: https://github.com/BGU-CS-VIL/WTConv

概述

论文《Wavelet Convolutions for Large Receptive Fields》提出了一种新型卷积层,称为WTConv(Wavelet Transform Convolution),旨在通过小波变换(Wavelet Transform)来扩展卷积神经网络(CNN)的感受野。该方法能够在不显著增加参数数量的情况下,获得接近全局的感受野,从而提高模型对低频信息的捕捉能力。
在这里插入图片描述

主要贡献

  1. 感受野扩展:传统的卷积神经网络通过增加卷积核的大小来扩展感受野,但这种方法在达到一定程度后会遇到参数过多的问题。WTConv通过小波变换实现了感受野的有效扩展,且参数数量仅以对数方式增长。

  2. 多频率响应:WTConv能够有效地响应不同频率的输入信号,增强了模型对形状的响应能力,而不仅仅是对纹理的响应。

  3. 架构兼容性:WTConv可以作为现有架构的替代层,适用于多种网络结构,如ConvNeXt和MobileNetV2,且在图像分类等下游任务中表现出色。
    在这里插入图片描述

WTConv如何在不增加参数的情况下扩展感受野

WTConv(Wavelet Transform Convolution)是一种新型卷积层,旨在通过小波变换(Wavelet Transform)有效扩展卷积神经网络(CNN)的感受野,而不显著增加模型的参数数量。这一方法的核心在于利用小波变换的特性,使得感受野的扩展与参数的增长呈对数关系。

  1. 小波变换的优势:小波变换能够将信号分解为不同频率的成分,这使得WTConv能够同时捕捉到低频和高频信息。通过这种方式,WTConv可以在保持较小卷积核的情况下,获得较大的感受野。

  2. 参数增长控制:传统的卷积层通过增加卷积核的大小来扩展感受野,但这会导致参数数量的急剧增加。WTConv的设计使得对于一个 k × k k \times k k×k 的感受野,所需的可训练参数数量仅以对数方式增长,这样可以有效避免过度参数化的问题[7][8]。

  3. 架构兼容性:WTConv可以作为现有网络架构的替代层,例如ConvNeXt和MobileNetV2,能够无缝集成到这些模型中,增强其对形状的响应能力,并提高对图像损坏的鲁棒性[5][10]。

实验结果

在多个图像分类任务中,WTConv表现出色,尤其是在处理复杂形状和纹理时,显示出更强的适应性和准确性,在图像分类任务中优于传统卷积层,尤其在处理图像损坏和复杂形状时表现出更强的鲁棒性。
。这表明WTConv不仅在理论上有效,而且在实际应用中也具有良好的性能。

通过这些机制,WTConv实现了感受野的有效扩展,同时保持了模型的参数效率,适应了现代深度学习对计算资源的需求。

代码:

import torch
import torch.nn as nn
import pywt
import pywt.dataimport torch.nn.functional as Fdef create_wavelet_filter(wave, in_size, out_size, type=torch.float):w = pywt.Wavelet(wave)dec_hi = torch.tensor(w.dec_hi[::-1], dtype=type)dec_lo = torch.tensor(w.dec_lo[::-1], dtype=type)dec_filters = torch.stack([dec_lo.unsqueeze(0) * dec_lo.unsqueeze(1),dec_lo.unsqueeze(0) * dec_hi.unsqueeze(1),dec_hi.unsqueeze(0) * dec_lo.unsqueeze(1),dec_hi.unsqueeze(0) * dec_hi.unsqueeze(1)], dim=0)dec_filters = dec_filters[:, None].repeat(in_size, 1, 1, 1)rec_hi = torch.tensor(w.rec_hi[::-1], dtype=type).flip(dims=[0])rec_lo = torch.tensor(w.rec_lo[::-1], dtype=type).flip(dims=[0])rec_filters = torch.stack([rec_lo.unsqueeze(0) * rec_lo.unsqueeze(1),rec_lo.unsqueeze(0) * rec_hi.unsqueeze(1),rec_hi.unsqueeze(0) * rec_lo.unsqueeze(1),rec_hi.unsqueeze(0) * rec_hi.unsqueeze(1)], dim=0)rec_filters = rec_filters[:, None].repeat(out_size, 1, 1, 1)return dec_filters, rec_filtersdef wavelet_transform(x, filters):b, c, h, w = x.shapepad = (filters.shape[2] // 2 - 1, filters.shape[3] // 2 - 1)x = F.conv2d(x, filters, stride=2, groups=c, padding=pad)x = x.reshape(b, c, 4, h // 2, w // 2)return xdef inverse_wavelet_transform(x, filters):b, c, _, h_half, w_half = x.shapepad = (filters.shape[2] // 2 - 1, filters.shape[3] // 2 - 1)x = x.reshape(b, c * 4, h_half, w_half)x = F.conv_transpose2d(x, filters, stride=2, groups=c, padding=pad)return xclass WTConv2d(nn.Module):def __init__(self, in_channels, out_channels, kernel_size=5, stride=1, bias=True, wt_levels=1, wt_type='db1'):super(WTConv2d, self).__init__()assert in_channels == out_channelsself.in_channels = in_channelsself.wt_levels = wt_levelsself.stride = strideself.dilation = 1self.wt_filter, self.iwt_filter = create_wavelet_filter(wt_type, in_channels, in_channels, torch.float)self.wt_filter = nn.Parameter(self.wt_filter, requires_grad=False)self.iwt_filter = nn.Parameter(self.iwt_filter, requires_grad=False)self.base_conv = nn.Conv2d(in_channels, in_channels, kernel_size, padding='same', stride=1, dilation=1,groups=in_channels, bias=bias)self.base_scale = _ScaleModule([1, in_channels, 1, 1])self.wavelet_convs = nn.ModuleList([nn.Conv2d(in_channels * 4, in_channels * 4, kernel_size, padding='same', stride=1, dilation=1,groups=in_channels * 4, bias=False) for _ in range(self.wt_levels)])self.wavelet_scale = nn.ModuleList([_ScaleModule([1, in_channels * 4, 1, 1], init_scale=0.1) for _ in range(self.wt_levels)])if self.stride > 1:self.do_stride = nn.AvgPool2d(kernel_size=1, stride=stride)else:self.do_stride = Nonedef forward(self, x):x_ll_in_levels = []x_h_in_levels = []shapes_in_levels = []curr_x_ll = xfor i in range(self.wt_levels):curr_shape = curr_x_ll.shapeshapes_in_levels.append(curr_shape)if (curr_shape[2] % 2 > 0) or (curr_shape[3] % 2 > 0):curr_pads = (0, curr_shape[3] % 2, 0, curr_shape[2] % 2)curr_x_ll = F.pad(curr_x_ll, curr_pads)curr_x =wavelet_transform(curr_x_ll, self.wt_filter)curr_x_ll = curr_x[:, :, 0, :, :]shape_x = curr_x.shapecurr_x_tag = curr_x.reshape(shape_x[0], shape_x[1] * 4, shape_x[3], shape_x[4])curr_x_tag = self.wavelet_scale[i](self.wavelet_convs[i](curr_x_tag))curr_x_tag = curr_x_tag.reshape(shape_x)x_ll_in_levels.append(curr_x_tag[:, :, 0, :, :])x_h_in_levels.append(curr_x_tag[:, :, 1:4, :, :])next_x_ll = 0for i in range(self.wt_levels - 1, -1, -1):curr_x_ll = x_ll_in_levels.pop()curr_x_h = x_h_in_levels.pop()curr_shape = shapes_in_levels.pop()curr_x_ll = curr_x_ll + next_x_llcurr_x = torch.cat([curr_x_ll.unsqueeze(2), curr_x_h], dim=2)next_x_ll = inverse_wavelet_transform(curr_x, self.iwt_filter)next_x_ll = next_x_ll[:, :, :curr_shape[2], :curr_shape[3]]x_tag = next_x_llassert len(x_ll_in_levels) == 0x = self.base_scale(self.base_conv(x))x = x + x_tagif self.do_stride is not None:x = self.do_stride(x)return xclass _ScaleModule(nn.Module):def __init__(self, dims, init_scale=1.0, init_bias=0):super(_ScaleModule, self).__init__()self.dims = dimsself.weight = nn.Parameter(torch.ones(*dims) * init_scale)self.bias = Nonedef forward(self, x):return torch.mul(self.weight, x)if __name__ == '__main__':# 创建一个随机输入张量,形状为 (batch_size,height×width,channels)input1 = torch.rand(1, 64,40, 40)# 实例化EFC模块block = WTConv2d(64,64,kernel_size=7)# 前向传播output = block(input1)# 打印输入和输出的形状print(input1.size())print(output.size())

输出结果:

torch.Size([1, 64, 40, 40])
torch.Size([1, 64, 40, 40])

http://www.ppmy.cn/news/1564311.html

相关文章

【软件工程】知识点总结(下)

目录 第六章:结构化开发方法 6.1结构化分析方法概述 6.2 数据流图 6.3 数据字典 6.4 E-R图 6.5 状态转换图 6.6 结构化设计目标和任务 6.7结构化设计的概念和原理 6.8面向数据流的设计方法 6.9 详细设计(过程设计) 6.10概要设计 第…

MySQL 数据库的备份和恢复(Linux)

数据丢失对任何企业来说都是致命的,而 MySQL 数据库作为大多数企业数据存储的核心,其备份与恢复的重要性不言而喻。尤其是在 Linux 环境下,如何高效、稳定地进行 MySQL 数据库的备份与恢复,成为数据库管理员的必修课。今天&#x…

Android 高版本如何获取App安装列表?

有个需求需要获取App内的安装列表,但是现在在高版本Android中,只能获取到一部分App效果,我获取的代码如下: val calendar Calendar.getInstance()val packageManager context.packageManagerval usageStatsManager context.getSystemService(Context.USAGE_STATS_SERVICE) …

python http server运行Angular 单页面路由时重定向,解决404问题

问题 当Angular在本地ng server运行时候,可以顺利访问各级路由。 但是运行ng build后,在dist 路径下的打包好的额index.html 必须要在服务器下运行才能加载。 在服务器下我们第一次访问路由页面时是没有问题的,但是尝试刷新页面或手动输入路…

【工具】curl工具

curl 官网: https://curl.se/ github: https://github.com/curl?languagec curl 命令 所有参数介绍在线文档 简单使用教程 邮件发送命令 注: 支持SMTP(或者POP3)协议,curl的版本必须高于7.20(含&…

《初始Linux:多平台兼容的强者,无缝衔接各类设备的桥梁 》

对于新手而言,Linux 虽然看似神秘,但只要迈出第一步,你会发现它其实充满了乐趣和挑战。在接下来的内容中,我将带你一步步揭开 Linux 的神秘面纱,从基础概念到实际操作,帮助你轻松入门,开启属于你…

【数据库】国产达梦数据库与mysql特点、区别、发展前景

国产达梦数据库与mysql的区别 国产达梦数据库(DM)和 MySQL 是两种不同的关系型数据库管理系统(RDBMS),它们有许多区别,包括特性、功能、性能、可用性、适用场景等。以下是它们之间的一些主要区别&#xff…

sparkRDD教程之必会的题目

1.前期准备 (1)看看上一期的博客,最好跟着上一期的博客把sparkRDD的基本命令给熟练掌握后,再来做这篇文章的任务。 上一期的博客:sparkRDD教程之基本命令-CSDN博客 (2)新建文件task6.scala …