【目标检测】YOLOv5多进程/多线程推理加速实验

这篇具有很好参考价值的文章主要介绍了【目标检测】YOLOv5多进程/多线程推理加速实验。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

最近在研究如何让YOLOv5推理得更快,总体看来,主要有以下这些思路:

  • 使用更快的 GPU,即:P100 -> V100 -> A100
  • 多卡GPU推理
  • 减小模型尺寸,即YOLOv5x -> YOLOv5l -> YOLOv5m -> YOLOv5s -> YOLOv5n
  • 进行半精度FP16推理与python detect.py --half
  • 减少–img-size,即 1280 -> 640 -> 320
  • 导出成ONNXOpenVINO格式,获得CPU加速
  • 导出到TensorRT获得GPU加速
  • 批量输入图片进行推理
  • 使用多进程/多线程进行推理

注:使用多卡GPU和多进程/多线程的推理并不会对单张图片推理起到加速作用,只适用于很多张图片一起进行推理的场景。

本篇主要来研究多进程/多线程是否能对YOLOv5算法推理起到加速作用。

实验环境

GPU:RTX2060
torch:1.7.1+cu110
检测图片大小:1920x1080
img-size:1920
使用半精度推理half=True
推理模型:yolov5m.pt

实验过程

先放实验代码(detect.py),根据官方源码进行了小改:

import configparser
import time
from pathlib import Path
import cv2
import torch
import threading
import sys
import multiprocessing as mp
sys.path.append("yolov5")
from models.experimental import attempt_load
from utils.datasets import LoadImages
from utils.general import check_img_size, non_max_suppression, scale_coords
from utils.plots import Annotator, colors
from utils.torch_utils import select_device
from concurrent.futures import ThreadPoolExecutor


Detect_path = 'D:/Data/detect_outputs'  # 检测图片输出路径


def detect(path, model_path, detect_size):
    source = path
    weights = model_path
    imgsz = detect_size
    conf_thres = 0.25
    iou_thres = 0.45
    device = ""
    augment = True
    save_img = True
    save_dir = Path(Detect_path)  # increment run

    device = select_device(device)
    half = device.type != 'cpu'  # half precision only supported on CUDA

    # Load model
    model = attempt_load(weights, map_location=device)  # load FP32 model
    stride = int(model.stride.max())  # model stride
    imgsz = check_img_size(imgsz, s=stride)  # check img_sizef
    if half:
        model.half()  # to FP16

    # Set Dataloader
    vid_path, vid_writer = None, None

    dataset = LoadImages(source, img_size=imgsz, stride=stride)

    # Get names and colors
    names = model.module.names if hasattr(model, 'module') else model.names
    # Run inference
    if device.type != 'cpu':
        model(torch.zeros(1, 3, imgsz, imgsz).to(device).type_as(next(model.parameters())))  # run once

    result_list = []

    for path, img, im0s, vid_cap in dataset:
        # 读取图片传到gpu上
        t1 = time.time()
        img = torch.from_numpy(img).to(device)
        print("read pictures cost time:", time.time() - t1)
        t2 = time.time()
        img = img.half() if half else img.float()  # uint8 to fp16/32
        img /= 255.0  # 0 - 255 to 0.0 - 1.0

        if img.ndimension() == 3:
            img = img.unsqueeze(0)
        print("process pictures cost time:", time.time() - t2)
        # Inference
        pred = model(img, augment=augment)[0]

        # Apply NMS
        pred = non_max_suppression(pred, conf_thres, iou_thres)

        # Process detections
        for i, det in enumerate(pred):  # detections per image
            p, s, im0, frame = path, '', im0s, getattr(dataset, 'frame', 0)
            p = Path(p)  # to Path
            save_path = str(save_dir / p.name)  # img.jpg
            s += '%gx%g ' % img.shape[2:]  # print string
            # print(s)  # 384x640
            s_result = ''  # 输出检测结果
            annotator = Annotator(im0, line_width=3, example=str(names))
            if len(det):
                # Rescale boxes from img_size to im0 size
                det[:, :4] = scale_coords(img.shape[2:], det[:, :4], im0.shape).round()

                # Print results
                for c in det[:, -1].unique():
                    n = (det[:, -1] == c).sum()  # detections per class
                    # s += f"{n} {names[int(c)]}{'s' * (n > 1)}, "  # add to string
                    s += f"{n} {names[int(c)]}, "  # add to string
                    s_result += f"{n} {names[int(c)]} "
                # Write results
                for *xyxy, conf, cls in reversed(det):
                    if save_img:
                        c = int(cls)
                        # label = f'{names[int(cls)]} {conf:.2f}'
                        label = f'{names[int(cls)]}'
                        # print(label)
                        annotator.box_label(xyxy, label, color=colors(c, True))
                    # print(xyxy)

            print(f'{s}')
            # print(f'{s_result}')
            result_list.append(s_result)

            # 将conf对象中的数据写入到文件中
            conf = configparser.ConfigParser()
            cfg_file = open("glovar.cfg", 'w')
            conf.add_section("default")  # 在配置文件中增加一个段
            # 第一个参数是段名,第二个参数是选项名,第三个参数是选项对应的值
            conf.set("default", "process", str(dataset.img_count))
            conf.set("default", "total", str(dataset.nf))
            conf.write(cfg_file)
            cfg_file.close()
            
            im0 = annotator.result()
            # Save results (image with detections)
            t3 = time.time()
            if save_img:
                if dataset.mode == 'image':
                    cv2.imwrite(save_path, im0)
                else:  # 'video' or 'stream'
                    if vid_path != save_path:  # new video
                        vid_path = save_path
                        if isinstance(vid_writer, cv2.VideoWriter):
                            vid_writer.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 = cv2.VideoWriter(save_path, cv2.VideoWriter_fourcc(*'mp4v'), fps, (w, h))
                    vid_writer.write(im0)
            print("write pictures cost time:", time.time() - t3)
    print('Done')


def run(path, model_path, detect_size):
    with torch.no_grad():
        detect(path, model_path, detect_size)

首先进行小批量的图片进行实验,下面输入两张图片进行检测。

原始推理

if __name__ == '__main__':
    s_t = time.time()
    path1 = "D:/Data/image/DJI_0001_00100.jpg"
    path2 = "D:/Data/image/DJI_0001_00530.jpg"
    model_path = "../weights/best.pt"
    detect_size = 1920
    run(path1, model_path, detect_size)
    run(path2, model_path, detect_size)
    print("Tatal Cost Time:", time.time() - s_t)

Tatal Cost Time: 3.496427059173584

线程池推理

开辟两个线程进行推理:

if __name__ == '__main__':
    s_t = time.time()
    pool = ThreadPoolExecutor(max_workers=2)
    path1 = "D:/Data/image/DJI_0001_00100.jpg"
    path2 = "D:/Data/image/DJI_0001_00530.jpg"
    model_path = "../weights/best.pt"
    detect_size = 1920
    pool.submit(run, path1, model_path, detect_size)
    pool.submit(run, path2, model_path, detect_size)
    pool.shutdown(wait=True)
    print("Tatal Cost Time:", time.time() - s_t)

Tatal Cost Time: 3.2433135509490967

开双线程推理和原始推理时间类似,再次验证了python中的”伪多线程”。

进程池推理

开辟两个进程进行推理:

if __name__ == '__main__':
    s_t = time.time()
    pool = mp.Pool(processes=2)
    path1 = "D:/Data/image/DJI_0001_00100.jpg"
    path2 = "D:/Data/image/DJI_0001_00530.jpg"
    model_path = "../weights/best.pt"
    detect_size = 1920
    pool.apply_async(run, (path1, model_path, detect_size,))
    pool.apply_async(run, (path2, model_path, detect_size,))
    pool.close()
    pool.join()
    print("Tatal Cost Time:", time.time() - s_t)

Tatal Cost Time: 6.020772695541382

双进程推理

双进程推理时间竟然是原始推理的两倍,以为是进程池的开销太大,于是换种写法,不使用进程池:

if __name__ == '__main__':
    s_t = time.time()
    path1 = "D:/Data/image/DJI_0001_00100.jpg"
    path2 = "D:/Data/image/DJI_0001_00530.jpg"
    model_path = "../weights/best.pt"
    detect_size = 1920
    p1 = mp.Process(target=run, args=(path1, model_path, detect_size,))
    p2 = mp.Process(target=run, args=(path2, model_path, detect_size,))
    p1.start()
    p2.start()
    p1.join()
    p2.join()
    print("Tatal Cost Time:", time.time() - s_t)

Tatal Cost Time: 6.089479446411133

发现双进程时间仍然较久,说明在数据较少时,进程的开销成本过高,这和我之前做的实验多线程和多进程的效率对比结果相类似。

于是下面将图像数量扩大到300张进行实验。

300pic-原始推理

if __name__ == '__main__':
    s_t = time.time()
    path1 = "D:/Data/image"
    path2 = "D:/Data/image2"
    path3 = "D:/Data/image3"
    model_path = "../weights/best.pt"
    detect_size = 1920
    run(path1, model_path, detect_size)
    run(path2, model_path, detect_size)
    run(path3, model_path, detect_size)
    print("Tatal Cost Time:", time.time() - s_t)

Tatal Cost Time: 62.02898120880127

300pic-多进程推理

if __name__ == '__main__':
    s_t = time.time()
    path1 = "D:/Data/image"
    path2 = "D:/Data/image2"
    path3 = "D:/Data/image3"
    model_path = "../weights/best.pt"
    detect_size = 1920
    p1 = mp.Process(target=run, args=(path1, model_path, detect_size,))
    p2 = mp.Process(target=run, args=(path2, model_path, detect_size,))
    p3 = mp.Process(target=run, args=(path3, model_path, detect_size,))
    p1.start()
    p2.start()
    p3.start()
    p1.join()
    p2.join()
    p3.join()
    print("Tatal Cost Time:", time.time() - s_t)

Tatal Cost Time: 47.85872721672058

和预期一样,当数据量提升上去时,多进程推理的速度逐渐超越原始推理。

总结

本次实验结果如下表所示:

图像处理张数 原始推理(s) 多线程推理(s) 多进程推理(s)
2 3.49 3.24 6.08
300 62.02 / 47.85

值得注意的是,使用多进程推理时,进程间保持独立,这意味着模型需要被重复在GPU上进行创建,因此,可以根据单进程所占显存大小来估算显卡所支持的最大进程数。

后续:在顶配机上进行实验

后面嫖到了组里i9-13700K+RTX4090的顶配主机,再进行实验,结果如下:

图像处理张数 原始推理(s) 多线程推理(s) 多进程推理(s)
2 2.21 2.09 3.92
300 29.23 / 17.61

后记:更正结论

后面觉得之前做的实验有些草率,尽管Python存在GIL的限制,但是在此类IO频繁的场景中,多线程仍然能缓解IO阻塞,从而实现加速,因此选用YOLOv5s模型,在4090上,对不同分辨率的图片进行测试:

输入图像分辨率:1920x1080

图像数量 原始推理(s) 双线程推理(s) 双进程推理(s)
2 1.92 1.85 3.92
100 7.02 4.91 6.52
200 13.07 8.10 9.66

输入图像分辨率:13400x9528文章来源地址https://www.toymoban.com/news/detail-413048.html

图像数量 原始推理(s) 双线程推理(s) 双进程推理(s)
2 6.46 4.99 7.03
100 190.85 119.43 117.12
200 410.95 239.84 239.51

到了这里,关于【目标检测】YOLOv5多进程/多线程推理加速实验的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Jetson nano部署Yolov5目标检测 + Tensor RT加速(超级详细版)

    在工作或学习中我们需要进行部署,下面这篇文章是我亲自部署jetson nano之后做出的总结,包括自己遇到一些报错和踩坑,希望对你们有所帮助 : ) 读卡器 SD卡  小螺丝刀 网线(更改语言需要网络) 烧录镜像就是要把SD卡里的东西给完全清除,好比我们电脑重装系统一样,

    2024年02月13日
    浏览(41)
  • yolov5实现目标检测系统(使用线程池)

    该系统包含以下几个部分: 从摄像头中读取数据 主线程将数据输出到窗口 后台线程完成计算机视觉的目标检测过程,并且将结果共享给主线程 主线程将结果画在输出图像上 下载yolvo5: YOLOv5的源码放在 Github 地址:https://github.com/ultralytics/yolov5 我使用到的是 yolov5s.pt,yolvo5学习

    2024年02月16日
    浏览(41)
  • yolov5-6.0项目部署+自用Pytorch模型转换rknn模型并在RK3568 linux(Debian)平台上使用qt部署使用NPU推理加速摄像头目标识别详细新手教程

    1 我们打开yolov的官网,Tags选择6.0版本 2. 下载该压缩包并解压到工程目录下 3. 我们这里使用pycharm,专门针对python的IDE,用起来非常方便,下载方式就是官网直接下载,用的是社区版 4. 我们需要安装环境,这里我推荐安装Anaconda在电脑上,这是一个非常方便的包管理工具,可

    2024年02月05日
    浏览(68)
  • YOLOv5/YOLOv8改进实战实验:新型***亚像素卷积***优化上采样技术提升目标检测效果(即插即用)

      这是一个用于上采样的子像素卷积(SubPixel Convolution)模块,它是一种常见的图像超分辨率的技术,也可以应用于目标检测模型(如YOLO)的特征图上采样。下面我会分几个部分详细介绍这个模块的原理和在YOLO中的应用:   模块介绍:SubPixelConvolution_s是一个PyTorch模块,

    2024年02月15日
    浏览(50)
  • 香橙派5使用NPU加速yolov5的实时视频推理(二)

            这一步就需要我们进入到Ubuntu20.04系统中了,我的Ubuntu系统中已经下载好了anaconda,使用anaconda的好处就是可以方便的安装一些库,而且还可以利用conda来配置虚拟环境,做到环境与环境之间相互独立。         对于我来说,使用了以下命令创建了一个名为rknn_cesh

    2024年02月02日
    浏览(53)
  • 香橙派5使用NPU加速yolov5的实时视频推理(一)

            寒假里,博主完成了树莓派4B搭载yolofastest-V2的ncnn加速,效果挺不错的,但总感觉还是稍微差点意思,于是就购买了一块香橙派5,想要用RK3588芯片自带的NPU来加速深度学习的部署,在2023年3月4日也是完成了香橙派5的NPU加速深度学习部分,其效果也确实非常可观,在画

    2024年02月02日
    浏览(66)
  • Intel N100工控机使用核显加速推理yolov5模型

    今年3月初开始,某平台开始陆续上货基于英特尔Alder Lake-N处理器系列的迷你主机。最先出现的是N95和N100两款处理器,迷你主机的整机价格已经打到800元左右的水平了,还是有挺高可玩性的。其中N100的规格如下: 这个cpu性能虽然不是很强,性能接近4代i5移动端,但功耗很低,

    2024年02月14日
    浏览(63)
  • YOLOV9目标检测-训练、验证、推理

    目录 一、模型介绍 1.1摘要 1.2模型概要 1.2.1Programmable Gradient Information (1)Auxiliary Reversible Branch (2)Multi-level Auxiliary Information 1.2.2Generalized ELAN 二、环境配置 三、数据集准备 四、预训练权重下载 五、训练 六、模型评估 ​七、模型推理 论文:yolov9 模型:yolov9 今天的深度学习方法

    2024年04月27日
    浏览(31)
  • Yolov5对本地视频进行推理时,实现跳帧检测,提高推理效率

    今天在使用Yolov5的detect.py对本地视频进行推理时,发现推理速度受硬件性能影响比较大,为提高检测效率,在部分没必要逐帧检测的情况下,可以考虑设置跳帧检测。 对detect.py观察了一番之后,发现视频读取靠的是dataloaders.py库,于是继续观察。 最终得出了以下解决方案:

    2024年02月12日
    浏览(64)
  • 【目标检测实验系列】YOLOv5模型改进:融入坐标注意力机制CA,多维度关注数据特征,高效涨点!(内含源代码,超详细改进代码流程)

            自我介绍:本人硕士期间全程放养,目前成果:一篇北大核心CSCD录用,两篇中科院三区已见刊,一篇中科院四区在投。如何找创新点,如何放养过程厚积薄发,如何写中英论文,找期刊等等。本人后续会以自己实战经验详细写出来,还请大家能够点个关注和赞,收藏一

    2024年01月20日
    浏览(82)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包