yolov8模型剪枝

部署运行你感兴趣的模型镜像

目 录

一、约束训练(constrained training)

二、剪枝

三:回调训练

本文在官方yolov8.2的基础上进行剪枝。yolov8官网链接:ultralytics/ultralytics: NEW - YOLOv8 🚀 in PyTorch > ONNX > OpenVINO > CoreML > TFLite (github.com)icon-default.png?t=N7T8https://github.com/ultralytics/ultralytics

本文的代码已上传GitHub,链接:yolov8_prune

GitHub - jasonDasuantou/yolov8_prune: This project demonstrates a systematic approach to model optimization, showcasing the importance of fine-tuning in the context of model pruning. It provides a foundation for further research and development in the field of efficient deep learning model deployment.This project demonstrates a systematic approach to model optimization, showcasing the importance of fine-tuning in the context of model pruning. It provides a foundation for further research and development in the field of efficient deep learning model deployment. - jasonDasuantou/yolov8_pruneicon-default.png?t=N7T8https://github.com/jasonDasuantou/yolov8_prune/tree/master


一、约束训练(constrained training)

1.理由

        剪枝(Pruning)是一种神经网络压缩技术,它的目的是减少网络的复杂度,提高计算效率,同时尽量保持模型的性能。剪枝通常涉及移除网络中的一些权重或神经元,从而减少模型的大小和计算需求。然而,剪枝可能会对模型的泛化能力产生负面影响,因为移除的权重可能包含了对模型性能重要的信息。

        为了缓解这个问题,约束化训练(Constrained Training)被引入。约束化训练是在训练过程中加入额外的约束来引导模型学习,以减少剪枝带来的负面影响。在BN(Batch Normalization)层添加L1正则化是一种常见的约束化训练方法。

        L1正则化倾向于产生稀疏的权重矩阵,这意味着在训练后,模型的BN层权重矩阵中会有更多接近于零的值。在下一步剪枝的时候,就把这些有很多0值的矩阵去掉,这样处理后的模型对预测结果影响不大。

2.做法

a.在./ultralytics/engine/trainer.py中添加以下内容:
                # Backward
                self.scaler.scale(self.loss).backward()
 
                # ========== added(新增) ==========
                # 1 constrained training
                l1_lambda = 1e-2 * (1 - 0.9 * epoch / self.epochs)
                for k, m in self.model.named_modules():
                    if isinstance(m, nn.BatchNorm2d):
                        m.weight.grad.data.add_(l1_lambda * torch.sign(m.weight.data))
                        m.bias.grad.data.add_(1e-2 * torch.sign(m.bias.data))
                # ========== added(新增) ==========
 
                # Optimize - https://pytorch.org/docs/master/notes/amp_examples.html
                if ni - last_opt_step >= self.accumulate:
                    self.optimizer_step()
                    last_opt_step = ni
 b.启动训练(/yolov8/train.py):
import os
from ultralytics import YOLO
import torch
os.environ['KMP_DUPLICATE_LIB_OK'] = 'True'


def main():
    model = YOLO(r'ultralytics/cfg/models/v8/yolov8s.yaml').load('runs/detect/yolov8s/weights/best.pt')
    model.train(data="data.yaml", amp=False, imgsz=640, epochs=100, batch=20, device=0, workers=0)


if __name__ == '__main__':
    main()

二.剪枝 

1.目的:结合L1或L0正则化来促进权重的稀疏性,然后剪除那些接近零的权重。

2.做法:在/yolov8/下新建文件prune.py,其中有三个参数:yolo加载的是约束训练后的模型;res_dir设置保存剪枝后的模型地址;fractor为剪枝率。具体内容如下:

from ultralytics import YOLO
import torch
from ultralytics.nn.modules import Bottleneck, Conv, C2f, SPPF, Detect
from copy import deepcopy

# Load a model
yolo = YOLO("./runs/detect/yolov8s/weights/last.pt")
# Save model address
res_dir = "./runs/detect/prune/weights/prune.pt"
# Pruning rate
factor = 0.75

yolo.info()
model = yolo.model
ws = []
bs = []

for name, m in model.named_modules():
    if isinstance(m, torch.nn.BatchNorm2d):
        w = m.weight.abs().detach()
        b = m.bias.abs().detach()
        ws.append(w)
        bs.append(b)
        # print(name, w.max().item(), w.min().item(), b.max().item(), b.min().item())

# keep

ws = torch.cat(ws)
threshold = torch.sort(ws, descending=True)[0][int(len(ws) * factor)]
print(threshold)


def prune_conv(conv1: Conv, conv2: Conv):
    gamma = conv1.bn.weight.data.detach()
    beta = conv1.bn.bias.data.detach()
    keep_idxs = []
    local_threshold = threshold
    while len(keep_idxs) < 8:
        keep_idxs = torch.where(gamma.abs() >= local_threshold)[0]
        local_threshold = local_threshold * 0.5
    n = len(keep_idxs)
    # n = max(int(len(idxs) * 0.8), p)
    # print(n / len(gamma) * 100)
    # scale = len(idxs) / n
    conv1.bn.weight.data = gamma[keep_idxs]
    conv1.bn.bias.data = beta[keep_idxs]
    conv1.bn.running_var.data = conv1.bn.running_var.data[keep_idxs]
    conv1.bn.running_mean.data = conv1.bn.running_mean.data[keep_idxs]
    conv1.bn.num_features = n
    conv1.conv.weight.data = conv1.conv.weight.data[keep_idxs]
    conv1.conv.out_channels = n

    if conv1.conv.bias is not None:
        conv1.conv.bias.data = conv1.conv.bias.data[keep_idxs]

    if not isinstance(conv2, list):
        conv2 = [conv2]

    for item in conv2:
        if item is not None:
            if isinstance(item, Conv):
                conv = item.conv
            else:
                conv = item
            conv.in_channels = n
            conv.weight.data = conv.weight.data[:, keep_idxs]


def prune(m1, m2):
    if isinstance(m1, C2f):  # C2f as a top conv
        m1 = m1.cv2

    if not isinstance(m2, list):  # m2 is just one module
        m2 = [m2]

    for i, item in enumerate(m2):
        if isinstance(item, C2f) or isinstance(item, SPPF):
            m2[i] = item.cv1

    prune_conv(m1, m2)


for name, m in model.named_modules():
    if isinstance(m, Bottleneck):
        prune_conv(m.cv1, m.cv2)

seq = model.model
for i in range(3, 9):
    if i in [6, 4, 9]: continue
    prune(seq[i], seq[i + 1])

detect: Detect = seq[-1]
last_inputs = [seq[15], seq[18], seq[21]]
colasts = [seq[16], seq[19], None]
for last_input, colast, cv2, cv3 in zip(last_inputs, colasts, detect.cv2, detect.cv3):
    prune(last_input, [colast, cv2[0], cv3[0]])
    prune(cv2[0], cv2[1])
    prune(cv2[1], cv2[2])
    prune(cv3[0], cv3[1])
    prune(cv3[1], cv3[2])

for name, p in yolo.model.named_parameters():
    p.requires_grad = True

#yolo.val(workers=0)  # 剪枝模型进行验证 yolo.val(workers=0)
yolo.info()
# yolo.export(format="onnx")  # 导出为onnx文件
# yolo.train(data="./data/data_nc5/data_nc5.yaml", epochs=100)  # 剪枝后直接训练微调
ckpt = {
            'epoch': -1,
            'best_fitness': None,
            'model': yolo.ckpt['ema'],
            'ema': None,
            'updates': None,
            'optimizer': None,
            'train_args': yolo.ckpt["train_args"],  # save as dict
            'date': None,
            'version': '8.0.142'}

torch.save(ckpt, res_dir)

最后为了保存的模型占用内存更小,重写了一下ckpt。

三:回调训练

1.目的:剪枝可能会暂时降低模型的性能,因为一些有用的权重被移除。回调训练可以帮助模型调整剩余的权重,以补偿被剪枝掉的权重,从而恢复或甚至提高模型的性能。

2.做法:

a.将先前在./ultralytics/engine/trainer.py中添加的L1正则化部分注释掉:
                # Backward
                self.scaler.scale(self.loss).backward()
 
                # # ========== added(新增) ==========
                # # 1 constrained training
                # l1_lambda = 1e-2 * (1 - 0.9 * epoch / self.epochs)
                # for k, m in self.model.named_modules():
                #     if isinstance(m, nn.BatchNorm2d):
                #         m.weight.grad.data.add_(l1_lambda * torch.sign(m.weight.data))
                #         m.bias.grad.data.add_(1e-2 * torch.sign(m.bias.data))
                # # ========== added(新增) ==========
 
                # Optimize - https://pytorch.org/docs/master/notes/amp_examples.html
                if ni - last_opt_step >= self.accumulate:
                    self.optimizer_step()
                    last_opt_step = ni
b.在./ultralytics/engine/trainer.py中的函数setup_model修改:
    def setup_model(self):
        """Load/create/download model for any task."""
        if isinstance(self.model, torch.nn.Module):  # if model is loaded beforehand. No setup needed
            return
 
        model, weights = self.model, None
        ckpt = None
        if str(model).endswith(".pt"):
            weights, ckpt = attempt_load_one_weight(model)
            cfg = weights.yaml
        else:
            cfg = model
        self.model = self.get_model(cfg=cfg, weights=weights, verbose=RANK == -1)  # calls Model(cfg, weights)
        # ========== added(新增) ==========
        # 2 finetune 回调训练
        self.model = weights
        # ========== added(新增) ==========
        return ckpt
c.我们再次启动训练(/yolov8/train.py):
import os
from ultralytics import YOLO
import torch
os.environ['KMP_DUPLICATE_LIB_OK'] = 'True'


def main():
    model = YOLO('runs/detect/prune/weights/prune.pt')
    model.train(data="data.yaml", imgsz=640, epochs=100, batch=20, device=0, workers=0)


if __name__ == '__main__':
    main()

您可能感兴趣的与本文相关的镜像

Yolo-v5

Yolo-v5

Yolo

YOLO(You Only Look Once)是一种流行的物体检测和图像分割模型,由华盛顿大学的Joseph Redmon 和Ali Farhadi 开发。 YOLO 于2015 年推出,因其高速和高精度而广受欢迎

<think>首先,用户的问题是:“我想查找YOLOv8模型剪枝的相关信息,如方法、技术、工具等 请问YOLOv8模型剪枝的方法和技术”。所以,我需要基于提供的引用,给出关于YOLOv8模型剪枝的方法、技术和工具的信息。 回顾提供的引用: - 引用[1]:提到基于DepGraph的YOLOv8模型剪枝方法,使用依赖图分析进行安全的模型剪枝,保持性能的同时减小模型大小和计算量。 - 引用[2]:提到YOLOv8模型剪枝笔记,使用DepGraph和Network Slimming网络瘦身技术。解释了maskbndict的用法,它记录了BN层的通道剪枝信息。 - 引用[3]:提到YOLOv8实战-模型剪枝,包括运行prune.py得到剪枝后的模型,并提到回调训练(fine-tuning)。 系统指令要求我使用中文回答,结构清晰,帮助用户逐步解决问题。另外,我需要生成相关问题。 我的回答应该包括: 1. **介绍YOLOv8模型剪枝**:简要解释模型剪枝的概念和重要性。 2. **主要方法和技术**:基于引用,描述DepGraph方法、Network Slimming等。 3. **工具和流程**:提到如何实操,如使用prune.py脚本。 4. **添加引用标识**:在引用相关段落时添加[^1]、[^2]、[^3]。 5. **生成相关问题**:在回答结束时,添加相关问题部分(使用§§相关问题§§)。 结构回答: - **开头**:直接回答用户的查询,概述YOLOv8模型剪枝。 - **主体**:分点解释方法、技术、工具。 - **结尾**:总结。 最后,生成相关问题。 关键点从引用中提取: - **方法**:基于DepGraph的依赖图分析(引用[1]),使用Network Slimming网络瘦身(引用[2])。 - **技术**:通过BN层的gamma参数计算maskbndict来标识剪枝通道(引用[2]),涉及模型加载和fine-tuning(引用[2][3])。 - **工具**:提到运行prune.py脚本(引用[3]),生成剪枝后的模型如prune.pt和ONNX文件。 现在,构建回答: 1. **概述**:YOLOv8模型剪枝主要通过结构化剪枝方法来减小模型大小,同时保持性能。常用方法是基于DepGraph的依赖图分析和Network Slimming。 2. **详细解释**: - **DepGraph方法**:使用依赖图分析模型结构,确保剪枝安全有效(引用[1])。 - **Network Slimming**:通过BN层的gamma参数绝对值来判断通道重要性;设置剪枝阈值,计算maskbndict记录剪枝信息(引用[2])。 - **实操流程**:如运行prune.py脚本进行剪枝,然后fine-tuning剪枝后的模型(引用[3])。 3. **工具**:Python脚本如prune.py,PyTorch用于模型加载和处理(引用[2][3])。 确保回答真实可靠:基于提供的引用,不添加外部知识。 添加引用: - 在描述DepGraph时加[^1] - 在描述maskbndict时加[^2] - 在描述实操时加[^3] 生成相关问题: - 相关问题应基于主题,如剪枝方法、优化、应用。 - 例如: 1. 如何设置DepGraph剪枝的阈值? 2. 剪枝后如何进行fine-tuning? 3. YOLOv8剪枝在实际应用中的优势? 最后,使用数学表达式时遵循指令:行内用$...$,独立用$$...$$。但由于用户问题涉及模型剪枝,可能不需要数学表达式,除非有公式;如果有,确保正确。 在回答中,可能提到gamma参数,可以写成行内数学表达式:如$\gamma$。 完整回答结构: - 开头段落 - 方法部分 - 技术部分 - 工具部分 - 总结 - §§相关问题§§ </think>### YOLOv8模型剪枝的方法和技术 基于您提供的引用,YOLOv8模型剪枝主要通过结构化剪枝技术实现,目的是在保持模型准确性的同时,显著减小模型大小和计算量。以下是详细的方法、技术和工具介绍,结合引用中的核心内容进行说明。 #### 1. **主要剪枝方法** YOLOv8剪枝的核心方法是基于依赖图(DepGraph)的分析技术,结合通道级结构化剪枝: - **DepGraph(依赖图分析)**:这是一种安全的结构化剪枝方法,通过构建模型层间的依赖关系图,识别可剪枝的通道或层,避免破坏模型的功能完整性。例如,在YOLOv8中,DepGraph用于分析卷积层和批量归一化(BN)层之间的连接,确保剪枝模型仍能高效运行[^1]。 - **Network Slimming(网络瘦身)**:该方法基于BN层的缩放因子($\gamma$参数)进行通道重要性评估。具体步骤包括: - 计算BN层$\gamma$参数的绝对值,作为通道重要性的指标。 - 设定剪枝阈值,移除$\gamma$值低于阈值的通道(即贡献较小的通道)。 - 最终生成`maskbndict`字典,记录每个BN层中被保留和剪枝的通道信息[^2]。 #### 2. **关键技术细节** - **剪枝过程**: - 训练时,模型通过稀疏正则化(如L1正则化)优化$\gamma$参数,使其趋向于稀疏分布。 - 剪枝阈值通常基于经验或验证集性能设置,例如移除$\gamma$值最低的20%~50%通道。 - 剪枝后,模型结构发生变化,例如ONNX文件大小减小,层间连接简化[^3]。 - **剪枝后处理**: - 加载剪枝模型:使用PyTorch加载剪枝后的权重和`maskbndict`,例如: ```python model = torch.load(save_path)["model"] maskbndict = torch.load(save_path)["maskbndict"] ``` `maskbndict`用于分析剪枝结构和后续优化[^2]。 - **回调训练(Fine-tuning)**:剪枝模型需通过fine-tuning恢复性能。引用[3]提到,在YOLOv8中,通常使用自定义脚本(如修改训练参数)对剪枝模型进行微调,以防止精度下降。 #### 3. **工具和实操流程** - **关键工具**: - **Python脚本**:如`prune.py`(引用[3]),用于执行剪枝操作并输出剪枝模型(如`prune.pt`)。 - **PyTorch框架**:处理模型加载、剪枝计算和`maskbndict`生成。 - **ONNX工具**:用于导出和可视化剪枝后的模型结构,便于部署。 - **标准流程**: 1. **依赖图分析**:构建YOLOv8模型的DepGraph,识别可剪枝层。 2. **通道剪枝**:基于$\gamma$参数计算`maskbndict`,移除低重要性通道。 3. **生成剪枝模型**:运行脚本(如`prune.py`)输出剪枝后的权重文件(如`prune.pt`)和精简的ONNX模型。 4. **Fine-tuning**:对剪枝模型进行微调,恢复精度[^3]。 #### 总结 YOLOv8模型剪枝主要依赖DepGraph和Network Slimming技术,强调结构化通道剪枝和安全依赖分析,能有效压缩模型大小(例如ONNX文件体积显著减小)[^1][^3]。实操中需注意$\gamma$阈值设置和fine-tuning策略,以平衡压缩率和性能。更多细节可参考原始引用中的完整流程。
评论 34
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值