YOLOV5解析

ネットワークは 3 つの主要コンポーネントで構成されます。
1) バックボーン: さまざまな画像粒度で画像特徴を集約して形成する畳み込みニューラル ネットワーク。
2) ネック: 画像の特徴を混合して結合し、画像の特徴を予測層に渡す一連のネットワーク層。
3) 出力: 画像の特徴を予測し、境界ボックスを生成し、カテゴリを予測します。
YOLOV5 の場合、V5s、V5m、V5l、V5x のいずれであっても、バックボーン、ネック、出力は同じです。唯一の違いは、モデルの奥行きと幅の設定です。
一般的な構造フレームワーク:
ここに画像の説明を挿入します
1 つずつ分析してみましょう:
1) バックボーンは
コードから始まり、一般的な概要があります。

# YOLOv5 backbone
backbone:
  # [from, number, module, args]
  [[-1, 1, Focus, [64, 3]],  # 0-P1/2
   [-1, 1, Conv, [128, 3, 2]],  # 1-P2/4
   [-1, 3, BottleneckCSP, [128]],
   [-1, 1, Conv, [256, 3, 2]],  # 3-P3/8
   [-1, 9, BottleneckCSP, [256]],
   [-1, 1, Conv, [512, 3, 2]],  # 5-P4/16
   [-1, 9, BottleneckCSP, [512]],
   [-1, 1, Conv, [1024, 3, 2]],  # 7-P5/32
   [-1, 1, SPP, [1024, [5, 9, 13]]],
   [-1, 3, BottleneckCSP, [1024, False]],  # 9
  ]

① バックボーンフォーカスの最初の層は、高解像度画像から定期的にピクセルを抽出し、それらを低解像度画像に再構成します。つまり、画像の 4 つの隣接する位置を積み重ね、wh 次元の情報を c チャネル空間に集中させます。 , 各点の受容野を改善し、元の情報の損失を減らします. このモジュールの設計は主に計算量の削減と高速化を目的としています。
著者の原文: Focus() モジュールは、mAP の増加ではなく、FLOPS の削減と速度の向上を目的として設計されています。

class Focus(nn.Module):
    # Focus wh information into c-space
    def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True):  # ch_in, ch_out, kernel, stride, padding, groups
        super(Focus, self).__init__()
        self.conv = Conv(c1 * 4, c2, k, s, p, g, act)

    def forward(self, x):  # x(b,c,w,h) -> y(b,4c,w/2,h/2)
        return self.conv(torch.cat([x[..., ::2, ::2], x[..., 1::2, ::2], x[..., ::2, 1::2], x[..., 1::2, 1::2]], 1))

ここに画像の説明を挿入します
YOLOv5 でのデータ フローの方向は次のとおりです:
YOLO V5 のデフォルトは 3x640x640 入力です。最初に 4 つのコピーをコピーし、次にスライス操作を通じて 4 つの画像を 4 つの 3x320x320 スライスにカットします。次に、concat を使用して 4 つのスライスを深度で接続します。出力は 12x320x320 であり、その後、畳み込みカーネル番号 64 の畳み込み層を通過して 64x320x320 の出力が生成され、最終的に、結果は、batch_borm および Leaky_relu を介して次の畳み込み層に入力されます。
② Backbone の第 3 層、BottleneckCSP モジュール。
BottleneckCSP モジュールには主に、ボトルネックと CSP の 2 つの部分が含まれています。

class BottleneckCSP(nn.Module):
    # CSP Bottleneck https://github.com/WongKinYiu/CrossStagePartialNetworks
    def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):  # ch_in, ch_out, number, shortcut, groups, expansion
        super(BottleneckCSP, self).__init__()
        c_ = int(c2 * e)  # hidden channels
        self.cv1 = Conv(c1, c_, 1, 1)
        self.cv2 = nn.Conv2d(c1, c_, 1, 1, bias=False)
        self.cv3 = nn.Conv2d(c_, c_, 1, 1, bias=False)
        self.cv4 = Conv(2 * c_, c2, 1, 1)
        self.bn = nn.BatchNorm2d(2 * c_)  # applied to cat(cv2, cv3)
        self.act = nn.LeakyReLU(0.1, inplace=True)
        self.m = nn.Sequential(*[Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)])

    def forward(self, x):
        y1 = self.cv3(self.m(self.cv1(x)))
        y2 = self.cv2(x)
        return self.cv4(self.act(self.bn(torch.cat((y1, y2), dim=1))))

ここに画像の説明を挿入します

このうち、yolov5 のモデル設定ファイルでは、 [-1, 9, BottleneckCSP, [512]]のように、BottleneckCSP モジュールの設定が False になっているものと、False になっていないものがあります。

[-1、3、ボトルネックCSP、[512、False]]、# 13

ここで False は、この BottleneckCSP でショートカット操作が実行されないことを意味します。
たとえば、
以下の左の図は False を使用しない BottleneckCSP であり、右の図は False を使用する BottleneckCSP です。
ここに画像の説明を挿入します
④SPPモジュール(空間ピラミッドプーリングモジュール)はそれぞれ5/9/13の最大プーリングを採用し、連結融合を行って受容野を増大させます。
SPP の入力は 512x20x20 です。1x1 畳み込み層を通過した後の出力は 256x20x20 です。その後、3 つの並列 Maxpool を通じてダウンサンプリングされます。結果は初期特徴に追加され、出力は 1024x20x20 になります。最終的に、512 畳み込みカーネルは次のようになります。 512x20x20 に復元するために使用されます。
ここに画像の説明を挿入します

2) ネック (PANet)
PANET は、マスク R-CNN および FPN フレームワークに基づいており、情報伝達を強化し、空間情報を正確に保持する機能を備えており、ピクセルを適切に配置してマスクを形成するのに役立ちます。
3) 損失関数 境界
回帰: CIOU (GIOU の改良)
客観性: GIOU
ここに画像の説明を挿入します
IOU: 交差と和の比率
GIOU の式の意味: まず 2 つのボックスの最小閉包面積 Ac を計算します (一般的な理解: 予測されたボックスと実際のボックスの両方が含まれます)ボックスの最小のボックスの面積)、次に IoU を計算し、次に閉包領域に対する 2 つのボックスに属さない閉包領域 Ac 内の面積の割合を計算し、最後に IoU を使用してこれを減算します。 GioUを取得する割合。
損失関数として:
ここに画像の説明を挿入します
分類: BCE (クロス エントロピー損失)
損失バランス: ciou=0.05、giou=1、bce=0.5
training
1; 環境
ubuntu、python3.8、torch 1.6、torchvision 0.7
2; トレーニングのためにネットワークに送信データ形式は
ここに画像の説明を挿入します
1 行に 5 個の浮動小数点データが含まれており、1 番目はラベルのシリアル番号、2 番目と 3 番目はターゲットの正規化後の中心点座標、4 番目と 5 番目は正規化されたターゲットの座標を表します。長さと幅。
3: 設定ファイル内のパラメータの説明

train_path: ./source_data/traindata  #训练数据集
val_path: ./source_data/valdata   #测试数据集

convertor_path: ./convertor/chouyan   #转换成训练模型所需要的的数据格式保存路径

task_name: chouyan_s    #任务名称,最后在这个文件夹下保存生成的模型

names: ["xiangyan"]    #类别名称


gpu_ids: "2"
imgsz: 416
epochs: 50     #训练总共跑50个epoch
batch_size: 4
eval_interval: 5    #每5个epoch保存一次模型

weights: ./weights/yolov5s.pt     #预训练模型

#weights: /data/dj/yolov5/work_dir/chouyan_s/2020-11-14/2020-11-14_15:17:32/epoch_15.pth   # 在测试的时候,指定#测试的模型

source: ./test_data/chouyan2/neg   #测试的数据集路径,只需要图像

output_pos: output_s_pos_neg    #测试结果保存的路径,可根据自己需要保存

3: 研修プログラムにおける注意事項
1) 設定パラメータ

if __name__ == '__main__':
	parser = argparse.ArgumentParser()
	parser.add_argument('--cfg', type=str, default='models/yolov5s.yaml', help='model.yaml path') #如果预训练模型是#yolov5s,这里需要保持一致,最终训练的模型就是yolov5s版本
	parser.add_argument('--data', type=str, default='config.yaml', help='data.yaml path')  # 配置文件名称
	parser.add_argument('--hyp', type=str, default='', help='hyp.yaml path (optional)')
	parser.add_argument('--epochs', type=int, default=300)
	parser.add_argument('--batch-size', type=int, default=16, help="Total batch size for all gpus.")
	parser.add_argument('--img-size', nargs='+', type=int, default=[416, 416], help='train,test sizes')  #图像resize到的尺寸,可根据自己实际任务需求,改成640等,需要32的倍数
	parser.add_argument('--rect', action='store_true', help='rectangular training')
	parser.add_argument('--resume', nargs='?', const='get_last', default=False,
						help='resume from given path/to/last.pt, or most recent run if blank.')
	parser.add_argument('--nosave', action='store_true', help='only save final checkpoint')
	parser.add_argument('--notest', action='store_true', help='only test final epoch')
	parser.add_argument('--noautoanchor', action='store_true', help='disable autoanchor check')
	parser.add_argument('--evolve', action='store_true', help='evolve hyperparameters')
	parser.add_argument('--bucket', type=str, default='', help='gsutil bucket')
	parser.add_argument('--cache-images', action='store_true', help='cache images for faster training')
	parser.add_argument('--weights', type=str, default='', help='initial weights path')
	parser.add_argument('--name', default='', help='renames results.txt to results_name.txt if supplied')
	parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')
	parser.add_argument('--multi-scale', action='store_true', help='vary img-size +/- 50%%')
	parser.add_argument('--single-cls', action='store_true', help='train as single-class dataset')
	parser.add_argument('--sync-bn', action='store_true', help='use SyncBatchNorm, only available in DDP mode')
	parser.add_argument('--local_rank', type=int, default=-1, help='DDP parameter, do not modify')
	opt = parser.parse_args()

2) モデルの保存パス

def init_logger(work_dir='./work_dir'):    #在ubuntu下训练一定不要写成work_dir='.\\work_dir',不然会找不到这个路径
	cur_time = datetime.datetime.now().strftime("%Y-%m-%d_%H:%M:%S")
	work_dir = os.path.join(work_dir, cur_time.split('_')[0], cur_time)

	mkdir_or_exist(os.path.abspath(work_dir))

	# log
	log_file = os.path.join(work_dir, 'log.log')
	logger = get_root_logger(log_file)
	return logger, work_dir

4. 最後に、python train.py を直接実行して直接トレーニングします。
テスト:
python detect.py を直接実行して直接トレーニングします。
YOLOv5 3 つのモデルの
ここに画像の説明を挿入します
トレーニング コード レビュー
1: トレーニング設定ファイルのロードの比較

2: モデルの作成
① モデル設定ファイルをインポート
self.yaml = yaml.load(f, Loader=yaml.FullLoader)
② モデル設定ファイルに従ってモデルを定義
if nc and nc != self.yaml['nc' ]:
self.yaml['nc'] = nc # yaml 値をオーバーライド
self.model, self.save = parse_model(deepcopy(self.yaml), ch=[ch])

 from  n    params  module                                  arguments                     
  0                -1  1      8800  models.common.Focus                     [3, 80, 3]                    
  1                -1  1    115520  models.common.Conv                      [80, 160, 3, 2]               
  2                -1  1    315680  models.common.BottleneckCSP             [160, 160, 4]                 
  3                -1  1    461440  models.common.Conv                      [160, 320, 3, 2]              
  4                -1  1   3311680  models.common.BottleneckCSP             [320, 320, 12]                
  5                -1  1   1844480  models.common.Conv                      [320, 640, 3, 2]              
  6                -1  1  13228160  models.common.BottleneckCSP             [640, 640, 12]                
  7                -1  1   7375360  models.common.Conv                      [640, 1280, 3, 2]             
  8                -1  1   4099840  models.common.SPP                       [1280, 1280, [5, 9, 13]]      
  9                -1  1  20087040  models.common.BottleneckCSP             [1280, 1280, 4, False]        
 10                -1  1    820480  models.common.Conv                      [1280, 640, 1, 1]             
 11                -1  1         0  torch.nn.modules.upsampling.Upsample    [None, 2, 'nearest']          
 12           [-1, 6]  1         0  models.common.Concat                    [1]                           
 13                -1  1   5435520  models.common.BottleneckCSP             [1280, 640, 4, False]         
 14                -1  1    205440  models.common.Conv                      [640, 320, 1, 1]              
 15                -1  1         0  torch.nn.modules.upsampling.Upsample    [None, 2, 'nearest']          
 16           [-1, 4]  1         0  models.common.Concat                    [1]                           
 17                -1  1   1360960  models.common.BottleneckCSP             [640, 320, 4, False]          
 18                -1  1    922240  models.common.Conv                      [320, 320, 3, 2]              
 19          [-1, 14]  1         0  models.common.Concat                    [1]                           
 20                -1  1   5025920  models.common.BottleneckCSP             [640, 640, 4, False]          
 21                -1  1   3687680  models.common.Conv                      [640, 640, 3, 2]              
 22          [-1, 10]  1         0  models.common.Concat                    [1]                           
 23                -1  1  20087040  models.common.BottleneckCSP             [1280, 1280, 4, False]        
 24      [17, 20, 23]  1     40374  models.yolo.Detect                      [1, [[15.086, 10.896, 12.185, 27.484, 28.191, 13.015], [26.104, 23.376, 53.062, 15.104, 18.795, 51.715], [45.032, 25.534, 31.85, 40.063, 49.745, 47.505]], [320, 640, 1280]]

③ 順方向の出力を取得する
m = self.model[-1] # Detect()
上記のモデル構造からもわかるように、yolov5 は 3 つのスケールの出力結果を最終層に格納しているため、順方向の結果を取得するには、直接取得しますモデルの最後の層の結果で十分です。
④設定したアンカーを[8、16、32]のスケールに従って正規化します。
m.anchors /= m.stride.view(-1, 1, 1) #m.stride=[8,16,32]
3: 作成されたモデル構造に従って、事前トレーニングされたモデルのパラメーターをロードします。
4: トレーニングを設定します。テクニック:
①model, optimizer = amp.initialize(model, optimizer, opt_level='O1', verbosity=0)
amp.initialize 関数の役割は、モデルの精度やトレーニング速度を向上させることではなく、ビデオ メモリの消費量を削減することです。
主に opt_level パラメータの設定に依存し、00 は元の単精度トレーニングに相当します。01 はほとんどの計算で半精度を使用しますが、すべてのモデル パラメーターは依然として単精度を維持します。より優れた単精度を備えたいくつかの計算 (ソフトマックスなど) では、単精度が維持されます。01 と比較して、02 もモデルパラメータを半精度に変更します。03 は基本的に、最初の実験の全精度および半精度の演算と同じです。最適化プロセス中にモデルが半精度を採用するかどうかに関係なく、保存されたモデルは単精度モデルであるため、他のアプリケーションでモデルを通常どおり使用できることに注意してください。
② 学習率の調整方法
Pytorch には学習率を調整するための 3 つの戦略があります:
1) 秩序ある調整: 等間隔調整 (Step)、オンデマンドでの学習率の調整 (MultiStep)、指数関数的減衰調整 (Exponential)、および CosineAnnealing
2) アダプティブ調整:ReduceLROnPlateau
3) カスタム調整:LambdaLR
③マルチGPUトレーニング処理
モデル = torch.nn.DataParallel(model)
torch.nn.DataParallelを呼び出す場合、GPUトレーニング設定時に2ブロック設定する必要がある もしくはそれ以上シングル カード トレーニングのみを設定した場合、エラーが報告されます。
④ モデルトレーニング中の堅牢性を高めるために、モデル前のパラメータに対して平均演算を実行します。
ema = torch_utils.ModelEMA(model)
5: トレーニングデータセットと検証データセットの処理
create_dataloader()
6: モデルのパラメータを設定します
。カテゴリの数、カテゴリ名、カテゴリの重みなど。
7: アンカーの設定
アンカーを変更する必要がありますか:
check_anchors(dataset, model=model, thr=hyp['anchor_t'], imgsz=imgsz)
アンカーのサイズ分布が独自のデータ セットは coco データ セットほど豊富ではありません。たとえば、小さなターゲットの検出。独自のデータ セットに基づいて新しいアンカーをクラスター化することをお勧めします。

# anchors:
#   - [10,13, 16,30, 33,23]  # P3/8
#   - [30,61, 62,45, 59,119]  # P4/16
#   - [116,90, 156,198, 373,326]  # P5/32
anchors:
  - [15.086,10.896,12.185,27.484,28.191,13.015]  # P3/8
  - [26.104,23.376,53.062,15.104,18.795,51.715]  # P4/16
  - [45.032,25.534,31.85,40.063,49.745,47.505]  # P5/32

utils の kmeans_anchor をローカルに直接コピーし、ローカルで実行します。kmean_anchors(path='E:\projects\YOLO5\data\chouyan.yaml', n=9, img_size=416, thr=8.0, gen=1000, verbose=True) coco
に従って新しい xxxx.yaml ファイルを作成します。
yaml でトレーニング セットのパスを設定します。n=9、つまり 9 点のクラスタリングを意味します。これは変更しないでください。 img_size トレーニング セットの画像サイズ、トレーニング セットのターゲット アスペクト比、トレーニングする必要があるターゲットは形状であるため、小さな細いバーを作成し、coco の thr=4.0 を 8.0 に変更して、ターゲットのアスペクト比を高めます。
8: トレーニング

for epoch in range(start_epoch, epochs):
	...
	for i, (imgs, targets, paths, _) in enumerate(dataloader):  #分批训练
		...
	imgs = imgs.to(device, non_blocking=True).float() / 255.0 #uint8 to float32
	...
	pred = model(imgs)  #前向处理
	loss, loss_items = compute_loss(pred, targets.to(device), model) #计算loss
	...
	results, correct,maps, times = test.test(...)  #验证集验证
	torch.save(...)  #保存模型

損失のソート
1) 前方処理結果:
ここに画像の説明を挿入します
3 つのスケールの 8、16、32 の出力
② 損失の計算
1) tcls、tbox、indexes、anchors = build_targets(p, target, model)

def build_targets(p, targets, model):  #p:三个尺度的特征图; targets:标签信息,det.anchors:预测的anchors
    # Build targets for compute_loss(), input targets(image,class,x,y,w,h)
    det = model.module.model[-1] if type(model) in (nn.parallel.DataParallel, nn.parallel.DistributedDataParallel) \
        else model.model[-1]  # Detect() module
    na, nt = det.na, targets.shape[0]  # number of anchors, targets
    tcls, tbox, indices, anch = [], [], [], []
    gain = torch.ones(6, device=targets.device)  # normalized to gridspace gain #shape[6]
    off = torch.tensor([[1, 0], [0, 1], [-1, 0], [0, -1]], device=targets.device).float()  # overlap offsets #shape[4,2]
    at = torch.arange(na).view(na, 1).repeat(1, nt)  # anchor tensor, same as .repeat_interleave(nt)  #shape[3,1]

    g = 0.5  # offset
    style = 'rect4'
    for i in range(det.nl):
        anchors = det.anchors[i]   #分别获取每个尺度上的基于特征图大小的的anchor #shape[3,2]
        gain[2:] = torch.tensor(p[i].shape)[[3, 2, 3, 2]]  # xyxy gain   #shape[6]  获取每个尺度上预测的类别,已经类别置信度,目标cx,cy,w,h

        # Match targets to anchors
        a, t, offsets = [], targets * gain, 0  #将gt的cx,cy,w,h换算到当前特征层对应的尺寸,以便和该层的anchor大小相对应
        if nt:
            r = t[None, :, 4:6] / anchors[:, None]  # wh ratio #t_w,h/anchors
            j = torch.max(r, 1. / r).max(2)[0] < model.hyp['anchor_t']  # compare  #判断了r和1/r与model.hyp['anchor_t']的大小关系,返回bool值
            # j = wh_iou(anchors, t[:, 4:6]) > model.hyp['iou_t']  # iou(3,n) = wh_iou(anchors(3,2), gwh(n,2))
            a, t = at[j], t.repeat(na, 1, 1)[j]  # filter  #过滤掉长宽比大于阈值anchor_t的预测长宽组合

            # overlaps 
            gxy = t[:, 2:4]  # grid xy  #获取gt的cx,cy
            z = torch.zeros_like(gxy)
            if style == 'rect2':
                j, k = ((gxy % 1. < g) & (gxy > 1.)).T
                a, t = torch.cat((a, a[j], a[k]), 0), torch.cat((t, t[j], t[k]), 0)
                offsets = torch.cat((z, z[j] + off[0], z[k] + off[1]), 0) * g
            elif style == 'rect4':
                j, k = ((gxy % 1. < g) & (gxy > 1.)).T
                l, m = ((gxy % 1. > (1 - g)) & (gxy < (gain[[2, 3]] - 1.))).T
                a, t = torch.cat((a, a[j], a[k], a[l], a[m]), 0), torch.cat((t, t[j], t[k], t[l], t[m]), 0)
                offsets = torch.cat((z, z[j] + off[0], z[k] + off[1], z[l] + off[2], z[m] + off[3]), 0) * g
            # t.shape=[9,6]  即扩充了gt的数量,由原来3个anchor扩充到现在9个anchor,在每个gt中心点附近再扩充2个gt中心点
        # Define
        b, c = t[:, :2].long().T  # image, class
        gxy = t[:, 2:4]  # grid xy
        gwh = t[:, 4:6]  # grid wh
        gij = (gxy - offsets).long()
        gi, gj = gij.T  # grid xy indices

        # Append
        indices.append((b, a, gj, gi))  # image, anchor, grid indices
        tbox.append(torch.cat((gxy - gij, gwh), 1))  # box
        anch.append(anchors[a])  # anchors
        tcls.append(c)  # class

    return tcls, tbox, indices, anch

トレーニング プロセス中の落とし穴:
現象: 勾配 nan
考えられる理由: 重みの大幅な減衰と重み係数が小さいため、勾配が小さすぎ、パイトーチ加速アーティファクトである Apex の保存範囲を超え、丸めエラーが発生します。
現象: 損失が常に高く、再現率が非常に低い
理由: 初期学習率の設定が高すぎます。
学習率がモデルの収束にこれほど大きな影響を与えるのはなぜですか? まず、ネットワークパラメータの更新プロセスを整理しましょう。
Pytorch モデルのパラメーター更新プロセス:
1. ネットワークの転送出力と実際のラベルの間の誤差を通じて損失を取得します。
2. loss.backward() による誤差の逆伝播を完了し、pytorch の内部メカニズムによる自動導出を完了して、各パラメーターの勾配 W_grad を取得します。

if mixed_precision:
				with amp.scale_loss(loss, optimizer) as scaled_loss:
					scaled_loss.backward()
			else:
				loss.backward()

通常のプログラムでは、勾配を取得した後、最適化アルゴリズムが開始され、重みが更新されます。しかし、yolov5 では、勾配累積トリックが使用されます。

if ni % accumulate == 0:
				optimizer.step()
				optimizer.zero_grad()

つまり、勾配を計算した後、最適化アルゴリズムはすぐには開始されませんが、次のバッチの損失が計算され、その後、このバッチの勾配が元の勾配に基づいて計算され、複数の勾配の累積が実現されます。バッチを 1 つの勾配にまとめ、この累積された勾配に基づいてモデル パラメーターが更新され、モデルの反復が完了します。
3. 最適化アルゴリズムを通じてモデルパラメータを更新します。
① 最適化プロセス:
最適化アルゴリズムの一般式は
W_data=W_data+W_grad*lrです
。ここで、W_data はモデル パラメーター、W_grad はパラメーターの勾配、lr は学習率です。

optimizer = optim.SGD(pg0, lr=hyp['lr0'], momentum=hyp['momentum'], nesterov=True)

pytorch フレームワークでは、一般的な最適化アルゴリズムはモデルパラメータに対して L2 正則化重み減衰処理を行い、重み減衰処理後は過学習を防ぐために出力重みが小さくなります。

@torch.no_grad()
    def step(self, closure=None):
        """Performs a single optimization step.

        Arguments:
            closure (callable, optional): A closure that reevaluates the model
                and returns the loss.
        """
        loss = None
        if closure is not None:
            with torch.enable_grad():
                loss = closure()

        for group in self.param_groups:
            weight_decay = group['weight_decay']
            momentum = group['momentum']
            dampening = group['dampening']
            nesterov = group['nesterov']

            for p in group['params']:
                if p.grad is None:
                    continue
                d_p = p.grad
                if weight_decay != 0:
                    d_p = d_p.add(p, alpha=weight_decay)  #权值L2正则化衰减
                if momentum != 0:
                    param_state = self.state[p]
                    if 'momentum_buffer' not in param_state:
                        buf = param_state['momentum_buffer'] = torch.clone(d_p).detach()
                    else:
                        buf = param_state['momentum_buffer']
                        buf.mul_(momentum).add_(d_p, alpha=1 - dampening)
                    if nesterov:
                        d_p = d_p.add(buf, alpha=momentum)
                    else:
                        d_p = buf

                p.add_(d_p, alpha=-group['lr'])  #权值更新

        return loss

②モデルパラメータ更新:

optimizer.step()

③ 傾きをゼロに戻します。

optimizer.zero_grad()

勾配によってモデルが 1 回更新され、次のラウンドのモデル更新では、次のラウンドの新しく計算された勾配が必要になります。
4. 更新されたモデルパラメータの平均処理

ema = torch_utils.ModelEMA(model) if rank in [-1, 0] else None

ema.update(model)

学習率を最適化するいくつかの方法:
① ウォームアップにより
、トレーニングの最初の数エポックまたはステップで学習率を小さくすることができます。小さな予熱学習率の下では、モデルはゆっくりと安定します。モデルが比較的安定したら、次に、トレーニング用に事前に設定された学習率を使用するため、モデルの収束が速くなり、モデルの効果が向上します。
これは、モデルの初期段階でのミニバッチの初期の過剰適合を遅らせ、分布の安定性を維持し、
モデルの深い層の安定性を維持するのに役立ちます。

if ni <= nw:
				xi = [0, nw]  # x interp
				# model.gr = np.interp(ni, xi, [0.0, 1.0])  # giou loss ratio (obj_loss = 1.0 or giou)
				accumulate = max(1, np.interp(ni, xi, [1, nbs / total_batch_size]).round())
				for j, x in enumerate(optimizer.param_groups):
					# bias lr falls from 0.1 to lr0, all other lrs rise from 0.0 to lr0
					x['lr'] = np.interp(ni, xi, [0.1 if j == 2 else 0.0, x['initial_lr'] * lf(epoch)])
					if 'momentum' in x:
						x['momentum'] = np.interp(ni, xi, [0.9, hyp['momentum']])

② 学習率調整スキーム
Pytorch が提供する学習率調整戦略は 3 つのカテゴリに分類されます:
a. 秩序ある調整: 等間隔調整 (Step)、オンデマンド学習率の調整 (MultiStep)、指数関数的減衰調整 (Exponential)、および CosineAnnealing。
b. 適応調整: 学習率 ReduceLROnPlateau を適応的に調整します。
c. カスタム調整: LambdaLR の学習率のカスタム調整。

おすすめ

転載: blog.csdn.net/jiafeier_555/article/details/109052569