目标检测 YOLOv5 - 模型压缩

目标检测 YOLOv5 - 模型压缩

flyfish

1 什么是剪枝

YOLOv5自带的模型压缩是怎样的呢?就是剪枝。
在一棵树中,把不重要的枝条剪掉,就是剪枝

在这里插入图片描述
园丁的手艺是不同的,不同的园丁剪的效果不同。
在这里插入图片描述

做模型的剪枝与园丁干得工作是一模一样,先看一个回归实例
在这里插入图片描述

拟合数据的结果有正合适,欠拟合,过拟合
直线就是欠拟合,一个每个数据点都经过的曲线就是过拟合了
再看他们的数学表达式,多项式的最高次数是不同的,剪枝就像去掉上图中3次方项和4次方项
在这里插入图片描述

剪枝方法多,因为把不重要的东西去掉,在定义什么东西不重要,各家有各家的方法
如果把项剪的太多了,曲线就变成值线了,这不是我们想要的。
当过拟合的时候,剪太多就把树枝剪的没剩几根,了就变成欠拟合;如果已经拟合的很合适了,再剪也欠拟合了。
期望是精度和召回率都不降低,降低的只有计算量

剪枝的生物学启示

在这里插入图片描述

深度学习的剪枝被认为是人脑中突触剪枝的一个想法,在人脑中之间发生的突触消除。修剪突触从出生时开始,一直持续到20多岁左右。

看一个神经网络
在这里插入图片描述

圈与圈之间的连线就是权重,权重也就是一堆堆的数

在这里插入图片描述
剪节点的可以叫 pruning node 或者 pruning neurons 剪神经元
剪线的可以叫 pruning connections 或者叫 pruning synapses 剪突触,剪权重

在这里插入图片描述
在这里插入图片描述

剪的结果
左图是原始权重矩阵,右图是阈值为0.1的修剪后的矩阵。高亮显示的权重将被删除或者置零。
在这里插入图片描述

weight剪枝的实现

怎么实现呢
在这里插入图片描述

原始的矩阵 * weight_mask = 新的矩阵
这样知道了代码里的weight_mask 和 bias_mask是个什么意思

彩票假说:寻找稀疏的、可训练的神经网络
彩票假说简单说就是是主要是随机初始化的密集神经网络包含一个初始化的子网,通过随机初始化权重的子网络仍可以达到原始网络的精度.

剪枝有unstructured和structured,这两者有什么区别
在这里插入图片描述

非结构化(左)和结构化(右)剪枝的区别:结构化剪枝去除卷积滤波器和内核行,而不仅仅是剪枝连接。
结构化剪枝和非结构化剪枝的主要区别在于剪枝权重的粒度。
非结构化剪枝主要是对单个权重进行裁剪
结构化剪枝的粒度较大,主要是把整行整列的权重移除掉(即把一个神经元去掉),对channel和Filter维度进行裁剪。
可以反过来说因为对单个权重进行裁剪的结果是unstructured,看上去有点乱。对channel和Filter裁剪的结果是structured的,根据裁剪之后是否保持了structe起了一个名字

YOLOv5的剪枝是怎么操作的

YOLOv5提供的一段剪枝代码

def sparsity(model):
    # Return global model sparsity
    a, b = 0., 0.
    for p in model.parameters():
        a += p.numel()
        b += (p == 0).sum()
    return b / a
def prune(model, amount=0.3):
    # Prune model to requested global sparsity
    import torch.nn.utils.prune as prune
    print('Pruning model... ', end='')
    for name, m in model.named_modules():
        if isinstance(m, nn.Conv2d):
            prune.l1_unstructured(m, name='weight', amount=amount)  # prune
            prune.remove(m, 'weight')  # make permanent
    print(' %.3g global sparsity' % sparsity(model))

写一段代码把YOLOv5的剪枝代码用上去,查看剪枝前和剪枝后的区别
关于函数prune.l1_unstructured中amount参数表示要修剪(去除)的参数数量(int或float)。如果是float,则应该在0.0到1.0之间,并表示要修剪参数的百分比。如果是int类型,则表示要删除参数的绝对数量。

import torch
from torch import nn
import torch.nn.utils.prune as prune
import torch.nn.functional as F
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
def sparsity(model):
    # Return global model sparsity
    a, b = 0., 0.
    for p in model.parameters():
        a += p.numel()
        b += (p == 0).sum()
    return b / a
def prune(model, amount=0.3):
    # Prune model to requested global sparsity
    import torch.nn.utils.prune as prune
    print('Pruning model... ', end='')
    for name, m in model.named_modules():
        if isinstance(m, nn.Conv2d):
            prune.l1_unstructured(m, name='weight', amount=amount)  # prune
            prune.remove(m, 'weight')  # make permanent
    print(' %.3g global sparsity' % sparsity(model))
class LeNet(nn.Module):
    def __init__(self):
        super(LeNet, self).__init__()
        # 1 input image channel, 6 output channels, 3x3 square conv kernel
        self.conv1 = nn.Conv2d(1, 6, 3)
        self.conv2 = nn.Conv2d(6, 16, 3)
        self.fc1 = nn.Linear(16 * 5 * 5, 120)  # 5x5 image dimension
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
        x = F.max_pool2d(F.relu(self.conv2(x)), 2)
        x = x.view(-1, int(x.nelement() / x.shape[0]))
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

model = LeNet().to(device=device)
module = model.conv1
print(list(module.named_parameters()))
prune(module, amount=0.3)
print(list(module.named_parameters()))

剪枝前

[('weight', Parameter containing:
tensor([[[[-0.2032, -0.0269, -0.0981],
          [-0.1920, -0.2737,  0.2451],
          [ 0.1116,  0.1331,  0.0147]]],
...
        [[[ 0.3109,  0.0082, -0.0080],
          [-0.3009, -0.0805, -0.0308],
          [-0.0347, -0.2851,  0.1614]]]], device='cuda:0', requires_grad=True)), ('bias', Parameter containing:
tensor([-0.0874,  0.2916,  0.2522,  0.2425, -0.2085,  0.2855], device='cuda:0',
       requires_grad=True))]

剪枝后

Pruning model...  0.267 global sparsity

[('bias', Parameter containing:
tensor([-0.0874,  0.2916,  0.2522,  0.2425, -0.2085,  0.2855], device='cuda:0',
       requires_grad=True)), ('weight', Parameter containing:
tensor([[[[-0.2032, -0.0000, -0.0981],
          [-0.1920, -0.2737,  0.2451],
          [ 0.1116,  0.1331,  0.0000]]],
...
        [[[ 0.3109,  0.0000, -0.0000],
          [-0.3009, -0.0805, -0.0000],
          [-0.0000, -0.2851,  0.1614]]]], device='cuda:0', requires_grad=True))]

我们看到不重要的权重变成了0

版权声明:本文为CSDN博主「TheOldManAndTheSea」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/flyfish1986/article/details/120886299

TheOldManAndTheSea

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

YOLOv5实战垃圾分类目标检测

使用YOLOv5完成垃圾分类的目标检测 课程链接:https://edu.csdn.net/course/detail/35284 垃圾分类是一项利国利民的民生工程,需要全社会的共同参与。YOLOv5是目前流行的

目标检测 YOLOv5 SPP模块

目标检测 YOLOv5 SPP模块 flyfish 版本YOLOv5 : v5 何恺明提出Spatial Pyramid Pooling(空间金字塔池化)论文是《Spatial Pyramid Pooling i