Dos meses después del yolo v4 presentado en el artículo anterior [ ejemplo de módulo opencv dnn (16) detección de objetivos object_detection - yolov4] , Ultralytics lanzó la primera versión oficial de YOLOV5, cuyo rendimiento es comparable al de YOLO V4.
Directorio de artículos
- 1. Explicación de las diferencias entre Yolo v5 y Yolo v4
-
- 1.1 Aumento de datos: mejora de datos
- 1.2 Anclajes de cuadro delimitador de aprendizaje automático: cuadro de anclaje adaptativo
- 1.3 Red parcial de etapas cruzadas (CSP)
- 1.4 Red de agregación Neck-Path (PANET)
- 1.5 Capa de detección universal Head-YOLO
- 1.5, Función de activación - función de activación
- 1.6 Función de optimización - función de optimización
- 1.7, Puntos de referencia: YOLO V5 VS YOLO V4
- 1.8 Comparación y resumen
- 2. prueba yolo v5
- 3. Entrenamiento de conjuntos de datos personalizados
Yolo v5 en realidad no tiene relación de herencia con Yolo v4. Ambos están mejorados en base a yolo v3. Sin embargo, debido a que no ha publicado artículos correspondientes, protocolos de código abierto y otros problemas, se ha cuestionado que no pueda considerarse como una nueva generación. de YOLO. Sin embargo, para nuestro aprendizaje y uso, siempre que pueda cazar ratones, un gato blanco o un gato negro es un buen gato.
1. Explicación de las diferencias entre Yolo v5 y Yolo v4
Compare YOLO V5 y V4 desde los siguientes aspectos, describa brevemente las características de sus respectivas nuevas tecnologías y compare las diferencias y similitudes entre los dos.
1.1 Aumento de datos: mejora de datos
YOLO V4 utiliza una combinación de múltiples tecnologías de mejora de datos para una sola imagen. Además de la distorsión geométrica clásica y la distorsión de iluminación, también utiliza de manera innovadora la tecnología de oclusión de imagen (borrado aleatorio, recorte, escondite, máscara de cuadrícula, mezcla). -Combinación de imágenes, el autor utiliza una combinación de tecnologías CutMix y Mosaic. Además, el autor también utilizó el entrenamiento de autoadversidad (SAT) para mejorar los datos.
El autor de YOLO V5 aún no ha publicado un artículo, por lo que su canal de aumento de datos solo puede entenderse desde la perspectiva del código.
YOLOV5 pasará cada lote de datos de entrenamiento a través del cargador de datos y al mismo tiempo mejorará los datos de entrenamiento.
El cargador de datos realiza tres tipos de mejora de datos: escalado, ajuste del espacio de color y mejora del mosaico.
Curiosamente, hay informes en los medios de que Glen Jocher, el autor de YOLO V5, es el creador de Mosaic Augmentation. Él cree que la enorme mejora en el rendimiento de YOLO V4 se debe en gran medida a la mejora de los datos en mosaico. Tal vez no esté convencido. lanzamiento de YOLO V4
, YOLO V5 se lanzó en solo dos meses. Por supuesto, continuar usando el nombre YOLO V5 o adoptar otros nombres en el futuro depende primero de si los resultados finales de la investigación de YOLO V5 realmente pueden liderar a YOLO V4.
Pero es innegable que la mejora de los datos de mosaico puede resolver eficazmente el "problema de objetos pequeños" más problemático en el entrenamiento de modelos, es decir, los objetos pequeños no se detectan con tanta precisión como los objetos grandes.
1.2 Anclajes de cuadro delimitador de aprendizaje automático: cuadro de anclaje adaptativo
En el YOLO V3 anterior, se utilizaron k-means y algoritmos de aprendizaje genético para analizar el conjunto de datos personalizados para obtener un cuadro de anclaje preestablecido adecuado para la predicción del cuadro de límites de objetos en el conjunto de datos personalizado.
En YOLO V5, el cuadro de anclaje se aprende automáticamente en función de los datos de entrenamiento. YOLO V4 no tiene un cuadro de anclaje adaptativo .
Para el conjunto de datos COCO, el tamaño del cuadro de anclaje bajo el tamaño de imagen de 640 × 640 se preestableció en el archivo de configuración *.yaml de YOLO V5:
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
Para conjuntos de datos personalizados, dado que el marco de reconocimiento de objetivos a menudo necesita escalar el tamaño de la imagen original y el tamaño del objeto de destino en el conjunto de datos puede ser diferente del conjunto de datos COCO, YOLO V5 aprenderá automáticamente el tamaño del cuadro de anclaje. de nuevo.
En la imagen de arriba, YOLO V5 está aprendiendo el tamaño del cuadro de anclaje automático. Para el conjunto de datos BDD100K, después de escalar la imagen en el modelo a 512, el cuadro de anclaje óptimo es:
1.3 Red parcial de etapas cruzadas (CSP)
Tanto YOLO V5 como V4 utilizan CSPDarknet como columna vertebral. El nombre completo de CSPNet es Cross Stage Partial Networks, que es una red parcial entre etapas. CSPNet resuelve el problema de duplicación de información de gradiente en la optimización de la red en otros grandes marcos de redes neuronales convolucionales Backbone e integra los cambios de gradiente en el mapa de características de principio a fin, reduciendo así la cantidad de parámetros del modelo y valores FLOPS, lo que no solo garantiza la velocidad de razonamiento. y precisión, y tamaño reducido del modelo.
1.4 Red de agregación Neck-Path (PANET)
Neck se utiliza principalmente para generar pirámides de características. La pirámide de características mejorará la detección de objetos en diferentes escalas por parte del modelo, permitiéndole reconocer el mismo objeto en diferentes tamaños y escalas.
Antes de que apareciera PANET, FPN había sido lo último en la capa de agregación de características del marco de detección de objetos hasta la aparición de PANET.
En la investigación de YOLO V4, se considera que PANET es la red de fusión de características más adecuada para YOLO, por lo que tanto YOLO V5 como V4 usan PANET como Neck para agregar características.
1.5 Capa de detección universal Head-YOLO
El modelo Head se utiliza principalmente para la parte de detección final. Aplica cuadros de anclaje en el mapa de características y produce un vector de salida final con probabilidades de clase, puntuaciones de objetos y cuadros delimitadores.
En el modelo YOLO V5, el modelo Head es el mismo que el de las versiones anteriores de YOLO V3 y V4.
Estos cabezales con diferentes escalas se utilizan para detectar objetos de diferentes tamaños (entrada 608, salida final con reducción de resolución 5 veces), cada cabezal tiene un total de (80 clases + 1 probabilidad + 4 coordenadas) * 3 cuadros de anclaje, un total de 255 canales.
1.5, Función de activación - función de activación
La elección de la función de activación es crucial para las redes de aprendizaje profundo. El autor de YOLO V5 utilizó las funciones de activación Leaky ReLU y Sigmoid.
En YOLO V5, la capa intermedia/oculta usa la función de activación Leaky ReLU y la capa de detección final usa la función de activación Sigmoide. YOLO V4 utiliza la función de activación Mish.
Mish supera a Swish en 39 puntos de referencia y a ReLU en 40 puntos de referencia, y algunos resultados muestran mejoras del 3 al 5 % en la precisión de los puntos de referencia. Pero tenga en cuenta que la activación de Mish es computacionalmente más costosa en comparación con ReLU y Swish.
1.6 Función de optimización - función de optimización
El autor de YOLO V5 nos proporciona dos funciones de optimización, Adam y SGD, y ambas hiperparámetros de entrenamiento coincidentes preestablecidos. El valor predeterminado es SGD.
YOLO V4 utiliza SGD.
El autor de YOLO V5 recomienda que si necesita entrenar conjuntos de datos personalizados más pequeños, Adam es una opción más adecuada, aunque la tasa de aprendizaje de Adam es generalmente menor que la de SGD.
Pero si entrenas un gran conjunto de datos, SGD funciona mejor que Adam para YOLOV5.
De hecho, no existe una conclusión unificada en la comunidad académica sobre cuál es mejor, SGD o Adam, y depende de la situación real del proyecto.
El cálculo de pérdidas de la serie YOLO de función de costo
se basa en la puntuación de objetividad, la puntuación de probabilidad de clase y la puntuación de regresión del cuadro delimitador.
YOLO V5 utiliza GIOU Loss como pérdida del cuadro delimitador y utiliza entropía cruzada binaria y función de pérdida Logits para calcular la pérdida de probabilidad de clase y la puntuación objetivo. Al mismo tiempo, también podemos usar el parámetro fl_gamma para activar la pérdida focal para calcular la función de pérdida.
YOLO V4 utiliza CIOU Loss como pérdida del cuadro delimitador. En comparación con otros métodos mencionados, CIOU brinda una convergencia más rápida y un mejor rendimiento.
Los resultados de la figura anterior se basan en Faster R-CNN y se puede ver que CIoU en realidad funciona mejor que GIoU.
1.7, Puntos de referencia: YOLO V5 VS YOLO V4
Antes de una discusión detallada en el artículo, solo podemos comparar el desempeño de los dos observando los indicadores COCO publicados por el autor y combinados con las evaluaciones de ejemplo posteriores realizadas por los grandes.
1.7.1 Evaluación oficial del desempeño
En las dos figuras anteriores, la relación entre FPS y ms/img está invertida. Después de la conversión de unidades, podemos encontrar que YOLO V5 puede alcanzar 250 FPS en la V100GPU y tiene un mAP alto.
Dado que el entrenamiento original de YOLO V4 está en 1080TI, que es mucho más bajo que el rendimiento de V100, y los puntos de referencia de AP_50 y AP_val son diferentes, es imposible obtener los puntos de referencia de los dos basándose únicamente en la tabla anterior.
Afortunadamente, WongKinYiu, el segundo autor de YOLO V4, utilizó la GPU V100 para proporcionar puntos de referencia comparables.
Como se puede ver en el gráfico, el rendimiento de los dos es realmente muy similar, pero según los datos, YOLO V4 sigue siendo el mejor marco de detección de objetos. YOLO V4 es altamente personalizable. Si no le temen a más configuraciones personalizadas, YOLO V4 basado en Darknet sigue siendo el más preciso.
Vale la pena señalar que YOLO V4 en realidad utiliza una gran cantidad de tecnologías de mejora de datos en la base de código Ultralytics YOLOv3. Estas tecnologías también se ejecutan en YOLO V5. El impacto de la tecnología de mejora de datos en los resultados debe esperar hasta el artículo del autor. análisis.
1.7.2 Tiempo de entrenamiento
Según la investigación de Roboflow, YOLO V5 entrena muy rápido, superando con creces a YOLO V4 en velocidad de entrenamiento. Para el conjunto de datos personalizado de Roboflow, YOLO V4 tardó 14 horas en alcanzar la evaluación de validación máxima, mientras que YOLO V5 solo tardó 3,5 horas.
1.7.3 Tamaño del modelo
Los tamaños de los diferentes modelos en la figura son: V5x: 367 MB, V5l: 192 MB, V5m: 84 MB, V5s: 27 MB, YOLOV4: 245 MB. El tamaño del modelo YOLO V5s es muy pequeño, lo que reduce los costos de implementación y favorece una implementación rápida
. del modelo.
1.7.4 Tiempo de razonamiento
En una sola imagen (tamaño de lote 1), YOLOV4 infiere en 22 ms y YOLOV5s infiere en 20 ms.
La implementación de YOLOV5 utiliza de forma predeterminada la inferencia por lotes (tamaño de lote 36) y divide el tiempo de procesamiento por lotes por la cantidad de imágenes en el lote. El tiempo de inferencia de una sola imagen puede alcanzar 7 ms, que es 140 FPS. Este es el estado actual de -la técnica en el campo de la detección de objetos.
Utilicé el modelo que entrené para realizar inferencia en tiempo real en 10,000 imágenes de prueba. La velocidad de inferencia de YOLOV5 es muy sorprendente. Cada imagen requiere solo 7 ms de tiempo de inferencia. Junto con el tamaño del modelo de más de 20 megabytes, no tiene rival en términos de flexibilidad.
Pero, de hecho, esto no es justo para YOLO V4. Dado que YOLO V4 no implementa el razonamiento por lotes predeterminado, está en desventaja en comparación. Deberían realizarse muchas pruebas en los dos marcos de detección de objetos bajo el mismo punto de referencia.
En segundo lugar, YOLO V4 lanzó recientemente una versión pequeña. La comparación de rendimiento y velocidad entre YOLO V5 y V4 pequeña requiere un análisis más práctico.
1.8 Comparación y resumen
En general, YOLO V4 es mejor que YOLO V5 en rendimiento, pero más débil que YOLO V5 en flexibilidad y velocidad.
Dado que YOLO V5 todavía se está actualizando rápidamente, los resultados finales de la investigación de YOLO V5 aún no se han analizado.
Personalmente, creo que para estos marcos de detección de objetos, el rendimiento de la capa de fusión de características es muy importante. Actualmente, ambos usan PANET, pero según la investigación de Google Brain, BiFPN es la mejor opción para la capa de fusión de características. Quien pueda integrar esta tecnología probablemente logrará mejoras significativas en el rendimiento.
Aunque YOLO V5 sigue siendo inferior, YOLO V5 todavía tiene las siguientes ventajas significativas:
-
El uso del marco Pytorch es muy fácil de usar y puede entrenar fácilmente sus propios conjuntos de datos. En comparación con el marco Darknet adoptado por YOLO V4, el marco Pytorch es más fácil de poner en producción.
-
El código es fácil de leer e integra una gran cantidad de tecnologías de visión por computadora, lo que favorece el aprendizaje y la referencia.
-
No solo es fácil configurar el entorno, sino que el entrenamiento del modelo también es muy rápido y la inferencia por lotes produce resultados en tiempo real.
-
Capacidad para realizar inferencias eficientes directamente en imágenes individuales, imágenes en lotes, vídeos e incluso entradas de puertos de cámara web
-
Puede convertir fácilmente el archivo de peso de Pytorch al formato ONXX utilizado por Android y luego convertirlo al formato utilizado por OPENCV, o convertirlo al formato IOS a través de CoreML e implementarlo directamente en la aplicación móvil.
-
Finalmente, la velocidad de reconocimiento de objetos de YOLO V5 hasta 140 FPS es muy impresionante y la experiencia del usuario es excelente.
2. prueba yolo v5
La dirección actual del proyecto yolo v5 es https://github.com/ultralytics/yolov y la versión se actualizó a v7.0.
2.1 prueba de Python
2.1.1 Instalación
git clone https://github.com/ultralytics/yolov5 # clone
cd yolov5
pip install -r requirements.txt # install
2.1.2 Razonamiento
-
Utilizando la inferencia del concentrador yolov5, el último modelo se descargará automáticamente desde la versión YOLOv5.
import torch # Model model = torch.hub.load("ultralytics/yolov5", "yolov5s") # or yolov5n - yolov5x6, custom # Images img = "https://ultralytics.com/images/zidane.jpg" # or file, Path, PIL, OpenCV, numpy, list # Inference results = model(img) # Results results.print() # or .show(), .save(), .crop(), .pandas(), etc. ```
-
La inferencia usando detect.py
detect.py ejecuta la inferencia en varias fuentes. El modelo se descarga automáticamente desde la última versión de YOLOv5 y los resultados se guardan en ejecuciones/detección.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/LNwODJXcvt4' # YouTube 'rtsp://example.com/media.mp4' # RTSP, RTMP, HTTP stream
2.1.3 Salida de prueba
Preste atención a la comparación de uso y eficiencia operativa de los parámetros --dnn y --half, centrándose en los datos de tiempo de tres indicadores de preproceso, inferencia y nms.
(yolo_pytorch) E:\DeepLearning\yolov5>python detect.py --weights yolov5n.pt --source data/images/bus.jpg
detect: weights=['yolov5n.pt'], source=data/images/bus.jpg, data=data\coco128.yaml, imgsz=[640, 640], conf_thres=0.25, iou_thres=0.45, max_det=1000, device=, view_img=False, save_txt=False, save_conf=False, save_crop=False, nosave=False, classes=None, agnostic_nms=False, augment=False, visualize=False, update=False, project=runs\detect, name=exp, exist_ok=False, line_thickness=3, hide_labels=False, hide_conf=False, half=False, dnn=False, vid_stride=1
YOLOv5 v7.0-167-g5deff14 Python-3.9.16 torch-1.13.1+cu117 CUDA:0 (NVIDIA GeForce GTX 1080 Ti, 11264MiB)
Fusing layers...
YOLOv5n summary: 213 layers, 1867405 parameters, 0 gradients
image 1/1 E:\DeepLearning\yolov5\data\images\bus.jpg: 640x480 4 persons, 1 bus, 121.0ms
Speed: 1.0ms pre-process, 121.0ms inference, 38.0ms NMS per image at shape (1, 3, 640, 640)
Results saved to runs\detect\exp2
(yolo_pytorch) E:\DeepLearning\yolov5>python detect.py --weights yolov5n.pt --source data/images/bus.jpg --device 0
detect: weights=['yolov5n.pt'], source=data/images/bus.jpg, data=data\coco128.yaml, imgsz=[640, 640], conf_thres=0.25, iou_thres=0.45, max_det=1000, device=0, view_img=False, save_txt=False, save_conf=False, save_crop=False, nosave=False, classes=None, agnostic_nms=False, augment=False, visualize=False, update=False, project=runs\detect, name=exp, exist_ok=False, line_thickness=3, hide_labels=False, hide_conf=False, half=False, dnn=True, vid_stride=1
YOLOv5 v7.0-167-g5deff14 Python-3.9.16 torch-1.13.1+cu117 CUDA:0 (NVIDIA GeForce GTX 1080 Ti, 11264MiB)
Fusing layers...
YOLOv5n summary: 213 layers, 1867405 parameters, 0 gradients
image 1/1 E:\DeepLearning\yolov5\data\images\bus.jpg: 640x480 4 persons, 1 bus, 11.0ms
Speed: 0.0ms pre-process, 11.0ms inference, 7.0ms NMS per image at shape (1, 3, 640, 640)
Results saved to runs\detect\exp2
(yolo_pytorch) E:\DeepLearning\yolov5>python detect.py --weights yolov5n.pt --source data/images/bus.jpg --dnn
detect: weights=['yolov5n.pt'], source=data/images/bus.jpg, data=data\coco128.yaml, imgsz=[640, 640], conf_thres=0.25, iou_thres=0.45, max_det=1000, device=, view_img=False, save_txt=False, save_conf=False, save_crop=False, nosave=False, classes=None, agnostic_nms=False, augment=False, visualize=False, update=False, project=runs\detect, name=exp, exist_ok=False, line_thickness=3, hide_labels=False, hide_conf=False, half=False, dnn=True, vid_stride=1
YOLOv5 v7.0-167-g5deff14 Python-3.9.16 torch-1.13.1+cu117 CUDA:0 (NVIDIA GeForce GTX 1080 Ti, 11264MiB)
Fusing layers...
YOLOv5n summary: 213 layers, 1867405 parameters, 0 gradients
image 1/1 E:\DeepLearning\yolov5\data\images\bus.jpg: 640x480 4 persons, 1 bus, 10.0ms
Speed: 0.0ms pre-process, 10.0ms inference, 4.0ms NMS per image at shape (1, 3, 640, 640)
Results saved to runs\detect\exp3
Otras comparaciones de pruebas
pre-process、 inference、 nms
cpu: 1 121 38
gpu: 0 11 7
dnn: 0 10 4
gpu-half: 0 10 4
dnn-half: 1 11 4
2.2 prueba de c++
Aquí, el módulo opencv dnn se utiliza para cargar el modelo de formato onnx exportado por yolov5 para realizar pruebas.
2.2.1 Exportación de modelo
El sitio web oficial en realidad proporciona archivos de exportación en formato onnx para cada versión del modelo, pero todos son modelos de media precisión y no se pueden usar directamente en opencv dnn.
Aquí tomamos yolov5x como ejemplo para exportar el modelo onnx. La primera vez que lo use, puede ver los parámetros del archivo py o verlo a través de la línea de comando, de la siguiente manera. Tenga en cuenta que al exportar, seleccione la versión adecuada de onnx opset para adaptarse a la versión opencv dnn .
(yolo_pytorch) E:\DeepLearning\yolov5>python export.py --weights yolov5x.pt --include onnx --opset 12
export: data=E:\DeepLearning\yolov5\data\coco128.yaml, weights=['yolov5x.pt'], imgsz=[640, 640], batch_size=1, device=cpu, half=False, inplace=False, keras=False, optimize=False, int8=False, dynamic=False, simplify=False, opset=12, verbose=False, workspace=4, nms=False, agnostic_nms=False, topk_per_class=100, topk_all=100, iou_thres=0.45, conf_thres=0.25, include=['onnx']
YOLOv5 v7.0-167-g5deff14 Python-3.9.16 torch-1.13.1+cu117 CPU
Fusing layers...
YOLOv5x summary: 444 layers, 86705005 parameters, 0 gradients
PyTorch: starting from yolov5x.pt with output shape (1, 25200, 85) (166.0 MB)
ONNX: starting export with onnx 1.14.0...
ONNX: export success 10.0s, saved as yolov5x.onnx (331.2 MB)
Export complete (15.0s)
Results saved to E:\DeepLearning\yolov5
Detect: python detect.py --weights yolov5x.onnx
Validate: python val.py --weights yolov5x.onnx
PyTorch Hub: model = torch.hub.load('ultralytics/yolov5', 'custom', 'yolov5x.onnx')
Visualize: https://netron.app
2.2.2, prueba de código opencv dnn c ++
El código del tema es el mismo que en yolov4, las principales diferencias son:
- El preprocesamiento se puede realizar de acuerdo con la situación, ya sea para escalar y llenar, asegurando que el tamaño sea consistente con la entrada de la red, consulte
formatToSquare()
la función. - Hay algunos pequeños ajustes en el procesamiento de datos de la salida de la red en el código de posprocesamiento.
El código completo es el siguiente.
#pragma once
#include "opencv2/opencv.hpp"
#include <fstream>
#include <sstream>
#include <random>
using namespace cv;
using namespace dnn;
float inpWidth;
float inpHeight;
float confThreshold, scoreThreshold, nmsThreshold;
std::vector<std::string> classes;
std::vector<cv::Scalar> colors;
bool letterBoxForSquare = true;
cv::Mat formatToSquare(const cv::Mat &source);
void postprocess(Mat& frame, cv::Size inputSz, const std::vector<Mat>& out, Net& net);
void drawPred(int classId, float conf, int left, int top, int right, int bottom, Mat& frame);
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<int> dis(100, 255);
int main()
{
// 根据选择的检测模型文件进行配置
confThreshold = 0.25;
scoreThreshold = 0.45;
nmsThreshold = 0.5;
float scale = 1/255.0; //0.00392
Scalar mean = {
0,0,0};
bool swapRB = true;
inpWidth = 640;
inpHeight = 640;
String model_dir = R"(E:\DeepLearning\yolov5)";
String modelPath = model_dir + R"(\yolov5n.onnx)";
String configPath;
String framework = "";
int backendId = cv::dnn::DNN_BACKEND_CUDA;
int targetId = cv::dnn::DNN_TARGET_CUDA;
String classesFile = R"(model\object_detection_classes_yolov3.txt)";
// Open file with classes names.
if(!classesFile.empty()) {
const std::string& file = classesFile;
std::ifstream ifs(file.c_str());
if(!ifs.is_open())
CV_Error(Error::StsError, "File " + file + " not found");
std::string line;
while(std::getline(ifs, line)) {
classes.push_back(line);
colors.push_back(cv::Scalar(dis(gen), dis(gen), dis(gen)));
}
}
// Load a model.
Net net = readNet(modelPath, configPath, framework);
net.setPreferableBackend(backendId);
net.setPreferableTarget(targetId);
std::vector<String> outNames = net.getUnconnectedOutLayersNames();
{
int dims[] = {
1,3,inpHeight,inpWidth};
cv::Mat tmp = cv::Mat::zeros(4, dims, CV_32F);
std::vector<cv::Mat> outs;
net.setInput(tmp);
for(int i = 0; i<10; i++)
net.forward(outs, outNames); // warmup
}
// Create a window
static const std::string kWinName = "Deep learning object detection in OpenCV";
cv::namedWindow(kWinName, 0);
// Open a video file or an image file or a camera stream.
VideoCapture cap;
//cap.open(0);
cap.open(R"(E:\DeepLearning\yolov5\data\images\bus.jpg)");
cv::TickMeter tk;
// Process frames.
Mat frame, blob;
while(waitKey(1) < 0) {
//tk.reset();
//tk.start();
cap >> frame;
if(frame.empty()) {
waitKey();
break;
}
// Create a 4D blob from a frame.
cv::Mat modelInput = frame;
if(letterBoxForSquare && inpWidth == inpHeight)
modelInput = formatToSquare(modelInput);
blobFromImage(modelInput, blob, scale, cv::Size2f(inpWidth, inpHeight), mean, swapRB, false);
// Run a model.
net.setInput(blob);
std::vector<Mat> outs;
//tk.reset();
//tk.start();
auto tt1 = cv::getTickCount();
net.forward(outs, outNames);
auto tt2 = cv::getTickCount();
tk.stop();
postprocess(frame, modelInput.size(), outs, net);
//tk.stop();
// Put efficiency information.
std::vector<double> layersTimes;
double freq = getTickFrequency() / 1000;
double t = net.getPerfProfile(layersTimes) / freq;
std::string label = format("Inference time: %.2f ms (%.2f ms)", t, /*tk.getTimeMilli()*/ (tt2 - tt1) / cv::getTickFrequency() * 1000);
cv::putText(frame, label, Point(0, 15), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 255, 0));
cv::imshow(kWinName, frame);
}
return 0;
}
cv::Mat formatToSquare(const cv::Mat &source)
{
int col = source.cols;
int row = source.rows;
int _max = MAX(col, row);
cv::Mat result = cv::Mat::zeros(_max, _max, CV_8UC3);
source.copyTo(result(cv::Rect(0, 0, col, row)));
return result;
}
void postprocess(Mat& frame, cv::Size inputSz, const std::vector<Mat>& outs, Net& net)
{
// yolov5 has an output of shape (batchSize, 25200, 85) (Num classes + box[x,y,w,h] + confidence[c])
auto tt1 = cv::getTickCount();
//float x_factor = frame.cols / inpWidth;
//float y_factor = frame.rows / inpHeight;
float x_factor = inputSz.width / inpWidth;
float y_factor = inputSz.height / inpHeight;
std::vector<int> class_ids;
std::vector<float> confidences;
std::vector<cv::Rect> boxes;
int rows = outs[0].size[1];
int dimensions = outs[0].size[2];
float *data = (float *)outs[0].data;
for(int i = 0; i < rows; ++i) {
float confidence = data[4];
if(confidence >= confThreshold) {
float *classes_scores = data + 5;
cv::Mat scores(1, classes.size(), CV_32FC1, classes_scores);
cv::Point class_id;
double max_class_score;
minMaxLoc(scores, 0, &max_class_score, 0, &class_id);
if(max_class_score > scoreThreshold) {
confidences.push_back(confidence);
class_ids.push_back(class_id.x);
float x = data[0];
float y = data[1];
float w = data[2];
float h = data[3];
int left = int((x - 0.5 * w) * x_factor);
int top = int((y - 0.5 * h) * y_factor);
int width = int(w * x_factor);
int height = int(h * y_factor);
boxes.push_back(cv::Rect(left, top, width, height));
}
}
data += dimensions;
}
std::vector<int> indices;
NMSBoxes(boxes, confidences, scoreThreshold, nmsThreshold, indices);
auto tt2 = cv::getTickCount();
std::string label = format("NMS time: %.2f ms", (tt2 - tt1) / cv::getTickFrequency() * 1000);
cv::putText(frame, label, Point(0, 30), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 255, 0));
for(size_t i = 0; i < indices.size(); ++i) {
int idx = indices[i];
Rect box = boxes[idx];
drawPred(class_ids[idx], confidences[idx], box.x, box.y,
box.x + box.width, box.y + box.height, frame);
}
}
void drawPred(int classId, float conf, int left, int top, int right, int bottom, Mat& frame)
{
rectangle(frame, Point(left, top), Point(right, bottom), Scalar(0, 255, 0));
std::string label = format("%.2f", conf);
Scalar color = Scalar::all(255);
if(!classes.empty()) {
CV_Assert(classId < (int)classes.size());
label = classes[classId] + ": " + label;
color = colors[classId];
}
int baseLine;
Size labelSize = getTextSize(label, FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);
top = max(top, labelSize.height);
rectangle(frame, Point(left, top - labelSize.height),
Point(left + labelSize.width, top + baseLine), color, FILLED);
cv::putText(frame, label, Point(left, top), FONT_HERSHEY_SIMPLEX, 0.5, Scalar());
}
2.2.3 Resultados de la prueba
Cuando la prueba anterior de Python usó GPU, la inferencia directa tomó 10 ms y NMS tomó 4 ms. Cuando se usa opencv dnn para abrir dnn aquí, la inferencia directa tomó ~7 ms y NMS tomó ~0,3 ms.
3. Entrenamiento de conjuntos de datos personalizados
Aquí, yolov5s se utiliza como modelo de preentrenamiento para entrenar un modelo de detección de objetivos que contiene 4 tipos de vehículos.
3.1 Preparación del conjunto de datos
Primero etiquete la imagen usted mismo, por ejemplo, tomando el formato voc como ejemplo, use la herramienta labelImg para etiquetar. El formato de archivo de etiquetado predeterminado de coco es xml, que debe convertirse a txt mediante un script (además, puede use directamente la herramienta labelme para guardarlo directamente en el formato txt requerido por yolo).
Aquí sólo nos centramos en las carpetas JPEGImages
y labels
. Una vez completada la anotación, coloque la imagen y el archivo de anotación generado en cualquier directorio. Por ejemplo E:\DeepLearning\yolov5\custom-data\vehicle
, luego coloque la imagen y el archivo de anotación en las carpetas de imágenes y etiquetas respectivamente (ruta predeterminada de yolov5; de lo contrario, deberá modificar la función img2label_paths en yolov5 /utils/dataloaders.py dos parámetros).
vehicle
├── images
│ ├── 20151127_114556.jpg
│ ├── 20151127_114946.jpg
│ └── 20151127_115133.jpg
├── labels
│ ├── 20151127_114556.txt
│ ├── 20151127_114946.txt
│ └── 20151127_115133.txt
Después de eso, prepare los archivos de lista train.txt, val.txt y test.txt para el conjunto de entrenamiento, el conjunto de verificación y el conjunto de prueba (opcional). Las rutas absolutas de las imágenes se almacenan en los tres archivos y la proporción se selecciona aleatoriamente, como 7:2:1.
3.2 Archivo de configuración
Copie los archivos data/coco.yaml y model/yolov5s.yaml al directorio del conjunto de datos y realice modificaciones.
Por ejemplo, el archivo de descripción del conjunto de datosmyvoc.yaml
train: E:/DeepLearning/yolov5/custom-data/vehicle/train.txt
val: E:/DeepLearning/yolov5/custom-data/vehicle/val.txt
# number of classes
nc: 4
# class names
names: ["car", "huoche", "guache", "keche"]
El archivo de configuración del modelo de red yolov5s.yaml
solo modifica el parámetro nc al número real de categorías de detección de objetivos
# Parameters
nc: 4 # number of classes
depth_multiple: 0.33 # model depth multiple
width_multiple: 0.50 # layer channel multiple
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
3.3 Formación
Como se mencionó anteriormente, una vez completado el trabajo de preparación, la estructura del directorio es la siguiente:
luego entrenamos 20 epocs y el script para el entrenamiento de una sola GPU es el siguiente:
python train.py
--weights yolov5s.pt
--cfg custom-data\vehicle\yolov5s.yaml
--data custom-data\vehicle\myvoc.yaml
--epoch 20
--batch-size=32
--img 640
--device 0
El contenido del resultado de la formación es
E:\DeepLearning\yolov5>python train.py --weights yolov5s.pt --cfg custom-data\vehicle\yolov5s.yaml --data custom-data\vehicle\myvoc.yaml --epoch 20 --batch-size=32 --img 640 --device 0
train: weights=yolov5s.pt, cfg=custom-data\vehicle\yolov5s.yaml, data=custom-data\vehicle\myvoc.yaml, hyp=data\hyps\hyp.scratch-low.yaml, epochs=20, batch_size=32, imgsz=640, rect=False, resume=False, nosave=False, noval=False, noautoanchor=False, noplots=False, evolve=None, bucket=, cache=None, image_weights=False, device=0, multi_scale=False, single_cls=False, optimizer=SGD, sync_bn=False, workers=8, project=runs\train, name=exp, exist_ok=False, quad=False, cos_lr=False, label_smoothing=0.0, patience=100, freeze=[0], save_period=-1, seed=0, local_rank=-1, entity=None, upload_dataset=False, bbox_interval=-1, artifact_alias=latest
fatal: unable to access 'http://github.com/ultralytics/yolov5.git/': Recv failure: Connection was reset
Command 'git fetch origin' timed out after 5 seconds
YOLOv5 v7.0-167-g5deff14 Python-3.9.16 torch-1.13.1+cu117 CUDA:0 (NVIDIA GeForce GTX 1080 Ti, 11264MiB)
hyperparameters: lr0=0.01, lrf=0.01, momentum=0.937, weight_decay=0.0005, warmup_epochs=3.0, warmup_momentum=0.8, warmup_bias_lr=0.1, box=0.05, cls=0.5, cls_pw=1.0, obj=1.0, obj_pw=1.0, iou_t=0.2, anchor_t=4.0, fl_gamma=0.0, hsv_h=0.015, hsv_s=0.7, hsv_v=0.4, degrees=0.0, translate=0.1, scale=0.5, shear=0.0, perspective=0.0, flipud=0.0, fliplr=0.5, mosaic=1.0, mixup=0.0, copy_paste=0.0
Comet: run 'pip install comet_ml' to automatically track and visualize YOLOv5 runs in Comet
TensorBoard: Start with 'tensorboard --logdir runs\train', view at http://localhost:6006/
from n params module arguments
0 -1 1 3520 models.common.Conv [3, 32, 6, 2, 2]
1 -1 1 18560 models.common.Conv [32, 64, 3, 2]
2 -1 1 18816 models.common.C3 [64, 64, 1]
3 -1 1 73984 models.common.Conv [64, 128, 3, 2]
4 -1 2 115712 models.common.C3 [128, 128, 2]
5 -1 1 295424 models.common.Conv [128, 256, 3, 2]
6 -1 3 625152 models.common.C3 [256, 256, 3]
7 -1 1 1180672 models.common.Conv [256, 512, 3, 2]
8 -1 1 1182720 models.common.C3 [512, 512, 1]
9 -1 1 656896 models.common.SPPF [512, 512, 5]
10 -1 1 131584 models.common.Conv [512, 256, 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 361984 models.common.C3 [512, 256, 1, False]
14 -1 1 33024 models.common.Conv [256, 128, 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 90880 models.common.C3 [256, 128, 1, False]
18 -1 1 147712 models.common.Conv [128, 128, 3, 2]
19 [-1, 14] 1 0 models.common.Concat [1]
20 -1 1 296448 models.common.C3 [256, 256, 1, False]
21 -1 1 590336 models.common.Conv [256, 256, 3, 2]
22 [-1, 10] 1 0 models.common.Concat [1]
23 -1 1 1182720 models.common.C3 [512, 512, 1, False]
24 [17, 20, 23] 1 24273 models.yolo.Detect [4, [[10, 13, 16, 30, 33, 23], [30, 61, 62, 45, 59, 119], [116, 90, 156, 198, 373, 326]], [128, 256, 512]]
YOLOv5s summary: 214 layers, 7030417 parameters, 7030417 gradients, 16.0 GFLOPs
Transferred 342/349 items from yolov5s.pt
AMP: checks passed
optimizer: SGD(lr=0.01) with parameter groups 57 weight(decay=0.0), 60 weight(decay=0.0005), 60 bias
train: Scanning E:\DeepLearning\yolov5\custom-data\vehicle\train... 998 images, 0 backgrounds, 0 corrupt: 100%|██████████| 998/998 [00:07<00:00, 141.97it/s]
train: New cache created: E:\DeepLearning\yolov5\custom-data\vehicle\train.cache
val: Scanning E:\DeepLearning\yolov5\custom-data\vehicle\val... 998 images, 0 backgrounds, 0 corrupt: 100%|██████████| 998/998 [00:13<00:00, 72.66it/s]
val: New cache created: E:\DeepLearning\yolov5\custom-data\vehicle\val.cache
AutoAnchor: 4.36 anchors/target, 1.000 Best Possible Recall (BPR). Current anchors are a good fit to dataset
Plotting labels to runs\train\exp13\labels.jpg...
Image sizes 640 train, 640 val
Using 8 dataloader workers
Logging results to runs\train\exp13
Starting training for 20 epochs...
Epoch GPU_mem box_loss obj_loss cls_loss Instances Size
0/19 6.36G 0.09633 0.038 0.03865 34 640: 100%|██████████| 32/32 [00:19<00:00, 1.66it/s]
Class Images Instances P R mAP50 mAP50-95: 100%|██████████| 16/16 [00:11<00:00, 1.45it/s]
all 998 2353 0.884 0.174 0.248 0.0749
Epoch GPU_mem box_loss obj_loss cls_loss Instances Size
1/19 9.9G 0.06125 0.03181 0.02363 26 640: 100%|██████████| 32/32 [00:14<00:00, 2.18it/s]
Class Images Instances P R mAP50 mAP50-95: 100%|██████████| 16/16 [00:10<00:00, 1.50it/s]
all 998 2353 0.462 0.374 0.33 0.105
Epoch GPU_mem box_loss obj_loss cls_loss Instances Size
2/19 9.9G 0.06124 0.02353 0.02014 18 640: 100%|██████████| 32/32 [00:14<00:00, 2.22it/s]
Class Images Instances P R mAP50 mAP50-95: 100%|██████████| 16/16 [00:10<00:00, 1.58it/s]
all 998 2353 0.469 0.472 0.277 0.129
Epoch GPU_mem box_loss obj_loss cls_loss Instances Size
3/19 9.9G 0.05214 0.02038 0.0175 27 640: 100%|██████████| 32/32 [00:14<00:00, 2.22it/s]
Class Images Instances P R mAP50 mAP50-95: 100%|██████████| 16/16 [00:10<00:00, 1.56it/s]
all 998 2353 0.62 0.64 0.605 0.279
Epoch GPU_mem box_loss obj_loss cls_loss Instances Size
4/19 9.9G 0.04481 0.01777 0.01598 23 640: 100%|██████████| 32/32 [00:14<00:00, 2.17it/s]
Class Images Instances P R mAP50 mAP50-95: 100%|██████████| 16/16 [00:10<00:00, 1.60it/s]
all 998 2353 0.803 0.706 0.848 0.403
Epoch GPU_mem box_loss obj_loss cls_loss Instances Size
5/19 9.9G 0.0381 0.01624 0.01335 19 640: 100%|██████████| 32/32 [00:14<00:00, 2.16it/s]
Class Images Instances P R mAP50 mAP50-95: 100%|██████████| 16/16 [00:10<00:00, 1.55it/s]
all 998 2353 0.651 0.872 0.8 0.414
Epoch GPU_mem box_loss obj_loss cls_loss Instances Size
6/19 9.9G 0.03379 0.01534 0.01134 28 640: 100%|██████████| 32/32 [00:14<00:00, 2.18it/s]
Class Images Instances P R mAP50 mAP50-95: 100%|██████████| 16/16 [00:10<00:00, 1.58it/s]
all 998 2353 0.94 0.932 0.978 0.608
Epoch GPU_mem box_loss obj_loss cls_loss Instances Size
7/19 9.9G 0.03228 0.01523 0.00837 10 640: 100%|██████████| 32/32 [00:14<00:00, 2.21it/s]
Class Images Instances P R mAP50 mAP50-95: 100%|██████████| 16/16 [00:09<00:00, 1.67it/s]
all 998 2353 0.862 0.932 0.956 0.591
Epoch GPU_mem box_loss obj_loss cls_loss Instances Size
8/19 9.9G 0.0292 0.01458 0.007451 20 640: 100%|██████████| 32/32 [00:14<00:00, 2.21it/s]
Class Images Instances P R mAP50 mAP50-95: 100%|██████████| 16/16 [00:10<00:00, 1.56it/s]
all 998 2353 0.97 0.954 0.986 0.658
Epoch GPU_mem box_loss obj_loss cls_loss Instances Size
9/19 9.9G 0.02739 0.01407 0.006553 29 640: 100%|██████████| 32/32 [00:15<00:00, 2.12it/s]
Class Images Instances P R mAP50 mAP50-95: 100%|██████████| 16/16 [00:10<00:00, 1.58it/s]
all 998 2353 0.982 0.975 0.993 0.74
Epoch GPU_mem box_loss obj_loss cls_loss Instances Size
10/19 9.9G 0.0248 0.01362 0.005524 30 640: 100%|██████████| 32/32 [00:14<00:00, 2.14it/s]
Class Images Instances P R mAP50 mAP50-95: 100%|██████████| 16/16 [00:10<00:00, 1.55it/s]
all 998 2353 0.985 0.973 0.993 0.757
Epoch GPU_mem box_loss obj_loss cls_loss Instances Size
11/19 9.9G 0.02377 0.01271 0.005606 27 640: 100%|██████████| 32/32 [00:15<00:00, 2.13it/s]
Class Images Instances P R mAP50 mAP50-95: 100%|██████████| 16/16 [00:10<00:00, 1.52it/s]
all 998 2353 0.964 0.975 0.989 0.725
Epoch GPU_mem box_loss obj_loss cls_loss Instances Size
12/19 9.9G 0.02201 0.01247 0.005372 33 640: 100%|██████████| 32/32 [00:14<00:00, 2.19it/s]
Class Images Instances P R mAP50 mAP50-95: 100%|██████████| 16/16 [00:10<00:00, 1.57it/s]
all 998 2353 0.988 0.988 0.994 0.83
Epoch GPU_mem box_loss obj_loss cls_loss Instances Size
13/19 9.9G 0.02103 0.01193 0.004843 22 640: 100%|██████████| 32/32 [00:14<00:00, 2.14it/s]
Class Images Instances P R mAP50 mAP50-95: 100%|██████████| 16/16 [00:10<00:00, 1.57it/s]
all 998 2353 0.981 0.987 0.994 0.817
Epoch GPU_mem box_loss obj_loss cls_loss Instances Size
14/19 9.9G 0.02017 0.01167 0.00431 22 640: 100%|██████████| 32/32 [00:14<00:00, 2.20it/s]
Class Images Instances P R mAP50 mAP50-95: 100%|██████████| 16/16 [00:09<00:00, 1.60it/s]
all 998 2353 0.96 0.952 0.987 0.782
Epoch GPU_mem box_loss obj_loss cls_loss Instances Size
15/19 9.9G 0.01847 0.01158 0.004043 32 640: 100%|██████████| 32/32 [00:14<00:00, 2.20it/s]
Class Images Instances P R mAP50 mAP50-95: 100%|██████████| 16/16 [00:10<00:00, 1.56it/s]
all 998 2353 0.988 0.992 0.994 0.819
Epoch GPU_mem box_loss obj_loss cls_loss Instances Size
16/19 9.9G 0.01771 0.0114 0.003859 24 640: 100%|██████████| 32/32 [00:14<00:00, 2.20it/s]
Class Images Instances P R mAP50 mAP50-95: 100%|██████████| 16/16 [00:10<00:00, 1.55it/s]
all 998 2353 0.967 0.96 0.99 0.832
Epoch GPU_mem box_loss obj_loss cls_loss Instances Size
17/19 9.9G 0.01665 0.01077 0.003739 32 640: 100%|██████████| 32/32 [00:14<00:00, 2.22it/s]
Class Images Instances P R mAP50 mAP50-95: 100%|██████████| 16/16 [00:10<00:00, 1.59it/s]
all 998 2353 0.992 0.995 0.994 0.87
Epoch GPU_mem box_loss obj_loss cls_loss Instances Size
18/19 9.9G 0.01559 0.01067 0.003549 45 640: 100%|██████████| 32/32 [00:14<00:00, 2.21it/s]
Class Images Instances P R mAP50 mAP50-95: 100%|██████████| 16/16 [00:10<00:00, 1.53it/s]
all 998 2353 0.991 0.995 0.995 0.867
Epoch GPU_mem box_loss obj_loss cls_loss Instances Size
19/19 9.9G 0.01459 0.01009 0.003031 31 640: 100%|██████████| 32/32 [00:14<00:00, 2.18it/s]
Class Images Instances P R mAP50 mAP50-95: 100%|██████████| 16/16 [00:11<00:00, 1.42it/s]
all 998 2353 0.994 0.995 0.994 0.885
20 epochs completed in 0.143 hours.
Optimizer stripped from runs\train\exp13\weights\last.pt, 14.4MB
Optimizer stripped from runs\train\exp13\weights\best.pt, 14.4MB
Validating runs\train\exp13\weights\best.pt...
Fusing layers...
YOLOv5s summary: 157 layers, 7020913 parameters, 0 gradients, 15.8 GFLOPs
Class Images Instances P R mAP50 mAP50-95: 100%|██████████| 16/16 [00:11<00:00, 1.37it/s]
all 998 2353 0.994 0.995 0.994 0.885
car 998 1309 0.995 0.999 0.995 0.902
huoche 998 507 0.993 0.988 0.994 0.895
guache 998 340 0.988 0.993 0.994 0.877
keche 998 197 0.999 1 0.995 0.866
Results saved to runs\train\exp13
Durante el proceso de entrenamiento, puedes utilizar el tensorboard para ver visualmente la curva de entrenamiento. Inícielo en el directorio yolov5 tensorboard --logdir runs\train
y luego http://localhost:6006/
acceda a él para verlo:
la velocidad de entrenamiento es muy rápida, con 998 imágenes, y solo toma unos 8 minutos entrenar 20epoc. El modelo de entrenamiento guardado se almacena en runs\train\exp13
el directorio.
Otras capturas de pantalla relacionadas
Utilice el script python detect.py --weights runs\train\exp13\weights\best.pt --source custom-data\vehicle\images\11.jpg
para probar el siguiente
cuadro de resultados