目次
序章
2020 年 5 月 18 日のリリース以降、いくつかのバージョンが繰り返されており、最新バージョンは v7 であり、セグメンテーション機能が追加されています。YOLOv5ネットワークの詳しい解説 や、 YoloシリーズのYolov5のコアとなる基礎知識 など、yolov5の原理やマークされたデータの使い方を解説したブログ記事がたくさんあります。
インストールが簡単で使いやすいため、検出方法のデファクト ベンチマークとなっています。
// 克隆代码库即可
git clone https://github.com/ultralytics/yolov5 # clone
cd yolov5
pip install -r requirements.txt # install
を使用すると、1 行のコードで完了するだけです。
import torch
# 加载模型
model = torch.hub.load('ultralytics/yolov5', 'yolov5s') # or yolov5n - yolov5x6, custom
# 图片路径
img = 'https://ultralytics.com/images/zidane.jpg' # or file, Path, PIL, OpenCV, numpy, list
# 执行检测推理
results = model(img)
# 检测结果可视化
results.print() # or .show(), .save(), .crop(), .pandas(), etc.
何?コードを 1 行も書きたくない. コードなしで開発し、カメラから直接効果を確認したい場合は、ウェアハウスで detect.py を実行することも要件を満たすことができます。
python detect.py --weights yolov5s.pt --source 0
詳細なパラメーターの意味は次のとおりです。ここで、 --weights は使用するトレーニング前の重みを指定し、 --source は検出するソース (画像、画像パス リスト、カメラ、またはネットワーク プッシュ ストリーム) を指定します。
python detect.py --weights yolov5s.pt --source 0 # webcam
img.jpg # image
vid.mp4 # video
screen # screenshot
path/ # directory
list.txt # list of images
list.streams # list of streams
'path/*.jpg' # glob
'https://youtu.be/Zgi9g1ksQHc' # YouTube
'rtsp://example.com/media.mp4' # RTSP, RTMP, HTTP stream
ネットワーク構造
YOLOv5 は、さまざまなサイズ ( n
、 s
、 m
、 l
、 ) に対してx
全体的なネットワーク アーキテクチャは同じですが、各サブモジュールで異なる深さと幅を使用して、yaml
ファイル内のdepth_multiple
パラメーターをそれぞれ処理しますwidth_multiple
。n
また、公式の, s
, m
, に加えて, , , もあります. 違いは, 後者の方がより解像度の高い画像用であるということです. たとえばl
, もちろん、 構造にはいくつかの違いがあります. 4前者は 32 回にダウンサンプリングし、3 つの予測フィーチャ レイヤーを使用するだけですが、予測フィーチャ レイヤー。x
n6
s6
m6
l6
x6
1280x1280
以前のバージョンと比較すると、 YOLOv5 は
v6.0
バージョン後に小さな変更があり、ネットワークの最初の層 (元はモジュールFocus
)が6x6
大小の畳み込み層に置き換えられています。この 2 つは理論的には同等ですが、一部の既存の GPU デバイス (および対応する最適化アルゴリズム) では、モジュール6x6
を使用するよりも大小の畳み込みレイヤーを使用する方が効率的です。Focus
詳細については、この問題 #4825 を参照してください。下の図は元のFocus
モジュール (前のものと同様Swin Transformer
)Patch Merging
で、2x2
隣接する各ピクセルを 1 つに分割しpatch
、patch
それぞれの同じ位置 (同じ色) のピクセルをまとめて 4 にしfeature map
、接続する3x3
前のサイズの畳み込み層.6x6
これは、1 つのサイズの畳み込み層を直接使用することと同じです。
ネック部分は(自社設計)
SPP
に交換し、両者の機能は同じですが、後者の方が効率的です。構造は、入力を複数の異なるサイズに並列に渡し、さらに融合を行うことで、ターゲットのマルチスケール問題をある程度解決できます。複数のサイズのレイヤーを介して入力をシリアル化する構造.ここで注意すべきは, 2 つのサイズのレイヤーをシリアル化した計算結果は 1 つのサイズのレイヤーの計算結果と同じであり, 3 つのサイズのレイヤーをシリアル化した計算結果は同一サイズのレイヤーと同じ レイヤーの計算結果は同じです。SPPF
Glenn Jocher
SPP
MaxPool
SPPF
5x5
MaxPool
5x5
MaxPool
9x9
MaxPool
5x5
MaxPool
13x13
MaxPool
データ増強
モザイク、4枚の写真を1枚の写真に結合
コピーペースト、いくつかのターゲットを画像にランダムに貼り付けます。データにはsegments
データ、つまり各ターゲットのインスタンスセグメンテーション情報が必要です。
Random affine(Rotation, Scale, Translation and Shear) はランダムにアフィン変換を行いますが、設定ファイルのハイパーパラメータによると、合計Scale
とTranslation
平行移動のみが使用されていることがわかります。
MixUpは 2 つの画像を一定の透明度でブレンドすることです. それが有用かどうかは明らかではありません. 結局, 論文もアブレーション実験もありません. コードで使用されるのはより大きなモデルのみでMixUp
、時間の 10% のみです。
Albumentations、主にフィルタリング、ヒストグラムの均等化、画質の変更などを行います。コードに記述されたコードは、パッケージがインストールされている場合にのみ有効になることがわかりますが、パッケージはプロジェクトファイルでコメントアウトされてalbumentations
いるrequirements.txt
ためalbumentations
、デフォルトでは有効になっていません。
Augment HSV(Hue, Saturation, Value) は、色相、彩度、明度をランダムに調整します。
ランダム横フリップ、ランダム横フリップ
多くのトレーニング戦略が YOLOv5 ソース コードで使用されています
- マルチスケールトレーニング(0.5~1.5x)、マルチスケールトレーニング、入力画像のサイズを640×640とすると、トレーニング時に使用するサイズは0.5×640~1.5×640の間でランダムに選択されるので注意どちらも 32 の整数倍です (ネットワークは最大 32 倍までダウンサンプリングするため)。
- AutoAnchor (カスタム データのトレーニング用) では、独自のデータ セットをトレーニングするときに、再クラスター化して、独自のデータ セットの目標に従ってアンカー テンプレートを生成できます。
- Warmup および Cosine LR スケジューラ、トレーニングの前にウォームアップして
Warmup
から、Cosine
学習率の低下戦略を使用します。- EMA (指数移動平均) は、トレーニング パラメーターに勢いを加えて更新プロセスをよりスムーズにするものと理解できます。
- 混合精度、混合精度トレーニングは、GPU ハードウェア サポートが提供されている場合、ビデオ メモリの使用量を削減し、トレーニングを高速化できます。
- ハイパーパラメータの進化、ハイパーパラメータの最適化、錬金術未経験者は触らないでデフォルトのままでいい。
YOLOv5 の損失は、主に次の 3 つの部分で構成されます。
- クラス損失、分類損失が使用されます
BCE loss
。陽性サンプルの分類損失のみを計算することに注意してください。 - Objectness loss ,
obj
the loss, is still usedBCE loss
. これは、obj
ネットワークと GT Box によって予測されたターゲット バウンディング ボックスを指すことに注意してくださいCIoU
。ここで計算されるのは、obj
すべてのサンプルの損失です。 - Location Loss、 Location Loss が使用されます
CIoU loss
。陽性サンプルの Location Loss のみを計算することに注意してください。
配備
yolov5 v6.0 (含まれていない) より前のバージョンでは、Focus レイヤーを使用するため、配置に多くの変更が加えられ、多くの複雑な操作が必要になります. 詳細については、詳細な記録を参照してください u バージョン YOLOv5 ターゲット検出 ncnn 実装 , 特定の変更手順は次のとおり YOLOv5 を検出して ncnn モバイル端末に展開
// 1.导出onnx
python models/export.py --weights yolov5s.pt --img 320 --batch 1
// 2.简化模型
python -m onnxsim yolov5s.onnx yolov5s-sim.onnx
// 3. 模型转换到ncnn
./onnx2ncnn yolov5s-sim.onnx yolov5s.param yolov5s.bin
// 4. 编辑 yolov5s.param文件
第4行到13行删除(也就是Slice和Concat层),将第二行由172改成164(一共删除了10层,第二行的173更改为164,计算方法173-(10-1)=164)
增加自定义层
YoloV5Focus focus 1 1 images 159
其中159是刚才删除的Concat层的输出
// 5. 支持动态尺寸输入
将reshape中的960,240,60更改为-1,或者其他 0=后面的数
// 6. ncnnoptimize优化
./ncnnoptimize yolov5s.param yolov5s.bin yolov5s-opt.param yolov5s-opt.bin 1
v6.0 以降では、代わりに 6x6 畳み込みを使用する方がはるかに便利です. デプロイに opencv の dnn モジュールを直接使用できます. 詳細については、YOLOv5、OpenCV、Python、および C++ を使用したオブジェクトの検出を参照してください。コード yolov5-opencv - cpp -python
ただし、opencv4.5.5以降としか連携できないので注意が必要で、主に6つのステップが含まれています。
// 1.加载模型
net = cv2.dnn.readNet('yolov5s.onnx')
// 2.加载图片
def format_yolov5(source):
# put the image in square big enough
col, row, _ = source.shape
_max = max(col, row)
resized = np.zeros((_max, _max, 3), np.uint8)
resized[0:col, 0:row] = source
# resize to 640x640, normalize to [0,1[ and swap Red and Blue channels
result = cv2.dnn.blobFromImage(resized, 1/255.0, (640, 640), swapRB=True)
return result
// 3.执行推理
predictions = net.forward()
output = predictions[0]
// 4.展开结果
def unwrap_detection(input_image, output_data):
class_ids = []
confidences = []
boxes = []
rows = output_data.shape[0]
image_width, image_height, _ = input_image.shape
x_factor = image_width / 640
y_factor = image_height / 640
for r in range(rows):
row = output_data[r]
confidence = row[4]
if confidence >= 0.4:
classes_scores = row[5:]
_, _, _, max_indx = cv2.minMaxLoc(classes_scores)
class_id = max_indx[1]
if (classes_scores[class_id] > .25):
confidences.append(confidence)
class_ids.append(class_id)
x, y, w, h = row[0].item(), row[1].item(), row[2].item(), row[3].item()
left = int((x - 0.5 * w) * x_factor)
top = int((y - 0.5 * h) * y_factor)
width = int(w * x_factor)
height = int(h * y_factor)
box = np.array([left, top, width, height])
boxes.append(box)
// 5.非极大值抑制
indexes = cv2.dnn.NMSBoxes(boxes, confidences, 0.25, 0.45)
result_class_ids = []
result_confidences = []
result_boxes = []
for i in indexes:
result_confidences.append(confidences[i])
result_class_ids.append(class_ids[i])
result_boxes.append(boxes[I])
// 6.可视化结果输出
class_list = []
with open("classes.txt", "r") as f:
class_list = [cname.strip() for cname in f.readlines()]
colors = [(255, 255, 0), (0, 255, 0), (0, 255, 255), (255, 0, 0)]
for i in range(len(result_class_ids)):
box = result_boxes[i]
class_id = result_class_ids[i]
color = colors[class_id % len(colors)]
conf = result_confidences[i]
cv2.rectangle(image, box, color, 2)
cv2.rectangle(image, (box[0], box[1] - 20), (box[0] + box[2], box[1]), color, -1)
cv2.putText(image, class_list[class_id], (box[0] + 5, box[1] - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,0,0))
円と長方形を検出するためのデータを生成する
次に、円と長方形を検出するプロジェクトは、yolov5 のトレーニング データの生成とトレーニング プロセスを示しています。しかし、データのラベル付けには時間と労力がかかります.生成されたデータを使用して、いくつかの実験を迅速に検証できれば素晴らしいと思いませんか.
Lable Studio [yoloV5 実戦記録] に基づく Xiaobai は、独自のデータセットをトレーニングすることもできます! labelimg に基づいて、深層学習を使用してオブジェクト検出を行う方法を教えます (2): データのラベル付け - プログラマー
yolov5 のラベリング フォーマットは非常にシンプルです. 写真は images フォルダに配置されます. labels フォルダの下に, 各写真ファイルには対応する同名の txt ファイルがあり, 各ターゲットのカテゴリ, 正規化された座標と幅が行ごとに保存されます.高く、多くの注釈ツールが yolo 注釈形式の直接エクスポートをサポートしており、VOC、coco などの形式から YOLO 形式に簡単に変換できるスクリプトも多数あります。
类别1 归一化中心点坐标x 归一化中心坐标y 归一化宽度 归一化高度
类别2 归一化中心点坐标x 归一化中心坐标y 归一化宽度 归一化高度
1. ここでは、検出円を例に、各ステップを詳しく紹介します。
1つ目は学習データの生成と可視化で、ランダムな点を中心に検出したい対象を半径60~100の円でランダムに描き、合計10万個の学習データを生成する
import os
import cv2
import math
import random
import numpy as np
from tqdm import tqdm
def generate():
img = np.zeros((640,640,3),np.uint8)
x = 100+random.randint(0, 400)
y = 100+random.randint(0, 400)
radius = random.randint(60,100)
r = random.randint(0,255)
g = random.randint(0,255)
b = random.randint(0,255)
cv2.circle(img, (x,y), radius, (b,g,r),-1)
return img, [x,y,radius]
def generate_batch(num=10000):
images_dir = "data/circle/images"
if not os.path.exists(images_dir):
os.makedirs(images_dir)
labels_dir = "data/circle/labels"
if not os.path.exists(labels_dir):
os.makedirs(labels_dir)
for i in tqdm(range(num)):
img, labels = generate()
cv2.imwrite(images_dir+"/"+str(i)+".jpg", img)
with open(labels_dir+"/"+str(i)+".txt", 'w') as f:
x, y, radius = labels
f.write("0 "+str(x/640)+" "+str(y/640)+" "+str(2*radius/640)+" "+str(2*radius/640)+"\n")
def show_gt(dir='data/circle'):
files = os.listdir(dir+"/images")
gtdir = dir+"/gt"
if not os.path.exists(gtdir):
os.makedirs(gtdir)
for file in tqdm(files):
imgpath = dir+"/images/"+file
img = cv2.imread(imgpath)
h,w,_ = img.shape
labelpath = dir+"/labels/"+file[:-3]+"txt"
with open(labelpath) as f:
lines = f.readlines()
for line in lines:
items = line[:-1].split(" ")
c = int(items[0])
cx = float(items[1])
cy = float(items[2])
cw = float(items[3])
ch = float(items[4])
x1 = int((cx - cw/2)*w)
y1 = int((cy - ch/2)*h)
x2 = int((cx + cw/2)*w)
y2 = int((cy + ch/2)*h)
cv2.rectangle(img, (x1,y1),(x2,y2),(0,255,0),2)
cv2.imwrite(gtdir+"/"+file, img)
if __name__=="__main__":
generate_batch()
show_gt()
次に、circle.yaml を作成します
train: data/circle/images/
val: data/circle/images/
# number of classes
nc: 1
# class names
names: ['circle']
2. 円形および長方形のターゲットを検出する場合は、生成スクリプトとデータ構成ファイルを調整する必要があります
import os
import cv2
import math
import random
import numpy as np
from tqdm import tqdm
def generate_circle():
img = np.zeros((640,640,3),np.uint8)
x = 100+random.randint(0, 400)
y = 100+random.randint(0, 400)
radius = random.randint(60,100)
r = random.randint(0,255)
g = random.randint(0,255)
b = random.randint(0,255)
cv2.circle(img, (x,y), radius, (b,g,r),-1)
return img, [x,y,radius*2,radius*2]
def generate_rectangle():
img = np.zeros((640,640,3),np.uint8)
x1 = 100+random.randint(0, 400)
y1 = 100+random.randint(0, 400)
w = random.randint(80, 200)
h = random.randint(80, 200)
x2 = x1 + w
y2 = y1 + h
r = random.randint(0,255)
g = random.randint(0,255)
b = random.randint(0,255)
cx = (x1+x2)//2
cy = (y1+y2)//2
cv2.rectangle(img, (x1,y1), (x2,y2), (b,g,r),-1)
return img, [cx,cy,w,h]
def generate_batch(num=100000):
images_dir = "data/shape/images"
if not os.path.exists(images_dir):
os.makedirs(images_dir)
labels_dir = "data/shape/labels"
if not os.path.exists(labels_dir):
os.makedirs(labels_dir)
for i in tqdm(range(num)):
if i % 2 == 0:
img, labels = generate_circle()
else:
img, labels = generate_rectangle()
cv2.imwrite(images_dir+"/"+str(i)+".jpg", img)
with open("data/shape/labels/"+str(i)+".txt", 'w') as f:
cx,cy,w,h = labels
f.write(str(i%2)+" "+str(cx/640)+" "+str(cy/640)+" "+str(w/640)+" "+str(h/640)+"\n")
def show_gt(dir='data/shape'):
files = os.listdir(dir+"/images")
gtdir = dir+"/gt"
if not os.path.exists(gtdir):
os.makedirs(gtdir)
for file in tqdm(files):
imgpath = dir+"/images/"+file
img = cv2.imread(imgpath)
h, w, _ = img.shape
labelpath = dir+"/labels/"+file[:-3]+"txt"
with open(labelpath) as f:
lines = f.readlines()
for line in lines:
items = line[:-1].split(" ")
c = int(items[0])
cx = float(items[1])
cy = float(items[2])
cw = float(items[3])
ch = float(items[4])
x1 = int((cx - cw/2)*w)
y1 = int((cy - ch/2)*h)
x2 = int((cx + cw/2)*w)
y2 = int((cy + ch/2)*h)
cv2.rectangle(img, (x1,y1),(x2,y2),(0,255,0),2)
cv2.putText(img, str(c), (x1,y1), 3,1,(0,0,255))
cv2.imwrite(gtdir+"/"+file, img)
if __name__=="__main__":
generate_batch()
show_gt()
対応する shape.yaml は、カテゴリ数が 2 であることに注意してください
train: data/shape/images/
val: data/shape/images/
# number of classes
nc: 2
# class names
names: ['circle', 'rectangle']
訓練
次のコマンドでトレーニングを開始します
python train.py --data circle.yaml --cfg yolov5s.yaml --weights '' --batch-size 64
円形と長方形の 2 種類のターゲットがある場合、コマンドは
python train.py --data shape.yaml --cfg yolov5s.yaml --weights '' --batch-size 64
トレーニング中に出力された統計、カテゴリ、および分布を見てください。
いくつかのエポックをトレーニングして結果を確認する
epoch, train/box_loss, train/obj_loss, train/cls_loss, metrics/precision, metrics/recall, metrics/mAP_0.5,metrics/mAP_0.5:0.95, val/box_loss, val/obj_loss, val/cls_loss, x/lr0, x/lr1, x/lr2
0, 0.03892, 0.011817, 0, 0.99998, 0.99978, 0.995, 0.92987, 0.0077891, 0.0030948, 0, 0.0033312, 0.0033312, 0.070019
1, 0.017302, 0.0049876, 0, 1, 0.9999, 0.995, 0.99105, 0.0031843, 0.0015662, 0, 0.0066644, 0.0066644, 0.040019
2, 0.011272, 0.0034826, 0, 1, 0.99994, 0.995, 0.99499, 0.0020194, 0.0010969, 0, 0.0099969, 0.0099969, 0.010018
3, 0.0080153, 0.0027186, 0, 1, 0.99994, 0.995, 0.995, 0.0013095, 0.00083033, 0, 0.0099978, 0.0099978, 0.0099978
4, 0.0067639, 0.0023831, 0, 1, 0.99996, 0.995, 0.995, 0.00099513, 0.00068878, 0, 0.0099978, 0.0099978, 0.0099978
5, 0.0061637, 0.0022279, 0, 1, 0.99996, 0.995, 0.995, 0.00090497, 0.00064193, 0, 0.0099961, 0.0099961, 0.0099961
6, 0.0058844, 0.002144, 0, 0.99999, 0.99998, 0.995, 0.995, 0.0009117, 0.00063328, 0, 0.0099938, 0.0099938, 0.0099938
7, 0.0056247, 0.00208, 0, 0.99999, 0.99999, 0.995, 0.995, 0.00086355, 0.00061343, 0, 0.0099911, 0.0099911, 0.0099911
8, 0.0054567, 0.0020223, 0, 1, 0.99999, 0.995, 0.995, 0.00081632, 0.00059592, 0, 0.0099879, 0.0099879, 0.0099879
9, 0.0053597, 0.0019864, 0, 1, 1, 0.995, 0.995, 0.00081379, 0.00058942, 0, 0.0099842, 0.0099842, 0.0099842
10, 0.0053103, 0.0019559, 0, 1, 1, 0.995, 0.995, 0.0008175, 0.00058669, 0, 0.00998, 0.00998, 0.00998
11, 0.0052146, 0.0019445, 0, 1, 1, 0.995, 0.995, 0.00083248, 0.00058731, 0, 0.0099753, 0.0099753, 0.0099753
12, 0.0050852, 0.0019065, 0, 1, 1, 0.995, 0.995, 0.00085092, 0.00058853, 0, 0.0099702, 0.0099702, 0.0099702
13, 0.0050589, 0.0019031, 0, 1, 1, 0.995, 0.995, 0.00086915, 0.00059267, 0, 0.0099645, 0.0099645, 0.0099645
14, 0.0049664, 0.0018693, 0, 1, 1, 0.995, 0.995, 0.00090856, 0.00059815, 0, 0.0099584, 0.0099584, 0.0099584
15, 0.0049839, 0.0018568, 0, 1, 1, 0.995, 0.995, 0.00093147, 0.00060425, 0, 0.0099517, 0.0099517, 0.0099517
16, 0.0049079, 0.0018459, 0, 1, 1, 0.995, 0.995, 0.0009656, 0.00061124, 0, 0.0099446, 0.0099446, 0.0099446
17, 0.0048693, 0.0018277, 0, 1, 1, 0.995, 0.995, 0.00099703, 0.00061948, 0, 0.009937, 0.009937, 0.009937
18, 0.0048052, 0.0018103, 0, 1, 1, 0.995, 0.995, 0.0010246, 0.00062618, 0, 0.0099289, 0.0099289, 0.0099289
19, 0.0047608, 0.0017947, 0, 1, 1, 0.995, 0.995, 0.0010439, 0.00063123, 0, 0.0099203, 0.0099203, 0.0099203
mAP は 99.5+ に達しました。これは非常に優れています。予測結果を見てください。
円形および長方形のオブジェクト
配備
最後に、次のコマンドを使用して検出します。パスをローカル パスに置き換えることを忘れないでください。
python detect.py --weights exps/yolov5s_circle/weights/best.pt --source data/circle/images
組み込みのデモは長すぎるため、さまざまな形式と互換性がありません。また、onnx の展開コードははるかに単純です。
import cv2
import numpy as np
import torch
from torchvision import transforms
import onnxruntime
from utils.general import non_max_suppression
def detect(img, ort_session):
img = img.astype(np.float32)
img = img / 255
img_tensor = img.transpose(2,0,1)[None]
ort_inputs = {ort_session.get_inputs()[0].name: img_tensor}
pred = torch.tensor(ort_session.run(None, ort_inputs)[0])
dets = non_max_suppression(pred, 0.25, 0.45)
return dets[0]
def demo():
ort_session = onnxruntime.InferenceSession("yolov5s.onnx", providers=['TensorrtExecutionProvider'])
img = cv2.imread("data/images/bus.jpg")
img = cv2.resize(img,(640,640))
dets = detect(img, ort_session)
for det in dets:
x1 = int(det[0])
y1 = int(det[1])
x2 = int(det[2])
y2 = int(det[3])
score = float(det[4])
cls = int(det[5])
info = "{}_{:.2f}".format(cls, score*100)
cv2.rectangle(img, (x1,y1),(x2,y2),(255,255,0))
cv2.putText(img, info, (x1,y1), 1, 1, (0,0,255))
cv2.imwrite("runs/detect/bus.jpg", img)
if __name__=="__main__":
demo()
要約する
この記事では、円検出と四角形検出の 2 つの例を通じてトレーニング データに必要なラベルを生成する方法を詳細に説明し、トレーニング、テスト、展開のプロセス全体のコード実装を提供します。