【AI系统】ShuffleNet 系列

本文涉及的产品
函数计算FC,每月15万CU 3个月
简介: 本文介绍了ShuffleNet系列模型,特别是其轻量化设计。ShuffleNet V1通过引入Pointwise Group Convolution和Channel Shuffle技术,在减少计算量的同时保持模型准确性。V2版本则进一步优化,考虑了设备运算速度,提出了四个轻量级网络设计原则,并通过Channel Split技术减少了内存访问成本,提升了模型效率。

本文会介绍 ShuffleNet 系列,重点在于其模型结构的轻量化设计,涉及如何降低深度网络计算量,在本文中会着重会讲解逐点分组卷积(Pointwise Group Convolution)和通道混洗(Channel Shuffle)两种新的运算,而 V2 版本则会从设备运算速度方面考虑将网络进行轻量化。

ShuffleNet V1 模型

ShuffleNet V1: 它的贡献在于,使用 Point-Wise 分组卷积和 Channel Shuffle 两个操作,在降低计算量同时保持准确率。网络使用更多的通道来帮助编码阶段提取更多的信息,同时又针对小网络提出了 ShuffleNet Unit。

ShuffleNet V1 比 MobileNet 在 ImageNet 分类任务上的 top-1 误差更低 (绝对 7.8%);在基于 ARM 的移动设备上,ShuffleNet 比 AlexNet 实现了约 13 倍的实际加速,同时保持了相当的精度;

设计思路

ShuffleNet V1 网络结构同样沿袭了稀疏连接的设计理念。作者通过分析 Xception 和 ResNeXt 模型,发现这两种结构通过卷积核拆分虽然计算复杂度均较原始卷积运算有所下降,然而拆分所产生的逐点卷积计算量却相当可观,成为了新的瓶颈。

例如对于 ResNeXt 模型逐点卷积占据了 93.4%的运算复杂度。可见,为了进一步提升模型的速度,就必须寻求更为高效的结构来取代逐点卷积。

受 ResNeXt 的启发,作者提出使用分组逐点卷积(group pointwise convolution)来代替原来的结构。通过将卷积运算的输入限制在每个组内,模型的计算量取得了显著的下降。

但在多层逐点卷积堆叠时,模型的信息流被分割在各个组内,组与组之间没有信息交换(如下 图(a)所示)。这将可能影响到模型的表征能力和识别精度。

因此,在使用分组逐点卷积的同时,需要引入组间信息交换的机制。也就是说,对于第二层卷积而言,每个卷积核需要同时接收各组的特征作为输入,如下图 (b)所示。作者指出,通过引入“通道重排”(channel shuffle,见下图 (c))可以很方便地实现这一机制;并且由于通道重排操作是可导的,因此可以嵌在网络结构中实现端到端的学习。

Shufflenet

逐点分组卷积

pointwise group convolutions,其实就是带分组的卷积核为 $1 \times 1 \quad\quad$ 的卷积,也就是说 pointwise convolution 是卷积核为 $1\times1 \quad\quad$ 的卷积。在 ResNeXt 中主要是对 $3\times 3\quad\quad$ 的卷积做分组操作,但是在 ShuffleNet 中,作者是对 $1 \times 1 \quad\quad$ 的卷积做分组的操作,因为作者认为 $1 \times1 \quad\quad$ 的卷积操作的计算量不可忽视。

分组就是将输入与输出的通道分成几组,比如输出与输入的通道数都是 4 个且分成 2 组,那第 1、2 通道的输出只使用第 1、2 通道的输入,同样那第 3、4 通道的输出只使用第 1、2 通道的输入。

也就是说,不同组的输出与输入没有关系了,减少联系必然会使计算量减小,但同时也会导致信息的丢失。当分成 g 组后,一层参赛量的大小由 $C_{out}\times(C_{in}\times K_{h}\times k_{w}) \quad\quad\quad$ 变成 $C_{out}\times(C_{in}\times K_{h}\times k_{w}/g)\quad\quad\quad\quad$ 。

特征图大小虽然没变化,但是每一组的使用量是原来的 $1/g \quad$,卷积也只用到所有参数的 $1/g \quad$。然后再循环 $g \quad$ 次,那么参数量与计算量如下:

$$ Params=C_{out}/g\times(C_{in}\times K_{h}\times k_{w}/g)\times (C_{in}\times K_{h}\times k_{w}/g)=C_{out}/g\times(H_{out}\times W_{out}) \\ $$

$$ Computation=C_{out}/g\times H_{out}\times W_{out} \times(C_{in}/g\times K_{h}\times K_{w}/g )^{2}\\ $$

通道重排

不同组之间是没有任何联系的,即得到的特征图只和对应组别的输入有关系。论文中也有这样的描述,这种分组因不同组之间没有任何联系,学习到的特征会非常有限,也很容易导致信息丢失,因此论文提出了通道重排(channel shuffle)。

通道重排的思想如上面图中所示。一般卷积操作中输入特征图的数量比如是 N,该卷积层的卷积核数量是 M,那么 M 个卷积核中的每一个卷积核都要和 N 个特征图的某个区域做卷积,然后相加作为一个卷积的结果。

假设你引入分组操作,设组数 为 g,那么 N 个输入特征图就被分成 g 个组,M 个卷积核就被分成 g 个组,然后在做卷积操作的时候,第一个组的 $M/g \quad\quad$ 个卷积核中的每一个都和第一个组的 $ N/g \quad\quad$ 个输入特征图做卷积得到结果,第二组同理,直到最后一组,如上图 a。不同的颜色代表不同的组,图中有三个组。

这种操作可以大大减少计算量,因为你每个卷积核不再是和输入的全部特征图做卷积,而是和一个组的特征图做卷积。但是如果多个组操作叠加在一起,如上图 a 的两个卷积层都有组操作,显然就会产生边界效应。

即某个输出通道 仅仅来自输入通道 的一小部分。这种计算方式使得网络模型学习得到的特征会非常局限。于是就有了 通道重排来解决这个问题,先看上图 b,在进行 GConv2 之前,对其输入图做一个分配,也就是每个组分成几个次组,然后将不同组的下面的组作为 GConv2 的一个组的输入,使得 GConv2 的每一个组都能卷积输入的所有组的特征图,这和图 c 的通道重排的思想是一样的。

# 通道混洗
def shuffle_channels(x, groups):
    # 先得到输入特征图的 shape,b:batch size,h,w:一张图的 size,c:通道数
    batch_size, channels, height, width = x.size()
    assert channels % groups == 0
    channels_per_group = channels // groups
    #在通道维度上将特征图 reshape 为 groups 行 channels_per_group 列的矩阵
    x = x.view(batch_size, groups, channels_per_group,height, width)
    #  矩阵转置
    x = x.transpose(1, 2).contiguous()
    x = x.view(batch_size, channels, height, width)
    #返回通道维度交叉排序后的 tensor
    return x

ShuffleNet 单元

基于残差块(residual block)和 通道重排(channel shuffle)设计的 ShuffleNet Unit 主要由深度卷积、逐点分组卷积和逐点分组卷积组成。

Shufflenet

# ShuffleNet 中 stride=1 的基本单元
class ShuffleNetUnitA(nn.Module):
    """ShuffleNet unit for stride=1"""
    def __init__(self, in_channels, out_channels, groups=3):
        super(ShuffleNetUnitA, self).__init__()
        assert in_channels == out_channels
        assert out_channels % 4 == 0
        bottleneck_channels = out_channels // 4
        self.groups = groups
        # 1*1 分组卷积降维
        self.group_conv1 = nn.Conv2d(in_channels, bottleneck_channels,
                                        1, groups=groups, stride=1)
        self.bn2 = nn.BatchNorm2d(bottleneck_channels)
        self.depthwise_conv3 = nn.Conv2d(bottleneck_channels,
                                         bottleneck_channels,
                                         3, padding=1, stride=1,
                                         groups=bottleneck_channels)
        self.bn4 = nn.BatchNorm2d(bottleneck_channels)
        # 1*1 分组卷积升维
        self.group_conv5 = nn.Conv2d(bottleneck_channels, out_channels,
                                     1, stride=1, groups=groups)
        self.bn6 = nn.BatchNorm2d(out_channels)

    def forward(self, x):
        out = self.group_conv1(x)
        out = F.relu(self.bn2(out))
        out = shuffle_channels(out, groups=self.groups)
        out = self.depthwise_conv3(out)
        out = self.bn4(out)
        out = self.group_conv5(out)
        out = self.bn6(out)
        out = F.relu(x + out)
        return out
# ShuffleNet 中 stride=2 的基本单元,下采样模块(下采样模块,concat)
class ShuffleNetUnitB(nn.Module):
    """ShuffleNet unit for stride=2"""
    def __init__(self, in_channels, out_channels, groups=3):
        super(ShuffleNetUnitB, self).__init__()
        # 右分支的通道数和左分支的通道数叠加 == 输出特征图的通道数 out_channel(重点,和上面的残差是不一样的)
        out_channels -= in_channels
        assert out_channels % 4 == 0
        bottleneck_channels = out_channels // 4
        self.groups = groups
        self.group_conv1 = nn.Conv2d(in_channels, bottleneck_channels,
                                     1, groups=groups, stride=1)
        self.bn2 = nn.BatchNorm2d(bottleneck_channels)
        self.depthwise_conv3 = nn.Conv2d(bottleneck_channels,
                                         bottleneck_channels,
                                         3, padding=1, stride=2,
                                         groups=bottleneck_channels)
        self.bn4 = nn.BatchNorm2d(bottleneck_channels)
        self.group_conv5 = nn.Conv2d(bottleneck_channels, out_channels,
                                     1, stride=1, groups=groups)
        self.bn6 = nn.BatchNorm2d(out_channels)

    def forward(self, x):
        # 右分支
        out = self.group_conv1(x)
        out = F.relu(self.bn2(out))
        ## Channel Shuffle
        out = shuffle_channels(out, groups=self.groups)
        out = self.depthwise_conv3(out)
        out = self.bn4(out)
        out = self.group_conv5(out)
        out = self.bn6(out)
        # 左分支:3*3 AVG Pool,stride=2
        x = F.avg_pool2d(x, 3, stride=2, padding=1)
        out = F.relu(torch.cat([x, out], dim=1))
        return out

网络结构与实现

ShuffleNet V1 架构主要由一组 ShuffleNet 单元组成,分为三个阶段。每个阶段的第一个构建块应用 stride = 2。一个阶段内的其他超参数保持不变,下一个阶段的输出通道加倍。,我们将每个 ShuffleNet 的瓶颈通道数设置为输出通道的 1/4。stage2 的第一个 block 上不用 GConv,用普通的 $1\times 1 \quad\quad$ 卷积,因为此时输入通道数只有 24,太少了。且每个 stage 中的第一个 block 的 stride=2(对应 ShuffleNet Unit 中的下采样模块,上图 c),其他 block 的 stride=1(对应 ShuffleNet 基本模块,上图 b)

#shuffleNet 网络
class ShuffleNet(nn.Module):
    """ShuffleNet for groups=3"""
    def __init__(self, groups=3, in_channels=3, num_classes=1000):
        super(ShuffleNet, self).__init__()

        self.conv1 = nn.Conv2d(in_channels, 24, 3, stride=2, padding=1)
        stage2_seq = [ShuffleNetUnitB(24, 240, groups=3)] + \
            [ShuffleNetUnitA(240, 240, groups=3) for i in range(3)]
        self.stage2 = nn.Sequential(*stage2_seq)
        stage3_seq = [ShuffleNetUnitB(240, 480, groups=3)] + \
            [ShuffleNetUnitA(480, 480, groups=3) for i in range(7)]
        self.stage3 = nn.Sequential(*stage3_seq)
        stage4_seq = [ShuffleNetUnitB(480, 960, groups=3)] + \
                     [ShuffleNetUnitA(960, 960, groups=3) for i in range(3)]
        self.stage4 = nn.Sequential(*stage4_seq)
        self.fc = nn.Linear(960, num_classes)

    def forward(self, x):
        net = self.conv1(x)
        net = F.max_pool2d(net, 3, stride=2, padding=1)
        net = self.stage2(net)
        net = self.stage3(net)
        net = self.stage4(net)
        net = F.avg_pool2d(net, 7)
        net = net.view(net.size(0), -1)
        net = self.fc(net)
        logits = F.softmax(net)
        return logits

ShuffleNet V2 模型

ShuffleNet v2: V2 的框架结构与 V1 基本相同,包括了 Conv1、Maxpool、Stage 2~5、Global pool 和 FC 等部分。唯一的不同是 V2 比 V1 多了一个 $1 \times 1$Conv5。V2 最大的贡献在于看到了 GPU 访存带宽(内存访问代价 MAC)对于模型推理时间的影响,而不仅仅是模型复杂度,也就是 FLOPs 和参数量 Params 对于推理时间的影响,并由此提出了 4 个轻量级网络设计的原则和一个新颖的卷积 block 架构。

设计思路

首先,作者分析了两个经典结构 ShuffleNet v1 和 MobileNet v2 的运行时间(如下图所示)。提出了一个关键点,之前的轻量级网络都是通过计算网络复杂度的一个间接度量,通过计算浮点运算量来描述轻量级网络的快慢。但是从来不直接考虑运行的速度。

在移动设备中的运行速度不仅仅需要考虑 FLOPs,还需要考虑其他的因素,比如内存访问成本(memory access cost)和平台特点(platform characterics)。所以,ShuffleNet v2 通过控制不同的环境来测试网络在设备上运行速度的快慢,而不是通过 FLOPs 来判断性能指标。

Shufflenet

因此,ShuffleNetv2 提出了设计应该考虑两个原则:

  • 应该使用直接度量(如速度)而不是间接度量(如 FLOPs);
  • 这些指标应该在目标平台上进行评估。

然后 根据这两个原则,提出了四种有效的网络设计原则:

  • G1: 相同的通道宽度最小化内存访问成本(MAC);
  • G2: 过多的群卷积增加 MAC;
  • G3: 网络碎片降低了并行度;
  • G4: 元素操作是不可忽略的。

通道分割

在 ShuffleNet V1 block 的基础上,ShuffleNet V2 block 引入通道分割(Channel Split)这个简单的算子来实现上述目的,如下图 (c) 所示。在每个单元 (block) 的开始,我们将输入特征图的 c 个通道切分成 (split) 两个分支 (branches):$c-c' \quad$ 个通道和 $c'$ 个通道。

#特征图 C 个通道进行切分
def split(x, groups):
    out = x.chunk(groups, dim=1)
    return out

根据 G3 网络碎片尽可能少,其中一个分支保持不变(shortcut connection),另外一个分支包含三个通道数一样的卷积来满足 G1。和 v1 不同,v2 block 的两个 $1\times1 \quad\quad$ 卷积不再使用分组卷积,一部分原因是为了满足 G2,另外一部分原因是一开始的通道切分 (split)操作已经完成了分组效果。

最后,对两个分支的结果进行拼接(concatnate),这样对于卷积 block 来说,输入输出通道数是一样的,符合 G1 原则。和 ShuffleNet v1 一样都使用通道打乱(channel shuffle)操作来保证两个分支的信息进行交互。

通道打乱之后的输出,就是下一个单元的输入。ShuffleNet v1 的 “Add” 操作不再使用,逐元素操作算子如:ReLU 和 DW 卷积 只存在于在右边的分支。

与此同时,我们将三个连续的逐元素操作算子:拼接(“Concat”)、通道打乱(“Channel Shuffle”)和通道拆分(“Channel Split”)合并成一个逐元素算子。根据 G4 原则,这些改变是有利的。

针对需要进行空间下采样的 block,卷积单元(block)进行了修改,通道切分算子被移除,然后 block 的输出通道数变为两倍,详细信息如下图(d) 所示。

Shufflenet

#Shuffle V2 block
class ShuffleUnit(nn.Module):
    def __init__(self, input_c: int, output_c: int, stride: int):
        super(ShuffleUnit, self).__init__()
        # 步长必须在 1 和 2 之间
        if stride not in [1, 2]:
            raise ValueError("illegal stride value.")
        self.stride = stride

        # 输出通道必须能二被等分
        assert output_c % 2 == 0
        branch_features = output_c // 2

        # 当 stride 为 1 时,input_channel 是 branch_features 的两倍
        # '<<' 是位运算,可理解为计算×2 的快速方法
        assert (self.stride != 1) or (input_c == branch_features << 1)

        # 捷径分支
        if self.stride == 2:
            # 进行下采样:3×3 深度卷积+1×1 卷积
            self.branch1 = nn.Sequential(
                self.depthwise_conv(input_c, input_c, kernel_s=3, stride=self.stride, padding=1),
                nn.BatchNorm2d(input_c),
                nn.Conv2d(input_c, branch_features, kernel_size=1, stride=1, padding=0, bias=False),
                nn.BatchNorm2d(branch_features),
                nn.ReLU(inplace=True)
            )
        else:
            # 不进行下采样:保持原状
            self.branch1 = nn.Sequential()

        # 主干分支
        self.branch2 = nn.Sequential(
            # 1×1 卷积+3×3 深度卷积+1×1 卷积
            nn.Conv2d(input_c if self.stride > 1 else branch_features, branch_features, kernel_size=1,
                      stride=1, padding=0, bias=False),
            nn.BatchNorm2d(branch_features),
            nn.ReLU(inplace=True),
            self.depthwise_conv(branch_features, branch_features, kernel_s=3, stride=self.stride, padding=1),
            nn.BatchNorm2d(branch_features),
            nn.Conv2d(branch_features, branch_features, kernel_size=1, stride=1, padding=0, bias=False),
            nn.BatchNorm2d(branch_features),
            nn.ReLU(inplace=True)
        )

    # 深度卷积
    @staticmethod
    def depthwise_conv(input_c, output_c, kernel_s, stride, padding, bias= False):
        return nn.Conv2d(in_channels=input_c, out_channels=output_c, kernel_size=kernel_s,
                         stride=stride, padding=padding, bias=bias, groups=input_c)

    def forward(self, x):
        if self.stride == 1:
            # 通道切分
            x1, x2 = split(x, 2)
            # 主干分支和捷径分支拼接
            out = torch.cat((x1, self.branch2(x2)), dim=1)
        else:
            # 通道切分被移除
            # 主干分支和捷径分支拼接
            out = torch.cat((self.branch1(x), self.branch2(x)), dim=1)
        # 通道混洗
        out = channel_shuffle(out, 2)
        return out

网络结构实现

上图 c,d 显示的卷积 block 叠加起来即组成了最后的 ShuffleNet v2 模型,这样堆叠后的网络是类似 ShuffleNet v1 模型的,v1 和 v2 block 的区别在于,v2 在全局平均池化层(global averaged pooling)之前添加了一个 卷积来混合特征(mix up features),而 v1 没有。但与 v1 一样,v2 的 block 的通道数是按照 0.5x 1x 等比例进行缩放,以生成不同复杂度的 ShuffleNet v2 网络,并标记为 ShuffleNet V2 0.5×、ShuffleNet V2 1× 等模型。

# V2 网络结构
class ShuffleNetV2(nn.Module):
    def __init__(self, stages_repeats, stages_out_channels, num_classes=1000, ShuffleUnit=ShuffleUnit):
        super(ShuffleNetV2, self).__init__()

        if len(stages_repeats) != 3:
            raise ValueError("expected stages_repeats as list of 3 positive ints")
        if len(stages_out_channels) != 5:
            raise ValueError("expected stages_out_channels as list of 5 positive ints")
        self._stage_out_channels = stages_out_channels

        # 输入通道
        input_channels = 3
        output_channels = self._stage_out_channels[0]

        self.conv1 = nn.Sequential(
            nn.Conv2d(input_channels, output_channels, kernel_size=3, stride=2, padding=1, bias=False),
            nn.BatchNorm2d(output_channels),
            nn.ReLU(inplace=True)
        )
        input_channels = output_channels
        self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)

        # 三个基本单元组层
        self.stage2: nn.Sequential
        self.stage3: nn.Sequential
        self.stage4: nn.Sequential

        stage_names = ["stage{}".format(i) for i in [2, 3, 4]]
        for name, repeats, output_channels in zip(stage_names, stages_repeats,
                                                  self._stage_out_channels[1:]):
            # 每个 Stage 的首个基础单元都需要进行下采样,其他单元不需要
            seq = [ShuffleUnit(input_channels, output_channels, 2)]
            for i in range(repeats - 1):
                seq.append(ShuffleUnit(output_channels, output_channels, 1))
            setattr(self, name, nn.Sequential(*seq))
            input_channels = output_channels
        output_channels = self._stage_out_channels[-1]
        self.conv5 = nn.Sequential(
            nn.Conv2d(input_channels, output_channels, kernel_size=1, stride=1, padding=0, bias=False),
            nn.BatchNorm2d(output_channels),
            nn.ReLU(inplace=True)
        )
        # 全局平局池化
        self.global_pool = nn.AdaptiveAvgPool2d((1, 1))
        # 全连接层
        self.fc = nn.Linear(output_channels, num_classes)
        # 权重初始化
        self.init_params()
    def init_params(self):
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                nn.init.kaiming_normal_(m.weight, mode='fan_out')
                if m.bias is not None:
                    nn.init.zeros_(m.bias)
            elif isinstance(m, nn.BatchNorm2d):
                nn.init.ones_(m.weight)
                nn.init.zeros_(m.bias)
            elif isinstance(m, nn.Linear):
                nn.init.normal_(m.weight, 0, 0.01)
                nn.init.zeros_(m.bias)

    def forward(self, x):
        x = self.conv1(x)
        x = self.maxpool(x)
        x = self.stage2(x)
        x = self.stage3(x)
        x = self.stage4(x)
        x = self.conv5(x)
        x = self.global_pool(x)
        x = x.view(x.size(0), -1)
        x = self.fc(x)
        return x

def shufflenet_v2_x0_5(num_classes=1000):
    """
    weight: https://download.pytorch.org/models/shufflenetv2_x0.5-f707e7126e.pth
    """
    model = ShuffleNetV2(stages_repeats=[4, 8, 4],
                         stages_out_channels=[24, 48, 96, 192, 1024],
                         num_classes=num_classes)

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

相关实践学习
【文生图】一键部署Stable Diffusion基于函数计算
本实验教你如何在函数计算FC上从零开始部署Stable Diffusion来进行AI绘画创作,开启AIGC盲盒。函数计算提供一定的免费额度供用户使用。本实验答疑钉钉群:29290019867
建立 Serverless 思维
本课程包括: Serverless 应用引擎的概念, 为开发者带来的实际价值, 以及让您了解常见的 Serverless 架构模式
目录
相关文章
|
7月前
|
人工智能 自然语言处理 搜索推荐
Perplexity AI和秘塔AI相比如何?
【2月更文挑战第16天】Perplexity AI和秘塔AI相比如何?
1107 1
Perplexity AI和秘塔AI相比如何?
|
7月前
|
机器学习/深度学习 网络架构 人工智能
AI - MoE(Mixture-of-Experts)结构
AI - MoE(Mixture-of-Experts)结构
151 1
|
7月前
|
传感器 机器学习/深度学习 算法
无人机视角yolo多模态、模型剪枝、国产AI芯片部署
无人机视角yolo多模态、模型剪枝、国产AI芯片部署
无人机视角yolo多模态、模型剪枝、国产AI芯片部署
|
3天前
|
机器学习/深度学习 人工智能 编解码
【AI系统】MobileVit 系列
MobileViT系列是基于Vision Transformer(ViT)架构设计的轻量级视觉模型,专为移动设备和嵌入式系统优化。MobileViT V1通过结合局部卷积和全局Transformer机制,实现了高性能与低资源消耗的平衡。V2进一步优化了Transformer中的多头自注意力机制,引入了线性复杂度的可分离自注意力,显著提升了计算效率。V3则对融合模块进行了简化,用1x1卷积替代3x3卷积,减少了参数量,同时引入了残差连接,进一步提升了模型性能。这些改进使MobileViT系列在保持高效的同时,能够在资源受限的设备上运行,表现出色。
24 8
【AI系统】MobileVit 系列
|
4天前
|
编解码 人工智能 算法
【AI系统】EfficientNet 系列
本文介绍了EfficientNet系列模型,特别是EfficientNet V1和V2。EfficientNet V1通过NAS技术同时探索网络的宽度、深度和分辨率对模型性能的影响,提出了复合模型缩放方法,以平衡三者关系,实现高效模型扩展。EfficientNet V2在此基础上,引入Fused-MBConv模块,采用渐进式学习策略和自适应正则化技术,进一步提升了模型的训练速度和推理效率。
33 6
【AI系统】EfficientNet 系列
|
3天前
|
机器学习/深度学习 存储 人工智能
【AI系统】什么是微分
自动微分是一种高效准确的计算机程序求导技术,广泛应用于计算流体力学、大气科学、工业设计仿真优化等领域。随着机器学习的发展,自动微分技术与编程语言、计算框架紧密结合,成为AI框架的核心功能之一。本文介绍了自动微分的基本概念及其与手动微分、数值微分和符号微分的区别和优势。
22 4
【AI系统】什么是微分
|
3天前
|
机器学习/深度学习 人工智能 自然语言处理
【AI系统】知识蒸馏原理
本文深入解析知识蒸馏(Knowledge Distillation, KD),一种将大型教师模型的知识高效转移至小型学生模型的技术,旨在减少模型复杂度和计算开销,同时保持高性能。文章涵盖知识蒸馏的基本原理、不同类型的知识(如响应、特征、关系知识)、蒸馏方式(离线、在线、自蒸馏)及Hinton的经典算法,为读者提供全面的理解。
19 2
【AI系统】知识蒸馏原理
|
5天前
|
机器学习/深度学习 人工智能 编解码
【AI系统】MobileNet 系列
本文详细介绍 MobileNet 系列模型,重点探讨其轻量化设计原则。从 MobileNetV1 开始,通过深度可分离卷积和宽度乘数减少参数量,实现低延迟、低功耗。后续版本 V2、V3、V4 逐步引入线性瓶颈、逆残差、Squeeze-and-Excitation 模块、新型激活函数 h-swish、NAS 搜索等技术,持续优化性能。特别是 MobileNetV4,通过通用倒瓶颈(UIB)和 Mobile MQA 技术,大幅提升模型效率,达到硬件无关的 Pareto 最优。文章结合最新深度学习技术,全面解析各版本的改进与设计思路。
25 8
|
5天前
|
机器学习/深度学习 人工智能 编解码
【AI系统】SqueezeNet 系列
本文概述了SqueezeNet及其后续版本SqueezeNext,两者均致力于设计轻量级的神经网络模型。SqueezeNet通过引入Fire模块,显著减少了模型参数量,实现了与AlexNet相当的精度,但参数量仅为后者1/50。SqueezeNext则进一步优化,不仅减少了参数量,还提升了模型运行速度和能效,特别适合在资源受限的设备上部署。文中详细介绍了这两个模型的核心设计理念、关键组件以及其实现方式,为理解和应用轻量化模型提供了宝贵资料。
26 5
|
5天前
|
机器学习/深度学习 人工智能 编解码
【AI系统】Transformer 模型小型化
本文介绍了几种轻量级的 Transformer 模型,旨在解决传统 Transformer 参数庞大、计算资源消耗大的问题。主要包括 **MobileVit** 和 **MobileFormer** 系列,以及 **EfficientFormer**。MobileVit 通过结合 CNN 和 Transformer 的优势,实现了轻量级视觉模型,特别适合移动设备。MobileFormer 则通过并行结构融合了 MobileNet 和 Transformer,增强了模型的局部和全局表达能力。
26 8