剪枝论文三( Sparse Structure Selection)

本文介绍一种剪枝方法( Sparse Structure Selection)。

1. 核心思想

如图为论文中提出的网络框架。F表示残差函数。灰色的block、group和neuron表示它们是不活跃的,对应的比例因子为0,可以被修剪。

在这里插入图片描述
根据作者在论文中所描述的,改论文的贡献体现在以下2个方面:

  1. 作者提出了一个统一的cnn模型训练和剪枝框架。特别地,通过在cnn的某些结构上引入缩放因子和相应的稀疏正则化,将其转化为一个联合稀疏正则化优化问题。
  2. 作者利用改进的随机加速近端梯度(APG)方法,利用稀疏正则化联合优化cnn和标度因子的权重。与以往采用启发式方法来强制稀疏性的方法相比,该方法无需进行微调和多阶段优化,具有更稳定的收敛性和更好的结果。

2. 损失函数

作者引入了一种新的参数——缩放因子 λ λ λ来缩放某些特定结构(神经元、组或块)的输出,并在训练过程中增加了 λ λ λ的稀疏性约束。目标是得到一个稀疏的 λ λ λ。即,如果 λ i = 0 λ_i= 0 λi=0,则可以安全地删除相应的结构,因为它的输出对后续的计算没有贡献。

作者给出的目标函数为:

在这里插入图片描述
式中, L ( y i , C ( x i , W , λ ) ) L(y_i, C(x_i, W, λ)) L(yi,C(xi,W,λ))是样本 x i x_i xi上的损失
R ( ⋅ ) R(·) R()是应用于每个权重的非结构化正则化,例如 l 2 l_2 l2范数作为权重衰减
R s ( ⋅ ) R_s(·) Rs() λ λ λ带权 γ γ γ的稀疏正则化。在论文中使用了最常用的凸松弛 l 1 l_1 l1范数,定义为 γ ∣ ∣ λ ∣ ∣ 1 γ||λ||_1 γλ1

3. 代码实现

3.1 优化器

  1. 使用SGD优化权重weight_decay
  2. 使用APGNAG优化gamma
# 使用SGD优化权重
optimizer1 = SGD([{'params': model.parameters()}], lr=args.lr,
                 momentum=args.momentum, weight_decay=args.weight_decay)

# 如果剪枝,使用APGNAG优化gamma
if args.sss:
    optimizer2 = APGNAG([{'params': model.lambda_block}],
                        lr=args.lr, momentum=args.momentum, gamma=args.gamma)

3.2 训练

根据以下损失函数学习:
在这里插入图片描述
式中,

  1. L ( y i , C ( x i , W , λ ) ) L(y_i, C(x_i, W, λ)) L(yi,C(xi,W,λ))是样本 x i x_i xi上的损失,使用交叉熵损失。
  2. 代码中未使用
  3. R s ( ⋅ ) R_s(·) Rs() γ ∣ ∣ λ ∣ ∣ 1 γ||λ||_1 γλ1,使用l1_loss损失。
def train(epoch):
    model.train()
    for batch_idx, (data, target) in enumerate(train_loader):
        if args.cuda:
            data, target = data.cuda(), target.cuda()
        data, target = Variable(data), Variable(target)

        # 如果剪枝
        if args.sss:
            optimizer1.zero_grad()
            optimizer2.zero_grad()

            # 将model中的前向传递函数foward函数替换为foward_sss
            output = model.forward_sss(data)
            loss = F.cross_entropy(output, target)
            pred = output.data.max(1, keepdim=True)[1]
            loss.backward()

            # 额外增加了r1_loss,用来优化lambda_block
            r1_loss = args.gamma * F.l1_loss(model.lambda_block, torch.zeros(model.lambda_block.size()).cuda(), reduction='sum')
            r1_loss.backward()
            optimizer1.step()
            optimizer2.step()

        # 不剪枝,只使用SGD正常优化权重
        else:
            optimizer1.zero_grad()
            output = model(data)
            loss = F.cross_entropy(output, target)
            pred = output.data.max(1, keepdim=True)[1]
            loss.backward()
            optimizer1.step()

3.4 前向传递函数

训练中采用的前向传递函数由原有的foward替换为foward_sss函数

class ResNet_cifar(nn.Module):
    def forward_sss(self, x):
        x = self.conv1(x)
        num_block = 0
        for block in self.layer1:
            x = block.forward_sss(x, self.lambda_block[num_block])
            num_block += 1
        for block in self.layer2:
            x = block.forward_sss(x, self.lambda_block[num_block])
            num_block += 1
        for block in self.layer3:
            x = block.forward_sss(x, self.lambda_block[num_block])
            num_block += 1
        x = self.avgpool(x)
        x = x.view(x.size(0), -1)
        x = self.fc(x)
        return x

其中block.forward_sss相比于block.forward的变化只有一个。就是增加:

output *= lambda

block.forward_sss如下

    def forward_sss(self, x, drop):
        '''
        剪枝:对比forward,增加了out *= drop
        argument:
            drop: lambda_block[num_block],第num_block个模块中使用的lambda
        '''
        residual = x
        if self.downsample is not None:
            residual = self.downsample(x)

        out = self.bn1(x)
        out = self.relu(out)
        out = self.conv1(out)

        out = self.bn2(out)
        out = self.relu(out)
        out = self.conv2(out)

        out = self.bn3(out)
        out = self.relu(out)
        out = self.conv3(out)

        out *= drop # 增加的代码
        out += residual

        return out

3.5 学习率更新

两个优化器的学习率都随着训练的增加而降低:

for epoch in range(args.start_epoch, args.epochs):
    if epoch in [args.epochs*0.5, args.epochs*0.75]:
        # 学习率随着训练的增加而降低
        for param_group in optimizer1.param_groups:
            param_group['lr'] *= 0.1
        if args.sss:
            for param_group in optimizer2.param_groups:
                param_group['lr'] *= 0.1

源代码

这篇论文主要介绍了一种稀疏结构搜索的方法来有效地调整深度神经网络中的参数,以达到更好的性能和更高的效率。作者将该方法称为Pruning with Neuron Selectivity Ratio (PNSR)。该方法的主要思想是通过对神经元的选择性比率进行剪枝,来寻找最优的网络结构。 具体来说,PNSR方法首先训练一个初始的完整模型,然后通过计算每个神经元的选择性比率来确定哪些神经元可以被剪枝。选择性比率是指在训练过程中神经元被激活的次数与总共经历的迭代次数之比。这个比率越高,说明这个神经元在网络中的作用越大,越不应该被剪枝。根据选择性比率,可以将网络中的神经元分为类:保留、可剪枝和不可剪枝。其中保留的神经元直接保留在模型中,不可剪枝的神经元则不能被剪枝,而可剪枝的神经元则可以被剪枝,并且剪枝后不会影响网络的性能。 接下来,PNSR方法使用一种叫做“剪枝重训练”的策略来进一步优化网络结构。这种策略的基本思想是先剪枝掉一些神经元,然后重新训练网络,使其在剪枝后的结构上取得最佳性能。具体来说,PNSR方法在剪枝后使用一种叫做“结构化剪枝”的方法来调整网络的结构,使得剪枝后的网络结构更加紧凑和简单。然后,PNSR方法使用该结构再次训练网络,直到网络达到最佳性能。 最后,作者在CIFAR-10和ImageNet数据集上对PNSR方法进行了实验。实验结果表明,PNSR方法可以显著减少模型的参数数量,同时保持网络性能不变或稍微下降。这表明,PNSR方法可以有效地提高深度神经网络的效率,并有望在实际应用中得到广泛应用。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值