Team Ai
Apppublic

Aniaaaa/code_site

sourceHugging Faceupdated 4y agoView on Hugging Face
0likes
val.py395 linesDownload Raw Back to root
1# YOLOv5 🚀 by Ultralytics, GPL-3.0 license2"""3Validate a trained YOLOv5 model accuracy on a custom dataset4 5Usage:6    $ python path/to/val.py --weights yolov5s.pt --data coco128.yaml --img 6407 8Usage - formats:9    $ python path/to/val.py --weights yolov5s.pt                 # PyTorch10                                      yolov5s.torchscript        # TorchScript11                                      yolov5s.onnx               # ONNX Runtime or OpenCV DNN with --dnn12                                      yolov5s.xml                # OpenVINO13                                      yolov5s.engine             # TensorRT14                                      yolov5s.mlmodel            # CoreML (macOS-only)15                                      yolov5s_saved_model        # TensorFlow SavedModel16                                      yolov5s.pb                 # TensorFlow GraphDef17                                      yolov5s.tflite             # TensorFlow Lite18                                      yolov5s_edgetpu.tflite     # TensorFlow Edge TPU19"""20 21import argparse22import json23import os24import sys25from pathlib import Path26 27import numpy as np28import torch29from tqdm import tqdm30 31FILE = Path(__file__).resolve()32ROOT = FILE.parents[0]  # YOLOv5 root directory33if str(ROOT) not in sys.path:34    sys.path.append(str(ROOT))  # add ROOT to PATH35ROOT = Path(os.path.relpath(ROOT, Path.cwd()))  # relative36 37from models.common import DetectMultiBackend38from utils.callbacks import Callbacks39from utils.dataloaders import create_dataloader40from utils.general import (LOGGER, check_dataset, check_img_size, check_requirements, check_yaml,41                           coco80_to_coco91_class, colorstr, emojis, increment_path, non_max_suppression, print_args,42                           scale_coords, xywh2xyxy, xyxy2xywh)43from utils.metrics import ConfusionMatrix, ap_per_class, box_iou44from utils.plots import output_to_target, plot_images, plot_val_study45from utils.torch_utils import select_device, time_sync46 47 48def save_one_txt(predn, save_conf, shape, file):49    # Save one txt result50    gn = torch.tensor(shape)[[1, 0, 1, 0]]  # normalization gain whwh51    for *xyxy, conf, cls in predn.tolist():52        xywh = (xyxy2xywh(torch.tensor(xyxy).view(1, 4)) / gn).view(-1).tolist()  # normalized xywh53        line = (cls, *xywh, conf) if save_conf else (cls, *xywh)  # label format54        with open(file, 'a') as f:55            f.write(('%g ' * len(line)).rstrip() % line + '\n')56 57 58def save_one_json(predn, jdict, path, class_map):59    # Save one JSON result {"image_id": 42, "category_id": 18, "bbox": [258.15, 41.29, 348.26, 243.78], "score": 0.236}60    image_id = int(path.stem) if path.stem.isnumeric() else path.stem61    box = xyxy2xywh(predn[:, :4])  # xywh62    box[:, :2] -= box[:, 2:] / 2  # xy center to top-left corner63    for p, b in zip(predn.tolist(), box.tolist()):64        jdict.append({65            'image_id': image_id,66            'category_id': class_map[int(p[5])],67            'bbox': [round(x, 3) for x in b],68            'score': round(p[4], 5)})69 70 71def process_batch(detections, labels, iouv):72    """73    Return correct predictions matrix. Both sets of boxes are in (x1, y1, x2, y2) format.74    Arguments:75        detections (Array[N, 6]), x1, y1, x2, y2, conf, class76        labels (Array[M, 5]), class, x1, y1, x2, y277    Returns:78        correct (Array[N, 10]), for 10 IoU levels79    """80    correct = np.zeros((detections.shape[0], iouv.shape[0])).astype(bool)81    iou = box_iou(labels[:, 1:], detections[:, :4])82    correct_class = labels[:, 0:1] == detections[:, 5]83    for i in range(len(iouv)):84        x = torch.where((iou >= iouv[i]) & correct_class)  # IoU > threshold and classes match85        if x[0].shape[0]:86            matches = torch.cat((torch.stack(x, 1), iou[x[0], x[1]][:, None]), 1).cpu().numpy()  # [label, detect, iou]87            if x[0].shape[0] > 1:88                matches = matches[matches[:, 2].argsort()[::-1]]89                matches = matches[np.unique(matches[:, 1], return_index=True)[1]]90                # matches = matches[matches[:, 2].argsort()[::-1]]91                matches = matches[np.unique(matches[:, 0], return_index=True)[1]]92            correct[matches[:, 1].astype(int), i] = True93    return torch.tensor(correct, dtype=torch.bool, device=iouv.device)94 95 96@torch.no_grad()97def run(98        data,99        weights=None,  # model.pt path(s)100        batch_size=32,  # batch size101        imgsz=640,  # inference size (pixels)102        conf_thres=0.001,  # confidence threshold103        iou_thres=0.6,  # NMS IoU threshold104        task='val',  # train, val, test, speed or study105        device='',  # cuda device, i.e. 0 or 0,1,2,3 or cpu106        workers=8,  # max dataloader workers (per RANK in DDP mode)107        single_cls=False,  # treat as single-class dataset108        augment=False,  # augmented inference109        verbose=False,  # verbose output110        save_txt=False,  # save results to *.txt111        save_hybrid=False,  # save label+prediction hybrid results to *.txt112        save_conf=False,  # save confidences in --save-txt labels113        save_json=False,  # save a COCO-JSON results file114        project=ROOT / 'runs/val',  # save to project/name115        name='exp',  # save to project/name116        exist_ok=False,  # existing project/name ok, do not increment117        half=True,  # use FP16 half-precision inference118        dnn=False,  # use OpenCV DNN for ONNX inference119        model=None,120        dataloader=None,121        save_dir=Path(''),122        plots=True,123        callbacks=Callbacks(),124        compute_loss=None,125):126    # Initialize/load model and set device127    training = model is not None128    if training:  # called by train.py129        device, pt, jit, engine = next(model.parameters()).device, True, False, False  # get model device, PyTorch model130        half &= device.type != 'cpu'  # half precision only supported on CUDA131        model.half() if half else model.float()132    else:  # called directly133        device = select_device(device, batch_size=batch_size)134 135        # Directories136        save_dir = increment_path(Path(project) / name, exist_ok=exist_ok)  # increment run137        (save_dir / 'labels' if save_txt else save_dir).mkdir(parents=True, exist_ok=True)  # make dir138 139        # Load model140        model = DetectMultiBackend(weights, device=device, dnn=dnn, data=data, fp16=half)141        stride, pt, jit, engine = model.stride, model.pt, model.jit, model.engine142        imgsz = check_img_size(imgsz, s=stride)  # check image size143        half = model.fp16  # FP16 supported on limited backends with CUDA144        if engine:145            batch_size = model.batch_size146        else:147            device = model.device148            if not (pt or jit):149                batch_size = 1  # export.py models default to batch-size 1150                LOGGER.info(f'Forcing --batch-size 1 square inference (1,3,{imgsz},{imgsz}) for non-PyTorch models')151 152        # Data153        data = check_dataset(data)  # check154 155    # Configure156    model.eval()157    cuda = device.type != 'cpu'158    is_coco = isinstance(data.get('val'), str) and data['val'].endswith(f'coco{os.sep}val2017.txt')  # COCO dataset159    nc = 1 if single_cls else int(data['nc'])  # number of classes160    iouv = torch.linspace(0.5, 0.95, 10, device=device)  # iou vector for mAP@0.5:0.95161    niou = iouv.numel()162 163    # Dataloader164    if not training:165        if pt and not single_cls:  # check --weights are trained on --data166            ncm = model.model.nc167            assert ncm == nc, f'{weights[0]} ({ncm} classes) trained on different --data than what you passed ({nc} ' \168                              f'classes). Pass correct combination of --weights and --data that are trained together.'169        model.warmup(imgsz=(1 if pt else batch_size, 3, imgsz, imgsz))  # warmup170        pad = 0.0 if task in ('speed', 'benchmark') else 0.5171        rect = False if task == 'benchmark' else pt  # square inference for benchmarks172        task = task if task in ('train', 'val', 'test') else 'val'  # path to train/val/test images173        dataloader = create_dataloader(data[task],174                                       imgsz,175                                       batch_size,176                                       stride,177                                       single_cls,178                                       pad=pad,179                                       rect=rect,180                                       workers=workers,181                                       prefix=colorstr(f'{task}: '))[0]182 183    seen = 0184    confusion_matrix = ConfusionMatrix(nc=nc)185    names = {k: v for k, v in enumerate(model.names if hasattr(model, 'names') else model.module.names)}186    class_map = coco80_to_coco91_class() if is_coco else list(range(1000))187    s = ('%20s' + '%11s' * 6) % ('Class', 'Images', 'Labels', 'P', 'R', 'mAP@.5', 'mAP@.5:.95')188    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.0189    loss = torch.zeros(3, device=device)190    jdict, stats, ap, ap_class = [], [], [], []191    callbacks.run('on_val_start')192    pbar = tqdm(dataloader, desc=s, bar_format='{l_bar}{bar:10}{r_bar}{bar:-10b}')  # progress bar193    for batch_i, (im, targets, paths, shapes) in enumerate(pbar):194        callbacks.run('on_val_batch_start')195        t1 = time_sync()196        if cuda:197            im = im.to(device, non_blocking=True)198            targets = targets.to(device)199        im = im.half() if half else im.float()  # uint8 to fp16/32200        im /= 255  # 0 - 255 to 0.0 - 1.0201        nb, _, height, width = im.shape  # batch size, channels, height, width202        t2 = time_sync()203        dt[0] += t2 - t1204 205        # Inference206        out, train_out = model(im) if training else model(im, augment=augment, val=True)  # inference, loss outputs207        dt[1] += time_sync() - t2208 209        # Loss210        if compute_loss:211            loss += compute_loss([x.float() for x in train_out], targets)[1]  # box, obj, cls212 213        # NMS214        targets[:, 2:] *= torch.tensor((width, height, width, height), device=device)  # to pixels215        lb = [targets[targets[:, 0] == i, 1:] for i in range(nb)] if save_hybrid else []  # for autolabelling216        t3 = time_sync()217        out = non_max_suppression(out, conf_thres, iou_thres, labels=lb, multi_label=True, agnostic=single_cls)218        dt[2] += time_sync() - t3219 220        # Metrics221        for si, pred in enumerate(out):222            labels = targets[targets[:, 0] == si, 1:]223            nl, npr = labels.shape[0], pred.shape[0]  # number of labels, predictions224            path, shape = Path(paths[si]), shapes[si][0]225            correct = torch.zeros(npr, niou, dtype=torch.bool, device=device)  # init226            seen += 1227 228            if npr == 0:229                if nl:230                    stats.append((correct, *torch.zeros((3, 0), device=device)))231                continue232 233            # Predictions234            if single_cls:235                pred[:, 5] = 0236            predn = pred.clone()237            scale_coords(im[si].shape[1:], predn[:, :4], shape, shapes[si][1])  # native-space pred238 239            # Evaluate240            if nl:241                tbox = xywh2xyxy(labels[:, 1:5])  # target boxes242                scale_coords(im[si].shape[1:], tbox, shape, shapes[si][1])  # native-space labels243                labelsn = torch.cat((labels[:, 0:1], tbox), 1)  # native-space labels244                correct = process_batch(predn, labelsn, iouv)245                if plots:246                    confusion_matrix.process_batch(predn, labelsn)247            stats.append((correct, pred[:, 4], pred[:, 5], labels[:, 0]))  # (correct, conf, pcls, tcls)248 249            # Save/log250            if save_txt:251                save_one_txt(predn, save_conf, shape, file=save_dir / 'labels' / (path.stem + '.txt'))252            if save_json:253                save_one_json(predn, jdict, path, class_map)  # append to COCO-JSON dictionary254            callbacks.run('on_val_image_end', pred, predn, path, names, im[si])255 256        # Plot images257        if plots and batch_i < 3:258            plot_images(im, targets, paths, save_dir / f'val_batch{batch_i}_labels.jpg', names)  # labels259            plot_images(im, output_to_target(out), paths, save_dir / f'val_batch{batch_i}_pred.jpg', names)  # pred260 261        callbacks.run('on_val_batch_end')262 263    # Compute metrics264    stats = [torch.cat(x, 0).cpu().numpy() for x in zip(*stats)]  # to numpy265    if len(stats) and stats[0].any():266        tp, fp, p, r, f1, ap, ap_class = ap_per_class(*stats, plot=plots, save_dir=save_dir, names=names)267        ap50, ap = ap[:, 0], ap.mean(1)  # AP@0.5, AP@0.5:0.95268        mp, mr, map50, map = p.mean(), r.mean(), ap50.mean(), ap.mean()269        nt = np.bincount(stats[3].astype(int), minlength=nc)  # number of targets per class270    else:271        nt = torch.zeros(1)272 273    # Print results274    pf = '%20s' + '%11i' * 2 + '%11.3g' * 4  # print format275    LOGGER.info(pf % ('all', seen, nt.sum(), mp, mr, map50, map))276 277    # Print results per class278    if (verbose or (nc < 50 and not training)) and nc > 1 and len(stats):279        for i, c in enumerate(ap_class):280            LOGGER.info(pf % (names[c], seen, nt[c], p[i], r[i], ap50[i], ap[i]))281 282    # Print speeds283    t = tuple(x / seen * 1E3 for x in dt)  # speeds per image284    if not training:285        shape = (batch_size, 3, imgsz, imgsz)286        LOGGER.info(f'Speed: %.1fms pre-process, %.1fms inference, %.1fms NMS per image at shape {shape}' % t)287 288    # Plots289    if plots:290        confusion_matrix.plot(save_dir=save_dir, names=list(names.values()))291        callbacks.run('on_val_end')292 293    # Save JSON294    if save_json and len(jdict):295        w = Path(weights[0] if isinstance(weights, list) else weights).stem if weights is not None else ''  # weights296        anno_json = str(Path(data.get('path', '../coco')) / 'annotations/instances_val2017.json')  # annotations json297        pred_json = str(save_dir / f"{w}_predictions.json")  # predictions json298        LOGGER.info(f'\nEvaluating pycocotools mAP... saving {pred_json}...')299        with open(pred_json, 'w') as f:300            json.dump(jdict, f)301 302        try:  # https://github.com/cocodataset/cocoapi/blob/master/PythonAPI/pycocoEvalDemo.ipynb303            check_requirements(['pycocotools'])304            from pycocotools.coco import COCO305            from pycocotools.cocoeval import COCOeval306 307            anno = COCO(anno_json)  # init annotations api308            pred = anno.loadRes(pred_json)  # init predictions api309            eval = COCOeval(anno, pred, 'bbox')310            if is_coco:311                eval.params.imgIds = [int(Path(x).stem) for x in dataloader.dataset.im_files]  # image IDs to evaluate312            eval.evaluate()313            eval.accumulate()314            eval.summarize()315            map, map50 = eval.stats[:2]  # update results (mAP@0.5:0.95, mAP@0.5)316        except Exception as e:317            LOGGER.info(f'pycocotools unable to run: {e}')318 319    # Return results320    model.float()  # for training321    if not training:322        s = f"\n{len(list(save_dir.glob('labels/*.txt')))} labels saved to {save_dir / 'labels'}" if save_txt else ''323        LOGGER.info(f"Results saved to {colorstr('bold', save_dir)}{s}")324    maps = np.zeros(nc) + map325    for i, c in enumerate(ap_class):326        maps[c] = ap[i]327    return (mp, mr, map50, map, *(loss.cpu() / len(dataloader)).tolist()), maps, t328 329 330def parse_opt():331    parser = argparse.ArgumentParser()332    parser.add_argument('--data', type=str, default=ROOT / 'data/coco128.yaml', help='dataset.yaml path')333    parser.add_argument('--weights', nargs='+', type=str, default=ROOT / 'yolov5s.pt', help='model.pt path(s)')334    parser.add_argument('--batch-size', type=int, default=32, help='batch size')335    parser.add_argument('--imgsz', '--img', '--img-size', type=int, default=640, help='inference size (pixels)')336    parser.add_argument('--conf-thres', type=float, default=0.001, help='confidence threshold')337    parser.add_argument('--iou-thres', type=float, default=0.6, help='NMS IoU threshold')338    parser.add_argument('--task', default='val', help='train, val, test, speed or study')339    parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')340    parser.add_argument('--workers', type=int, default=8, help='max dataloader workers (per RANK in DDP mode)')341    parser.add_argument('--single-cls', action='store_true', help='treat as single-class dataset')342    parser.add_argument('--augment', action='store_true', help='augmented inference')343    parser.add_argument('--verbose', action='store_true', help='report mAP by class')344    parser.add_argument('--save-txt', action='store_true', help='save results to *.txt')345    parser.add_argument('--save-hybrid', action='store_true', help='save label+prediction hybrid results to *.txt')346    parser.add_argument('--save-conf', action='store_true', help='save confidences in --save-txt labels')347    parser.add_argument('--save-json', action='store_true', help='save a COCO-JSON results file')348    parser.add_argument('--project', default=ROOT / 'runs/val', help='save to project/name')349    parser.add_argument('--name', default='exp', help='save to project/name')350    parser.add_argument('--exist-ok', action='store_true', help='existing project/name ok, do not increment')351    parser.add_argument('--half', action='store_true', help='use FP16 half-precision inference')352    parser.add_argument('--dnn', action='store_true', help='use OpenCV DNN for ONNX inference')353    opt = parser.parse_args()354    opt.data = check_yaml(opt.data)  # check YAML355    opt.save_json |= opt.data.endswith('coco.yaml')356    opt.save_txt |= opt.save_hybrid357    print_args(vars(opt))358    return opt359 360 361def main(opt):362    check_requirements(requirements=ROOT / 'requirements.txt', exclude=('tensorboard', 'thop'))363 364    if opt.task in ('train', 'val', 'test'):  # run normally365        if opt.conf_thres > 0.001:  # https://github.com/ultralytics/yolov5/issues/1466366            LOGGER.info(emojis(f'WARNING: confidence threshold {opt.conf_thres} > 0.001 produces invalid results ⚠️'))367        run(**vars(opt))368 369    else:370        weights = opt.weights if isinstance(opt.weights, list) else [opt.weights]371        opt.half = True  # FP16 for fastest results372        if opt.task == 'speed':  # speed benchmarks373            # python val.py --task speed --data coco.yaml --batch 1 --weights yolov5n.pt yolov5s.pt...374            opt.conf_thres, opt.iou_thres, opt.save_json = 0.25, 0.45, False375            for opt.weights in weights:376                run(**vars(opt), plots=False)377 378        elif opt.task == 'study':  # speed vs mAP benchmarks379            # python val.py --task study --data coco.yaml --iou 0.7 --weights yolov5n.pt yolov5s.pt...380            for opt.weights in weights:381                f = f'study_{Path(opt.data).stem}_{Path(opt.weights).stem}.txt'  # filename to save to382                x, y = list(range(256, 1536 + 128, 128)), []  # x axis (image sizes), y axis383                for opt.imgsz in x:  # img-size384                    LOGGER.info(f'\nRunning {f} --imgsz {opt.imgsz}...')385                    r, _, t = run(**vars(opt), plots=False)386                    y.append(r + t)  # results and times387                np.savetxt(f, y, fmt='%10.4g')  # save388            os.system('zip -r study.zip study_*.txt')389            plot_val_study(x=x)  # plot390 391 392if __name__ == "__main__":393    opt = parse_opt()394    main(opt)395