【AI系统】ESPNet 系列

简介: 本文介绍了ESPNet系列,专注于高分辨率图像的语义分割,强调了其高效的计算性能和低内存、功耗特性。ESPNet V1提出了ESP模块,通过分解标准卷积为point-wise卷积和空洞卷积金字塔,大幅减少了参数量和计算成本。ESPNet V2则进一步优化,采用了分组卷积和深度空洞分离卷积,增强了模型的有效感受野,同时降低了浮点计算量,适用于多种视觉任务。

本文将会介绍 ESPNet 系列,该网络主要应用在高分辨率图像下的语义分割,在计算内存占用、功耗方面都非常高效,重点介绍一种高效的空间金字塔卷积模块(ESP Module);而在 ESPNet V2 上则是会更进一步给大家呈现如何利用分组卷积核,深度空洞分离卷积学习巨大有效感受野,进一步降低浮点计算量和参数量。

ESPNet V1 模型

ESPNet V1:应用在高分辨图像下的语义分割,在计算、内存占用、功耗方面都非常高效。主要贡献在于基于传统卷积模块,提出高效空间金子塔卷积模块(ESP Module),有助于减少模型运算量和内存、功率消耗,来提升终端设备适用性,方便部署到移动端。

ESP 模块

基于卷积因子分解的原则,ESP(Efficient spatial pyramid)模块将标准卷积分解成 point-wise 卷积和空洞卷积金字塔(spatial pyramid of dilated convolutions)。point-wise 卷积将输入的特征映射到低维特征空间,即采用 K 个 1x1xM 的小卷积核对输入的特征进行卷积操作,1x1 卷积的作用其实就是为了降低维度,这样就可以减少参数。空洞卷积金字塔使用 K 组空洞卷积的同时下采样得到低维特征,这种分解方法能够大量减少 ESP 模块的参数和内存,并且保证了较大的感受野(如下图 a 所示)。

ESPNet

上图 (b) 展示了 ESP 模块采用的减少-分裂-转换-合并策略。下面来计算下一共包含的参数,其实在效果上,以这种轻量级的网络作为 backbone 效果肯定不如那些参数量大的网络模型,比如 Resnet,但是在运行速度上有很大优势。

如上图所示,对 ESP 模块的第一部分来说,d1×1×M 的卷积核,将 M 维的输入特征降至 d 维。此时参数为:MN/K,第二部分参数量为 Kn2(N/K)2,和标准卷积结构相比,参数数量降低很多。

为了减少计算量,又引入了一个简单的超参数 K,它的作用是统一收缩网络中各个 ESP 模块的特征映射维数。Reduce 对于给定 K,ESP 模块首先通过逐点卷积将特征映射从 m 维空间缩减到 N/K 维空间(上图 a 中的步骤 1)。通过 Split 将低维特征映射拆分到 K 个并行分支上。

然后每个分支使用 2k1k=1,...,k1 给出的 n×n 个扩张速率不同的卷积核同时处理这些特征映射(上图 a 中的步骤 2)。最后将 K 个并行扩展卷积核的输出连接起来,产生一个 n 维输出特征图。

下面代码使用 PyTorch 来实现具体的 ESP 模块:

class ESPModule(nn.Module):
    def __init__(self, in_channels, out_channels, K=5, ks=3, stride=1,act_type='prelu',):
        super(ESPModule, self).__init__()
        self.K = K
        self.stride = stride
        self.use_skip = (in_channels == out_channels) and (stride == 1)
        channel_kn = out_channels // K
        channel_k1 = out_channels - (K -1) * channel_kn
        self.perfect_divisor = channel_k1 == channel_kn

        if self.perfect_divisor:
            self.conv_kn = conv1x1(in_channels, channel_kn, stride)
        else:
            self.conv_kn = conv1x1(in_channels, channel_kn, stride)
            self.conv_k1 = conv1x1(in_channels, channel_k1, stride)

        self.layers = nn.ModuleList()
        for k in range(1, K+1):
            dt = 2**(k-1)       # dilation
            channel = channel_k1 if k == 1 else channel_kn
            self.layers.append(ConvBNAct(channel, channel, ks, 1, dt, act_type=act_type))

    def forward(self, x):
        if self.use_skip:
            residual = x

        transform_feats = []
        if self.perfect_divisor:
            x = self.conv_kn(x)     # Reduce
            for i in range(self.K):
                transform_feats.append(self.layers[i](x))   # Split --> Transform

            for j in range(1, self.K):
                transform_feats[j] += transform_feats[j-1]      # Merge: Sum
        else:
            x1 = self.conv_k1(x)    # Reduce
            xn = self.conv_kn(x)    # Reduce
            transform_feats.append(self.layers[0](x1))      # Split --> Transform
            for i in range(1, self.K):
                transform_feats.append(self.layers[i](xn))   # Split --> Transform

            for j in range(2, self.K):
                transform_feats[j] += transform_feats[j-1]      # Merge: Sum

        x = torch.cat(transform_feats, dim=1)               # Merge: Concat

        if self.use_skip:
            x += residual

        return x
AI 代码解读

HFF 特性

虽然将扩张卷积的输出拼接在一起会给 ESP 模块带来一个较大的有效感受野,但也会引入不必要的棋盘或网格假象,如下图所示。

ESPNet

上图(a)举例说明一个网格伪像,其中单个活动像素(红色)与膨胀率 r = 2 的 3×3 膨胀卷积核卷积。

上图(b)具有和不具有层次特征融合(Hierarchical feature fusion,HFF)的 ESP 模块特征图可视化。ESP 中的 HFF 消除了网格伪影。彩色观看效果最佳。

为了解决 ESP 中的网格问题,使用不同膨胀率的核获得的特征映射在拼接之前会进行层次化添加(上图 b 中的 HFF)。该解决方案简单有效,且不会增加 ESP 模块的复杂性,这与现有方法不同,现有方法通过使用膨胀率较小的卷积核学习更多参数来消除网格误差[Dilated residual networks,Understanding convolution for semantic segmentation]。为了改善网络内部的梯度流动,ESP 模块的输入和输出特征映射使用元素求和[Deep residual learning for image recognition]进行组合。

网络结构与实现

ESPNet 使用 ESP 模块学习卷积核以及下采样操作,除了第一层是标准的大步卷积。所有层(卷积和 ESP 模块)后面都有一个批归一化和一个 PReLU 非线性,除了最后一个点卷积,它既没有批归一化,也没有非线性。最后一层输入 softmax 进行像素级分类。

ESPNet 的不同变体如下图所示。第一个变体,ESPNet-A(图 a),是一种标准网络,它以 RGB 图像作为输入,并使用 ESP 模块学习不同空间层次的表示,以产生一个分割掩码。第二种 ESP - b(图 b)通过在之前的跨步 ESP 模块和之前的 ESP 模块之间共享特征映射,改善了 ESPNet-A 内部的信息流。第三种变体,ESPNet-C(图 c),加强了 ESPNet-B 内部的输入图像,以进一步改善信息的流动。这三种变量产生的输出的空间维度是输入图像的 1 / 8。第四种变体,ESPNet(图 d),在 ESPNet- c 中添加了一个轻量级解码器(使用 reduceupsample-merge 的原理构建),输出与输入图像相同空间分辨率的分割 mask。

ESPNet

从 ESPNet- a 到 ESPNet 的路径。红色和绿色色框分别代表负责下采样和上采样操作的模块。空间级别的 l 在(a)中的每个模块的左侧。本文将每个模块表示为(#输入通道,#输出通道)。这里,conv-n 表示 n × n 卷积。

为了在不改变网络拓扑结构的情况下构建具有较深计算效率的边缘设备网络,超参数α控制网络的深度;ESP 模块在空间层次 l 上重复 αl 次。在更高的空间层次(l = 0 和 l = 1),cnn 需要更多的内存,因为这些层次的特征图的空间维数较高。为了节省内存,ESP 和卷积模块都不会在这些空间级别上重复。

ESPNet V2 模型

ESPNet V2:是由 ESPNet V1 改进来的,一种轻量级、能耗高效、通用的卷积神经网络,利用分组卷积核深度空洞分离卷积学习巨大有效感受野,进一步降低浮点计算量和参数量。同时在图像分类、目标检测、语义分割等任务上检验了模型效果。

ESPNet V2 与 V1 版本相比,其特点如下:

  1. 将原来 ESPNet 的 point-wise convolutions 替换为 group point-wise convolutions;

  2. 将原来 ESPNet 的 dilated convolutions 替换为 depth-wise dilated convolution;

  3. HFF 加在 depth-wise dilated separable convolutions 和 point-wise (or 1 × 1)卷积之间,去除 gridding artifacts;

  4. 使用 group point-wise convolution 替换 K 个 point-wise convolutions;

  5. 加入平均池化(average pooling ),将输入图片信息加入 EESP 中;

  6. 使用级联(concatenation)取代对应元素加法操作(element-wise addition operation )

DDConv 模块

深度分离空洞卷积(Depth-wise dilated separable convolution,DDConv)分两步:

  • 对每个输入通道执行空洞率为 r 的 DDConv,从有效感受野学习代表性特征。

  • 标准 1x1 卷积学习 DDConv 输出的线性组合特征。

深度分离空洞卷积与其他卷积的参数量与感受野对比如下表所示。

Convolution type Parameters Eff.receptive field
Standard n2cˆc n×n
Group n2cˆcg n×n
Depth-wise separable n2c+cˆc n×n
Depth-wise dilated separable n2c+cˆc nr×nr

EESP 模块

EESP 模块结构如下图,图 b 中相比于 ESPNet,输入层采用分组卷积,DDConv+Conv1x1 取代标准空洞卷积,依然采用 HFF 的融合方式,(c)是(b)的等价模式。当输入通道数 M=240,g=K=4, d=M/K=60,EESP 比 ESP 少 7 倍的参数。

ESPNet

描述了一个新的网络模块 EESP,它利用深度可分离扩张和组逐点卷积设计,专为边缘设备而设计。该模块受 ESPNet 架构的启发,基于 ESP 模块构建,使用了减少-分割-变换-合并的策略。通过组逐点和深度可分离扩张卷积,该模块的计算复杂度得到了显著的降低。进一步,描述了一种带有捷径连接到输入图像的分层 EESP 模块,以更有效地学习多尺度的表示。

如上图中 b 所示,能够降低 Md+n2d2KMdg+(n2+d)dK 倍计算复杂度,K 为空洞卷积金字塔层数。考虑到单独计算 K 个 point-wise 卷积等同于单个分组数为 K 的 point-wise 分组卷积,而分组卷积的在实现上更高效,于是改进为上图 c 的最终结构。

class EESP(nn.Module):
    '''
       按照 REDUCE ---> SPLIT ---> TRANSFORM --> MERGE 
    '''

    def __init__(self, nIn, nOut, stride=1, k=4, r_lim=7, down_method='esp'):                   
        super().__init__()
        self.stride = stride
        n = int(nOut / k)
        n1 = nOut - (k - 1) * n
        assert down_method in ['avg', 'esp']
        assert n == n1, "n(={}) and n1(={}) should be equal for Depth-wise Convolution ".format(n, n1)

        self.proj_1x1 = CBR(nIn, n, 1, stride=1, groups=k)

        #3x3 核的膨胀率和感受野之间的映射
        map_receptive_ksize = {
   3: 1, 5: 2, 7: 3, 9: 4, 11: 5, 13: 6, 15: 7, 17: 8}
        self.k_sizes = list()
        for i in range(k):
            ksize = int(3 + 2 * i)
            # 达到感受野极限后,回落到 3x3,膨胀率为 1 的基础卷积
            ksize = ksize if ksize <= r_lim else 3
            self.k_sizes.append(ksize)
        #根据感受野对这些核大小进行排序(升序)
        #这使我们能够忽略分层中具有相同有效感受野的核(在我们的例子中为 3×3)
        # 特征融合,因为 3x3 感受野的核不具有网格伪影。
        self.k_sizes.sort()
        self.spp_dw = nn.ModuleList()

        for i in range(k):
            d_rate = map_receptive_ksize[self.k_sizes[i]]
            self.spp_dw.append(CDilated(n, n, kSize=3, stride=stride, groups=n, d=d_rate))

        self.conv_1x1_exp = CB(nOut, nOut, 1, 1, groups=k)
        self.br_after_cat = BR(nOut)
        self.module_act = nn.PReLU(nOut)
        self.downAvg = True if down_method == 'avg' else False

    def forward(self, input):

        # Reduce --> 将高维特征映射投影到低维空间
        output1 = self.proj_1x1(input)
        output = [self.spp_dw[0](output1)]
        # 计算每个分支的输出并分层融合它们
        # i.e. Split --> Transform --> HFF
        for k in range(1, len(self.spp_dw)):
            out_k = self.spp_dw[k](output1)
            # HFF
            #我们不组合具有相同感受野的分支(在我们的例子中是 3x3)

            out_k = out_k + output[k - 1]
            #融合后应用批量定额,然后添加到列表中
            output.append(out_k)
        # Merge
        expanded = self.conv_1x1_exp( 
            self.br_after_cat( 
                torch.cat(output, 1) 
            )
        )
        del output
        # 如果下采样,则返回连接的向量
        # 因为下采样功能会将其与 avg 合并。合并特征图,然后对其进行阈值处理
        if self.stride == 2 and self.downAvg:
            return expanded

        # 如果输入向量和连接向量的维数相同,则将它们相加 (RESIDUAL LINK)
        if expanded.size() == input.size():
            expanded = expanded + input

        # 使用激活函数对特征图进行阈值处理 (PReLU in this case)
        return self.module_act(expanded)
AI 代码解读

Strided EESP 模块

为了在多尺度下能够有效地学习特征,对上图 1c 的网络做了四点改动(如下图所示):

1.对 DDConv 添加 stride 属性。

2.右边的 shortcut 中带了平均池化操作,实现维度匹配。

3.将相加的特征融合方式替换为 concat 形式,增加特征的维度。

4.融合原始输入图像的下采样信息,使得特征信息更加丰富。具体做法是先将图像下采样到与特征图的尺寸相同的尺寸,然后使用第一个卷积,一个标准的 3×3 卷积,用于学习空间表示。再使用第二个卷积,一个逐点卷积,用于学习输入之间的线性组合,并将其投影到高维空间。

05ESPNet05.png


class DownSampler(nn.Module):
    '''
   具有两个并行分支的下采样功能:平均池和步长为 2 的 EESP 块。然后,使用激活函数将这些分支的输出特征图连接起来并进行阈值处理,以产生最终输出。
    '''

    def __init__(self, nin, nout, k=4, r_lim=9, reinf=True):

        super().__init__()
        nout_new = nout - nin
        self.eesp = EESP(nin, nout_new, stride=2, k=k, r_lim=r_lim, down_method='avg')
        self.avg = nn.AvgPool2d(kernel_size=3, padding=1, stride=2)
        if reinf:
            self.inp_reinf = nn.Sequential(
                CBR(config_inp_reinf, config_inp_reinf, 3, 1),
                CB(config_inp_reinf, nout, 1, 1)
            )
        self.act =  nn.PReLU(nout)

    def forward(self, input, input2=None):

        avg_out = self.avg(input)
        eesp_out = self.eesp(input)
        output = torch.cat([avg_out, eesp_out], 1)
        if input2 is not None:

            w1 = avg_out.size(2)
            while True:
                input2 = F.avg_pool2d(input2, kernel_size=3, padding=1, stride=2)
                w2 = input2.size(2)
                if w2 == w1:
                    break
            output = output + self.inp_reinf(input2)

        return self.act(output)
AI 代码解读

如果您想了解更多AI知识,与AI专业人士交流,请立即访问昇腾社区官方网站https://www.hiascend.com/或者深入研读《AI系统:原理与架构》一书,这里汇聚了海量的AI学习资源和实践课程,为您的AI技术成长提供强劲动力。不仅如此,您还有机会投身于全国昇腾AI创新大赛和昇腾AI开发者创享日等盛事,发现AI世界的无限奥秘~

目录
打赏
0
相关文章
【最佳实践系列】AI程序员让我变成全栈:基于阿里云百炼DeepSeek的跨语言公告系统实战
本文介绍了如何在Java开发中通过跨语言编程,利用阿里云百炼服务平台的DeepSeek大模型生成公告内容,并将其嵌入前端页面。
算法系统协同优化,vivo与港中文推出BlueLM-V-3B,手机秒变多模态AI专家
BlueLM-V-3B是由vivo与香港中文大学共同研发的多模态大型语言模型,专为移动设备优化。它通过算法和系统协同优化,实现了高效部署和快速生成速度(24.4 token/s),并在OpenCompass基准测试中取得优异成绩(66.1分)。模型小巧,语言部分含27亿参数,视觉编码器含4000万参数,适合移动设备使用。尽管如此,低端设备可能仍面临资源压力,实际应用效果需进一步验证。论文链接:https://arxiv.org/abs/2411.10640。
37 9
Memobase:开源AI长期记忆系统,让AI真正记住每个用户的秘密武器
Memobase 是一个开源的长期记忆系统,专为生成式 AI 应用设计,通过用户画像和时间感知记忆功能,帮助 AI 记住、理解并适应用户需求。
100 0
AI对话网站一键生成系统源码
可以添加进自己的工具箱,也可以嵌入自己博客的页面中,引流效果杠杠的,新拟态设计风格,有能力的大佬可以进行二开,仅提供学习,用户可输入网站名称、AI默认的开场白、AI头像昵称、AI网站中引流的你的网站等等内容,所有生成的网页全部保存到你的服务器上
64 27
AI对话网站一键生成系统源码
AI实践:智能工单系统的技术逻辑与应用
智能工单系统是企业服务管理的核心工具,通过多渠道接入、自然语言处理等技术,实现工单自动生成、分类和分配。它优化了客户服务流程,提高了效率与透明度,减少了运营成本,提升了客户满意度。系统还依托知识库和机器学习,持续改进处理策略,助力企业在竞争中脱颖而出。
171 5
校企合作|TsingtaoAI携手潍坊学院,共建AI驱动的党建信息化系统
TsingtaoAI与潍坊学院近日达成合作,正式签署《人工智能党建信息化系统开发》技术开发合同,计划在未来两年内联合开发一套集党员教育、党务管理、党建活动智能化以及数据可视化于一体的智能党建系统。本次合作将充分结合TsingtaoAI在AI大模型领域的技术优势和潍坊学院的学术资源,为推动党建工作的数字化、智能化和高效化注入新的动力。
69 10
Casevo:开源的社会传播模拟系统,基于 AI 模拟人类认知、决策和社会交互,预测社会传播现象
Casevo 是中国传媒大学推出的开源社会传播模拟系统,结合大语言模型和多智能体技术,支持复杂社会网络建模与动态交互,适用于新闻传播、社会计算等领域。
144 22
Casevo:开源的社会传播模拟系统,基于 AI 模拟人类认知、决策和社会交互,预测社会传播现象
AigcPanel:开源的 AI 虚拟数字人系统,一键安装开箱即用,支持视频合成、声音合成和声音克隆
AigcPanel 是一款开源的 AI 虚拟数字人系统,支持视频合成、声音克隆等功能,适用于影视制作、虚拟主播、教育培训等多种场景。
451 12
AigcPanel:开源的 AI 虚拟数字人系统,一键安装开箱即用,支持视频合成、声音合成和声音克隆

热门文章

最新文章