当前位置:网站首页>Use yolov5 to train your own data set; Installation and use of yolov5; Interpretation of yolov5 source code

Use yolov5 to train your own data set; Installation and use of yolov5; Interpretation of yolov5 source code

2022-06-22 03:36:00 m0_ sixty-seven million four hundred and one thousand six hundr

* disclaimer :
1 This method is for reference only
2 The operation methods of other bloggers have been copied , To paste the path .
3*

Scene one :Anconda Environment basic operation

Scene two :yolov5 Use

Scene three :yolo v5 Train your own dataset

Scene 4 :yolov5 Source code interpretation

Scene one :Anconda Environment basic operation

1: Basic commands
see Anaconda Version information for conda -V
see python Version information python
open Jupyter Notebook command jupyter notebook perhaps ipython notebook
sign out python Input environment : ctrl+z
The command line terminates a running program command : ctrl + c

see opencv Version information :
 Please add a picture description

2: Create and use your own virtual environment

Generate a file called jiance Environment , Used for identification tasks :
conda create -n jiance python=3.7

Enter this environment , That is to activate the environment
source activate jiance windows Next : activate jiance

The next step is In this environment, you can download the packages you need pip insatll numpy Or is it conda install numpy=1.10

Get out of this environment :source deactivate

See what environments have been created :conda info --envs

View the created package :conda list

Quit this jiance Environmental Science : linux Next source deactivate windows Next deactivate

3: Delete package 、 Delete environment Or update the package

Delete numpy package :conda remove numpy Or designate conda remove numpy=1.10

to update numpy package : conda update numpy

to update jiance All the bags inside : conda update - -all

Search for numpy package : conda search numpy

Delete jiance The command of this environment : conda env remove -n jiance

4: Shared environment

For example, my present jiance I downloaded a lot of packages in this environment , Equipped with a recognized environment , Others want to use my environment or I want to quickly migrate projects from my computer to other computers :

First enter my environment : activate jiance Execute this statement conda env export > name .yaml

 for example  conda env export > environment.yaml  
 The first part of the command  conda env export  Used to output the names of all packages in the environment 
 Through the second half environment.yaml Save it to and name it “environment.yaml”

 Please add a picture description
What others need to do is get this yaml file : conda env create -f environment.yaml

Scene two :yolov5 Use

1: install pytorch (windows nothing GPU) Environmental Science

Switch to the above jiance In the environment of

conda install pytorch==1.8.0 torchvision==0.9.0 torchaudio==0.8.0 cpuonly

2: download yolo v5

yolo v5 github Address
 Please add a picture description

3: Unzip the downloaded project , use pycharm open , Then associate the project with us in Anconda Create a good virtual environment in
 Please add a picture description
4. Download dependency

Mode one : stay pycharm Execution in terminal pip install -r requirements.txt

 Please add a picture description
Mode two : Enter... At the top of the project folder cmd You can go to windows Open the terminal to the project folder , Switch to virtual environment activate jiance And then type the command pip install -r requirements.txt
 Please add a picture description
5: Download the weight file

Weight file download address
 Please add a picture description
 Please add a picture description

test

The path in the command It can be changed flexibly ,yolo v5 The version of now has 6 A version update , The directory structure of each version may be different , for example bus.jpg stay v1 The version is inference Under the folder , stay v6 Version is in data Under the folder , The following command is v6 Version of ( It's just that the path is different )

 Please add a picture description

The test image

python detect.py --source=data/images/bus.jpg


python detect.py --source=data/images/people.jpg  --weights=weights/yolov5s.pt

The confidence level exceeds 0.4 Show it

python detect.py --source=data/images/people.jpg  --weights=weights/yolov5s.pt  --conf  0.4

 Please add a picture description
 Please add a picture description
Test video

python detect.py --source=data/images/1.mp4  --weights=weights/yolov5s.pt

camera

python detect.py --source 0


python detect.py --source 0  --weights=weights/yolov5s.pt

Model integration detection

python detect.py --source=data/images/people.jpg  --weights=weights/yolov5s.pt yolov5l.pt

Change the test save path
 Insert picture description here

Scene three :yolo v5 Train your own dataset

 Please add a picture description
 Insert picture description here

1.1 Training skills

object detection YOLOv5 anchor Set up
yolov5 anchors Setup details

【Python】 Calculation VOC Format XML File the target area and aspect ratio and generate histogram
 Please add a picture description
 Please add a picture description
Remember to put it in jyputer It is convenient to save and view the results

# -*- coding: utf-8 -*-
"""
Created on Sun Jan 10 21:48:48 2021

@author: YaoYee
"""

import os
import xml.etree.cElementTree as et
import numpy as np
import matplotlib.pyplot as plt
from tqdm import tqdm
import cv2

path = ".......................Annotations" # Your path 
files = os.listdir(path)

area_list = []
ratio_list = []


def file_extension(path):
    return os.path.splitext(path)[1]


for xmlFile in tqdm(files, desc='Processing'):
    if not os.path.isdir(xmlFile):
        if file_extension(xmlFile) == '.xml':
            tree = et.parse(os.path.join(path, xmlFile))
            root = tree.getroot()
            filename = root.find('filename').text
            # print("--Filename is", xmlFile)

            for Object in root.findall('object'):
                bndbox = Object.find('bndbox')
                xmin = bndbox.find('xmin').text
                ymin = bndbox.find('ymin').text
                xmax = bndbox.find('xmax').text
                ymax = bndbox.find('ymax').text

                area = (int(ymax) - int(ymin)) * (int(xmax) - int(xmin))
                area_list.append(area)
                # print("Area is", area)

                ratio = (int(ymax) - int(ymin)) / (int(xmax) - int(xmin))
                ratio_list.append(ratio)
                # print("Ratio is", round(ratio,2))

square_array = np.array(area_list)
square_max = np.max(square_array)
square_min = np.min(square_array)
square_mean = np.mean(square_array)
square_var = np.var(square_array)
plt.figure(1)
plt.hist(square_array, 20)
plt.xlabel('Area in pixel')
plt.ylabel('Frequency of area')
plt.title('Area
' 
      + 'max=' + str(square_max) + ', min=' + str(square_min) + '
' 
      + 'mean=' + str(int(square_mean)) + ', var=' + str(int(square_var))
          )
plt.savefig('aabb1.jpg')


ratio_array = np.array(ratio_list)
ratio_max = np.max(ratio_array)
ratio_min = np.min(ratio_array)
ratio_mean = np.mean(ratio_array)
ratio_var = np.var(ratio_array)


plt.figure(2)
plt.hist(ratio_array, 20)
plt.xlabel('Ratio of length / width')
plt.ylabel('Frequency of ratio')
plt.title('Ratio
' 
      + 'max=' + str(round(ratio_max, 2)) + ', min=' + str(round(ratio_min, 2)) + '
' 
      + 'mean=' + str(round(ratio_mean, 2)) + ', var=' + str(round(ratio_var, 2))
          )
plt.savefig('aabb.jpg')

First, calculate the aspect ratio , And then in yolov5 Create a new... In the program python file test.py, Calculate the anchor box manually :

import utils.autoanchor as autoAC
 
#  Recalculate dataset  anchors
new_anchors = autoAC.kmean_anchors('./data/mydata.yaml', 9, 640, 5.0, 1000, True)
print(new_anchors)

Output 9 The new anchor box is calculated based on its own data set , You can replace it with the configuration file you are using in order *.yaml in ( such as yolov5s.yaml) , You can train again .

 Please add a picture description
 Insert picture description here
 Please add a picture description
Hyperparametric evolution Hyperparameter Evolution
 Please add a picture description

python train.py --resume

many GPU Training

During the training , If the size of your picture is 320x256, You want the input of the model to be the same 320x256. Then you just need to add --img 320 --rect

1.2 Train your own dataset

First step : Use labelImg Label your own dataset

Don't have a Chinese path

B Station teaching video —>LabelImg Instructions for using the labeling tool

Training ideas : yolov5 Two training methods are supported : The first method is to write the path of the training file directly to txt File in . The second method is to directly transfer in the folder where the training files are located . Training v1.0 Code for ( The other versions are the same , It's just v1.0 Code may be more prone to problems )

The second step : Divide the training set / Test set

We follow the second way :
.
Create the following folders , stay yolov5 Create mydata Folder , And then in mydata Under the folder

.
all_images Put pictures in the folder
all_xml Put in the folder xml file
make_txt.py Files are used to partition data sets
train_val.py Folders are used to convert labels
 Insert picture description here
Pay attention to two py The document should be built in mydata Under the folder

 Insert picture description here
make_txt.py

import os
import random
# Nothing needs to be changed  , Just change the division ratio below 
trainval_percent = 0.1  
train_percent = 0.9    
xmlfilepath = 'all_images'
txtsavepath = 'ImageSets'
total_xml = os.listdir(xmlfilepath)
num = len(total_xml)
list = range(num)
tv = int(num * trainval_percent)
tr = int(tv * train_percent)
trainval = random.sample(list, tv) # From all list Back in tv A number of items 
train = random.sample(trainval, tr)
if not os.path.exists('ImageSets/'):
    os.makedirs('ImageSets/')
ftrainval = open('ImageSets/trainval.txt', 'w')
ftest = open('ImageSets/test.txt', 'w')
ftrain = open('ImageSets/train.txt', 'w')
fval = open('ImageSets/val.txt', 'w')
for i in list:
    name = total_xml[i][:-4] + '
'
    if i in trainval:
        ftrainval.write(name)
        if i in train:
            ftest.write(name)
        else:
            fval.write(name)
    else:
        ftrain.write(name)
ftrainval.close()
ftrain.close()
fval.close()
ftest.close()

 Insert picture description here

The third step : Next, prepare labels, Also is to voc Format conversion to yolo Format

function train_val.py, On the one hand, the document will all_xml in xml The file to txt File in all_labels In the folder , On the other hand, generate the data storage architecture required for training .( Here, if your data is directly txt If you want to change the label, you can comment out the function of label conversion ) The code is as follows :

 Please add a picture description
train_val.py

import xml.etree.ElementTree as ET
import pickle
import os
import shutil
from os import listdir, getcwd
from os.path import join
sets = ['train', 'trainval']
# Change here ...............
classes = ['dog' , 'cat']
def convert(size, box):
    dw = 1. / size[0]
    dh = 1. / size[1]
    x = (box[0] + box[1]) / 2.0
    y = (box[2] + box[3]) / 2.0
    w = box[1] - box[0]
    h = box[3] - box[2]
    x = x * dw
    w = w * dw
    y = y * dh
    h = h * dh
    return (x, y, w, h)
def convert_annotation(image_id):
    in_file = open('all_xml/%s.xml' % (image_id))
    out_file = open('all_labels/%s.txt' % (image_id), 'w')
    tree = ET.parse(in_file)
    root = tree.getroot()
    size = root.find('size')
    w = int(size.find('width').text)
    h = int(size.find('height').text)
    for obj in root.iter('object'):
        difficult = obj.find('difficult').text
        cls = obj.find('name').text
        if cls not in classes or int(difficult) == 1:
            continue
        cls_id = classes.index(cls)
        xmlbox = obj.find('bndbox')
        b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text),
             float(xmlbox.find('ymax').text))
        bb = convert((w, h), b)
        out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '
')
wd = getcwd()
print(wd)
for image_set in sets:
    if not os.path.exists('all_labels/'):
        os.makedirs('all_labels/')
    image_ids = open('ImageSets/%s.txt' % (image_set)).read().strip().split()
    image_list_file = open('images_%s.txt' % (image_set), 'w')
    labels_list_file=open('labels_%s.txt'%(image_set),'w')
    for image_id in image_ids:
        image_list_file.write('%s.jpg
' % (image_id))
        labels_list_file.write('%s.txt
'%(image_id))
        convert_annotation(image_id) # If the tag is already txt Format , Comment out this line , be-all txt Store in all_labels Folder .
    image_list_file.close()
    labels_list_file.close()


def copy_file(new_path,path_txt,search_path):# Parameters 1: Where to store the new file    Parameters 2: Set up for the previous step train,val Path of training data txt file    Parameters 3: File location for search 
    if not os.path.exists(new_path):
        os.makedirs(new_path)
    with open(path_txt, 'r') as lines:
        filenames_to_copy = set(line.rstrip() for line in lines)
        # print('filenames_to_copy:',filenames_to_copy)
        # print(len(filenames_to_copy))
    for root, _, filenames in os.walk(search_path):
        # print('root',root)
        # print(_)
        # print(filenames)
        for filename in filenames:
            if filename in filenames_to_copy:
                shutil.copy(os.path.join(root, filename), new_path)

# Search the target according to the path of the divided training file , And copy it to yolo New path in format 
copy_file('./images/train/','./images_train.txt','./all_images')
copy_file('./images/val/','./images_trainval.txt','./all_images')
copy_file('./labels/train/','./labels_train.txt','./all_labels')
copy_file('./labels/val/','./labels_trainval.txt','./all_labels')

 Insert picture description here

Step four : Create your own yaml file , Sure copy once yolov5 in data Under the coco128.yaml

train:  ./mydata/images/train/ 
val:  ./mydata/images/val/ 



nc: 2
names: ['dog' ,'cat']

Step five : Modify the configuration file of the network model , modify models/yolov5s.yaml The content of , Select one according to the parameters of your actual running model .yaml Make changes , I chose yolov5s.yaml.

Mainly modify the number of categories nc Value Of course, you can also modify your network structure .

Step six : Training

The training skills are above , Of course, you can go to train.py Modify the default value of the corresponding attribute in .

 python train.py --data coco.yaml( Data and information , Generally, we also designate ourselves )     --cfg yolov5s.yaml ( Network structure information , have access to yolov5s Of  , You can also use your own ) --weights ''  ( there weights Is to specify whether or not to train on the basis of others ) --batch-size 64

Training v1 The problem is :
TypeError: can't convert cuda:0 device type tensor to numpy. Use Tensor.cpu() to copy the tensor to host memory first. Click the error prompt file directly
 Insert picture description here

The second question is : No corresponding label
No labels found in D:.B_yolov5_weeksyolov5_1.0mydataImages.
.
In fact, this problem is because we make labels The problem of , That is, you go mydata Under folder lables There are... In the folder train and val Folder to put the corresponding label, But mine is empty .
.
In fact, we have generated the corresponding lables, stay mydata There will be superfluous lables Folder ,
This labels Under folder train and val Folder is not empty , Replace mydata In a folder labels Folder

1.3 Performance evaluation

 Please add a picture description

Validate the model

python val.py --data data/coco128.yaml  --weights weighs/myyolo.pt --batch-size 6

Visualization of training process :

tensorboard --logdir  ./runs

And then enter... On the browser side http://localhsot:6006/#scalars It depends on what the above command returns

Scene 4 :yolov5 Source code interpretation

 Please add a picture description

Supporting documents

1: export.py —> The model is converted to onnx file

 Insert picture description here

# netron Yes pt File compatibility is poor  ,  The purpose of this document is to put pt The weight file of is converted to   onnx Format , Convenient in netron And other tools 
# Back  -i  The website is added to get from Tsinghua mirror , faster  
#  command     pip install onnx>=1.7.0  -i https://pypi.tuna.tsinghua.edu.cn/simple       
#         pip install coremltools==4.0 -i  https://pypi.tuna.tsinghua.edu.cn/simple  
             
#  Change orders :  python   export.py --weights weights/yolov5s.pt   --img 640  --batch 1

netron github Check online
fast open
 Please add a picture description

2.yolov5s.yaml—> Network structure file

 Please add a picture description
 Please add a picture description
 Please add a picture description
 Please add a picture description
 Insert picture description here
 Please add a picture description

3. Model building code activations.py–> Activate function definition file

Activate function definition code

 Please add a picture description

4 Model building code common.py—> Network component code

This file defines the implementation of many network components , Some are as follows .

 Insert picture description here
 Insert picture description here
 Insert picture description here
 Please add a picture description
 Please add a picture description
 Insert picture description here
 Insert picture description here
 Insert picture description here

5: Data set preprocessing to load code parsing —>Augmentations.py And Datasets.py

 Please add a picture description

6: Index related codes and losses Loss Code —>metrics.py And Loss.py

 Please add a picture description

7: autoanchor.py And torch_utils.py And general.py

 Please add a picture description

8: detect.py

 Insert picture description here
 Please add a picture description
 Please add a picture description

9: val.py

 Please add a picture description

10: train.py

 Insert picture description here
 Insert picture description here

11: yolov5 - v6 edition detect.py Comment code

# YOLOv5 ?? by Ultralytics, GPL-3.0 license
#  Test script 
"""
Run inference on images, videos, directories, streams, etc.

Usage:
    $ python path/to/detect.py --weights yolov5s.pt --source 0  # webcam
                                                             img.jpg  # image
                                                             vid.mp4  # video
                                                             path/  # directory
                                                             path/*.jpg  # glob
                                                             'https://youtu.be/Zgi9g1ksQHc'  # YouTube
                                                             'rtsp://example.com/media.mp4'  # RTSP, RTMP, HTTP stream
"""

import argparse
import os
import sys
from pathlib import Path

import cv2
import torch
import torch.backends.cudnn as cudnn

FILE = Path(__file__).resolve()
ROOT = FILE.parents[0]  # YOLOv5 root directory
if str(ROOT) not in sys.path:
    sys.path.append(str(ROOT))  # add ROOT to PATH
ROOT = Path(os.path.relpath(ROOT, Path.cwd()))  # relative

from models.common import DetectMultiBackend
from utils.datasets import IMG_FORMATS, VID_FORMATS, LoadImages, LoadStreams
from utils.general import (LOGGER, check_file, check_img_size, check_imshow, check_requirements, colorstr,
                           increment_path, non_max_suppression, print_args, scale_coords, strip_optimizer, xyxy2xywh)
from utils.plots import Annotator, colors, save_one_box
from utils.torch_utils import select_device, time_sync


@torch.no_grad()
def run(weights=ROOT / 'yolov5s.pt',  # model.pt path(s)
        source=ROOT / 'data/images',  # file/dir/URL/glob, 0 for webcam
        imgsz=(640, 640),  # inference size (height, width)
        conf_thres=0.25,  # confidence threshold
        iou_thres=0.45,  # NMS IOU threshold
        max_det=1000,  # maximum detections per image
        device='',  # cuda device, i.e. 0 or 0,1,2,3 or cpu
        view_img=False,  # show results
        save_txt=False,  # save results to *.txt
        save_conf=False,  # save confidences in --save-txt labels
        save_crop=False,  # save cropped prediction boxes
        nosave=False,  # do not save images/videos
        classes=None,  # filter by class: --class 0, or --class 0 2 3
        agnostic_nms=False,  # class-agnostic NMS
        augment=False,  # augmented inference
        visualize=False,  # visualize features
        update=False,  # update all models
        project=ROOT / 'runs/detect',  # save results to project/name
        name='exp',  # save results to project/name
        exist_ok=False,  # existing project/name ok, do not increment
        line_thickness=3,  # bounding box thickness (pixels)
        hide_labels=False,  # hide labels
        hide_conf=False,  # hide confidences
        half=False,  # use FP16 half-precision inference
        dnn=False,  # use OpenCV DNN for ONNX inference
        ):
    source = str(source)

    #  If you have parameters --nosave  Just don't save  , not  nosave  Namely   preservation   , That is to say   We   No settings   Parameters nosave When ,nosave  The value is False
    save_img = not nosave and not source.endswith('.txt')  # save images , Here, you can save without receiving the parameter command 

    #  Determine whether the file we have passed in is a file ,   namely  source Tested   It's pictures and videos    still   Open camera 
    is_file = Path(source).suffix[1:] in (IMG_FORMATS + VID_FORMATS)

    #  have a look source Is it specified  rtsp:// ,rtmp:// The form such as  , If it is   It shows that we use   camera 
    is_url = source.lower().startswith(('rtsp://', 'rtmp://', 'http://', 'https://'))

    #  See if it's true   Using a camera 
    webcam = source.isnumeric() or source.endswith('.txt') or (is_url and not is_file)
    if is_url and is_file:
        #  Judge source Whether there is , Download if it doesn't exist 
        source = check_file(source)  # download

    # Directories  If you enter a command line with  --exist_ok   , Then the above parameter attributes exist_ok The value of is  true
    # project In the previous parameter definitions , The default is ROOT / 'runs/detect'    name The default is  exp  , So the function of parameters   Under your project   Create a   Catalog  runs / detect / exp
    save_dir = increment_path(Path(project) / name, exist_ok=exist_ok)

    #  If it is an incoming parameter --save-txt , That is to say, the coordinates of the detected frame are expressed in txt Save up  ,    Will be in  save_dir namely  runs / detect / exp/ Next create a  labels Folder 
    #  If   No input , Namely   Default   yes false , Will not create 
    (save_dir / 'labels' if save_txt else save_dir).mkdir(parents=True, exist_ok=True)  # make dir

    # Load model, Use torch_utils in  select_device()  function   According to  device Value    Make device selection 
    device = select_device(device)

    #  Load model 
    model = DetectMultiBackend(weights, device=device, dnn=dnn)
    stride, names, pt, jit, onnx, engine = model.stride, model.names, model.pt, model.jit, model.onnx, model.engine

    #  Check if the image size is  s( The default is 32) Integer multiple , If not    Just adjust it to  32 Integer multiple 
    imgsz = check_img_size(imgsz, s=stride)  # check image size

    # Half   If the device is  cpu , Just use Float  16   ,   If it is Gpu  Just use float32
    half &= (pt or jit or engine) and device.type != 'cpu'  # half precision only supported by PyTorch on CUDA
    if pt or jit:
        model.model.half() if half else model.model.float()

    # Dataloader   Different data loading methods can be set through different input sources 
    if webcam:
        #  Check whether the environment supports   Picture shows 
        view_img = check_imshow()  #  Support  True
        cudnn.benchmark = True  # set True to speed up constant image size inference

        dataset = LoadStreams(source, img_size=imgsz, stride=stride, auto=pt)
        bs = len(dataset)  # batch_size
    else:
        #  Load pictures or videos  ,dataset become LoadImages An instantiated object of this class 
        dataset = LoadImages(source, img_size=imgsz, stride=stride, auto=pt)
        bs = 1  # batch_size
    vid_path, vid_writer = [None] * bs, [None] * bs

    # wramup yes common.py in  DetectMultiBackend Class    One way ,   Warm up training   The effect of this method is that the learning rate is very small , Slowly increase to the value we set 
    # Run inference
    model.warmup(imgsz=(1, 3, *imgsz), half=half)  # warmup
    dt, seen = [0.0, 0.0, 0.0], 0

    '''
    path   picture  / video   route 
    im  Conduct resize +pad  In the following picture  ,   Such as (3,  640  ,512  )  The format is (c, h ,w)
    img0s  primary size picture   for example  (  1080 , 810 ,3 )
    vid_cap  The current picture is None ,    Reading video is   For video source 
    '''

    for path, im, im0s, vid_cap, s in dataset:

        # torch_utils  Inside  time_sync()  function ,   Role is    Time synchronization   ( wait for GPU Operation is completed ) , Return current time 
        t1 = time_sync()

        #  hold nparray The format of the array is converted to pytorch Format 
        im = torch.from_numpy(im).to(device)

        # uint8 to fp16/32  If  half by true Words , Also, the semi precision , namely 16 Bit accuracy 
        im = im.half() if half else im.float()

        # 0 - 255 to 0.0 - 1.0   hold resize After the im Value of each pixel  /255  , Let it be in 0-1 Between , In order to better adapt to the model 
        im /= 255

        # im  As we know before shapes yes    ( 3,  640  ,512 )  In the form of  , therefore shape The length is 3
        if len(im.shape) == 3:
            im = im[None]  # expand for batch dim        Here is an extension shape The operation of becomes   (batch-size  ,   3 ,640  ,512) In the form of 

        # torch_utils  Inside  time_sync()  function ,   Role is    Time synchronization   ( wait for GPU Operation is completed ) , Return current time 
        t2 = time_sync()

        # dt  The previous designation is  [ 0.0  , 0.0 , 0.0]   dt[0] = dt[0]+ t2 - t1     So it's updated dt First value of 
        dt[0] += t2 - t1

        # Inference  visualization      ,  This parameter compares   Weirdo  , For example, you test bus.jpg , commonly   Will be in exp{id} Generated under the folder   Check the next picture  , If this parameter is brought in 
        #  Then what is generated is no longer   The detected image is    One    Empty folder  , Name is bus
        visualize = increment_path(save_dir / Path(path).stem, mkdir=True) if visualize else False

        #  take   After processing the   The graph is brought into the model for prediction 
        '''
        
         How many prediction boxes are there  ,   All in all 32 Double down sampling ,  16 Double down sampling  , 8 Double down sampling 
        (  h/32  * w/32   +   h/16  *  w/16  +  h/8  *  w/8  )   * 3
         because   The previous input is no longer the size of the same width and height specified previously : 416,   640, Instead, it uses   The form of adaptive scaling , So it is no longer the same width and height 
         For example, it can be   Input   yes   (640  , 608   )  , Then it changes     (      640/32 * 608/32  + ....   ) =23940 
        
        pred  Namely model Output result , It's the shape yes ([1, 23940, 85])   That is to say   Yes 23940 Boxes ,  85 Namely  5+80 ,  1 yes batch-size Value 
        
        85 On the dimension of  
        pred[ ..., 0:4 ]  To predict coordinate information  , The information of the coordinate box is shown in   xywh  Format 
        pred[ ... , 4 ]  For confidence c 
        pred[ ... , 5:-1    ]   from index 5 - ending   yes  80 Category information 
              
        '''
        pred = model(im, augment=augment, visualize=visualize)

        # torch_utils  Inside  time_sync()  function ,   Role is    Time synchronization   ( wait for GPU Operation is completed ) , Return current time 
        t3 = time_sync()

        # dt  The previous designation is  [ dt[0]+ t2 - t1   , 0.0 , 0.0]   dt[1] = dt[1] + t3 -t2     So it's updated dt The second value of 
        dt[1] += t3 - t2

        #  according to   You passed in the detection command  --conf-thres   --iou-thres ,  --classes ,   --agnostic-nms  --max-det equivalence 
        #  call general.py in non_max_suppression( )  #  Non maximum suppression algorithm  , Filter box 
        pred = non_max_suppression(pred, conf_thres, iou_thres, classes, agnostic_nms, max_det=max_det)

        ''''
                  pred :   The output of forward propagation , The result model model Output  , The format of the box is  xywh
            conf_thres :   Confidence threshold 
             iou_thres :   Carry out nms When calculating iou threshold 
               classes :   Specify whether to keep the specified category 
         agnostic_nums :   Conduct nms Whether to also remove the box between different categories 
               max_det :   The maximum number of detections on a graph 
               
         after nms operation     Format of prediction box   xywh---->  x1y1 x2y2  That is, the format of the upper left corner and the lower right corner 
        
        '''

        # dt  The previous designation is  [ dt[0]+ t2 - t1   , dt[1] + t3 -t2  , 0.0]   dt[2] = dt[2] +  time_sync() - t3     So it's updated dt The third value of 
        dt[2] += time_sync() - t3

        '''
         So at this time  dt Value    yes    [ dt[0]+ t2 - t1   , dt[1] + t3 -t2  , dt[2] +  time_sync() - t3]  
         , respectively,   Represents the   Before a picture is sent into the model   Picture manipulation   One processing time       ( Include conversion to torch Format ,   Pixel point /255,  and  shape Dimension from 3  To 4 Transformation  )
         This picture is sent to   After the model  , After processing   Of    Time end 
         And after the model  , Send in   In the post-processing stage   nms    Carry out inside   Not maximally suppressed    Period of time 
        
         In this way, we can calculate the 
        
        '''
        # Second-stage classifier (optional)
        # pred = utils.general.apply_classifier(pred, classifier_model, im, im0s)

        # Process predictions
        #  The frame generated on each picture is processed 
        for i, det in enumerate(pred):  # per image     i It's framed id , for example nms After filtering, only 7 Boxes  ,det Is the information of the box 

            seen += 1  #  The previous setting is 0

            if webcam:  # batch_size >= 1
                p, im0, frame = path[i], im0s[i].copy(), dataset.count
                s += f'{i}: '
            else:
                p, im0, frame = path, im0s.copy(), getattr(dataset, 'frame', 0)

            p = Path(p)  # to Path   Get the path of the picture    ,

            #  save_dir  We know that the front is    runs / detect / exp      p.name Namely   We specify the name of the test file 
            #   for example people.jpg   that   save_path refer to   runs/detect/exp/people.jpg
            #   For example 1.mp4 ,    that    Namely    runs/detect/exp/1.mp4
            save_path = str(save_dir / p.name)  # im.jpg

            #  If it's a picture  ,labels Information    runs / detect / exp /labels/people.txt
            txt_path = str(save_dir / 'labels' / p.stem) + ('' if dataset.mode == 'image' else f'_{frame}')  # im.txt

            #  Set up the shape Print information s , For the following printing  , 384 x 640  In the form of 
            s += '%gx%g ' % im.shape[2:]  # print string

            gn = torch.tensor(im0.shape)[[1, 0, 1, 0]]  # normalization gain whwh  Normalized gain whwh

            #  Suppose we add... To the command line  --save-crop , The purpose of this sentence is to retain the trimmed prediction box , That is, if save_crop It's true , We are in img0 Do operations on a copy of , Otherwise, it would be im0 On the operation 
            imc = im0.copy() if save_crop else im0  # for save_crop

            #  call plots.py Medium class Annotator Class to instantiate 
            annotator = Annotator(im0, line_width=line_thickness, example=str(names))
            if len(det):
                # Rescale boxes from img_size to im0 size     x1y1  x2y2
                #  Adjust the coordinates of the prediction box :  be based on resize+ pad The coordinates of the picture ---->  Based on the original size The coordinates of the picture  ,
                #  Use general Medium scale_coords()  function 
                det[:, :4] = scale_coords(im.shape[2:], det[:, :4], im0.shape).round()

                # Print results
                #  Print the detected category information   .    That is to say   det[:, -1]
                for c in det[:, -1].unique():
                    n = (det[:, -1] == c).sum()  # detections per class  The function is to detect the number of each category 
                    s += f"{n} {names[int(c)]}{'s' * (n > 1)}, "  # add to string
                    #  s front   It's defined   by  384 X 640  Print format , in other words   Here we go +  Operation of category information 
                    #  s  yes  string  type  ,   If   Last   yes  3 A dog  ,2 A cat 
                    #  s  The end result is  384x640 3 dog 2 cat

                # Write results  Save forecast results 
                for *xyxy, conf, cls in reversed(det):
                    if save_txt:  # Write to file
                        xywh = (xyxy2xywh(torch.tensor(xyxy).view(1, 4)) / gn).view(-1).tolist()  # normalized xywh
                        line = (cls, *xywh, conf) if save_conf else (cls, *xywh)  # label format

                        #  write in 
                        with open(txt_path + '.txt', 'a') as f:
                            f.write(('%g ' * len(line)).rstrip() % line + '
')

                    #  Whether to draw a frame on the original drawing  ,  for example   If we introduce --view-img, That is, whether to display the following pictures or videos 
                    if save_img or save_crop or view_img:  # Add bbox to image
                        c = int(cls)  # integer class

                        #  If the parameter passed in   yes   --hide_labels  Namely   hide label Information about , Then take None ,
                        #  If confidence is hidden   Then say yes   Just show   Category information    ,names Is based on model Acquired , names[c] Point to category information 
                        # conf  Point to the confidence level of this category 
                        label = None if hide_labels else (names[c] if hide_conf else f'{names[c]} {conf:.2f}')
                        annotator.box_label(xyxy, label, color=colors(c, True))
                        if save_crop:
                            save_one_box(xyxy, imc, file=save_dir / 'crops' / names[c] / f'{p.stem}.jpg', BGR=True)

            # Print time (inference-only)

            #  therefore    The information printed here 
            #  For example, the following console information is printed  416x640 3 persons,  Done. (0.255s)         0.255s Is the time of reasoning in the model 
            LOGGER.info(f'{s}Done. ({t3 - t2:.3f}s)')

            # Stream results
            im0 = annotator.result()

            #  If it is   --view-img , Show ,
            if view_img:
                cv2.imshow(str(p), im0)  # str(p)  Is the path of the above picture 
                cv2.waitKey(3000)  # 1 millisecond

            # Save results (image with detections)
            #  Save the picture / The operation of video 
            if save_img:
                if dataset.mode == 'image':
                    cv2.imwrite(save_path, im0)
                else:  # 'video' or 'stream'
                    if vid_path[i] != save_path:  # new video
                        vid_path[i] = save_path
                        if isinstance(vid_writer[i], cv2.VideoWriter):
                            vid_writer[i].release()  # release previous video writer
                        if vid_cap:  # video
                            fps = vid_cap.get(cv2.CAP_PROP_FPS)
                            w = int(vid_cap.get(cv2.CAP_PROP_FRAME_WIDTH))
                            h = int(vid_cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
                        else:  # stream
                            fps, w, h = 30, im0.shape[1], im0.shape[0]
                            save_path += '.mp4'
                        vid_writer[i] = cv2.VideoWriter(save_path, cv2.VideoWriter_fourcc(*'mp4v'), fps, (w, h))
                    vid_writer[i].write(im0)

    # Print results
    # dt =  [  Processing before feeding into the model    ,   Detection in the model  , nms Handle   ]
    t = tuple(x / seen * 1E3 for x in dt)  # speeds per image
    #  such as t It is calculated that   t= [ 1.00012 , 236.41212 ,   2.00011  ]
    #  Print... To the console   Speed: 1.0 ms pre-process , 236.4 ms inference,  2.0 ms NMS per image at shape (1, 3, 640, 640)
    LOGGER.info(f'Speed: %.1f  ms pre-process, %.1f  ms inference, %.1f ms NMS per image at shape {(1, 3, *imgsz)}' % t)

    #  Print where the saved results are 
    if save_txt or save_img:
        s = f"
{len(list(save_dir.glob('labels/*.txt')))} labels saved to {save_dir / 'labels'}" if save_txt else ''
        LOGGER.info(f"Results saved to {colorstr('bold', save_dir)}{s}")

    if update:
        strip_optimizer(weights)  # update model (to fix SourceChangeWarning)


def parse_opt():
    #  establish   Parameter resolution object parser()
    parser = argparse.ArgumentParser()
    # add_argument()  Is to add   One   What attribute 
    # nargs  refer to  --->   Should read   The number of command line parameters for ,  *  Express  0 Or more  ,   + Express  1 Or more 
    # action --   The command line   Action when parameters are encountered , action='store_true' , Indicates that the variable is set to as long as it has a parameter passed during runtime True

    parser.add_argument('--weights', nargs='+', type=str, default=ROOT / 'yolov5s.pt', help='model path(s)')
    parser.add_argument('--source', type=str, default=ROOT / 'data/images', help='file/dir/URL/glob, 0 for webcam')
    parser.add_argument('--imgsz', '--img', '--img-size', nargs='+', type=int, default=[640], help='inference size h,w')
    parser.add_argument('--conf-thres', type=float, default=0.25, help='confidence threshold')
    parser.add_argument('--iou-thres', type=float, default=0.45, help='NMS IoU threshold')
    parser.add_argument('--max-det', type=int, default=1000, help='maximum detections per image')
    parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')
    parser.add_argument('--view-img', action='store_true', help='show results')
    parser.add_argument('--save-txt', action='store_true', help='save results to *.txt')
    parser.add_argument('--save-conf', action='store_true', help='save confidences in --save-txt labels')
    parser.add_argument('--save-crop', action='store_true', help='save cropped prediction boxes')
    parser.add_argument('--nosave', action='store_true', help='do not save images/videos')
    parser.add_argument('--classes', nargs='+', type=int, help='filter by class: --classes 0, or --classes 0 2 3')
    parser.add_argument('--agnostic-nms', action='store_true', help='class-agnostic NMS')
    parser.add_argument('--augment', action='store_true', help='augmented inference')
    parser.add_argument('--visualize', action='store_true', help='visualize features')
    parser.add_argument('--update', action='store_true', help='update all models')
    parser.add_argument('--project', default=ROOT / 'runs/detect', help='save results to project/name')
    parser.add_argument('--name', default='exp', help='save results to project/name')
    parser.add_argument('--exist-ok', action='store_true', help='existing project/name ok, do not increment')
    parser.add_argument('--line-thickness', default=3, type=int, help='bounding box thickness (pixels)')
    parser.add_argument('--hide-labels', default=False, action='store_true', help='hide labels')
    parser.add_argument('--hide-conf', default=False, action='store_true', help='hide confidences')
    parser.add_argument('--half', action='store_true', help='use FP16 half-precision inference')
    parser.add_argument('--dnn', action='store_true', help='use OpenCV DNN for ONNX inference')

    #  use parse_args()  Function to parse the obtained parameters 
    #  That is to say, this sentence   Got   We type... From the command line   Content and carried out   analysis , Other properties are defaulted 
    opt = parser.parse_args()

    opt.imgsz *= 2 if len(opt.imgsz) == 1 else 1  # expand
    print_args(FILE.stem, opt)
    return opt


def main(opt):
    #  Before the final test , Take a look at it requirements.txt In demand   Whether the operating environment is installed  , If not, update and download 
    check_requirements(exclude=('tensorboard', 'thop'))
    run(**vars(opt))


if __name__ == "__main__":
    opt = parse_opt()
    main(opt)

    """
    weights : Weight of training 
    source : Test data :   It can be a picture / video    route  , It can also be 0 , Namely   Computer camera  , It can also be  rtsp  Equal video stream 
    
    img-size :   Network input image size            
    conf-thresh :  Confidence threshold 
    iou-thresh:   do nums Of iou threshold 
    device:  Model of equipment 
    
    view-img:   Whether to show the following pictures / video   , The default is False
    save-txt:   Whether to change the predicted frame coordinates to txt Save as file , Default False
    save-conf:  Whether to change the predicted frame coordinates to txt Save as file , Default False
    save-dir:   Pictures after network prediction / The path to save the video 
    classes:    Set to keep only some categories  , Form like 0 ,2 ,3
    
    agnostic-nms:    Conduct nms Whether to also remove the box between different categories  ,  The default is  False
    augment :     Reasoning is often done   scale  , Flip (TTA) Reasoning 
    update:       If True , All the models are strip_optimizer operation , Take out pt Optimizer information in the file , The default is false
    """

12: yolov5 - v6 edition val.py Comment code

# YOLOv5 ?? by Ultralytics, GPL-3.0 license
#  Model validation scripts 
"""
Validate a trained YOLOv5 model accuracy on a custom dataset

Usage:
    $ python path/to/val.py --data coco128.yaml --weights yolov5s.pt --img 640
"""

import argparse
import json
import os
import sys
from pathlib import Path
from threading import Thread

import numpy as np
import torch
from tqdm import tqdm

FILE = Path(__file__).resolve()
ROOT = FILE.parents[0]  # YOLOv5 root directory
if str(ROOT) not in sys.path:
    sys.path.append(str(ROOT))  # add ROOT to PATH
ROOT = Path(os.path.relpath(ROOT, Path.cwd()))  # relative

from models.common import DetectMultiBackend
from utils.callbacks import Callbacks
from utils.datasets import create_dataloader
from utils.general import (LOGGER, box_iou, check_dataset, check_img_size, check_requirements, check_yaml,
                           coco80_to_coco91_class, colorstr, increment_path, non_max_suppression, print_args,
                           scale_coords, xywh2xyxy, xyxy2xywh)
from utils.metrics import ConfusionMatrix, ap_per_class
from utils.plots import output_to_target, plot_images, plot_val_study
from utils.torch_utils import select_device, time_sync


def save_one_txt(predn, save_conf, shape, file):
    # Save one txt result
    gn = torch.tensor(shape)[[1, 0, 1, 0]]  # normalization gain whwh

    for *xyxy, conf, cls in predn.tolist():
        #  take xyxy Format ---->xywh , And normalize it 
        xywh = (xyxy2xywh(torch.tensor(xyxy).view(1, 4)) / gn).view(-1).tolist()  # normalized xywh

        #  If we introduce  --save-conf  Write the confidence in txt In the middle 
        line = (cls, *xywh, conf) if save_conf else (cls, *xywh)  # label format
        with open(file, 'a') as f:
            f.write(('%g ' * len(line)).rstrip() % line + '
')


def save_one_json(predn, jdict, path, class_map):
    # Save one JSON result {"image_id": 42, "category_id": 18, "bbox": [258.15, 41.29, 348.26, 243.78], "score": 0.236}
    #  Get the id

    image_id = int(path.stem) if path.stem.isnumeric() else path.stem

    #  Get coordinate information , Convert to xywh
    box = xyxy2xywh(predn[:, :4])  # xywh
    '''
     It is worth noting that , What we said before  xywh yes   Center point coordinates and width and height  
    x1y1x2y2 It's the upper left corner   The coordinates of the lower right corner 
    
     and coco Of json The coordinate format of the box in the format is  ,x1y1wh  , Upper left corner   And width and height 
    '''

    #  So the coordinate information of the center point is transformed into   The information in the upper left corner 
    box[:, :2] -= box[:, 2:] / 2  # xy center to top-left corner
    for p, b in zip(predn.tolist(), box.tolist()):
        jdict.append({'image_id': image_id,
                      'category_id': class_map[int(p[5])],
                      'bbox': [round(x, 3) for x in b],
                      'score': round(p[4], 5)})

        '''
        jdit  Namely json Dictionaries , Used to store information 
        image_id :    picture id , That is, which picture the information comes from 
        category_id :    Category information  ,coco91 claass() , Mapping from index to index 0-90
         therefore :   p[5]  Get the category information  ,  int After conversion  , utilize class_map  To get at coco91 Information in 
        
        bbox:  Coordinates of the frame 
        scorce :   Confidence score 
        
        '''


#  Return to the correct prediction matrix  , Each box is   It's using  x1y1 x2y2 The format of 
def process_batch(detections, labels, iouv):
    """
    Return correct predictions matrix. Both sets of boxes are in (x1, y1, x2, y2) format.
    Arguments:
        detections (Array[N, 6]), x1, y1, x2, y2, conf, class
        labels (Array[M, 5]), class, x1, y1, x2, y2
    Returns:
        correct (Array[N, 10]), for 10 IoU levels
    """
    #  Initialize prediction evaluation 
    correct = torch.zeros(detections.shape[0], iouv.shape[0], dtype=torch.bool, device=iouv.device)

    #  If the box_iou() Function calculation     Two box iou  ,  namely  labels yes [  Category  ,x1y1x2y2 ]   detections x1 ,y1 ,x2,y2 ,conf ,class
    iou = box_iou(labels[:, 1:], detections[:, :4])

    #  choose iou Greater than   threshold   And   Category matching  , iouv[0]  That is, the initial threshold is set to 0.5
    x = torch.where((iou >= iouv[0]) & (labels[:, 0:1] == detections[:, 5]))  # IoU above threshold and classes match

    if x[0].shape[0]:
        matches = torch.cat((torch.stack(x, 1), iou[x[0], x[1]][:, None]), 1).cpu().numpy()  # [label, detection, iou]
        if x[0].shape[0] > 1:
            matches = matches[matches[:, 2].argsort()[::-1]]
            matches = matches[np.unique(matches[:, 1], return_index=True)[1]]
            # matches = matches[matches[:, 2].argsort()[::-1]]
            matches = matches[np.unique(matches[:, 0], return_index=True)[1]]
        matches = torch.Tensor(matches).to(iouv.device)
        correct[matches[:, 1].long()] = matches[:, 2:3] >= iouv
    return correct


@torch.no_grad()
def run(data,
        weights=None,  # model.pt path(s)
        batch_size=32,  # batch size
        imgsz=640,  # inference size (pixels)
        conf_thres=0.001,  # confidence threshold
        iou_thres=0.6,  # NMS IoU threshold
        task='val',  # train, val, test, speed or study
        device='',  # cuda device, i.e. 0 or 0,1,2,3 or cpu
        workers=8,  # max dataloader workers (per RANK in DDP mode)
        single_cls=False,  # treat as single-class dataset
        augment=False,  # augmented inference
        verbose=False,  # verbose output
        save_txt=False,  # save results to *.txt
        save_hybrid=False,  # save label+prediction hybrid results to *.txt
        save_conf=False,  # save confidences in --save-txt labels
        save_json=False,  # save a COCO-JSON results file
        project=ROOT / 'runs/val',  # save to project/name
        name='exp',  # save to project/name
        exist_ok=False,  # existing project/name ok, do not increment
        half=True,  # use FP16 half-precision inference
        dnn=False,  # use OpenCV DNN for ONNX inference
        model=None,
        dataloader=None,
        save_dir=Path(''),
        plots=True,
        callbacks=Callbacks(),
        compute_loss=None,
        ):
    # Initialize/load model and set device
    #  Initialize and load the model , And carry out   devices setting up 

    #  Determine if you are training   call val ,   If yes, get the training equipment 
    training = model is not None  #  If model There is , It is equivalent to training  model  , training = true
    if training:  # called by train.py     By train.py summon 
        device, pt, jit, engine = next(model.parameters()).device, True, False, False  # get model device, PyTorch model
        half &= device.type != 'cpu'  # half precision only supported on CUDA
        model.half() if half else model.float()

    else:  # called directly , Otherwise    It's our normal   Parameters   Call passed  , That is, the call passed in from our command line 

        #  Use torch_utils in  select_device()  function   According to  device Value    Make device selection 
        device = select_device(device, batch_size=batch_size)

        # Directories  If you enter a command line with  --exist_ok   , Then the above parameter attributes exist_ok The value of is  true
        # project In the previous parameter definitions , The default is ROOT / 'runs/detect'    name The default is  exp  , So the function of parameters   Under your project   Create a   Catalog  runs /val / exp
        save_dir = increment_path(Path(project) / name, exist_ok=exist_ok)  # increment run

        #  If it is an incoming parameter --save-txt , That is to say, the coordinates of the detected frame are expressed in txt Save up  ,    Will be in  save_dir namely  runs / detect / exp/ Next create a  labels Folder 
        #  If   No input , Namely   Default   yes false , Will not create 
        (save_dir / 'labels' if save_txt else save_dir).mkdir(parents=True, exist_ok=True)  # make dir

        # Load model     #  Load model , utilize common.py Medium DetectMultiBackend Class to load   Model 
        model = DetectMultiBackend(weights, device=device, dnn=dnn)
        stride, pt, jit, engine = model.stride, model.pt, model.jit, model.engine
        # stride  Is the sample size under the model 

        #  Check if the image size is  s( The default is 32) Integer multiple , If not    Just adjust it to  32 Integer multiple 
        imgsz = check_img_size(imgsz, s=stride)  # check image size

        #  If the device is not cpu also gpu The number is 1 , Change the model from float32  To  float16, Increase the speed of forward propagation 
        # half yes   Half precision prediction , That is to say float16,  Here you can see   yes    Half precision prediction is only  CUDA Support 
        half &= (pt or jit or engine) and device.type != 'cpu'  # half precision only supported by PyTorch on CUDA

        if pt or jit:
            #  If half really , The model takes half precision prediction , Otherwise, it would be  float32 forecast 
            model.model.half() if half else model.model.float()
        elif engine:
            batch_size = model.batch_size
        else:
            half = False
            batch_size = 1  # export.py models default to batch-size 1   # The default batch size of the model is 1
            device = torch.device('cpu')
            LOGGER.info(f'Forcing --batch-size 1 square inference shape(1,3,{imgsz},{imgsz}) for non-PyTorch backends')

        #  Use general. In translation check_dataset() function , Check the incoming  data Whether there is 
        data = check_dataset(data)  # check

    # Configure
    # eval() when  ,  The frame will automatically BN  and Dropout Hold on , Use the trained value , Is not enabled  BN and Dropout
    model.eval()

    #  Judge whether it is  coco Data sets , That is, whether or not the  coco Data set to validate the model 
    is_coco = isinstance(data.get('val'), str) and data['val'].endswith('coco/val2017.txt')  # COCO dataset

    #  If the parameter passed in is --single-cls  ,  There is only one category in the dataset ,nc Set as 1
    nc = 1 if single_cls else int(data['nc'])  # number of classes

    #  Set up IOU threshold , from 0.5 - 0.95 , every other  0.05 Take one 
    iouv = torch.linspace(0.5, 0.95, 10).to(device)  # iou vector for [email protected]:0.95

    # niou It's the one above iou Number 
    niou = iouv.numel()

    # Dataloader
    if not training:
        #  If   Not being summoned during training , That is to say   say   This is what we ordered to start 
        # wramup yes common.py in  DetectMultiBackend Class    One way ,   Warm up training   The effect of this method is that the learning rate is very small , Slowly increase to the value we set 
        model.warmup(imgsz=(1, 3, imgsz, imgsz), half=half)  # warmup

        #  If task That is to say   The verification method is speed , then  pad Set to 0 , That is, no filling  ,      If not , It is normal val ,test ,train  or study
        pad = 0.0 if task == 'speed' else 0.5

        #  If it is  train ,val ,test A kind of  , That's what it is task  In itself  ,  If   yes  study Or is it speed A kind of , Start here   Convert to val In the form of 
        #  Point to   Picture location , That is to do the operation   Path to picture 
        task = task if task in ('train', 'val', 'test') else 'val'  # path to train/val/test images

        #  call datasets.py Medium create_dataloader() Function to create dataloader
        #   Notice the rect Parameter is true ,yolov5 Our test evaluation is based on rectangular reasoning 
        # data[task]  Point to the image path 
        dataloader = create_dataloader(data[task], imgsz, batch_size, stride, single_cls, pad=pad, rect=pt,
                                       workers=workers, prefix=colorstr(f'{task}: '))[0]

    #  Initialize the number of test pictures 
    seen = 0

    #  call metrics.py Of ConfusionMatrix  Confusion matrix class   Instantiation   One    Confusion matrix 
    confusion_matrix = ConfusionMatrix(nc=nc)

    #  Get the name of the category 
    names = {k: v for k, v in enumerate(model.names if hasattr(model, 'names') else model.module.names)}

    #  If it is coco Data sets  , Just use  general.py Medium coco80_to_coco91_class() , Also is to 80 The categories are changed to paper What happened in 91 Classification length ,
    #  Create a list[  range(1 ,90)] ,    If not    Namely list(range(1000))
    #  The function is to get coco Index of the category of the dataset  ,  Input coco  The category of the dataset is 80 , Theoretically, the index is 0-79  , But its index belongs to 1-90
    class_map = coco80_to_coco91_class() if is_coco else list(range(1000))

    #  Set up s, That is, like the string displayed on the screen 
    s = ('%20s' + '%11s' * 6) % ('Class', 'Images', 'Labels', 'P', 'R', '[email protected]', '[email protected]:.95')

    #  Initialize various evaluation indicators 
    dt, p, r, f1, mp, mr, map50, map = [0.0, 0.0, 0.0], 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0
    # dt  Is still  [0.0 ,  0.0 ,  0.0 ] Used to record running time  ,p Namely   precision ,  r It's the recall rate 

    #  initialization   Loss of test set 
    loss = torch.zeros(3, device=device)

    #  initialization json A dictionary of documents  , Statistics  ,ap
    jdict, stats, ap, ap_class = [], [], [], []

    pbar = tqdm(dataloader, desc=s, bar_format='{l_bar}{bar:10}{r_bar}{bar:-10b}')  # progress bar

    #  Yes dataloader Traversal 
    for batch_i, (im, targets, paths, shapes) in enumerate(pbar):

        # torch_utils  Inside  time_sync()  function ,   Role is    Time synchronization   ( wait for GPU Operation is completed ) , Return current time 
        t1 = time_sync()

        #  The first stage :  Preprocess the picture before sending it to the model ................................................................
        if pt or jit or engine:
            #  After loading the picture, put it on the corresponding device for processing 
            im = im.to(device, non_blocking=True)
            targets = targets.to(device)

        # uint8 to fp16/32  If  half by true Words , Also, the semi precision , namely 16 Bit accuracy 
        im = im.half() if half else im.float()  # uint8 to fp16/32

        # 0 - 255 to 0.0 - 1.0   hold resize After the im Value of each pixel  /255  , Let it be in 0-1 Between , In order to better adapt to the model 
        im /= 255

        #  Get the processed shape
        nb, _, height, width = im.shape  # batch size, channels, height, width

        # torch_utils  Inside  time_sync()  function ,   Role is    Time synchronization   ( wait for GPU Operation is completed ) , Return current time 
        t2 = time_sync()
        dt[0] += t2 - t1  # dt by [ dt[0]+t2-t1  ,  0  ,  0    ]

        # Inference...........................................................................................
        #  The second stage    Send the processed image to the model  ,  You can see if it's  training Time call val.py So that is model(im)
        #  Otherwise, it would be  model(im, augment=augment, val=True) , out yes   Predicted results  ,train_out For training results 
        #  If we adopt   Called from the command line val.py , No training , So at this point  out That's what makes sense ,  train_out It makes no sense 

        out, train_out = model(im) if training else model(im, augment=augment, val=True)  # inference, loss outputs

        # torch_utils  Inside  time_sync()  function ,   Role is    Time synchronization   ( wait for GPU Operation is completed ) , Return current time 
        dt[1] += time_sync() - t2  # dt by [ dt[0]+t2-t1  ,  dt[1]+time_sync()-t2 ,  0    ]

        '''
        out  Namely model Output result , It's the shape yes ([1, 23940, 85])   That is, if   Yes 23940 Boxes ,  85 Namely  5+80 ,  1 yes batch-size Value 

        85 On the dimension of  
        out[ ..., 0:4 ]  To predict coordinate information  , The information of the coordinate box is shown in   xywh  Format 
        out[ ... , 4 ]  For confidence c 
        out[ ... , 5:-1    ]   from index 5 - ending   yes  80 Category information 

        '''
        # Loss
        if compute_loss:  #  The default is None , in other words    Only call during training val.py ,  Then incoming computer_loss by true
            #  At this time, it will be mentioned through train_out The result calculates and returns the... Of the test set   box-loss , obj-loss  And cls-loss
            loss += compute_loss([x.float() for x in train_out], targets)[1]  # box, obj, cls

        #  Construct a tensor
        targets[:, 2:] *= torch.Tensor([width, height, width, height]).to(device)  # to pixels

        #   If we   Incoming  --save-hybrid , Will save the tag and forecast   Mixed results 
        #  That is to say save_hybird  by false Words  , That is to say  lb = []
        lb = [targets[targets[:, 0] == i, 1:] for i in range(nb)] if save_hybrid else []  # for autolabelling

        # NMS.................................................................................................................

        # torch_utils  Inside  time_sync()  function ,   Role is    Time synchronization   ( wait for GPU Operation is completed ) , Return current time 
        t3 = time_sync()

        #  according to   You passed in the detection command  --conf-thres   --iou-thres ,  --classes ,   --agnostic-nms  --max-det equivalence 
        #  call general.py in non_max_suppression( )  #  Non maximum suppression algorithm  , Filter box 
        out = non_max_suppression(out, conf_thres, iou_thres, labels=lb, multi_label=True, agnostic=single_cls)

        ''''
                   out :   The output of forward propagation , The result model model Output  , The format of the box is  xywh
            conf_thres :   Confidence threshold 
             iou_thres :   Carry out nms When calculating iou threshold 
         agnostic_nums :   Conduct nms Whether to also remove the box between different categories 

         after nms operation     Format of prediction box   xywh---->  x1y1 x2y2  That is, the format of the upper left corner and the lower right corner 

        '''
        # torch_utils  Inside  time_sync()  function ,   Role is    Time synchronization   ( wait for GPU Operation is completed ) , Return current time 
        dt[2] += time_sync() - t3
        # dt by [ dt[0]+t2-t1  ,  dt[1]+time_sync()-t2 , dt[2]+time_sync() - t3     ]  In this way, the time at each stage can be calculated 

        # Metrics indicators 
        #  Process the information generated by each picture , Including statistics , Write information to txt Go to the folder  , Generate json Document Dictionary , Statistics tp etc.   ,
        for si, pred in enumerate(out):

            # per image     si It's framed id , for example nms After filtering, only 7 Boxes  ,pred Is the information of the prediction box 

            # targets[ :,  0]   It is the number of the picture to which the label belongs   , That is, the last labels You will get the original annotation lables Information 
            labels = targets[targets[:, 0] == si, 1:]
            nl = len(labels)  #  It means that the original label has n A detection target 

            #  Get the category of the tag  ,  For example, the original picture has 3 individual   Dog  ,2 A cat  , That's the top  nl  by  5  ,
            #  Explain that there are objects on the original picture , So the statistics are 5 Categories of objects ,  The number of categories is 2 , If nl by 0 , Explain that there is no object on the original picture , So the number of categories is 0
            tcls = labels[:, 0].tolist() if nl else []  # target class

            # Path( paths[i] ) ,   obtain index by si The path to the picture of  ,     shapes[si][0]  Is to get this picture shape
            path, shape = Path(paths[si]), shapes[si][0]

            # seen  Used to count how many pictures 
            seen += 1

            #  Judge   Whether the forecast box information is empty 
            if len(pred) == 0:
                if nl:  #  If it's empty  , It depends on whether there is no detection target in the original image 
                    stats.append((torch.zeros(0, niou, dtype=torch.bool), torch.Tensor(), torch.Tensor(), tcls))
                continue

            # Predictions , See if there is any incoming  --single-cls  pred  With 85 Dimensional information  , front   yes   Deal with the later  xywhc ,80 Category information 
            #  But after nms , Receivable  xywh--> x1y1x2y2  top left corner  , The form in the lower right corner  , So it is   x1 y1 x2 y2 c + 80
            if single_cls:
                pred[:, 5] = 0
            predn = pred.clone()
            #  call  general.py Make the corresponding conversion 
            scale_coords(im[si].shape[1:], predn[:, :4], shape, shapes[si][1])  # native-space pred

            # Evaluate
            if nl:
                #  take   Target box   Convert to   x1y1x2y2 In the form of    call  general.py Make the corresponding conversion 
                tbox = xywh2xyxy(labels[:, 1:5])  # target boxes

                scale_coords(im[si].shape[1:], tbox, shape, shapes[si][1])  # native-space labels

                labelsn = torch.cat((labels[:, 0:1], tbox), 1)  # native-space labels, Inherent label 
                correct = process_batch(predn, labelsn, iouv)

                ''' 
                  As defined above process_batch() Return to the correct prediction matrix  
                 predn  yes  (Array[N, 6]), x1, y1, x2, y2, conf, class
                 labels (Array[M, 5]), class, x1, y1, x2, y2
                 
                 '''
                if plots:  #  The default is true
                    confusion_matrix.process_batch(predn, labelsn)
            else:
                correct = torch.zeros(pred.shape[0], niou, dtype=torch.bool)

            #  stay  stats []  Add    The correct prediction matrix for each picture  ,  Degree of confidence       Forecast category information  ,   Target category information 
            stats.append((correct.cpu(), pred[:, 4].cpu(), pred[:, 5].cpu(), tcls))  # (correct, conf, pcls, tcls)

            # Save/log
            #  If you pass in  --save-txt  , Namely   take   The information of the prediction box is displayed in txt Save the form of ,
            if save_txt:
                #  Save on it save_one_txt() Function definition directory  ,    namely   runs / val / labels/  +   .txt
                save_one_txt(predn, save_conf, shape, file=save_dir / 'labels' / (path.stem + '.txt'))

            #  If the incoming is  --save-json ;   preservation coco Format limit json Document Dictionary 
            if save_json:
                #  Call the... Defined above save_one_json()
                save_one_json(predn, jdict, path, class_map)  # append to COCO-JSON dictionary

            callbacks.run('on_val_image_end', pred, predn, path, names, im[si])

        # Plot images
        #  Draw the second 1 ,2  individual batch The picture of ground truth and   Forecast box and save 
        #  Save in   runs / val / exp /    va1-batch1_labels.jpg
        if plots and batch_i < 3:
            f = save_dir / f'val_batch{batch_i}_labels.jpg'  # labels
            Thread(target=plot_images, args=(im, targets, paths, f, names), daemon=True).start()
            #  Save in   runs / val / exp /    val-batch1_pred.jpg
            f = save_dir / f'val_batch{batch_i}_pred.jpg'  # predictions
            Thread(target=plot_images, args=(im, output_to_target(out), paths, f, names), daemon=True).start()

    # Compute metrics, All the pictures have been processed  , Start calculating metrics 
    #  take stats The information of the list is spliced together 
    stats = [np.concatenate(x, 0) for x in zip(*stats)]  # to numpy

    if len(stats) and stats[0].any():
        #  call metrics.py The total  ap_per_class  Calculate the indicators for each category  , precision p = tp/tp +fp   Recall rate  = tp/p   ,  map
        tp, fp, p, r, f1, ap, ap_class = ap_per_class(*stats, plot=plots, save_dir=save_dir, names=names)

        #  therefore ap50 Namely  iou The threshold for  0.5 When the ap   ,   For each category   ap  [  iou>0.5 The accuracy of   ,    iou>0.55 Yes, accuracy , ....   ]
        ap50, ap = ap[:, 0], ap.mean(1)  # [email protected], [email protected]:0.95       ap Equivalent to AP (iou The threshold for 0.5  - 0.95) Average value  , This is every category 

        mp, mr, map50, map = p.mean(), r.mean(), ap50.mean(), ap.mean()  #  Here are all the category properties 

        # nt It's a list  , Test set   How many target boxes are there in each category , That is, how many categories are marked in the test set 
        nt = np.bincount(stats[3].astype(np.int64), minlength=nc)  # number of targets per class
    else:
        nt = torch.zeros(1)

    # Print results, Print the results 
    pf = '%20s' + '%11i' * 2 + '%11.3g' * 4  # print format
    LOGGER.info(pf % ('all', seen, nt.sum(), mp, mr, map50, map))

    # Print results per class , Print category information 
    if (verbose or (nc < 50 and not training)) and nc > 1 and len(stats):
        for i, c in enumerate(ap_class):
            LOGGER.info(pf % (names[c], seen, nt[c], p[i], r[i], ap50[i], ap[i]))

    # Print speeds, Print speed information 
    t = tuple(x / seen * 1E3 for x in dt)  # speeds per image
    if not training:
        shape = (batch_size, 3, imgsz, imgsz)
        #  such as t It is calculated that   t= [ 1.00012 , 236.41212 ,   2.00011  ]
        #  Print... To the console  Speed: 1.0 ms pre-process ( Preprocessing ) , 236.4 ms inference( Reasoning ), 2.0 ms NMS per image at shape (1,3,640, 640)
        LOGGER.info(f'Speed: %.1f  ms pre-process, %.1f  ms inference, %.1f  ms NMS per image at shape {shape}' % t)

    # Plots
    if plots:
        confusion_matrix.plot(save_dir=save_dir, names=list(names.values()))
        callbacks.run('on_val_end')

    # Save JSON
    #  Use previously saved json Format forecast results  , adopt cocoapi Evaluation indicators 
    #  It should be noted that   The tab of the test set needs to be converted to coco Of json Format 
    if save_json and len(jdict):
        w = Path(weights[0] if isinstance(weights, list) else weights).stem if weights is not None else ''  # weights
        anno_json = str(Path(data.get('path', '../coco')) / 'annotations/instances_val2017.json')  # annotations json
        pred_json = str(save_dir / f"{w}_predictions.json")  # predictions json
        LOGGER.info(f'
Evaluating pycocotools mAP... saving {pred_json}...')
        with open(pred_json, 'w') as f:
            json.dump(jdict, f)

        try:  # https://github.com/cocodataset/cocoapi/blob/master/PythonAPI/pycocoEvalDemo.ipynb
            check_requirements(['pycocotools'])
            from pycocotools.coco import COCO
            from pycocotools.cocoeval import COCOeval

            anno = COCO(anno_json)  # init annotations api
            pred = anno.loadRes(pred_json)  # init predictions api
            eval = COCOeval(anno, pred, 'bbox')
            if is_coco:
                eval.params.imgIds = [int(Path(x).stem) for x in dataloader.dataset.img_files]  # image IDs to evaluate
            eval.evaluate()
            eval.accumulate()
            eval.summarize()
            map, map50 = eval.stats[:2]  # update results ([email protected]:0.95, [email protected])
        except Exception as e:
            LOGGER.info(f'pycocotools unable to run: {e}')

    # Return results , Print the location where the result information is saved 
    model.float()  # for training
    if not training:
        s = f"
{len(list(save_dir.glob('labels/*.txt')))} labels saved to {save_dir / 'labels'}" if save_txt else ''
        LOGGER.info(f"Results saved to {colorstr('bold', save_dir)}{s}")
    maps = np.zeros(nc) + map
    for i, c in enumerate(ap_class):
        maps[c] = ap[i]

    return (mp, mr, map50, map, *(loss.cpu() / len(dataloader)).tolist()), maps, t


def parse_opt():
    #  establish   Parameter resolution object parser()
    parser = argparse.ArgumentParser()
    # add_argument()  Is to add   One   What attribute 
    # nargs  refer to  --->   Should read   The number of command line parameters for ,  *  Express  0 Or more  ,   + Express  1 Or more 
    # action --   The command line   Action when parameters are encountered , action='store_true' , Indicates that the variable is set to as long as it has a parameter passed during runtime True

    parser.add_argument('--data', type=str, default=ROOT / 'data/coco128.yaml', help='dataset.yaml path')
    parser.add_argument('--weights', nargs='+', type=str, default=ROOT / 'yolov5s.pt', help='model.pt path(s)')
    parser.add_argument('--batch-size', type=int, default=32, help='batch size')
    parser.add_argument('--imgsz', '--img', '--img-size', type=int, default=640, help='inference size (pixels)')
    parser.add_argument('--conf-thres', type=float, default=0.001, help='confidence threshold')
    parser.add_argument('--iou-thres', type=float, default=0.6, help='NMS IoU threshold')
    parser.add_argument('--task', default='val', help='train, val, test, speed or study')
    parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')
    parser.add_argument('--workers', type=int, default=8, help='max dataloader workers (per RANK in DDP mode)')
    parser.add_argument('--single-cls', action='store_true', help='treat as single-class dataset')
    parser.add_argument('--augment', action='store_true', help='augmented inference')
    parser.add_argument('--verbose', action='store_true', help='report mAP by class')
    parser.add_argument('--save-txt', action='store_true', help='save results to *.txt')
    parser.add_argument('--save-hybrid', action='store_true', help='save label+prediction hybrid results to *.txt')
    parser.add_argument('--save-conf', action='store_true', help='save confidences in --save-txt labels')
    parser.add_argument('--save-json', action='store_true', help='save a COCO-JSON results file')
    parser.add_argument('--project', default=ROOT / 'runs/val', help='save to project/name')
    parser.add_argument('--name', default='exp', help='save to project/name')
    parser.add_argument('--exist-ok', action='store_true', help='existing project/name ok, do not increment')
    parser.add_argument('--half', action='store_true', help='use FP16 half-precision inference')
    parser.add_argument('--dnn', action='store_true', help='use OpenCV DNN for ONNX inference')

    #  use parse_args()  Function to parse the obtained parameters 
    #  That is to say, this sentence   Got   We type... From the command line   Content and carried out   analysis , Other properties are defaulted 
    opt = parser.parse_args()

    #  Use general.py Medium def check_yaml()  Function to view the data Whether there is 
    opt.data = check_yaml(opt.data)  # check YAML

    opt.save_json |= opt.data.endswith('coco.yaml')
    opt.save_txt |= opt.save_hybrid
    print_args(FILE.stem, opt)  #  Print opt Information about 
    return opt


def main(opt):
    #  Before the final test , Take a look at it requirements.txt In demand   Whether the operating environment is installed  , If not, update and download 
    check_requirements(requirements=ROOT / 'requirements.txt', exclude=('tensorboard', 'thop'))

    #  If opt Properties of task stay  train ,val test in , Verify the set on the normal test , Test set 
    if opt.task in ('train', 'val', 'test'):  # run normally
        if opt.conf_thres > 0.001:  # https://github.com/ultralytics/yolov5/issues/1466
            #  Print information :   Warning :   Confidence threshold   Such as  0.20  Far greater than  0.001  Will result in invalid  map value 
            LOGGER.info(f'WARNING: confidence threshold {opt.conf_thres} >> 0.001 will produce invalid mAP values.')
        run(**vars(opt))

    else:
        #  Judge  opt.weights  yes   A list of , If it is   A list of   Assign a value to weights , If not a list , Perform list conversion 
        weights = opt.weights if isinstance(opt.weights, list) else [opt.weights]

        #  half by True Namely   Say the accuracy is halved , Convert to   float 32
        opt.half = True  # FP16 for fastest results

        #  If task The value of is  speed , It is equivalent to using the form of fast verification 
        if opt.task == 'speed':  # speed benchmarks
            # python val.py --task speed --data coco.yaml --batch 1 --weights yolov5n.pt yolov5s.pt...

            #  Set related properties 
            opt.conf_thres, opt.iou_thres, opt.save_json = 0.25, 0.45, False

            for opt.weights in weights:
                run(**vars(opt), plots=False)

        #  If the incoming is  study In the form of , On Assessment yolov5 Series and yolov3-spp The indicators under each scale are visualized 
        elif opt.task == 'study':  # speed vs mAP benchmarks
            # python val.py --task study --data coco.yaml --iou 0.7 --weights yolov5n.pt yolov5s.pt...
            for opt.weights in weights:
                f = f'study_{Path(opt.data).stem}_{Path(opt.weights).stem}.txt'  # filename to save to
                x, y = list(range(256, 1536 + 128, 128)), []  # x axis (image sizes), y axis
                for opt.imgsz in x:  # img-size
                    LOGGER.info(f'
Running {f} --imgsz {opt.imgsz}...')
                    r, _, t = run(**vars(opt), plots=False)
                    y.append(r + t)  # results and times
                np.savetxt(f, y, fmt='%10.4g')  # save
            os.system('zip -r study.zip study_*.txt')
            plot_val_study(x=x)  # plot


if __name__ == "__main__":
    opt = parse_opt()
    main(opt)
    """



           data :   Dataset configuration file               The default is   data/coco128.yaml
        weights :   Test model weight file             The default is   yolov5s.pt
            
     batch-size :   The batch at the time of forward propagation             The default is   32,    
          imgsz :  The size of the image input resolution         The default is   640
     conf-thres :   Confidence threshold when filtering boxes       The default is   0.001 
      iou-thres :   Conduct NMS When IOU threshold       The default is   0.6,
        
         
         device :   Test equipment  cpu  Or is it 0  representative GPU     
       save-txt :   Whether or not to txt Save the frame coordinates of model prediction in the form of file                     The default is   False
      save-conf :   Whether to save the confidence information to the above save—txt In the middle                 The default is   False
    save-json :  Is it in accordance with coco Of josn Save forecast box in format , And use cocoapi Make an assessment     The default is   Fasle
    
     single-cls :   Whether the dataset has only one category      The default is   False
        augment :   Whether to use TTA          The default is   False
        verbose :   Whether to print... For each category mAP      The default is   False
            
        project :  The default is  ROOT / 'runs/val'   That is to say, it will be in runs Create under file val Folder 
           name :  The default is  exp                 That is to say, it will be in val Create under folder exp Folder to store the results 
           task :   The default is val                  Set the test form 
    """

13: yolov5 - v6 edition train.py Comment code

# YOLOv5 ?? by Ultralytics, GPL-3.0 license
#  Model training script 
"""
Train a YOLOv5 model on a custom dataset

Usage:
    $ python path/to/train.py --data coco128.yaml --weights yolov5s.pt --img 640
"""
import argparse
import math
import os
import random
import sys
import time
from copy import deepcopy
from datetime import datetime
from pathlib import Path

import numpy as np
import torch
import torch.distributed as dist
import torch.nn as nn
import yaml
from torch.cuda import amp
from torch.nn.parallel import DistributedDataParallel as DDP
from torch.optim import SGD, Adam, lr_scheduler
from tqdm import tqdm

FILE = Path(__file__).resolve()
ROOT = FILE.parents[0]  # YOLOv5 root directory
if str(ROOT) not in sys.path:
    sys.path.append(str(ROOT))  # add ROOT to PATH
ROOT = Path(os.path.relpath(ROOT, Path.cwd()))  # relative

import val  # for end-of-epoch mAP
from models.experimental import attempt_load
from models.yolo import Model
from utils.autoanchor import check_anchors
from utils.autobatch import check_train_batch_size
from utils.callbacks import Callbacks
from utils.datasets import create_dataloader
from utils.downloads import attempt_download
from utils.general import (LOGGER, check_dataset, check_file, check_git_status, check_img_size, check_requirements,
                           check_suffix, check_yaml, colorstr, get_latest_run, increment_path, init_seeds,
                           intersect_dicts, labels_to_class_weights, labels_to_image_weights, methods, one_cycle,
                           print_args, print_mutation, strip_optimizer)
from utils.loggers import Loggers
from utils.loggers.wandb.wandb_utils import check_wandb_resume
from utils.loss import ComputeLoss
from utils.metrics import fitness
from utils.plots import plot_evolve, plot_labels
from utils.torch_utils import EarlyStopping, ModelEMA, de_parallel, select_device, torch_distributed_zero_first

'''

 For example, we are in the environment variable of our computer   Got up   One is called key Value  , If it points to the address of a compiler :  home/jdk
os.getenv(key ) The role of   Is to get the value of this point :   namely  home/jdk ;
 If there is no indication  key The direction of   yes   A value  ,  that os.getenv(key )  Just go back to one None
 If we don't specify key The direction of  , os.getenv(Key  , 'value does not exist ' )   If it cannot be found, it will return   value does not exist

 So here   Use  os.getenv('LOCAL_RANK', -1)  , If you can't find it  LOCAL_RANK The value of the point  , Just   Is to return the specified -1,  that LOCAL_RANK = int(-1)=-1
 So here   Use  os.getenv('RANK', -1)   , If you can't find it  RANK The value of the point  , Just   Is to return the specified -1,  that RANK = int(-1)=-1
 So here   Use  os.getenv('WORLD_SIZE', 1)   , If you can't find it  WORLD_SIZE The value of the point  , Just   Is to return the specified 1,  that RANK = int(1)=1

RANK Number the process  
LOCAL_RANK by GPU Number 

'''

LOCAL_RANK = int(os.getenv('LOCAL_RANK', -1))  # https://pytorch.org/docs/stable/elastic/run.html
RANK = int(os.getenv('RANK', -1))
WORLD_SIZE = int(os.getenv('WORLD_SIZE', 1))


def train(hyp,  # path/to/hyp.yaml or hyp dictionary
          opt,
          device,
          callbacks
          ):
    #  obtain  opt Properties of 
    save_dir, epochs, batch_size, weights, single_cls, evolve, data, cfg, resume, noval, nosave, workers, freeze = 
    Path(opt.save_dir), opt.epochs, opt.batch_size, opt.weights, opt.single_cls, opt.evolve, opt.data, opt.cfg, 
    opt.resume, opt.noval, opt.nosave, opt.workers, opt.freeze

    # ............................................. Set the storage path of the training generated model ....................................
    #  If no hyperparametric evolution is specified  , So the following statement opt.save_dir = str(increment_path(Path(opt.project) / opt.name, exist_ok=opt.exist_ok))
    #   opt.project ----> runs/tarin     opt.name----->exp  So it's equivalent to save_dir  by   runs/train/exp
    #  If hyperparametric evolution is used  , opt.project ----> runs/evolve      save_dir  Namely  runs/evolve /exp 了 

    #  Set the storage location of the training model  , That is to say   runs / train/exp/weights
    w = save_dir / 'weights'  # weights dir

    #  Create the corresponding   route 
    (w.parent if evolve else w).mkdir(parents=True, exist_ok=True)  # make dir

    #  stay weights Create  last.pt   and   best.pt
    last, best = w / 'last.pt', w / 'best.pt'

    # .......................................... Load the super parameter configuration file ..........................................
    # Hyperparameters , Judge  hyp Whether it's a string  , If it's a string , It shows that the program has not started evolution , Or go down main() In the function , It has become the corresponding dictionary 
    if isinstance(hyp, str):
        #  If it's a string  ,  Just call safe_load()  Function to load the corresponding hyp The super parameter configuration file is given to  hyp, Now hyp For the dictionary 
        with open(hyp, errors='ignore') as f:
            hyp = yaml.safe_load(f)  # load hyps dict

    #  call general.py Medium  colorstr( ) function   Print   It's different   Color   To modify   Hyperparameters , Output  :      Super parametric   Parameters : key = value  ,  Learning rate these 
    LOGGER.info(colorstr('hyperparameters: ') + ', '.join(f'{k}={v}' for k, v in hyp.items()))

    # Save run settings ,    If there is no hyperparametric evolution  , So that is   Open by writing  runs/train/exp Under the  hyp.yaml, Just write it in hyp
    if not evolve:
        with open(save_dir / 'hyp.yaml', 'w') as f:
            yaml.safe_dump(hyp, f, sort_keys=False)

        #  preservation opt Configuration of  , That is, after you input the command and change it opt Information 
        with open(save_dir / 'opt.yaml', 'w') as f:
            yaml.safe_dump(vars(opt), f, sort_keys=False)

    # Loggers
    data_dict = None
    if RANK in [-1, 0]:
        loggers = Loggers(save_dir, weights, opt, hyp, LOGGER)  # loggers instance
        if loggers.wandb:
            data_dict = loggers.wandb.data_dict
            if resume:
                weights, epochs, hyp = opt.weights, opt.epochs, opt.hyp

        # Register actions
        for k in methods(loggers):
            callbacks.register_action(k, callback=getattr(loggers, k))

    # Config ,   Without evolution  ,  plots by true
    plots = not evolve  # create plots

    #  Check whether the device is cpu ,  If it is cpu ,cuda by  false  ,   If not cpu , So that is cuda  by true , That's it  GPU
    cuda = device.type != 'cpu'
    init_seeds(1 + RANK)  #  Set random seeds 

    #  ................................................... obtain data Specified training set information ..................................
    with torch_distributed_zero_first(LOCAL_RANK):

        #  call general Medium check_dataset() Function to   Check    Check the incoming  data Whether there is  , Back is a dictionary 
        data_dict = data_dict or check_dataset(data)  # check if None
        #   With   Training default   Folder data Under the  coco128.yaml For example  , It's stored inside 
        '''
         path: ../datasets/coco128   Path information 
        train: images/train2017   Path information of training picture 
          val: images/train2017   Verify the path information of the picture 
         test:   optional   Serious information about the test  
 
           nc: 1    Type and quantity information 
        names: ['person']    Name information of corresponding category 
     download: https://ultralytics.com/assets/coco128.zip   If no download address exists  , But we usually specify our training set 
        
        '''
    #    ............................................. Get the path of the training set data set .............................................................
    #  from  data designated   In file    obtain   Training set   route  train_path  ,   Get the path of the validation set  val_path
    train_path, val_path = data_dict['train'], data_dict['val']

    #  If we pass  --single_cls , It is equivalent to that we specify that the dataset has only one class  , nc by 1 ,  Otherwise, it will be from data_dict Get in there nc The number of 
    nc = 1 if single_cls else int(data_dict['nc'])  # number of classes

    #  If    The incoming assignment is a single category    however   stay  data designated yaml The category name in the file is not   A category  ,  that names=[ 'item' ]
    #  Otherwise  , Namely    Page you pass in the specified single category and just  yaml The category name in the file is also a  ,  that  names= data_dict['names'] , Namely   That category name 
    names = ['item'] if single_cls and len(data_dict['names']) != 1 else data_dict['names']  # class names

    #  If   The number of names does not equal   Number of categories specified  ,   Print : 3 A name has been found  ,  But the number of categories in the data is  4
    assert len(names) == nc, f'{len(names)} names found for nc={nc} dataset in {data}'  # check

    #  see   Verify that the path to the set is str , That is, the validation set path is specified  ,   And if so coco/val2017.txt ending  , So that is coco Data sets , Otherwise it's not 
    is_coco = isinstance(val_path, str) and val_path.endswith('coco/val2017.txt')  # COCO dataset

    #  .................................................... See if you use pre training ...........................................
    # Model weights Whether we use others' training model as our pre training    ,  #  Check if the document is   Ends with the specified suffix    Check if the document is   Ends with the specified suffix 
    check_suffix(weights, '.pt')  # check weights
    pretrained = weights.endswith('.pt')  #  If we specify  --weight ''  ,  Default not specified is  yolov5s.pt

    if pretrained:  #  If it is true , That is, pre training 
        with torch_distributed_zero_first(LOCAL_RANK):
            #  If the corresponding file is not found locally , Then try downloading , from google Automatically download the model on the cloud disk 
            #  But because of the Internet , Most likely, the download failed , therefore ,  If you want to use it, you should download it first 
            weights = attempt_download(weights)  # download if not found locally

        #  Loading model and parameters 
        ckpt = torch.load(weights, map_location=device)  # ckpt Namely  checkpoint
        #     Just call common.py class Model, Instantiate a model , Load model 
        #     Model creation , It can be done by opt.cfg  To create   , It's fine too   adopt ckpt['model'].yaml To create 
        #     The difference is whether it is reusme ,resume Will opt.cfg Set to null  , Then according to ckpt['model'].yaml To create a model 
        model = Model(cfg or ckpt['model'].yaml, ch=3, nc=nc, anchors=hyp.get('anchors')).to(device)  # create
        exclude = ['anchor'] if (cfg or hyp.get('anchors')) and not resume else []  # exclude keys
        #  hyp.get('achors')  Is to generate several pre selected box super parameters  , cfg  Is the structure of the model  , So   exclude=['anchor']
        #   This also affects whether to remove anchor Of key, That is to say ( No load anchor ) , If resume Do not load anchor
        #  This is mainly because the saved model will be saved anchors , Sometimes users customize anchor after , Again resume, It is based on coco Data sets anchor Will override your own settings anchor
        csd = ckpt['model'].float().state_dict()  # checkpoint state_dict as FP32
        csd = intersect_dicts(csd, model.state_dict(), exclude=exclude)  # intersect
        model.load_state_dict(csd, strict=False)  # load
        #  Display the key value pairs for loading pre training weights and creating models 
        LOGGER.info(f'Transferred {len(csd)}/{len(model.state_dict())} items from {weights}')  # report
    else:
        #  That is, we don't use other people's pre training models  , Just call common.py Class in Model, Instantiate a model
        model = Model(cfg, ch=3, nc=nc, anchors=hyp.get('anchors')).to(device)  # create

    # ........................................................ Freeze the model layer ..........................................
    #  Freeze the model layer  , Set the name of the frozen layer 
    #  You can see  https://github.com/ultralytics/yolov5/issues/679
    # Freeze ,   That is, whether the demand is frozen  , The default is [0] ,   If you pass in --freeze  Is to freeze the requirements of some layers 
    freeze = [f'model.{x}.' for x in (freeze if len(freeze) > 1 else range(freeze[0]))]  # layers to freeze
    for k, v in model.named_parameters():
        v.requires_grad = True  # train all layers
        if any(x in k for x in freeze):
            LOGGER.info(f'freezing {k}')
            v.requires_grad = False  #  By way of requires_gard  Set to false , That is, no gradient calculation 

    # ..................................... Enter the size setting and batch-size Set up .................................................
    #  If you add some structure to your network structure , Make the smallest eigenvalue of your output no longer 32 Times down sampling , Remember to change the content here ......................
    # Image size ,  If not 32 Under sampling , for example 128 times , To change   Many   About the setting of down sampling , For example, the picture should be 128 And so on   . ..... ......
    gs = max(int(model.stride.max()), 32)  # grid size (max stride)

    #  call  general.py In the document  check_img_size( )  Function to check whether the image size is  s( The default is 32) Integer multiple , If not    Just adjust it to  32 Integer multiple 
    imgsz = check_img_size(opt.imgsz, gs, floor=gs * 2)  # verify imgsz is gs-multiple

    # Batch size ,    The default is  16
    if RANK == -1 and batch_size == -1:  # single-GPU only, estimate best batch size

        #  call  autobatch.py Medium  check_train_batch_size  ,  The main check is the training set batch_size Compliance with specifications 
        batch_size = check_train_batch_size(model, imgsz)
        loggers.on_params_update({"batch_size": batch_size})

    # Optimizer ....................................   Optimizer settings  , The default optimizer is SGD..........................................
    # nbs Nominal batc_size:
    #  For example, the above settings are set by default opt.batch_size by 16 , nbs=64
    #  Then the gradient of the model is accumulated  64/16=  4  (accumulate) After that  , Updating the model once , Expansion in disguise batch_size

    nbs = 64  # nominal batch size
    accumulate = max(round(nbs / batch_size), 1)  # accumulate loss before optimizing

    #  according to accumlate Set the weight attenuation factor  ,  weight_decay  stay hyp It is used in  0.00036
    #  The new attenuation coefficient is   =   0.00036 * batch_size  *   With the above rules (nbs /batch_size  )      /nbs
    hyp['weight_decay'] *= batch_size * accumulate / nbs  # scale weight_decay Weight decay 
    LOGGER.info(f"Scaled weight_decay = {hyp['weight_decay']}")

    g0, g1, g2 = [], [], []  # optimizer parameter groups
    #  Set optimizer list , That is to divide the model into 3 Group  , (weight    , bias    , All other parameters )  To optimize 

    for v in model.modules():
        if hasattr(v, 'bias') and isinstance(v.bias, nn.Parameter):  # bias
            g2.append(v.bias)
            #  bias  Load on  g2 In the optimization list of 

        if isinstance(v, nn.BatchNorm2d):  # weight (no decay)
            g0.append(v.weight)  #  If it is bn layer  ,  Add weights without attenuation to g0 in  , That is to say BN Layers do not have weight attenuation 

        elif hasattr(v, 'weight') and isinstance(v.weight, nn.Parameter):  # weight (with decay  attenuation )
            g1.append(v.weight)  #  Add the weight of the attenuated layer to g1 in 

    #  If we pass in a parameter   yes   --adam , in other words opt.adam by true , therefore   We're going to use Adam Optimizer 
    if opt.adam:
        optimizer = Adam(g0, lr=hyp['lr0'], betas=(hyp['momentum'], 0.999))  # adjust beta1 to momentum
    #  Otherwise, the default is SGD   Optimizer 
    else:
        optimizer = SGD(g0, lr=hyp['lr0'], momentum=hyp['momentum'], nesterov=True)

    #  Set up weight Optimization method 
    optimizer.add_param_group({'params': g1, 'weight_decay': hyp['weight_decay']})  #  Attenuation operation 

    #  Set up biases Optimization method 
    optimizer.add_param_group({'params': g2})  # add g2 (biases)

    #  Print information :  optimizer : Adam with parameter groups ,   How many weight The weight has no attenuation  , How many weight Weight falloff  , How many bias
    LOGGER.info(f"{colorstr('optimizer:')} {type(optimizer).__name__} with parameter groups "
                f"{len(g0)} weight (no decay), {len(g1)} weight  , {len(g2)} bias")
    del g0, g1, g2

    # ................................................ Set the attenuation of learning rate  , Here is the cosine annealing method for attenuation ..............................
    # Scheduler    Set learning rate attenuation  , Here is the cosine annealing method for attenuation 
    if opt.linear_lr:
        lf = lambda x: (1 - x / (epochs - 1)) * (1.0 - hyp['lrf']) + hyp['lrf']  # linear  Annealing formula 
    else:
        lf = one_cycle(1, hyp['lrf'], epochs)  # cosine 1->hyp['lrf']
    scheduler = lr_scheduler.LambdaLR(optimizer, lr_lambda=lf)  # plot_lr_scheduler(optimizer, scheduler, epochs)

    # ...................................................................................................................
    # EMA   call torch_utils.py In the document  ModelEMA class    Model EMA , Is to make an index of the parameters in the network model   moving average   , Smooth the weights  , It has better effect on some training 
    ema = ModelEMA(model) if RANK in [-1, 0] else None

    # Resume
    #  Initialize the start of training epoch And the best results 
    # best_fitness  In order to [  0.0 ,  0.0 ,  0.1  ,  0.9  ]   Is the coefficient multiplied by  [  accuracy p ,  Recall rate , [email protected] , [email protected]:0.95 ] Then sum up and get 
    # best_fitness To preserve best.pt
    start_epoch, best_fitness = 0, 0.0
    if pretrained:  # pretrained = weights.endswith('.pt')  That is, whether to use pre training 

        # Optimizer
        if ckpt['optimizer'] is not None:  #  If ckpt Medium optimizer  There's something  , is not  None  , Namely true
            optimizer.load_state_dict(ckpt['optimizer'])
            best_fitness = ckpt['best_fitness']

        # EMA
        #  Create... For the model EMA Exponential moving average  , If GPU The number of processes is greater than 1 , Do not create 
        # EMA( Exponentially moving average ):  An averaging method that gives higher weight to recent data 
        #  use EMA Methods average the parameters of the model  , In order to improve the test indicators and increase the people of the model , Reduce model weight jitter 
        if ema and ckpt.get('ema'):
            ema.ema.load_state_dict(ckpt['ema'].float().state_dict())
            ema.updates = ckpt['updates']

        # Epochs
        start_epoch = ckpt['epoch'] + 1

        if resume:
            assert start_epoch > 0, f'{weights} training to {epochs} epochs is finished, nothing to resume.'

        '''
         If the new settings epochs Less than loaded epoch
         It will be regarded as the newly set epochs For the number of rounds that need to be retrained instead of the total number of rounds 
        '''
        if epochs < start_epoch:
            LOGGER.info(f"{weights} has been trained for {ckpt['epoch']} epochs. Fine-tuning for {epochs} more epochs.")
            epochs += ckpt['epoch']  # finetune additional epochs

        del ckpt, csd

    '''
    DP Training : Reference resources   https://github.com/ultralytics/yolov5/issues/679
    DataParallel Pattern , Only single machine and multi card are supported 
    RANK Number the process  ,    If set rank=-1 And there are many pieces GPU , Then use DataParallel Model 
    rank =-1  And GPU The number of 1 , No distributed training  
    
    '''
    # DP mode
    if cuda and RANK == -1 and torch.cuda.device_count() > 1:
        #  You have more than one card   Warning : Not recommended DP Training  , In order to get better distributed multi block GPU Training results  , Use  torch.distributed.run  Method 
        #  see also https://github.com/ultralytics/yolov5/issues/475 More on gpu The tutorial begins 
        LOGGER.warning('WARNING: DP not recommended, use torch.distributed.run for best DDP Multi-GPU results.
'
                       'See Multi-GPU Tutorial at https://github.com/ultralytics/yolov5/issues/475 to get started.')
        model = torch.nn.DataParallel(model)

    # SyncBatchNorm  sync_bn Indicates whether to use cross card synchronization 
    if opt.sync_bn and cuda and RANK != -1:
        model = torch.nn.SyncBatchNorm.convert_sync_batchnorm(model).to(device)
        LOGGER.info('Using SyncBatchNorm()')

    # ................................................ Create a training set based on the training set path train_loader..................................
    # Trainloader, Use datasets.py Medium create_datalaoder()  Method 
    train_loader, dataset = create_dataloader(train_path, imgsz, batch_size // WORLD_SIZE, gs, single_cls,
                                              hyp=hyp, augment=True, cache=opt.cache, rect=opt.rect, rank=LOCAL_RANK,
                                              workers=workers, image_weights=opt.image_weights, quad=opt.quad,
                                              prefix=colorstr('train: '), shuffle=True)

    #  Get the maximum category value in the tag    And with the number of categories nc  compare 
    mlc = int(np.concatenate(dataset.labels, 0)[:, 0].max())  # max label class , The maximum number of categories of labels 
    nb = len(train_loader)  # number of batches
    #    mlc If it is greater than nc     Print :  Number of label categories mlc  More than in    Defined in the dataset nc value  ,    Of the allowed tags   The category value is in  0 -- nc-1 Between 
    assert mlc < nc, f'Label class {mlc} exceeds nc={nc} in {data}. Possible class labels are 0-{nc - 1}'

    # ................................................ To create a training set based on the validation set path val_loader..................................
    # Process 0
    if RANK in [-1, 0]:
        #  Use datasets.py Medium create_datalaoder()  Method to create a validation set val_loader
        val_loader = create_dataloader(val_path, imgsz, batch_size // WORLD_SIZE * 2, gs, single_cls,
                                       hyp=hyp, cache=None if noval else opt.cache, rect=True, rank=-1,
                                       workers=workers, pad=0.5,
                                       prefix=colorstr('val: '))[0]

        # .............................. Draw all of them lables1 Type information of  , Center point distribution information , And length and width information ..................................
        if not resume:  #  If not   Breakpoint training 
            labels = np.concatenate(dataset.labels, 0)  #  Get dataset The tag value of 
            # c = torch.tensor(labels[:, 0])  # classes
            # cf = torch.bincount(c.long(), minlength=nc) + 1.  # frequency
            # model._initialize_biases(cf.to(device))

            if plots:  # plots = not evolve    That is to say, if we don't turn on hyperparametric evolution  , That is to say plots by true , Then print the relevant information 

                #  call plots.py Medium plot_lables  Information  ,  save_dir The path to  runs/train/exp/ Next  ,
                plot_labels(labels, names, save_dir)

            # Anchors , If the parameter we pass in is   --noautoanchor  Then go to true ,   Is to turn off the automatic calculation of the aiming box  ,
            #  here  not ot opt.noautoanchor  for  flase  , Just don't do what's inside   sentence ,  Otherwise, by default ,  It will start the automatic aiming frame calculation 

            #  ........ .............. ............. Whether to enable auto aiming frame calculation ........................................................
            if not opt.noautoanchor:
                #  By default, auto aiming calculation is enabled , call autoanchor.py Medium check_anchors Turn on   Automatically calculate the best aiming frame , That is to calculate the best aiming frame 
                check_anchors(dataset, model=model, thr=hyp['anchor_t'], imgsz=imgsz)
            model.half().float()  # pre-reduce anchor precision

        #  That is to say   Breakpoint training   Auto frame calculation will not be enabled 
        callbacks.run('on_pretrain_routine_end')

    # DDP mode
    #  If  rank It's not equal to -1, Then use DistributedDataParallel Pattern 
    # local__rank by GPU Number  ,rank For the process  , for example rank =3 ,local_rank= 0  It means the first one 3 The first block in a process GPU
    if cuda and RANK != -1:
        model = DDP(model, device_ids=[LOCAL_RANK], output_device=LOCAL_RANK)

    # Model attributes................................ Model property settings .........................................
    nl = de_parallel(model).model[-1].nl  # number of detection layers (to scale hyps)

    #  Set the coefficient of classification loss according to the number of categories of your data set   Positioning loss and confidence loss coefficient 
    hyp['box'] *= 3 / nl  # scale to layers
    hyp['cls'] *= nc / 80 * 3 / nl  # scale to classes and layers
    hyp['obj'] *= (imgsz / 640) ** 2 * 3 / nl  # scale to image size and layers

    hyp['label_smoothing'] = opt.label_smoothing  #  towards hyp Intermediate placement label_smoothing Properties of  , If you pass in --label-smoothing  Is to open the class balance tab 

    model.nc = nc  # attach number of classes to model  Number of associated categories 
    model.hyp = hyp  # attach hyperparameters to model , Associated super parameters 

    #  Get the category weight from the training sample label (   And the number of targets in the category    ---  That is, category frequency  -- In inverse proportion  )
    model.class_weights = labels_to_class_weights(dataset.labels, nc).to(device) * nc  # attach class weights

    #  Get the name of the category 
    model.names = names

    # Start training........................................ Start training .......................................................
    t0 = time.time()

    #  Get the number of iterations of warm-up training , That is, the number of warm-up exercises 
    nw = max(round(hyp['warmup_epochs'] * nb), 1000)  # number of warmup iterations, max(3 epochs, 1k iterations)
    # nw = min(nw, (epochs - start_epoch) / 2 * nb)  # limit warmup to < 1/2 of training
    last_opt_step = -1

    #  initialization mAP and results Value 
    maps = np.zeros(nc)  # mAP per class
    results = (0, 0, 0, 0, 0, 0, 0)  # P, R, [email protected], [email protected], val_loss(box, obj, cls)

    '''
     Set the number of rounds of learning rate attenuation 
     The purpose is to interrupt after training  .--resume Then the training can also normally link up with the previous training to reduce the learning rate 
    
    '''
    scheduler.last_epoch = start_epoch - 1  # do not move

    #  adopt torch1.6 The above comes with api Set hybrid accuracy training 
    scaler = amp.GradScaler(enabled=cuda)

    #  If you pass in --patience Parameters , perform train.py Medium  EarlyStopping function    The number of training does not increase the performance by more than epochs when , Stop training 
    stopper = EarlyStopping(patience=opt.patience)

    compute_loss: ComputeLoss = ComputeLoss(model)  #  initialization  model Of  loss

    #  Corresponding to the size yes  640 Training  ,640 verification 
    #  Being used    How many threads 
    #  The results are in  runs/train/exp Next 
    #   Training   that epochs
    LOGGER.info(f'Image sizes {imgsz} train, {imgsz} val
'
                f'Using {train_loader.num_workers * WORLD_SIZE} dataloader workers
'
                f"Logging results to {colorstr('bold', save_dir)}
"
                f'Starting training for {epochs} epochs...')

    # ---------------------------------------------- Start the iteration epoch -------------------------------------------------------
    for epoch in range(start_epoch, epochs):
        model.train()  #  Start training 

        # Update image weights (optional, single-GPU only)
        if opt.image_weights:
            '''
             If you set the image sampling policy 
             Then, according to the previously initialized image sampling weight model.class_weights as well as  maps  coordination    Number of categories per picture 
             adopt random.choice Generate image index indices So as to sample 
            
             What kind of images does this real dataset use  , Focus can be set ,     If the original maps The larger  ,1-maps It becomes smaller 
             Such a big one maps The weight of is set   For the small ,   for example   There are many labels in the dataset classese  After this operation weights It will also be smaller 
                       
            '''
            cw = model.class_weights.cpu().numpy() * (1 - maps) ** 2 / nc  # class weights
            iw = labels_to_image_weights(dataset.labels, nc=nc, class_weights=cw)  #  Get the weight of image sampling 
            dataset.indices = random.choices(range(dataset.n), weights=iw, k=dataset.n)  # rand weighted idx

        # Update mosaic border (optional)
        # b = int(random.uniform(0.25 * imgsz, 0.75 * imgsz + gs) // gs * gs)
        # dataset.mosaic_border = [b - imgsz, -b]  # height, width borders

        #  Average loss information printed when initializing training 
        mloss = torch.zeros(3, device=device)  # mean losses
        if RANK != -1:
            '''
            DDP Scramble data under the model ,ddp.sampler The random sampling data is based on epoch+ seed As random seeds 
             Every time epoch Different , Random seeds are different 
            
            '''
            train_loader.sampler.set_epoch(epoch)
        pbar = enumerate(train_loader)
        LOGGER.info(('
' + '%10s' * 7) % ('Epoch', 'gpu_mem', 'box', 'obj', 'cls', 'labels', 'img_size'))

        if RANK in [-1, 0]:
            # tqdm  Create a progress bar , Facilitate the display of information during training 
            pbar = tqdm(pbar, total=nb, bar_format='{l_bar}{bar:10}{r_bar}{bar:-10b}')  # progress bar
        optimizer.zero_grad()  #  Zero clearing 

        for i, (imgs, targets, paths, _) in pbar:  # batch -------------------------------------------------------------
            #  Calculate the number of iterations  iteration
            ni = i + nb * epoch  # number integrated batches (since train start)
            imgs = imgs.to(device, non_blocking=True).float() / 255  # uint8 to float32, 0-255 to 0.0-1.0

            # Warmup
            '''
             Warm up ( front  nw Sub iteration )
             stay   front nw In the next iteration , Select... According to the following methods accumulate And the learning rate 
            '''
            if ni <= nw:
                xi = [0, nw]  # x interp     nw = max(round(hyp['warmup_epochs'] * nb), 1000)  We have already defined how many times to warm up 
                # compute_loss.gr = np.interp(ni, xi, [0.0, 1.0])  # iou loss ratio (obj_loss = 1.0 or iou)
                accumulate = max(1, np.interp(ni, xi, [1, nbs / batch_size]).round())
                for j, x in enumerate(optimizer.param_groups):
                    '''
                    bias The learning rate has increased from 0.1 Down to the benchmark learning rate lr*lf(epoch)
                     The learning rate of other parameters ranges from 0 Add to lr* lf(epoch)
                    lf Attenuation function for cosine annealing set above 
                    
                    '''
                    # bias lr falls from 0.1 to lr0, all other lrs rise from 0.0 to lr0
                    x['lr'] = np.interp(ni, xi, [hyp['warmup_bias_lr'] if j == 2 else 0.0, x['initial_lr'] * lf(epoch)])

                    #  Pillar  momentum Also from the 0.9  Slowly to   hyp['momentum']
                    if 'momentum' in x:
                        x['momentum'] = np.interp(ni, xi, [hyp['warmup_momentum'], hyp['momentum']])

            # Multi-scale
            #  Multiscale training , from  imgsz* 0.5 , imgsz* 1.5   +  gs  Select the size randomly 
            if opt.multi_scale:
                sz = random.randrange(imgsz * 0.5, imgsz * 1.5 + gs) // gs * gs  # size
                sf = sz / max(imgs.shape[2:])  # scale factor
                if sf != 1:
                    ns = [math.ceil(x * sf / gs) * gs for x in imgs.shape[2:]]  # new shape (stretched to gs-multiple)
                    imgs = nn.functional.interpolate(imgs, size=ns, mode='bilinear', align_corners=False)

            # Forward
            #  Mixed precision training 
            with amp.autocast(enabled=cuda):  # autocast The context should only contain the forward process of the network ( Include loss The calculation of ) , Do not include back propagation 
                pred = model(imgs)  # forward   Forward operation 

                #  In front of  437 Right and left of the line ,    compute_loss: ComputeLoss = ComputeLoss(model)   It's initialized  model Of  loss
                #  The main application is  loss.py Medium ComputeLoss Class instantiates a ComputerLoss,
                #  By using the  fl_gamma:  Greater than 0 Set up , Enable  Focallost ,focallost The main solution is   Target detection phase one-stage  The problem of imbalance between positive and negative samples 
                #  meanwhile  , ComputeLoss Medium   It's called  metrics.py Medium  bbox_iou ,    Enabled by default  Ciou=True , That is to say   By default Ciou To investigate the positioning loss of the frame 
                loss, loss_items = compute_loss(pred, targets.to(device))  # loss scaled by batch_size
                if RANK != -1:
                    loss *= WORLD_SIZE  # gradient averaged between devices in DDP mode
                if opt.quad:
                    loss *= 4.

            # Backward  Back propagation 
            scaler.scale(loss).backward()

            # Optimize , Optimize   :    Model back propagation accumulate And then update the parameters once according to the accumulated gradient 
            if ni - last_opt_step >= accumulate:
                '''
               scaler.step()  First, the value of the gradient  unscale Come back 
                If the value of the gradient is not  infs  perhaps NaNs ,   So called optimizer.step() To update the weights .
                otherwise , Ignore step call  , So as to ensure that the weight is not updated ( Not destroyed )             
                '''
                scaler.step(optimizer)  # optimizer.step  Update parameters 

                #  Be ready to  , See if you want to increase scaler
                scaler.update()
                optimizer.zero_grad()  #  Gradient clear 

                #  use EMA Methods average the parameters of the model  , In order to improve the test indicators and increase the people of the model , Reduce model weight jitter 
                if ema:
                    ema.update(model)
                last_opt_step = ni

            #  Make a note of   Some of the information , Show some information 
            if RANK in [-1, 0]:
                #  Print video memory  , Rounds carried out  , Loss  ,target The number and   The image size Etc 
                mloss = (mloss * i + loss_items) / (i + 1)  # update mean losses, Calculation   How many times do you train   Average loss

                mem = f'{torch.cuda.memory_reserved() / 1E9 if torch.cuda.is_available() else 0:.3g}G'  # (GB)
                pbar.set_description(('%10s' * 2 + '%10.4g' * 5) % (
                    f'{epoch}/{epochs - 1}', mem, *mloss, targets.shape[0], imgs.shape[-1]))

                callbacks.run('on_train_batch_end', ni, model, imgs, targets, paths, plots, opt.sync_bn)
            # end batch ------------------------------------------------------------------------------------------------

        #  Every batch After completion , Will adjust the learning rate 
        # Scheduler
        lr = [x['lr'] for x in optimizer.param_groups]  # for loggers
        scheduler.step()  #  Adjust the learning rate 

        # .................................................... Judge whether it is the last round of training , If yes, start the validation set .........................
        if RANK in [-1, 0]:
            # mAP
            callbacks.run('on_train_epoch_end', epoch=epoch)

            #  add to include Properties of 
            ema.update_attr(model, include=['yaml', 'nc', 'hyp', 'names', 'stride', 'class_weights'])

            #  Judge that epoch Whether it's the last round 
            final_epoch = (epoch + 1 == epochs) or stopper.possible_stop

            #  Test the test set , And calculate mAP Equal index 
            #  The test was conducted with EMA Model  , If you pass in  --noval Parameters  , noval Namely  true, not noval Namely  false , That is to say   Validation set validation will not be started 
            if not noval or final_epoch:  # Calculate mAP
                results, maps, _ = val.run(data_dict,
                                           batch_size=batch_size // WORLD_SIZE * 2,
                                           imgsz=imgsz,
                                           model=ema.ema,
                                           single_cls=single_cls,
                                           dataloader=val_loader,
                                           save_dir=save_dir,
                                           plots=False,
                                           callbacks=callbacks,
                                           compute_loss=compute_loss)

            # Update best mAP , Update the best mAP
            fi = fitness(np.array(results).reshape(1, -1))  # weighted combination of [P, R, [email protected], [email protected]]
            if fi > best_fitness:
                best_fitness = fi
            log_vals = list(mloss) + list(results) + lr
            callbacks.run('on_fit_epoch_end', log_vals, epoch, best_fitness, fi)

            # Save model................................................. Save the model ...................................
            #  preservation & Loading belt checkpoint The model is used for inference or resuming training
            #  Save the model , And preserved epoch,results,optimizer Etc 
            # optimizer Saving will not be completed in the last round 
            # model What is kept is EMA Model of 

            if (not nosave) or (final_epoch and not evolve):  # if save

                ckpt = {'epoch': epoch,
                        'best_fitness': best_fitness,
                        'model': deepcopy(de_parallel(model)).half(),
                        'ema': deepcopy(ema.ema).half(),
                        'updates': ema.updates,
                        'optimizer': optimizer.state_dict(),
                        'wandb_id': loggers.wandb.wandb_run.id if loggers.wandb else None,
                        'date': datetime.now().isoformat()}

                # Save last, best and delete, Save the latest weights to  last.pt in 
                torch.save(ckpt, last)
                #  See if it's assembled fitness, If yes, save the weight to  best.pt in 
                if best_fitness == fi:
                    torch.save(ckpt, best)
                if (epoch > 0) and (opt.save_period > 0) and (epoch % opt.save_period == 0):
                    torch.save(ckpt, w / f'epoch{epoch}.pt')
                del ckpt
                callbacks.run('on_model_save', last, epoch, final_epoch, best_fitness, fi)

            # Stop Single-GPU
            if RANK == -1 and stopper(epoch=epoch, fitness=fi):
                break

            # Stop DDP TODO: known issues shttps://github.com/ultralytics/yolov5/pull/4576
            # stop = stopper(epoch=epoch, fitness=fi)
            # if RANK == 0:
            #    dist.broadcast_object_list([stop], 0)  # broadcast 'stop' to all ranks

        # Stop DPP
        # with torch_distributed_zero_first(RANK):
        # if stop:
        #    break  # must break all DDP ranks

        # end epoch ----------------------------------------------------------------------------------------------------
    # end training -----------------------------------------------------------------------------------------------------

    if RANK in [-1, 0]:
        #  Print epoch Total time 
        LOGGER.info(f'
{epoch - start_epoch + 1} epochs completed in {(time.time() - t0) / 3600:.3f} hours.')

        for f in last, best:
            if f.exists():

                #  call general.py Medium  strip_optimizer( )  function ............................. Split the optimizer information in the weight file ...............
                strip_optimizer(f)  # strip optimizers

                #  If it is the best , Call again   Validation set to validate the model 
                if f is best:
                    LOGGER.info(f'
Validating {f}...')
                    results, _, _ = val.run(data_dict,
                                            batch_size=batch_size // WORLD_SIZE * 2,
                                            imgsz=imgsz,
                                            model=attempt_load(f, device).half(),
                                            iou_thres=0.65 if is_coco else 0.60,  # best pycocotools results at 0.65
                                            single_cls=single_cls,
                                            dataloader=val_loader,
                                            save_dir=save_dir,
                                            save_json=is_coco,
                                            verbose=True,
                                            plots=True,
                                            callbacks=callbacks,
                                            compute_loss=compute_loss)  # val best model with plots
                    if is_coco:
                        callbacks.run('on_fit_epoch_end', list(mloss) + list(results) + lr, epoch, best_fitness, fi)

        callbacks.run('on_train_end', last, best, plots, epoch, results)
        LOGGER.info(f"Results saved to {colorstr('bold', save_dir)}")

    #  Video memory release 
    torch.cuda.empty_cache()
    return results


def parse_opt(known=False):
    #  establish   Parameter resolution object parser()
    parser = argparse.ArgumentParser()
    # add_argument()  Is to add   One   What attribute 
    # nargs  refer to  --->   Should read   The number of command line parameters for ,  *  Express  0 Or more  ,   + Express  1 Or more 
    # action --   The command line   Action when parameters are encountered , action='store_true' , Indicates that the variable is set to as long as it has a parameter passed during runtime True

    parser.add_argument('--weights', type=str, default=ROOT / 'yolov5s.pt', help='initial weights path')
    parser.add_argument('--cfg', type=str, default='', help='model.yaml path')
    parser.add_argument('--data', type=str, default=ROOT / 'data/coco128.yaml', help='dataset.yaml path')
    parser.add_argument('--hyp', type=str, default=ROOT / 'data/hyps/hyp.scratch.yaml', help='hyperparameters path')
    parser.add_argument('--epochs', type=int, default=300)
    parser.add_argument('--batch-size', type=int, default=16, help='total batch size for all GPUs, -1 for autobatch')
    parser.add_argument('--imgsz', '--img', '--img-size', type=int, default=640, help='train, val image size (pixels)')
    parser.add_argument('--rect', action='store_true', help='rectangular training')
    parser.add_argument('--resume', nargs='?', const=True, default=False, help='resume most recent training')
    parser.add_argument('--nosave', action='store_true', help='only save final checkpoint')
    parser.add_argument('--noval', action='store_true', help='only validate final epoch')
    parser.add_argument('--noautoanchor', action='store_true', help='disable autoanchor check')
    parser.add_argument('--evolve', type=int, nargs='?', const=300, help='evolve hyperparameters for x generations')
    parser.add_argument('--bucket', type=str, default='', help='gsutil bucket')
    parser.add_argument('--cache', type=str, nargs='?', const='ram', help='--cache images in "ram" (default) or "disk"')
    parser.add_argument('--image-weights', action='store_true', help='use weighted image selection for training')
    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 multi-class data as single-class')
    parser.add_argument('--adam', action='store_true', help='use torch.optim.Adam() optimizer')
    parser.add_argument('--sync-bn', action='store_true', help='use SyncBatchNorm, only available in DDP mode')
    parser.add_argument('--workers', type=int, default=8, help='max dataloader workers (per RANK in DDP mode)')
    parser.add_argument('--project', default=ROOT / 'runs/train', help='save to project/name')
    parser.add_argument('--name', default='exp', help='save to project/name')
    parser.add_argument('--exist-ok', action='store_true', help='existing project/name ok, do not increment')
    parser.add_argument('--quad', action='store_true', help='quad dataloader')
    parser.add_argument('--linear-lr', action='store_true', help='linear LR')
    parser.add_argument('--label-smoothing', type=float, default=0.0, help='Label smoothing epsilon')
    parser.add_argument('--patience', type=int, default=100, help='EarlyStopping patience (epochs without improvement)')
    parser.add_argument('--freeze', nargs='+', type=int, default=[0], help='Freeze layers: backbone=10, first3=0 1 2')
    parser.add_argument('--save-period', type=int, default=-1, help='Save checkpoint every x epochs (disabled if < 1)')
    parser.add_argument('--local_rank', type=int, default=-1, help='DDP parameter, do not modify')

    # Weights & Biases arguments
    parser.add_argument('--entity', default=None, help='W&B: Entity')
    parser.add_argument('--upload_dataset', nargs='?', const=True, default=False, help='W&B: Upload data, "val" option')
    parser.add_argument('--bbox_interval', type=int, default=-1, help='W&B: Set bounding-box image logging interval')
    parser.add_argument('--artifact_alias', type=str, default='latest', help='W&B: Version of dataset artifact to use')

    #  use parse_args()  Function to parse the obtained parameters 
    #  That is to say, this sentence   Got   We type... From the command line   Content and carried out   analysis , Other properties are defaulted 
    opt = parser.parse_known_args()[0] if known else parser.parse_args()
    return opt


def main(opt, callbacks=Callbacks()):
    # Checks, RANK The value of is defined at the beginning 
    if RANK in [-1, 0]:
        #  call  general.py In the document  print_args()  Function to print relevant information 
        print_args(FILE.stem, opt)

        #  call  general.py In the document  check_git_status() function , #  This function checks whether the current branch version is git Same as the previous version , If not ,
        #  If the version is backward, users will be reminded , This function is in the trian.py Function is used in 
        check_git_status()

        #  Before the final test , Take a look at it requirements.txt In demand   Whether the operating environment is installed  , If not, update and download 
        check_requirements(exclude=['thop'])

    # Resume , If the command we enter on the keyboard has  --resume , That is, breakpoint training   , And no hyperparametric evolution is specified 
    if opt.resume and not check_wandb_resume(opt) and not opt.evolve:  # resume an interrupted run
        #  If you pass in --resume  Namely true , isinstance(opt.resume, str)  It's wrong 
        #  call general.py Medium  get_latest_run()  Get the weight file of recent training   last.pt    to ckpt
        ckpt = opt.resume if isinstance(opt.resume, str) else get_latest_run()  #  Specify the path of the most recently run model 

        #  Judge ckpt Is it a document  ,  If it is not a file, an error is reported 
        assert os.path.isfile(ckpt), 'ERROR: --resume checkpoint does not exist'  #  Warning :  The recovery point checked does not exist 

        #  If it's a file , Go to the superior directory to find opt.yaml To resume training 
        with open(Path(ckpt).parent.parent / 'opt.yaml', errors='ignore') as f:
            opt = argparse.Namespace(**yaml.safe_load(f))  # replace
        opt.cfg, opt.weights, opt.resume = '', ckpt, True  # reinstate
        LOGGER.info(f'Resuming training from {ckpt}')  #  Print information :   From  ckpt ( The latest last.pt File office ) Break point training 
    else:
        #  Otherwise, it would be   No, resume , Or into  --evolve , So hyperparametric evolution  , above not opt.evolve Namely false, Then execute the following statement 
        opt.data, opt.cfg, opt.hyp, opt.weights, opt.project = 
        check_file(opt.data), check_yaml(opt.cfg), check_yaml(opt.hyp), str(opt.weights), str(opt.project)  # checks
        assert len(opt.cfg) or len(opt.weights), 'either --cfg or --weights must be specified'
        #  If something goes wrong , len(opt.cfg) or len(opt.weights),    To print, you must specify ——cfg or ——weights

        if opt.evolve:
            opt.project = str(ROOT / 'runs/evolve')
            opt.exist_ok, opt.resume = opt.resume, False  # pass resume to exist_ok and disable resume
        opt.save_dir = str(increment_path(Path(opt.project) / opt.name, exist_ok=opt.exist_ok))
        #  That is, hyperparametric evolution cannot be related to   Breakpoint training  ,      If   Turn on hyperparametric evolution , You must specify one first  cfg perhaps weights
        # opt.save_dir =runs/evolve/exp

    #  Use torch_utils in  select_device()  function   According to  device Value    Make device selection 
    device = select_device(opt.device, batch_size=opt.batch_size)

    # DDP mode
    """
     Distributed training related settings 
     Set up DDP Parameters of the pattern 
    world_size :  Indicates the number of global processes 
    global_rank:  Process number 
    
    """
    if LOCAL_RANK != -1:
        assert torch.cuda.device_count() > LOCAL_RANK, 'insufficient CUDA devices for DDP command'
        assert opt.batch_size % WORLD_SIZE == 0, '--batch-size must be multiple of CUDA device count'
        #  The batch size must be CUDA Integral multiples of the device 

        assert not opt.image_weights, '--image-weights argument is not compatible with DDP training'
        # --image-weights Parameters and DDP Training incompatible 

        assert not opt.evolve, '--evolve argument is not compatible with DDP training'  # -evolve Parameters and DDP Training incompatible 
        torch.cuda.set_device(LOCAL_RANK)
        device = torch.device('cuda', LOCAL_RANK)  #  according to  Gpu Select the equipment with the number of 

        #  Initialize the process array 
        dist.init_process_group(backend="nccl" if dist.is_nccl_available() else "gloo")

    # Train opt.evole  If it is false , That is, there is no hyperparametric evolution 
    if not opt.evolve:

        #  Just start training ...................................................
        train(opt.hyp, opt, device, callbacks)
        if WORLD_SIZE > 1 and RANK == 0:
            LOGGER.info('Destroying process group... ')  #  Destroying process group , start WORLD_SIZE =1 ,RANK by -1 , So I won't execute this sentence 
            dist.destroy_process_group()
        #  That is, hyperparametric evolution cannot be related to   Breakpoint training  ,      If   Turn on hyperparametric evolution , Must learn to specify a first  cfg perhaps weights

        # Evolve hyperparameters (optional),  It began to evolve hyp Parameters in 

        '''
        yolov5 A method of hyperparametric optimization is provided –Hyperparameter Evolution, That is, hyperparametric evolution . Hyperparametric evolution is a kind of utilization   Genetic algorithm (ga) (GA) 
         The method of hyperparametric optimization , We can use this method to select a more appropriate super parameter . The default parameters provided are also passed in COCO Data sets are evolved using hyperparameters .
         Because hyperparametric evolution will consume a lot of resources and time , If the result of default parameter training can meet your use , It is also a good choice to use default parameters .

        '''

    else:  # evolve If it's true, start hyperparametric evolution 
        # hyp Parameter evolution in   An evolutionary list of meta (  Mutation scale   ,     minimum value  ,  Maximum limit          )
        meta = {'lr0': (1, 1e-5, 1e-1),  # initial learning rate (SGD=1E-2, Adam=1E-3)
                'lrf': (1, 0.01, 1.0),  # final OneCycleLR learning rate (lr0 * lrf)
                'momentum': (0.3, 0.6, 0.98),  # SGD momentum/Adam beta1
                'weight_decay': (1, 0.0, 0.001),  # optimizer weight decay
                'warmup_epochs': (1, 0.0, 5.0),  # warmup epochs (fractions ok)
                'warmup_momentum': (1, 0.0, 0.95),  # warmup initial momentum
                'warmup_bias_lr': (1, 0.0, 0.2),  # warmup initial bias lr
                'box': (1, 0.02, 0.2),  # box loss gain
                'cls': (1, 0.2, 4.0),  # cls loss gain
                'cls_pw': (1, 0.5, 2.0),  # cls BCELoss positive_weight
                'obj': (1, 0.2, 4.0),  # obj loss gain (scale with pixels)
                'obj_pw': (1, 0.5, 2.0),  # obj BCELoss positive_weight
                'iou_t': (0, 0.1, 0.7),  # IoU training threshold
                'anchor_t': (1, 2.0, 8.0),  # anchor-multiple threshold
                'anchors': (2, 2.0, 10.0),  # anchors per output grid (0 to ignore)
                'fl_gamma': (0, 0.0, 2.0),  # focal loss gamma (efficientDet default gamma=1.5)
                'hsv_h': (1, 0.0, 0.1),  # image HSV-Hue augmentation (fraction)
                'hsv_s': (1, 0.0, 0.9),  # image HSV-Saturation augmentation (fraction)
                'hsv_v': (1, 0.0, 0.9),  # image HSV-Value augmentation (fraction)
                'degrees': (1, 0.0, 45.0),  # image rotation (+/- deg)
                'translate': (1, 0.0, 0.9),  # image translation (+/- fraction)
                'scale': (1, 0.0, 0.9),  # image scale (+/- gain)
                'shear': (1, 0.0, 10.0),  # image shear (+/- deg)
                'perspective': (0, 0.0, 0.001),  # image perspective (+/- fraction), range 0-0.001
                'flipud': (1, 0.0, 1.0),  # image flip up-down (probability)
                'fliplr': (0, 0.0, 1.0),  # image flip left-right (probability)
                'mosaic': (1, 0.0, 1.0),  # image mosaic (probability)
                'mixup': (1, 0.0, 1.0),  # image mixup (probability)
                'copy_paste': (1, 0.0, 1.0)}  # segment copy-paste (probability)

        #  Open the file of the super parameter 
        with open(opt.hyp, errors='ignore') as f:
            hyp = yaml.safe_load(f)  # load hyps dict ,   Also is to  hyp Document to hyp

            if 'anchors' not in hyp:  # anchors commented in hyp.yaml , That is, how many prior boxes are generated for each feature graph  ,  If anchors be not in , Set to 3
                hyp['anchors'] = 3

        opt.noval, opt.nosave, save_dir = True, True, Path(opt.save_dir)  # only val/save final epoch
        # ei = [isinstance(x, (int, float)) for x in hyp.values()]  # evolvable indices

        #  That is to say  .  After evolution evolve_yaml, evolve_csv Store in ,  opt.save_dir =runs/evolve/exp   Next 
        evolve_yaml, evolve_csv = save_dir / 'hyp_evolve.yaml', save_dir / 'evolve.csv'

        if opt.bucket:  #  If the incoming is  --bucket , Is cloud disk Google 
            os.system(f'gsutil cp gs://{opt.bucket}/evolve.csv {save_dir}')  # download evolve.csv if exists

        #  At this point we already know the original hyperparameter  hyp    and   One   List of hyperparametric evolution rules  meta
        '''
         The evolutionary algorithm here is  :    According to the previous training hyp To identify a base hyp Re evolution mutation :
         The specific methods : 
         Through every previous evolution results To determine each of the previous  hyp The weight 
         With   Every  hyp And each hyp After the weight of   There are two ways of evolution 
        
         The first one is  single:   According to each hyp Select a previous one at random hyp As  base hyp    ,    random.choice(range(n)) , weights=w
        
         The second kind  weighted:  According to each hyp The weight of   Yes, all the previous hyp To merge   To obtain a base hyp  (x*w.reeshape(n,1) ) .sum(0) / w.sum()       
        
        evolve.csv Will record after each evolution results+hyp
         Every time you evolve ,hyp According to the previous result Sort from big to small ;
         According to fitness Each evolution before the function calculation results in hyp The weight of 
         In determining which evolutionary approach single  still weighted , To evolve 
        
        
        '''

        # for _ in range(300) :  How many times has it evolved 
        for _ in range(opt.evolve):  # generations to evolve
            if evolve_csv.exists():  # if evolve.csv exists: select best hyps and mutate , The path of preserving results after evolution exists 

                # Select parent(s)   The parents in genetic algorithm 

                #  Choose the way of evolution  , If it is   Choose the first one single
                parent = 'single'  # parent selection method: 'single' or 'weighted'

                x = np.loadtxt(evolve_csv, ndmin=2, delimiter=',', skiprows=1)  #  load evolve_csv file 
                #    x The value of is (p, R, mAP, F1, test_lossese=(box, obj, cls) ),  After that is hyp Value 

                #  At most... Before 5 The result of sub evolution 
                n = min(5, len(x))  # number of previous results to consider
                x = x[np.argsort(-fitness(x))][:n]  # top n mutations

                #  according to results Calculation hyp The weight of , call metrics.py Medium fitness()
                w = fitness(x) - fitness(x).min() + 1E-6  # weights (sum > 0)

                #  If you choose the first method  single Namely   random.choice(range(n)) , weights=w.............................
                if parent == 'single' or len(x) == 1:
                    x = x[random.choices(range(n), weights=w)[0]]  # weighted selection

                #  If you choose the second way  weighted  Namely    (x*w.reeshape(n,1) ) .sum(0) / w.sum() ..................
                elif parent == 'weighted':
                    x = (x * w.reshape(n, 1)).sum(0) / w.sum()  # weighted combination

                # Mutate, Hyperparametric evolution 
                mp, s = 0.8, 0.2  # mp  It means mutation probability  Mutation probability  ,   s refer to sigma
                npr = np.random
                npr.seed(int(time.time()))

                #  Get the initial value of mutation     meta It's a dictionary   ,  meta[k][0] Is to choose   What kind of parameters 
                g = np.array([meta[k][0] for k in hyp.keys()])  # gains 0-1
                ng = len(meta)
                v = np.ones(ng)

                #  Set mutation 
                while all(v == 1):  # mutate until a change occurs (prevent duplicates)
                    v = (g * (npr.random(ng) < mp) * npr.randn(ng) * npr.random() * s + 1).clip(0.3, 3.0)

                #  Add mutation to  base  hyp On 
                # [i+7]  Because x Middle front 7 Numbers are results Indicators of ( p , R , mAP ,F1 , test_lossese=(box , obj ,cls) ), Then comes the super parameter hyp
                for i, k in enumerate(hyp.keys()):  # plt.hist(v.ravel(), 300)
                    hyp[k] = float(x[i + 7] * v[i])  # mutate

            #  trim hyp Within the specified range 
            # Constrain to limits
            for k, v in meta.items():
                hyp[k] = max(hyp[k], v[1])  # lower limit
                hyp[k] = min(hyp[k], v[2])  # upper limit
                hyp[k] = round(hyp[k], 5)  # significant digits

            # Train mutation. Training 
            results = train(hyp.copy(), opt, device, callbacks)

            # Write mutation results
            #  write in results It's the same as hyp To evolve.csv in  ,   evolve.csv Document the result of each behavior evolution 
            #  The first in a line  7 A digital   by ( p , R , mAP ,F1 , test_lossese=(box , obj ,cls) ) , And then hyp
            #  preservation hyp To yaml In the document 
            print_mutation(results, hyp.copy(), save_dir, opt.bucket)

        # Plot results, call  plots.py Medium plot_evolve() The function draws   Evolution results 
        plot_evolve(evolve_csv)
        #  Hyperparametric evolution is complete   ,   Where the results are saved  ,    Use the best super parameters  , Which is the result of your evolution ,  for example : python train.py --hyp {evolve_yaml}')
        LOGGER.info(f'Hyperparameter evolution finished
'
                    f"Results saved to {colorstr('bold', save_dir)}
"
                    f'Use best hyperparameters example: $ python train.py --hyp {evolve_yaml}')


def run(**kwargs):
    # Usage: import train; train.run(data='coco128.yaml', imgsz=320, weights='yolov5m.pt')
    opt = parse_opt(True)
    for k, v in kwargs.items():
        setattr(opt, k, v)
    main(opt)


if __name__ == "__main__":
    opt = parse_opt()
    main(opt)

'''

python train.py --cfg models/yolov5s.yaml --weights weights/yolov5s.pt --data data/ab.yaml --img 640 --batch 16 --epoch 300  

opt

   weights :   Loaded weight file                  The default is  yolov5s.pt 
       cfg :   The configuration file that stores the model structure          The default is  ''
      data :   Storage training 、 File of test data        The default is  data/coco128.yaml
       hyp :   Training super parameter configuration file            The default is  data/hyps/hyp.scratch.yaml
    epochs :   Total number of workouts                      The default is  300
    
batch-size :   The size of each batch ,  One epochs=  Total training amount /batch-size    The default is  16 
     imgsz :   Enter picture size                     The default is  640

     noval :   No validation , The default is False , That is, if you do not specify this parameter   It will be verified  , But the validation takes place at the last epoch
      rect :   Whether to use rectangular reasoning                             The default is  False
    resume :   Breakpoint training ( Continue training from the last time you interrupted training )      The default is  False
    nosave :   Do not save the model                                   The default is  False

noautoanchor   Do not actively adjust anchor                           The default is  False
    evolve :   Whether to carry out hyperparametric evolution                          The default is  False

     cache :   Whether to cache pictures to memory in advance , To speed up training       The default is  False 
    device :   Training equipment ,cpu / 0 / 0,1,2,3               The default is  ' '
multi-scale    Whether to carry out multi-scale training                          The default is  False
single-cls :   Whether the dataset has only one category                      The default is  False
      adam :   Whether to use adam Optimizer                          The default is  False
   sync-bn :   Whether to use cross card synchronization BN, Only in DDP Use... In mode 
   workers :  dataloader Maximum worker Number                  The default is  8
label-smoothing   Label smoothing operation                             The default is  0.0  
  patience :   Training times will not increase performance more than the specified number of times , Stop training early     The default is 100
                
   project :  The default is  ROOT / 'runs/train'   That is to say, it will be in runs Create under file train Folder 
      name :  The default is  exp                 That is to say, it will be in val Create under folder exp Folder to store the results  
    
    
    bucket :   Google cloud disk bucket                             The default is  False    
  exist-ok :   The default is  False            help='existing project/name ok, do not increment'
      quad :   The default is  False            help='quad dataloader' 
 linear-lr :   The default is  False            help='linear LR'
    freeze :   The default is  [0]              help='Freeze layers: backbone=10, first3=0 1 2'
save-period    The default is  -1,              help='Save checkpoint every x epochs (disabled if < 1)'
local_rank :   The default is  -1,              help='DDP parameter, do not modify'
image-weights  The default is  False            help='use weighted image selection for training'
   
 Weights & Biases arguments
        entity :  The default is  None,         help='W&B: Entity'
upload_dataset :  The default is  False         help='W&B: Upload data, "val" option'
 bbox_interval :  The default is  -1            help='W&B: Set bounding-box image logging interval'
artifact_alias :  The default is  latest        help='W&B: Version of dataset artifact to use'

'''

you did it

原网站

版权声明
本文为[m0_ sixty-seven million four hundred and one thousand six hundr]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/173/202206220315036023.html