yolov5 モデルの枝刈りを最初から開始する·
1 はじめに
[プロセス全体で、AttributeError、ModuleNotFoundError、RuntimeError、SyntaxError、TypeError などの解決策を含む、normal train、spareTrain、prune、finetune で 10 を超える問題が発生しました。詳細はコンテンツを参照してください。]
既存のモデルを ARM プラットフォームに移植するには、モデルの精度を確保しながら、モデルの計算電力消費と推論時間を削減します。
私たちは以前にYOLOv5、YOLOv7、YOLOv8 を比較する実験を行い、さまざまなバージョンのモデルの推論時間と精度を組み合わせ、ほとんどの人のブログの説明を含む多くの情報をチェックし、ほとんどの人の経験と組み合わせると、次のように感じました。 yolov5 は汎化能力が優れています。したがって、独自のモデルをトレーニングして X86 および ARM プラットフォームにデプロイすることを検討したとき、小規模モデルの軽量デプロイメントを容易にするために、yolov5 用にモデルをトレーニングおよびプルーニングしました。
もちろん、ターゲット検出の推論時間を短縮するために、最終モデルで INT8 量子化を実行する必要もあります。
2 GitHub からソースコードを取得する
次のパスからソース コードをダウンロードします。
https://github.com/midasklr/yolov5prune/tree/v6.0
この記事では、上記の GitHub から 6.0 バージョンを削除しています。
3原則
[いくつかのブログ/記事での yolov5 プルーニングの紹介に基づいて、yolov5 モデル プルーニングの原理を簡単にまとめてみましょう]
3.1 原則
主要論文: ネットワークスリミングによる効率的な畳み込みネットワークの学習
参考: 効率的な ConvNet のためのプルーニング フィルター( https://arxiv.org/abs/1608.08710 )
参照: https://blog.csdn.net/qq_42835363/article/details/129125376?spm=1001.2014.3001.5501
参照: https://blog.csdn.net/IEEE_FELLOW/article/details/117236025
入力は BN (バッチ正規化) レイヤーを通過して、正規化された分布を取得します。BN 層には 2 つのトレーニング可能なパラメーター γ (ガンマ) と β (ベータ) があります。
ガンマとベータγが 0 に近い場合、入力は 0 を乗算したことと同じになります。このとき、チャネルの畳み込みは 0 を出力しますが、これは意味がありません。したがって、そのような冗長チャネルを削除してもモデルのパフォーマンスに影響はないと考えられます。
通常のネットワーク トレーニングでは、初期化によりガンマは一般に 1 付近に分布します。ガンマを 0 に近づけるには、L1 正則化を追加して係数をスパースにすることでガンマを制限できます。論文では、gammaL1 正則化を追加したトレーニングをスパース トレーニングと呼んでいます。
スパーストレーニング後、まばらに小さな層が切り出され、対応するアクティベーションも非常に小さいため、後続への影響は非常に小さいです。このプロセスを繰り返すことで、小さなモデルが得られます。手順は図に示されています。 1.
図1
3.2 ネットワークスリム化プロセス
① まずネットワークを初期化し、BN 層のパラメータに L1 正則化を追加してネットワークをトレーニングします。
② ネットワーク内のγ(ガンマ)の統計をとり、ネットワークを枝刈りする枝刈り率を設定します。
③ 枝刈りしたネットワークを微調整して枝刈り作業を完了します。
4 つの具体的な実装ステップ
4.1 仮想環境のインストール
ダウンロードしたソースコードを解凍し、yolov5prune_6.0ディレクトリに入り、以下の操作を順番に実行します。
# 1 创建虚拟环境
conda create -n yolov5prune
# 2 激活虚拟环境
conda activate yolov5prune
# 3 安装虚拟环境(根据yolov5prune_6.0根目录下的requirements.txt安装)
pip install -r requirements.txt
4.2 設定パラメータ
4.2.1 データセットパラメータ
独自のデータセットの構造は次のとおりです
--datasTrain
------images
----------train # 存放训练数据集的图片(.jpg)
----------val
----------test
------labels
----------train # 存放训练图片对应的标签文件(.txt)
----------val
----------tes
/yolov5prune_6.0/data/ ディレクトリに、coco128.yaml の構造に従って my_yolov5.yaml ファイルを作成します。内容は以下の通りです
# Train/val/test sets as
# 1) dir: path/to/imgs,
# 2) file: path/to/imgs.txt, or
# 3) list: [path/to/imgs1, path/to/imgs2, ..]
path: /home/user/hlj/MyTrain/datasTrain3_More/ # dataset root dir
train: images/train/ # train images (relative to 'path') 128 images
val: images/val/ # val images (relative to 'path') 128 images
test: images/test/ # test images (optional)
nc: 11 # number of classes
names: ['pedes', 'car', 'bus', 'truck', 'bike', 'elec', 'tricycle', 'coni', 'warm', 'tralight', 'specVehi']
4.2.2 モデル構造パラメータ
yolov5prune_6.0/models/yolov5s.yaml のターゲット検出タイプを変更して、独自のデータセット内のターゲット検出タイプの数に適応させます。次のように
nc: 11
4.2.3 train.pyのパラメータ
train.py にパラメータを設定します。主に次のものが含まれます。
'--weights', default='./yolov5s.pt' # 由于我要从头训练,所以注释了此参数
'--cfg', default='./models/yolov5s.yaml'
'--data', default='./data/my_yolov5.yaml'
'--epochs', default=300 # 由于从头训练,所以epochs值设的比较大
'--batch-size', default=-1
'--imgsz', default=640 # 考虑部署
4.3 通常のトレーニング
4.3.1 準備
SSH接続しているので、最初にtmuxセッションを作成/開きます
tmux new -s prunesession
[最初に Ctrl+B を押し、次に d を単独で押して] セッションを終了した場合、次回セッションに入るにはコマンドを使用する必要があります。
tmux a -t prunesession
セッションに参加するには、まずプロジェクト ディレクトリに入り、仮想環境をアクティブ化します (アクティブ化されている場合は無視できます)。
cd ../yolov5prune_6.0/
source activate yolov5prune
トレーニング後にセッションを削除する
tmux kill-session -t prunesession
4.3.2 トレーニングと問題解決
次のコマンドを実行してトレーニングを実行します
python3 train.py
【質問1】
train.py ファイルを実行すると、次のエラーが報告されました。
ModuleNotFoundError: No module named 'utils.loggers.wandb'
他の人の戦略に従って、U God の yolov5_6.0 バージョンに対応するコードをダウンロードし、yolov5_6.0\utils\loggers\ ディレクトリ内の wandb フォルダー全体を yolov5prune_6 にコピーします。 0\utils\loggers ディレクトリ。
【質問2】
python3 train.py を再起動すると、train.py でパラメータを設定するときに、「--weights」パラメータをコメントアウトできないという問題が報告されています。
AttributeError: 'Namespace' object has no attribute 'weights'
したがって、「weights」パラメーターは次のように設定されます。これは、事前トレーニングされた重みが使用されず、モデルが最初からトレーニングされることを意味します。
'--weights', default=''
【質問3】
理由はわかりませんが、ubuntu では次のような numpy の問題が報告されていますが、ローカルで実行する場合にはそのような問題は発生しません。
raise AttributeError(__former_attrs__[attr])
AttributeError: module 'numpy' has no attribute 'int'.
新しいバージョンの numpy には np.int が含まれていないことが判明しましたが、これはソース コードを変更することで解決できます。
以下に示すように、yolov5prune_6.0/utils/ ディレクトリの datasets.py 内のすべての...astype(np.int) を...astype(int) に変更します。
441 bi = np.floor(np.arange(n) / batch_size).astype(int) # batch index
483 self.batch_shapes = np.ceil(np.array(shapes) * img_size / stride + pad).astype(int) * stride
854 b = xywh2xyxy(b.reshape(-1, 4)).ravel().astype(int)
以下に示すように、yolov5prune_6.0/utils/ ディレクトリ内の general.py 内のすべての...astype(np.int) を...astype(int) に変更します。
510 classes = labels[:, 0].astype(int) # labels = [class xywh]
525 class_counts = np.array([np.bincount(x[:, 0].astype(int), minlength=nc) for x in labels])
【質問4】
File "/home/user/hlj/MyTrain/yolov5prune_6.0/utils/loss.py", line 217, in build_targets
indices.append((b, a, gj.clamp_(0, gain[3] - 1), gi.clamp_(0, gain[2] - 1)))
RuntimeError: result type Float can't be cast to the desired output type long int
参照参照: https://blog.csdn.net/Thebest_jack/article/details/125649451 次の操作を実行します。
yolov5prune_6.0/utils/ ディレクトリ内の loss.py のソース コードを変更します。
#(1) 183行左右
for i in range(self.nl):
anchors, shape = self.anchors[i], p[i].shape # anchors = self.anchors[i]
gain[2:6] = torch.tensor(p[i].shape)[[3, 2, 3, 2]] # xyxy gain
#(2)218行后
# indices.append((b, a, gj.clamp_(0, gain[3] - 1), gi.clamp_(0, gain[2] - 1)))
上一行代码改为如下
indices.append((b, a, gj.clamp_(0, shape[2] - 1), gi.clamp_(0, shape[3] - 1))) # image, anchor, grid indices
【質問5】
このエポック中に、次のような質問が報告されています。
File "..../yolov5prune_6.0/utils/plots.py", line 116, in text
w, h = self.font.getsize(text) # text width, height
AttributeError: 'FreeTypeFont' object has no attribute 'getsize'
これは、新しいバージョンの Pillow がインストールされ、pip install tf-models-official によって getsize 機能が削除され
、Pillow 9.5 にダウングレードすることで問題が解決されたためです。問題を解決するには、次の方法を試してください。[質問 8] を参照してください。
pip install Pillow==9.5
【質問6】
epoch 0 が終了し、val が終了すると、次の問題が報告されます。
File ".....\yolov5prune_6.0\utils\callbacks.py", line 77, in run
logger['callback'](*args, **kwargs)
TypeError: on_fit_epoch_end() missing 1 required positional argument: 'fi'
公式のソース コードを見つけて、yolov5_6.0/utils/ の下にあるロガー ファイル全体をコピーします。これで問題ありません。バージョンの不一致が原因である可能性があります。
【質問7】
yolov5prune_6.0/utils/general.py line471
return re.sub(pattern="[|@#!?·$€%&()=??^*;:,¨′><+]", repl="_", string=s)
SyntaxError:(unicoda error)'utf-8' code can't decode byte 0xal in position 6: invalid start byte。
「utf-8」コードがサポートされていないのが問題のはずで、以下のエンコード形式を追加しましたが、結局解決できませんでした。対応する関数の機能を調べたところ、文字列をクリーンアップする (特殊文字をアンダースコアに置き換える) だけであることがわかったので、そのコード行を直接変更しました。これはプログラム全体には影響しませんでした。
# -*- coding: utf-8 -*-
【まとめ】
いろいろ問題が続き、[質問5]はどうでもよくなりましたが、最終的には python3 train.py が正常に動作するようになりました。
4.4 スパーストレーニング
4.4.1 パラメータの設定
train_sparity.pyのパラメータを設定する
'--st', action='store_true',default=True,
'--sr', type=float, default=0.0001,
'--weights', type=str, default=ROOT / '',
'--cfg', type=str, default='./models/yolov5s.yaml',
'--data', type=str, default='./data/my_yolov5.yaml',
'--epochs', type=int, default=300
'--batch-size', type=int, default=-1, # 注意【问题8】的发生
'--imgsz', '--img', '--img-size', type=int, default=640,
'--adam', action='store_true', default=True,
4.4.2 希薄なトレーニングと問題
次のコマンドを実行してスパーストレーニングを実行します。
python train_sparity.py
【質問8】
loggers.on_params_update({"batch_size": batch_size})
AttributeError: 'Loggers' object has no attribute 'on_params_update'
autobatch が原因と思われるので、最初にパラメータ '-batch-size'、type=int、default=-1 を固定値のdefault=2 に変更したところ、epoch0 は正常になるようになりました。しかし、[問題 5]の問題がまだ残っており、学習には影響しませんが、解決する必要があると感じています。結局のところ、これは AttributeError の問題です。解決策は次のとおりです。
# pillow版本太新的原因,新版的getsize属性被删除掉了。
pip3 uninstall pillow
pip3 nstall pillow==9.5
【質問9】
Epoch0のval終了後、以下の問題が報告されました
File "/home/user/hlj/MyTrain/yolov5prune_6.0/utils/callbacks.py", line 77, in run
logger['callback'](*args, **kwargs)
TypeError: Loggers.on_fit_epoch_end() takes 5 positional arguments but 6 were given
この問題は、[問題 6] を解決するために、プロジェクト内の utils/loggers/init.py ファイルを公式ファイルに置き換えたことが原因です。 def on_fit_epoch_end(self, vals, bn_weights, epoch, best_fitness init.py ファイル内、fi) bn_weights を減らすには、このプロジェクトの prune プロジェクトの下にファイルを再コピーします。
4.5 剪定
4.5.1 パラメータの設定
トリミング率パラメータを設定します。小さいものから大きいものまで試すことができます。cfg のモデル ファイルが重みに対応している必要があることに注意してください。そうしないと、prune の実行プロセス中にキーの値が一致しないという問題が発生します。トリミングの完了後に、対応するモデル pruned_model.pt が保存されます。
prune.py ファイルで、次のパラメータを変更します。
'--data', type=str, default=ROOT / 'data/my_yolov5.yaml',
'--weights', nargs='+', type=str, default=ROOT / 'runs/train/spaweight/last.pt'
'--cfg', type=str, default='./models/yolov5s.yaml',
'--percent', type=float, default=0.1,
'--batch-size', type=int, default=16,
'--imgsz', '--img', '--img-size', type=int, default=640,
走る
python prune.py
【質問10】
SyntaxError: Non-UTF-8 code starting with '\xe5' in file /home/user/hlj/MyTrain/yolov5prune_6.0/prune.py on line 400, but no encoding declared; see https://peps.python.org/pep-0263/ for details
解決策: 該当する行を見つけて、コメント内容コードの形式に問題があることがわかり、削除するか、中国語を英語に変更します。
【質問11】
return func(*args, **kwargs)
TypeError: run() got an unexpected keyword argument 'cfg'
解決策は、次のように prune.py ソース コードの run() 関数にパラメータを追加することです。
cfg = './model/yolov5s.yaml'
4.5.2 枝刈り
スパーストレーニング後にモデル best.pt をプルーニングします。
パラメータが設定されている場合は、python prune.pyを直接実行します。
python prune.py
それ以外の場合、渡される重みはスパース トレーニングによって取得された重みです。
python prune.py --weights runs/train/exp_sparity/weights/best.pt --percent 0.5 --cfg models/yolov5s.yaml
カットが完了すると、対応するモデル pruned_model.pt がルート ディレクトリに保存されます。
4.6 プルーニングされたネットワークの微調整
4.6.1 パラメータの設定
以下のように、finetune_pruned.py の関連パラメータを変更します。
'--weights', type=str, default=ROOT / 'pruned_model.pt',
'--cfg', type=str, default='./models/yolov5s.yaml',
'--data', type=str, default=ROOT / 'data/my_yolov5.yaml',
'--epochs', type=int, default=100
'--batch-size', type=int, default=16,
'--imgsz', '--img', '--img-size', type=int, default=640,
'--adam', action='store_true', default=True,
'--workers', type=int, default=8,
'--project', default=ROOT / 'runs/finetune',
4.6.2 微調整
Finetune_pruned.py のパラメータが変更されていない場合は、以下を実行します。
python finetune_pruned.py --weights pruned_model.pt --adam --epochs 100
finetune_pruned.pyのパラメータを直接変更しているので直接実行
python finetune_pruned.py
実行すると[問題9]が報告されますが、該当する解決策に従ってFine_tuneは正常に起動できます。
4.7 循環スパーストレーニング -> プルーニング -> ネットワークの微調整
今回はスパース化→枝刈り→微調整を1回だけ行いました。通常のトレーニングでもスパース トレーニングでも、事前トレーニングされたモデルは使用されません。比較には、モデルのさまざまなスケール (10%、20%、30%) でトリミングした後の微調整結果が含まれます。
表 2 ターゲット検出モデルの枝刈りの比較
電車 | スパリティ | 剪定前 | フィンチューン | フィンチューン | フィンチューン | |
---|---|---|---|---|---|---|
プルーン0.1 | プルーン0.2 | プルーン0.3 | ||||
モデルサイズ(M) | 13.8M | 13.8M | 13.8 | 12.3M | 10.5M | 868万 |
P | 0.915 | 0.923 | 0.919 | 0.916 | 0.899 | 0.892 |
R | 0.832 | 0.808 | 0.816 | 0.829 | 0.84 | 0.842 |
[email protected] | 0.885 | 0.879 | 0.881 | 0.887 | 0.887 | 0.888 |
[email protected]:.95 | 0.642 | 0.628 | 0.633 | 0.639 | 0.64 | 0.642 |
(1) 基本的な mAP を確保しながら、yolov5s モデルの 30% をプルーニングすることが実現可能であることがわかります。
(2) モデルの適切性を考慮すると、高精度で汎化能力の高いモデルをトレーニングすることは通常は困難であるため、プロジェクト データとモデルのプルーニングと微調整が必要になることがよくあります。
(3) リアルタイムビデオストリームの検出における微調整モデルの効果をテストするモデルのトレーニングスケールが 640 であるため、遠方のターゲットの再現率が低く、交差点の交通量が多い場合には検出漏れの現象が発生します。