midasklr / yolov5prune

547 stars 113 forks source link

剪枝失败 #1

Open tfwn opened 3 years ago

tfwn commented 3 years ago

环境:yolov5 是刚刚从github 拉取的最新版本 + yolov5prune 部分文件。 因为在 yolov5prune 工程中测试,发现无法运行。所以复制 train_sparsity.py prune.py prune_utils.py 到 最新的 yolov5 工程中测试。

  1. 在 yolov5 工程中修改 data/yolov5ss.yaml 使用自己的数据训练。 在 models/yolov5ss.yaml 中修改 depth_multiple:0.01 width_multiple:0.01 使用 train_sparsity.py 进行训练,最终可以得到非常小的参数文件。 python train.py --adam --epochs 100 python train_sparsity.py --st --sr 0.0001 --weights runs/train/exp/lest.pt --adam --epochs 100

  2. 训练完成后进行剪枝 python prune.py --weights runs/train/exp1/weights/last.pt --percent 0.3

    d30b10c658380390f9c278c65701809

    到这里无法进行下去了。

问题:

  1. 在 models/yolov5ss.yaml 中修改 depth_multiple:0.01 width_multiple:0.01 这里是否影响后面的剪枝?以及是不是能够这样修改
  2. 请问你的yolov5prune 是基于哪个版本的 yolov5 ? 为啥使用自己的数据无法进行训练 能否和最新的yolov5 进行一下同步呢 非常感谢!
midasklr commented 3 years ago

环境:yolov5 是刚刚从github 拉取的最新版本 + yolov5prune 部分文件。 因为在 yolov5prune 工程中测试,发现无法运行。所以复制 train_sparsity.py prune.py prune_utils.py 到 最新的 yolov5 工程中测试。

  1. 在 yolov5 工程中修改 data/yolov5ss.yaml 使用自己的数据训练。 在 models/yolov5ss.yaml 中修改 depth_multiple:0.01 width_multiple:0.01 使用 train_sparsity.py 进行训练,最终可以得到非常小的参数文件。 python train.py --adam --epochs 100 python train_sparsity.py --st --sr 0.0001 --weights runs/train/exp/lest.pt --adam --epochs 100
  2. 训练完成后进行剪枝 python prune.py --weights runs/train/exp1/weights/last.pt --percent 0.3
d30b10c658380390f9c278c65701809

到这里无法进行下去了。 问题:

  1. 在 models/yolov5ss.yaml 中修改 depth_multiple:0.01 width_multiple:0.01 这里是否影响后面的剪枝?以及是不是能够这样修改
  2. 请问你的yolov5prune 是基于哪个版本的 yolov5 ? 为啥使用自己的数据无法进行训练 能否和最新的yolov5 进行一下同步呢 非常感谢!
  1. depth_multiple:0.01 width_multiple:0.01这两个参数不能这么设置,yolov5s/m/l/x模型有固定的参数设置不可改变,这个参数在:https://github.com/ultralytics/yolov5/blob/daab682b06f8416319c99bdf25aec56616bf6ac1/models/yolo.py#L236

引入后然后通过https://github.com/ultralytics/yolov5/blob/daab682b06f8416319c99bdf25aec56616bf6ac1/models/yolo.py#L249

https://github.com/ultralytics/yolov5/blob/daab682b06f8416319c99bdf25aec56616bf6ac1/models/yolo.py#L254

控制C3模块的深度和宽度,可以详细看看:https://github.com/ultralytics/yolov5/blob/daab682b06f8416319c99bdf25aec56616bf6ac1/models/yolo.py#L234

的实现。 对于yolov5s.yaml给出的: depth_multiple: 0.33 # model depth multiplewidth_multiple: 0.50 # layer channel multiple 设置,例如第一个focus模块参数: [[-1, 1, Focus, [64, 3]], # 0-P1/2 计算出来的实际参数: [[-1, 1, Focus, [32, 3]]

  1. 这个项目基于最新yolov5 v5.0. 建议先熟悉原始yolov5训练自己数据集,然后再剪枝.
tfwn commented 3 years ago
  1. 设置 depth_multiple :0.166 和 width_multiple:0.007, 训练自己的数据,使用 yolov5 依然可以正常训练并得到有效的参数,精度会有损失挺多。
9aae7e75c7bcfb008eafe24258e10a6

代码中应该有一些判断。

  1. 在yolov5 V4.0 上训练了自己的数据集。前几天重新拉取了 yolov5 v5.0, 重新训练了一次,也没有问题。 不过我确实没有仔细看过yolov5 的代码,只是跟着一些文档跑了一下试了试效果。

    另外,拉取yolov5prune 并使用自己的数据集训练出现下面的问题

    dbc095987156ea8847f209188a729bb
midasklr commented 3 years ago
  1. 设置 depth_multiple :0.166 和 width_multiple:0.007, 训练自己的数据,使用 yolov5 依然可以正常训练并得到有效的参数,精度会有损失挺多。
9aae7e75c7bcfb008eafe24258e10a6

代码中应该有一些判断。

  1. 在yolov5 V4.0 上训练了自己的数据集。前几天重新拉取了 yolov5 v5.0, 重新训练了一次,也没有问题。 不过我确实没有仔细看过yolov5 的代码,只是跟着一些文档跑了一下试了试效果。 另外,拉取yolov5prune 并使用自己的数据集训练出现下面的问题
dbc095987156ea8847f209188a729bb
  1. depth_multiple :0.166 和 width_multiple:0.007是不能这么设置你的,具体看模型解析那部分。 你自己设置的 depth_multiple :0.166 和 width_multiple:0.007因为没有预训练模型,从头训练实际效果会比较差。
    1. 已经修复,因为之前转ncnn时候删了一段代码,见: https://github.com/midasklr/yolov5prune/commit/b20bafd23cbe2761fa6d194a660aecd7baead9b3

git pull一下最新代码

midasklr commented 3 years ago

btw,如果你设计的新模型yolov5xs: depth_multiple :0.166 和 width_multiple:0.007 那么可以随便看看某一层,例如: https://github.com/midasklr/yolov5prune/blob/b20bafd23cbe2761fa6d194a660aecd7baead9b3/models/yolov5s.yaml#L21 这个C3模块: [-1, 9, C3, [512]], 根据https://github.com/midasklr/yolov5prune/blob/b20bafd23cbe2761fa6d194a660aecd7baead9b3/models/yolo.py#L562 计算得到C3的 n = max(round(n gd), 1) if n > 1 else n = max(round(90.166),1) = ... = 1 也即是C3有一个bottleneck结构; 然后根据https://github.com/midasklr/yolov5prune/blob/b20bafd23cbe2761fa6d194a660aecd7baead9b3/models/yolo.py#L567 算出c2 = make_divisible(c2 gw, 8) = make_divisible(c2 gw, 8) = make_divisible(3.584, 8) = 8 这样设计出来的模型本身效果会非常差,而且需要你把backbone在imagenet上训练然后提取出来权重做后面检测的预训练权重才会有效果的。

tfwn commented 3 years ago

btw,如果你设计的新模型yolov5xs: depth_multiple :0.166 和 width_multiple:0.007 那么可以随便看看某一层,例如: https://github.com/midasklr/yolov5prune/blob/b20bafd23cbe2761fa6d194a660aecd7baead9b3/models/yolov5s.yaml#L21

这个C3模块: [-1, 9, C3, [512]], 根据 https://github.com/midasklr/yolov5prune/blob/b20bafd23cbe2761fa6d194a660aecd7baead9b3/models/yolo.py#L562

计算得到C3的 n = max(round(n gd), 1) if n > 1 else n = max(round(90.166),1) = ... = 1 也即是C3有一个bottleneck结构; 然后根据 https://github.com/midasklr/yolov5prune/blob/b20bafd23cbe2761fa6d194a660aecd7baead9b3/models/yolo.py#L567

算出c2 = make_divisible(c2 gw, 8) = make_divisible(c2 gw, 8) = make_divisible(3.584, 8) = 8 这样设计出来的模型本身效果会非常差,而且需要你把backbone在imagenet上训练然后提取出来权重做后面检测的预训练权重才会有效果的。

  1. 非常感谢您的说明和解答。 因为之前想要做使用最少资源的一个检测,只需要检测几种物体,如人脸口罩手势等。 在知道 yolov5 时试了一下,又单纯的想着缩减资源消耗,就使用了 depth_multiple :0.166 和 width_multiple:0.007 。感觉效果没有特别差,资源的消耗也特别少,就先留着了。

  2. 谢谢,我再试一下能不能跑通流程。

lijain commented 3 years ago

我现在遇到和这位兄弟一样的问题;我用的是yolov5m6,你prune.py中pruned_yaml={}的内容我换为yolov5m6中的结构。还是遇到下面的问题,请大佬帮忙看下: Fusing layers... Model Summary: 396 layers, 35439396 parameters, 0 gradients, 51.4 GFLOPS model.module_list: <generator object Module.named_children at 0x7fb461068cf0> prune module : dict_keys([]) model_list: {} bn_weights: tensor([]) prune.py:382: UserWarning: This overload of nonzero is deprecated: nonzero() Consider using one of the following signatures instead: nonzero(*, bool as_tuple) (Triggered internally at /opt/conda/conda-bld/pytorch_1603729066392/work/torch/csrc/utils/python_arg_parser.cpp:882.) percent_limit = (sorted_bn == highest_thre).nonzero()[0, 0].item() / len(bn_weights) Traceback (most recent call last): File "prune.py", line 818, in opt=opt File "prune.py", line 382, in test_prune percent_limit = (sorted_bn == highest_thre).nonzero()[0, 0].item() / len(bn_weights) IndexError: index 0 is out of bounds for dimension 0 with size 0

midasklr commented 3 years ago

Fusing layers...

你这里可能是进行了conv bn融合,导致没有了bn层,无法基于bn层进行剪枝. 另外yolov5m6模型结构和yolov5模型结构不一样,后者有24个模块, 如果要对yolov5s/m/l/x6进行剪枝,需要做一些细微的修改.

lijain commented 3 years ago

感谢你的解答: conv bn融合?我就是直接稀疏化训练,这块不知道是哪里融合的? 你说的修改是指prune.py中pruned_yaml={}的内容的修改? 方便加qq?我的qq是1632401541

midasklr commented 3 years ago

感谢你的解答: conv bn融合?我就是直接稀疏化训练,这块不知道是哪里融合的? 你说的修改是指prune.py中pruned_yaml={}的内容的修改? 方便加qq?我的qq是1632401541

model.fuse()会进行conv bn层融合,进而不会有bn层. 无论是yolov5 还是yolov56 ,稀疏训练的关键都在这段 https://github.com/midasklr/yolov5prune/blob/f981e8e21220da0dafbc12bda2761691dd104ca1/train_sparsity.py#L325

注意观察训练时候bn层的随着epoch分布变化是否变得稀疏. 剪枝的时候,字典model_list是所有需要剪枝的bn层,但是你这里为空,应该是: https://github.com/midasklr/yolov5prune/blob/f981e8e21220da0dafbc12bda2761691dd104ca1/prune.py#L350 这部分获得需要剪枝的bn层有问题,需要调试看看.

lijain commented 3 years ago

大佬你好,我重现下载你最新的代码,复现你的yolov5s剪枝: 1 我模型稀疏化训练时,没稀疏化训练的模型是54.63M,稀疏化训练变成了81.66M 2 用稀疏的模型进行剪枝,剪枝0.5 Traceback (most recent call last): File "prune.py", line 803, in opt=opt File "prune.py", line 451, in test_prune pruned_model = ModelPruned(maskbndict=maskbndict, cfg=pruned_yaml, ch=3).cuda() File "/home/mnt_abc004-data/mq/Project/yolov5prune-main/models/yolo.py", line 250, in init self.model, self.save, self.from_to_map = parse_pruned_model(self.maskbndict, deepcopy(self.yaml), ch=[ch]) # model, savelist File "/home/mnt_abc004-data/mq/Project/yolov5prune-main/models/yolo.py", line 711, in parse_prunedmodel m = nn.Sequential([m(args) for _ in range(n)]) if n > 1 else m(*args) # module File "/home/mnt_abc004-data/mq/Project/yolov5prune-main/models/pruned_common.py", line 38, in init self.cv3 = Conv(cv3in+cv2out, cv3out, 1) File "/home/mnt_abc004-data/mq/Project/yolov5prune-main/models/common.py", line 37, in init self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=False) File "/home/mnt_abc004-data/mq/Anaconda3/envs/yolov5-t/lib/python3.7/site-packages/torch/nn/modules/conv.py", line 412, in init False, _pair(0), groups, bias, padding_mode) File "/home/mnt_abc004-data/mq/Anaconda3/envs/yolov5-t/lib/python3.7/site-packages/torch/nn/modules/conv.py", line 83, in init self.reset_parameters() File "/home/mnt_abc004-data/mq/Anaconda3/envs/yolov5-t/lib/python3.7/site-packages/torch/nn/modules/conv.py", line 86, in reset_parameters init.kaiminguniform(self.weight, a=math.sqrt(5)) File "/home/mnt_abc004-data/mq/Anaconda3/envs/yolov5-t/lib/python3.7/site-packages/torch/nn/init.py", line 379, in kaiminguniform fan = _calculate_correct_fan(tensor, mode) File "/home/mnt_abc004-data/mq/Anaconda3/envs/yolov5-t/lib/python3.7/site-packages/torch/nn/init.py", line 348, in _calculate_correct_fan fan_in, fan_out = _calculate_fan_in_and_fan_out(tensor) File "/home/mnt_abc004-data/mq/Anaconda3/envs/yolov5-t/lib/python3.7/site-packages/torch/nn/init.py", line 280, in _calculate_fan_in_and_fan_out receptive_field_size = tensor[0][0].numel() IndexError: index 0 is out of bounds for dimension 0 with size 0 麻烦大佬解答下,谢谢了

lijain commented 3 years ago

刚看了下打印信息: Gamma value that less than 1.0117 are set to zero!

| layer name | origin channels | remaining channels | | model.0.conv.bn | 32 | 32 | | model.1.bn | 64 | 64 | | model.2.cv1.bn | 32 | 32 | | model.2.cv2.bn | 32 | 27 | | model.2.cv3.bn | 64 | 33 | | model.2.m.0.cv1.bn | 32 | 32 | | model.2.m.0.cv2.bn | 32 | 32 | | model.3.bn | 128 | 11 | | model.4.cv1.bn | 64 | 64 | | model.4.cv2.bn | 64 | 27 | | model.4.cv3.bn | 128 | 0 | | model.4.m.0.cv1.bn | 64 | 64 | | model.4.m.0.cv2.bn | 64 | 64 | | model.4.m.1.cv1.bn | 64 | 64 | | model.4.m.1.cv2.bn | 64 | 64 | | model.4.m.2.cv1.bn | 64 | 64 | | model.4.m.2.cv2.bn | 64 | 64 | | model.5.bn | 256 | 20 | | model.6.cv1.bn | 128 | 128 | | model.6.cv2.bn | 128 | 96 | | model.6.cv3.bn | 256 | 127 | | model.6.m.0.cv1.bn | 128 | 128 | | model.6.m.0.cv2.bn | 128 | 128 | | model.6.m.1.cv1.bn | 128 | 128 | | model.6.m.1.cv2.bn | 128 | 128 | | model.6.m.2.cv1.bn | 128 | 128 | | model.6.m.2.cv2.bn | 128 | 128 | | model.7.bn | 512 | 446 | | model.8.cv1.bn | 256 | 109 | | model.8.cv2.bn | 512 | 181 | | model.9.cv1.bn | 256 | 182 | | model.9.cv2.bn | 256 | 142 | | model.9.cv3.bn | 512 | 468 | | model.9.m.0.cv1.bn | 256 | 236 | | model.9.m.0.cv2.bn | 256 | 197 | | model.10.bn | 256 | 69 | | model.13.cv1.bn | 128 | 4 | | model.13.cv2.bn | 128 | 12 | | model.13.cv3.bn | 256 | 65 | | model.13.m.0.cv1.bn | 128 | 12 | | model.13.m.0.cv2.bn | 128 | 15 | | model.14.bn | 128 | 3 | | model.17.cv1.bn | 64 | 0 | | model.17.cv2.bn | 64 | 1 | | model.17.cv3.bn | 128 | 115 | | model.17.m.0.cv1.bn | 64 | 0 | | model.17.m.0.cv2.bn | 64 | 12 | | model.18.bn | 128 | 22 | | model.20.cv1.bn | 128 | 15 | | model.20.cv2.bn | 128 | 18 | | model.20.cv3.bn | 256 | 230 | | model.20.m.0.cv1.bn | 128 | 6 | | model.20.m.0.cv2.bn | 128 | 63 | | model.21.bn | 256 | 144 | | model.23.cv1.bn | 256 | 151 | | model.23.cv2.bn | 256 | 79 | | model.23.cv3.bn | 512 | 337 | | model.23.m.0.cv1.bn | 256 | 124 | | model.23.m.0.cv2.bn | 256 | 210 |

是因为我的model.4.cv3.bn,model.17.cv1.bn ,model.17.m.0.cv1.bn值为0的缘故,剪枝不了? 那为何的稀疏化模型会变大呢?

midasklr commented 3 years ago

del.0.conv.bn | 32 | 32 | | model.1.bn | 64 | 64 | | model.2.cv1.bn | 32 | 32 | | model.2.cv2.bn | 32 |

原始的yolov5s模型大概28M; 剪枝时,每层bn至少要保留1个channel,所以剪枝比例不能随意设置.你这里模型为什么会是54M?

lijain commented 3 years ago

这块我也不是很清楚,我就是根据你的步骤进行操作: 1 训练命令:python train.py --data models/ql_infrared/voc_infrared.yaml --cfg models/ql_infrared/yolov5s.yaml --weights pretain_weights/yolov5s.pt --batch-size 8 --cache-images --device 0,1 --sync-bn 2 稀疏化训练:python train_sparsity.py --st --sr 0.001 --data models/ql_infrared/voc_infrared.yaml --cfg models/ql_infrared/yolov5s.yaml --weights runs/train/exp/weights/best.pt --batch-size 8 --cache-images --device 0,1 --sync-bn 3 剪枝 : python prune.py --weights runs/train/exp7/weights/best.pt --data models/ql_infrared/voc_infrared.yaml --percent 0.5

midasklr commented 3 years ago

这块我也不是很清楚,我就是根据你的步骤进行操作: 1 训练命令:python train.py --data models/ql_infrared/voc_infrared.yaml --cfg models/ql_infrared/yolov5s.yaml --weights pretain_weights/yolov5s.pt --batch-size 8 --cache-images --device 0,1 --sync-bn 2 稀疏化训练:python train_sparsity.py --st --sr 0.001 --data models/ql_infrared/voc_infrared.yaml --cfg models/ql_infrared/yolov5s.yaml --weights runs/train/exp/weights/best.pt --batch-size 8 --cache-images --device 0,1 --sync-bn 3 剪枝 : python prune.py --weights runs/train/exp7/weights/best.pt --data models/ql_infrared/voc_infrared.yaml --percent 0.5

54.63M应该是你的last.pt,除了保存模型,还保存了其他训练中参数,见:https://github.com/midasklr/yolov5prune/blob/f981e8e21220da0dafbc12bda2761691dd104ca1/train.py#L396 实际未剪枝模型大概28M(fp32, no model.half()). 正常训练map多少?稀疏训练map多少?

lijain commented 3 years ago

我的看了下train.py没有问题。保存其它 我是用自己数据集训练的,map还行。 之前直接看这个连接操作:https://blog.csdn.net/jacke121/article/details/117250264 在稀疏训练时模型就减少了一半。感觉很奇怪。