YOLOV5代码精读之export.py

一、main函数

def main(opt):
    for opt.weights in (opt.weights if isinstance(opt.weights, list) else [opt.weights]):
        run(**vars(opt))


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

这段代码是一个Python程序的主体部分,主要用于处理命令行参数,并调用运行函数 run。下面是对代码的逐步分解和详细解释:

  1. def main(opt):

    • 定义了一个名为 main 的函数,接收一个参数 optopt 是一个包含用户输入选项的对象(一般是在命令行中传递的参数)。
  2. for opt.weights in (opt.weights if isinstance(opt.weights, list) else [opt.weights]):

    • 这行代码是一个循环,目的是处理 opt.weights
    • opt.weights 可能是一个列表,也可能是一个单一的值:
      • 如果 opt.weights 是一个列表(即模型权重文件的列表),则直接使用这个列表。
      • 如果 opt.weights 不是列表(即只有一个权重文件),则将其转换为包含单个元素的列表形式 [opt.weights]
    • 这样处理的目的是确保在后续的操作中,无论用户输入的是单个权重文件还是多个权重文件,都可以统一处理。
  3. run(**vars(opt))

    • 这里调用了之前定义的 run 函数,并用 **vars(opt) 将 opt 对象的所有属性转换为关键字参数传递给 run
    • vars(opt) 返回一个字典,包含 opt 对象的所有属性及其值,** 运算符用于将字典解包为关键字参数。
  4. if __name__ == "__main__":

    • 这是Python约定俗成的代码块,用来判断当前模块是否是主程序(即直接运行的模块)。
    • 如果是主程序,那么执行以下代码。
  5. opt = parse_opt()

    • 调用 parse_opt 函数,这个函数的作用是解析命令行传入的参数,并返回一个包含这些参数的对象,通常是用来获取用户输入的设置。
  6. main(opt)

    • 调用 main 函数,并将 parse_opt 返回的参数传递给它。

这段代码的主要功能是处理用户通过命令行传递的参数,并启动程序的主要运行逻辑。它确保支持单个或多个模型权重文件,并将所有解析后的参数传递给后续的 run 函数进行处理。整体上,这段代码是程序入口的标准结构,负责管理参数解析和功能调用。

 二、parse_opt函数

def parse_opt():
    parser = argparse.ArgumentParser()
    parser.add_argument('--data', type=str, default=ROOT / 'data/qd.yaml', help='dataset.yaml path')
    parser.add_argument('--weights', nargs='+', type=str, default=ROOT / 'yolov5s.pt', help='model.pt path(s)')
    parser.add_argument('--imgsz', '--img', '--img-size', nargs='+', type=int, default=[640, 640], help='image (h, w)')
    parser.add_argument('--batch-size', type=int, default=1, help='batch size')
    parser.add_argument('--device', default='cpu', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')
    parser.add_argument('--half', action='store_true', help='FP16 half-precision export')
    parser.add_argument('--inplace', action='store_true', help='set YOLOv5 Detect() inplace=True')
    parser.add_argument('--keras', action='store_true', help='TF: use Keras')
    parser.add_argument('--optimize', action='store_true', help='TorchScript: optimize for mobile')
    parser.add_argument('--int8', action='store_true', help='CoreML/TF INT8 quantization')
    parser.add_argument('--dynamic', action='store_true', help='ONNX/TF/TensorRT: dynamic axes')
    parser.add_argument('--simplify', action='store_true', help='ONNX: simplify model')
    parser.add_argument('--opset', type=int, default=12, help='ONNX: opset version')
    parser.add_argument('--verbose', action='store_true', help='TensorRT: verbose log')
    parser.add_argument('--workspace', type=int, default=4, help='TensorRT: workspace size (GB)')
    parser.add_argument('--nms', action='store_true', help='TF: add NMS to model')
    parser.add_argument('--agnostic-nms', action='store_true', help='TF: add agnostic NMS to model')
    parser.add_argument('--topk-per-class', type=int, default=100, help='TF.js NMS: topk per class to keep')
    parser.add_argument('--topk-all', type=int, default=100, help='TF.js NMS: topk for all classes to keep')
    parser.add_argument('--iou-thres', type=float, default=0.45, help='TF.js NMS: IoU threshold')
    parser.add_argument('--conf-thres', type=float, default=0.25, help='TF.js NMS: confidence threshold')
    parser.add_argument(
        '--include',
        nargs='+',
        default=['onnx'],
        help='torchscript, onnx, openvino, engine, coreml, saved_model, pb, tflite, edgetpu, tfjs, paddle')
    opt = parser.parse_args()
    print_args(vars(opt))
    return opt

这段代码定义了一个名为 parse_opt 的函数,其主要功能是解析命令行参数并返回这些参数的值。以下是对代码逐步分解和详细解释:

  1. 引入 argparse 模块

    parser = argparse.ArgumentParser()
    

    这行代码创建了一个参数解析器的实例,argparse.ArgumentParser() 是用于处理命令行参数的标准模块。

  2. 定义参数: 下面的几行代码使用 add_argument 方法为解析器添加各种参数。这些参数在程序执行时会被命令行传递:

    • --data:指定数据集的 YAML 文件路径,类型为字符串,默认为 ROOT / 'data/qd.yaml'
    • --weights:指定模型权重文件路径,可以同时接受多个文件,类型为字符串,默认为 ROOT / 'yolov5s.pt'
    • --imgsz--img--img-size:指定图像的高度和宽度,接受多个整数,默认为 [640, 640]
    • --batch-size:指定每批次的样本数量,类型为整数,默认为 1
    • --device:指定使用的设备,默认为 'cpu'
    • --half:一个布尔值,设置为 True 则表示导出时使用 FP16 半精度。
    • --inplace:设置 YOLOv5 的检测模块是否在原地运行。
    • --keras:使用 Keras 进行 TensorFlow 的导出。
    • --optimize:优化 TorchScript 模型以适应移动设备。
    • --int8:表示是否进行 CoreML/TF 的 INT8 量化。
    • --dynamic:设置 ONNX/TensorFlow/TensorRT 的动态轴。
    • --simplify:简化 ONNX 模型。
    • --opset:指定 ONNX 的操作集版本,默认为 12
    • --verbose:在 TensorRT 中启用详细日志。
    • --workspace:指定 TensorRT 的工作区大小,默认为 4GB
    • --nms:添加非最大抑制 (NMS) 到模型。
    • --agnostic-nms:添加无关类别的 NMS。
    • --topk-per-class:在 TF.js 中,每个类别保留的最大数量,默认为 100
    • --topk-all:在 TF.js 中,所有类别保留的最大数量,默认为 100
    • --iou-thres:在 TF.js 中,IoU 阈值,默认为 0.45
    • --conf-thres:在 TF.js 中,置信度阈值,默认为 0.25
    • --include:指定要导出的格式,默认包括 onnx
  3. 解析参数

    opt = parser.parse_args()
    

    这行代码解析命令行参数,并将解析结果存储在 opt 变量中。

  4. 打印参数

    print_args(vars(opt))
    

    使用 print_args 函数打印参数的字典表示形式,vars(opt) 将 opt 对象转换为字典,显示所有参数及其值。

  5. 返回参数

    return opt
    

    最后,返回包含所有解析参数的对象 opt

parse_opt 函数的主要功能是解析命令行传入的各种参数,这些参数用于控制模型导出的特性和行为。通过定义多种可能的参数选项,用户能够灵活地配置程序,选择数据集、权重文件、设备类型、图像大小、批处理大小等。此外,还能调节导出时的精度、优化选项和输出格式。最终,返回的参数将被用于后续的模型导出过程。

 三、run函数

def run(
        data=ROOT / 'data/coco128.yaml',  # 'dataset.yaml path'
        weights=ROOT / 'yolov5s.pt',  # weights path
        imgsz=(640, 640),  # image (height, width)
        batch_size=1,  # batch size
        device='cpu',  # cuda device, i.e. 0 or 0,1,2,3 or cpu
        include=('torchscript', 'onnx'),  # include formats
        half=False,  # FP16 half-precision export
        inplace=False,  # set YOLOv5 Detect() inplace=True
        keras=False,  # use Keras
        optimize=False,  # TorchScript: optimize for mobile
        int8=False,  # CoreML/TF INT8 quantization
        dynamic=False,  # ONNX/TF/TensorRT: dynamic axes
        simplify=False,  # ONNX: simplify model
        opset=12,  # ONNX: opset version
        verbose=False,  # TensorRT: verbose log
        workspace=4,  # TensorRT: workspace size (GB)
        nms=False,  # TF: add NMS to model
        agnostic_nms=False,  # TF: add agnostic NMS to model
        topk_per_class=100,  # TF.js NMS: topk per class to keep
        topk_all=100,  # TF.js NMS: topk for all classes to keep
        iou_thres=0.45,  # TF.js NMS: IoU threshold
        conf_thres=0.25,  # TF.js NMS: confidence threshold
):
    t = time.time()
    include = [x.lower() for x in include]  # to lowercase
    fmts = tuple(export_formats()['Argument'][1:])  # --include arguments
    flags = [x in include for x in fmts]
    assert sum(flags) == len(include), f'ERROR: Invalid --include {include}, valid --include arguments are {fmts}'
    jit, onnx, xml, engine, coreml, saved_model, pb, tflite, edgetpu, tfjs, paddle = flags  # export booleans
    file = Path(url2file(weights) if str(weights).startswith(('http:/', 'https:/')) else weights)  # PyTorch weights

    # Load PyTorch model
    device = select_device(device)
    if half:
        assert device.type != 'cpu' or coreml, '--half only compatible with GPU export, i.e. use --device 0'
        assert not dynamic, '--half not compatible with --dynamic, i.e. use either --half or --dynamic but not both'
    model = attempt_load(weights, device=device, inplace=True, fuse=True)  # load FP32 model

    # Checks
    imgsz *= 2 if len(imgsz) == 1 else 1  # expand
    if optimize:
        assert device.type == 'cpu', '--optimize not compatible with cuda devices, i.e. use --device cpu'

    # Input
    gs = int(max(model.stride))  # grid size (max stride)
    imgsz = [check_img_size(x, gs) for x in imgsz]  # verify img_size are gs-multiples
    im = torch.zeros(batch_size, 3, *imgsz).to(device)  # image size(1,3,320,192) BCHW iDetection

    # Update model
    model.eval()
    for k, m in model.named_modules():
        if isinstance(m, Detect):
            m.inplace = inplace
            m.dynamic = dynamic
            m.export = True

    for _ in range(2):
        y = model(im)  # dry runs
    if half and not coreml:
        im, model = im.half(), model.half()  # to FP16
    shape = tuple((y[0] if isinstance(y, tuple) else y).shape)  # model output shape
    metadata = {'stride': int(max(model.stride)), 'names': model.names}  # model metadata
    LOGGER.info(f"\n{colorstr('PyTorch:')} starting from {file} with output shape {shape} ({file_size(file):.1f} MB)")

    # Exports
    f = [''] * len(fmts)  # exported filenames
    warnings.filterwarnings(action='ignore', category=torch.jit.TracerWarning)  # suppress TracerWarning
    if jit:  # TorchScript
        f[0], _ = export_torchscript(model, im, file, optimize)
    if engine:  # TensorRT required before ONNX
        f[1], _ = export_engine(model, im, file, half, dynamic, simplify, workspace, verbose)
    if onnx or xml:  # OpenVINO requires ONNX
        f[2], _ = export_onnx(model, im, file, opset, dynamic, simplify)
    if xml:  # OpenVINO
        f[3], _ = export_openvino(file, metadata, half)
    if coreml:  # CoreML
        f[4], _ = export_coreml(model, im, file, int8, half)
    if any((saved_model, pb, tflite, edgetpu, tfjs)):  # TensorFlow formats
        assert not tflite or not tfjs, 'TFLite and TF.js models must be exported separately, please pass only one type.'
        assert not isinstance(model, ClassificationModel), 'ClassificationModel export to TF formats not yet supported.'
        f[5], s_model = export_saved_model(model.cpu(),
                                           im,
                                           file,
                                           dynamic,
                                           tf_nms=nms or agnostic_nms or tfjs,
                                           agnostic_nms=agnostic_nms or tfjs,
                                           topk_per_class=topk_per_class,
                                           topk_all=topk_all,
                                           iou_thres=iou_thres,
                                           conf_thres=conf_thres,
                                           keras=keras)
        if pb or tfjs:  # pb prerequisite to tfjs
            f[6], _ = export_pb(s_model, file)
        if tflite or edgetpu:
            f[7], _ = export_tflite(s_model, im, file, int8 or edgetpu, data=data, nms=nms, agnostic_nms=agnostic_nms)
            if edgetpu:
                f[8], _ = export_edgetpu(file)
            add_tflite_metadata(f[8] or f[7], metadata, num_outputs=len(s_model.outputs))
        if tfjs:
            f[9], _ = export_tfjs(file)
    if paddle:  # PaddlePaddle
        f[10], _ = export_paddle(model, im, file, metadata)

    # Finish
    f = [str(x) for x in f if x]  # filter out '' and None
    if any(f):
        cls, det, seg = (isinstance(model, x) for x in (ClassificationModel, DetectionModel, SegmentationModel))  # type
        dir = Path('segment' if seg else 'classify' if cls else '')
        h = '--half' if half else ''  # --half FP16 inference arg
        s = "# WARNING ⚠️ ClassificationModel not yet supported for PyTorch Hub AutoShape inference" if cls else \
            "# WARNING ⚠️ SegmentationModel not yet supported for PyTorch Hub AutoShape inference" if seg else ''
        LOGGER.info(f'\nExport complete ({time.time() - t:.1f}s)'
                    f"\nResults saved to {colorstr('bold', file.parent.resolve())}"
                    f"\nDetect:          python {dir / ('detect.py' if det else 'predict.py')} --weights {f[-1]} {h}"
                    f"\nValidate:        python {dir / 'val.py'} --weights {f[-1]} {h}"
                    f"\nPyTorch Hub:     model = torch.hub.load('ultralytics/yolov5', 'custom', '{f[-1]}')  {s}"
                    f"\nVisualize:       https://netron.app")
    return f  # return list of exported files/dirs

3.1 函数定义及参数:

def run(
    data=ROOT / 'data/coco128.yaml',  # 数据集配置文件路径
    weights=ROOT / 'yolov5s.pt',  # 模型权重路径
    imgsz=(640, 640),  # 输入图像的高度和宽度
    batch_size=1,  # 批处理大小
    device='cpu',  # 使用的设备 (如 CPU 或 GPU)
    include=('torchscript', 'onnx'),  # 导出格式
    half=False,  # 是否使用 FP16 半精度导出
    inplace=False,  # 是否在推理时使用原地模式
    keras=False,  # 是否使用 Keras
    optimize=False,  # 是否优化 TorchScript 模型以适应移动设备
    int8=False,  # 是否使用 INT8 量化
    dynamic=False,  # 动态轴
    simplify=False,  # 是否简化 ONNX 模型
    opset=12,  # ONNX 操作集版本
    verbose=False,  # 是否详细输出 TensorRT 日志
    workspace=4,  # TensorRT 的工作空间大小 (GB)
    nms=False,  # 是否添加 NMS
    agnostic_nms=False,  # 是否添加无类别 NMS
    topk_per_class=100,  # 保留的每个类别的 Top K
    topk_all=100,  # 保留的所有类别的 Top K
    iou_thres=0.45,  # IoU 阈值
    conf_thres=0.25,  # 置信度阈值
):

这个函数负责执行模型的导出,参数包括数据集路径、模型权重路径、图像大小、设备类型等。

这段代码是YOLOv5模型导出功能的一个部分,采用了参数化的方式来定义众多配置项。以下是对每个参数的详细解释:

  1. data=ROOT / 'data/coco128.yaml':指定数据集配置文件的路径。coco128.yaml通常包含关于数据集的信息,如图像路径、类别等。

  2. weights=ROOT / 'yolov5s.pt':指定要加载的模型权重文件的路径。yolov5s.pt是YOLOv5模型的PyTorch权重文件。

  3. imgsz=(640, 640):定义输入图像的尺寸,表示图像的高度和宽度。在YOLOv5中,图像需要是正方形的,以便于处理。

  4. batch_size=1:设定每次处理的图像数量,常用于批量推理。

  5. device='cpu':指定要使用的计算设备。可以是CPU或CUDA设备(例如'0','1',等表示GPU)。

  6. include=('torchscript', 'onnx'):指定要导出的模型格式。在这里,表示将模型导出为TorchScript和ONNX格式。

  7. half=False:指示是否使用半精度浮点数(FP16)进行导出,通常加速推理。

  8. inplace=False:决定是否在模型检测过程中使用就地操作,即是否直接修改输入数据。

  9. keras=False:指示是否使用Keras框架。

  10. optimize=False:表示是否将TorchScript模型进行优化以适应移动设备。

  11. int8=False:指示是否进行量化以实现INT8精度,通常用于CoreML或TensorFlow模型。

  12. dynamic=False:指定是否使用动态轴,适用于ONNX、TensorFlow和TensorRT模型,使得模型可以接收不同大小的输入。

  13. simplify=False:指示是否简化ONNX模型,以降低复杂度和提高推理速度。

  14. opset=12:指示ONNX的算子版本,版本号通常需要与不同的框架兼容。

  15. verbose=False:设定TensorRT的日志详细程度,如果为True,会输出更详细的日志信息。

  16. workspace=4:指定TensorRT的工作空间大小,单位为GB,影响最大内存使用。

  17. nms=False:表示是否在TensorFlow模型中添加非极大值抑制(NMS),用于处理检测结果。

  18. agnostic_nms=False:指示是否使用不区分类别的非极大值抑制(NMS)。

  19. topk_per_class=100:在执行NMS时,对于每个类别保留前100个检测结果。

  20. topk_all=100:在执行NMS时,对于所有类别保留的最多前100个检测结果。

  21. iou_thres=0.45:定义交并比(IoU)阈值,用于NMS算法,通常设定在0到1之间,值越高则越严格。

  22. conf_thres=0.25:定义检测到的目标的置信度概率阈值,只有高于这个概率的目标才会被保留(即被认为是有效的检测)。

这段代码主要定义了一系列配置参数,目的是为YOLOv5模型的导出过程提供灵活的选项。通过这些参数,用户可以指定数据集、权重文件、输入图像尺寸、设备选择,以及导出模型的格式和相关技术细节。这些配置使得用户能够根据需要自定义模型的导出流程,从而适应不同的应用场景和性能要求。

3.2 时间记录与格式处理:

    t = time.time()
    include = [x.lower() for x in include]  # to lowercase
    fmts = tuple(export_formats()['Argument'][1:])  # --include arguments
    flags = [x in include for x in fmts]
    assert sum(flags) == len(include), f'ERROR: Invalid --include {include}, valid --include arguments are {fmts}'
    jit, onnx, xml, engine, coreml, saved_model, pb, tflite, edgetpu, tfjs, paddle = flags  # export booleans
    file = Path(url2file(weights) if str(weights).startswith(('http:/', 'https:/')) else weights)  # PyTorch weights

记录开始时间,处理所需的导出格式并验证其有效性。

下面是对给定代码的逐步分解和详细解释:

t = time.time()
  • 这行代码记录当前的时间戳。它通常用于测量某段代码的运行时间,t可以用于后续的时间计算。
include = [x.lower() for x in include]  # to lowercase
  • 这是一个列表推导式,它将include列表中的所有字符串转换为小写字母。这可能是为了确保不论用户输入的大小写如何,处理时都能统一为小写,更加方便后续的比较操作。
fmts = tuple(export_formats()['Argument'][1:])  # --include arguments
  • 这行代码调用export_formats()函数并提取其返回值中的'Argument'列,[1:]切片操作用于去掉第一个元素,形成一个元组(fmts)。这些元素是可用的导出格式的参数(如torchscriptonnx等)。
flags = [x in include for x in fmts]
  • 这是另一个列表推导式,创建一个布尔列表flags。它检查fmts中的每个元素是否存在于include列表中,返回的列表中每个对应的元素为TrueFalse
assert sum(flags) == len(include), f'ERROR: Invalid --include {include}, valid --include arguments are {fmts}'
  • 这一行用来验证用户提供的include参数是否有效。它计算flags列表中True的数量,并与include的长度进行比较。如果数量不相等,说明有无效的include参数,程序会抛出一个错误,提示用户提供的参数无效,并显示合法的参数选项。
jit, onnx, xml, engine, coreml, saved_model, pb, tflite, edgetpu, tfjs, paddle = flags  # export booleans
  • 这一行代码将flags列表中的布尔值逐一解包并赋值给多个变量。这些变量代表每种导出格式的启用状态,如jit表示TorchScript导出是否被启用,onnx表示ONNX导出等。可以通过这些布尔变量在后续代码中决定是否执行某些导出操作。
file = Path(url2file(weights) if str(weights).startswith(('http:/', 'https:/')) else weights)  # PyTorch weights
  • 这行代码根据weights的内容是否为网址来决定如何处理它。如果是URL,那么调用url2file函数将其转为本地文件路径。如果不是,则直接将weights转为Path对象。最终,变量file会存储PyTorch模型权重文件的路径。

该代码段主要实现了以下功能:

  1. 记录当前时间,用于性能测量;
  2. 将用户输入的导出格式转换为小写,以统一处理;
  3. 获取支持的导出格式,并验证用户输入的格式是否有效;
  4. 根据用户输入构建一个布尔值列表,表示各个导出格式的启用状态;
  5. 处理模型权重输入,支持URL和本地文件路径。

这段代码是整个导出过程的一部分,为后续不同格式的模型导出设置参数和状态,确保用户的输入有效并准备好所需的模型文件。

3.3 加载模型:

 # Load PyTorch model
    device = select_device(device)
    if half:
        assert device.type != 'cpu' or coreml, '--half only compatible with GPU export, i.e. use --device 0'
        assert not dynamic, '--half not compatible with --dynamic, i.e. use either --half or --dynamic but not both'
    model = attempt_load(weights, device=device, inplace=True, fuse=True)  # load FP32 model

确定使用的设备(CPU或GPU),然后从权重文件加载模型。

这段代码主要用于加载一个PyTorch模型,并根据指定的设备和条件进行相应的处理。下面是逐行分解和详细解释:

  1. device = select_device(device)

    • 这一行调用了 select_device 函数,目的是根据传入的设备参数选择并返回合适的设备。常见的设备类型包括CPU和GPU。这个函数会检查设备是否可用,并返回一个可以用来进行模型推理的设备对象。
  2. if half:

    • 这个条件语句检查变量 half 的值。若 half 为真,表示用户希望使用半精度浮点数(FP16)格式,这种格式通常用于GPU上,以加速模型推理过程并节省内存。
  3. assert device.type != 'cpu' or coreml, '--half only compatible with GPU export, i.e. use --device 0'

    • 这一行是一个断言语句,用于确保如果使用半精度(FP16),则当前设备不能是CPU,或者必须使用CoreML。换句话说,只有在GPU上才能使用半精度导出。如果这个条件不满足,程序会抛出一个错误,提示用户。
  4. assert not dynamic, '--half not compatible with --dynamic, i.e. use either --half or --dynamic but not both'

    • 另一个断言语句,用于确保不能同时使用动态轴(dynamic)和半精度(FP16)。也就是说,用户必须选择这两者之一来导出模型。如果两者都为真,会抛出错误。
  5. model = attempt_load(weights, device=device, inplace=True, fuse=True)

    • 这一行调用 attempt_load 函数以加载指定的模型权重文件(weights)。这个函数接受以下参数:
      • weights:要加载的权重文件的路径。
      • device:之前选择的设备(CPU或GPU)。
      • inplace=True:指明在模型推理时是否使用原地计算。
      • fuse=True:开启模型各层的融合操作,常用于提高效率,使模型更快。
    • 最终,成功加载的模型会赋值给变量 model,通常是一个FP32模型。

这段代码的主要功能是加载一个PyTorch模型,同时考虑用户指定的设备(CPU或GPU)以及是否使用半精度(FP16)进行推理。它通过一系列的检查确保了设备、精度和动态轴的兼容性,以便在加载模型时能实现最佳性能。这是YOLOv5模型导出的重要步骤之一,确保在适当的环境中准备好模型进行推理。

3.4 输入准备:

 # Checks
    imgsz *= 2 if len(imgsz) == 1 else 1  # expand
    if optimize:
        assert device.type == 'cpu', '--optimize not compatible with cuda devices, i.e. use --device cpu'

    # Input
    gs = int(max(model.stride))  # grid size (max stride)
    imgsz = [check_img_size(x, gs) for x in imgsz]  # verify img_size are gs-multiples
    im = torch.zeros(batch_size, 3, *imgsz).to(device)  # image size(1,3,320,192) BCHW iDetection

创建一个零张量作为模型的输入数据,形状为 [batch_size, 3, height, width]。

让我们逐步分解并详细解释这段代码:

# Checks
imgsz *= 2 if len(imgsz) == 1 else 1  # expand
  1. Checks:这是一个检查部分,目的是确保输入的图像尺寸 imgsz 是有效的。
  2. *imgsz = 2 if len(imgsz) == 1 else 1:这行代码检测 imgsz 的长度。如果 imgsz 只有一个值(例如,传递了单个整数作为图像尺寸),那么它将该值乘以 2。这通常是为了在模型输入时,需要的图像尺寸格式为高度和宽度两个值,例如将(640)扩展为(640, 640)。
if optimize:
    assert device.type == 'cpu', '--optimize not compatible with cuda devices, i.e. use --device cpu'
  1. if optimize:这部分代码检查是否设置了 optimize 标志。如果设置了,表示用户希望对模型进行优化。
  2. assert device.type == 'cpu':这行代码确保当前设备是 CPU。如果用户在使用 GPU 设备时试图启用优化,则会引发 AssertionError,并显示提示信息,说明优化不兼容 CUDA 设备。
# Input
gs = int(max(model.stride))  # grid size (max stride)
  1. # Input:这是标识输入部分的注释。
  2. gs = int(max(model.stride)):这里获取模型的最大步幅(stride),并将其转换为整数。步幅决定了网络的分辨率和处理速度,通常在目标检测模型中,与网络是如何处理输入图像的有关,尤其是如何将输入图像分割成网格进行处理。
imgsz = [check_img_size(x, gs) for x in imgsz]  # verify img_size are gs-multiples
  1. imgsz = [check_img_size(x, gs) for x in imgsz]:这行代码对 imgsz 中的每个图像大小 x 进行检查,以确保它们都是 gs 的倍数。此操作确保输入图像的大小在网络层之间进行传递时不会出现问题,常见于深度学习中。
im = torch.zeros(batch_size, 3, *imgsz).to(device)  # image size(1,3,320,192) BCHW iDetection
  1. im = torch.zeros(batch_size, 3, *imgsz).to(device):这行代码创建一个形状为 (batch_size, 3, height, width) 的零张量。这里 3 表示图像的通道数(通常是 RGB),imgsz 是前面计算出来的高度和宽度。然后将这个张量放置到指定的设备上(CPU 或 GPU),以便模型可以使用。

该段代码的主要功能是处理和准备输入图像的尺寸和格式,以便于后续的模型推理。具体地,它完成了以下任务:

  • 根据输入图像的长度扩展其尺寸。
  • 检查是否启用优化,并确保优化兼容 CPU。
  • 确定网格大小,以便后续处理。
  • 验证图像尺寸为网格大小的倍数,确保符合模型的输入要求。
  • 创建对应形状的张量用于输入模型。

这对于任何需要通过卷积神经网络进行目标检测的深度学习任务来说是一个关键的预处理步骤。

3.5 更新模型配置与干运行:

    model.eval()
    for k, m in model.named_modules():
        if isinstance(m, Detect):
            m.inplace = inplace
            m.dynamic = dynamic
            m.export = True

    for _ in range(2):
        y = model(im)  # dry runs
    if half and not coreml:
        im, model = im.half(), model.half()  # to FP16
    shape = tuple((y[0] if isinstance(y, tuple) else y).shape)  # model output shape
    metadata = {'stride': int(max(model.stride)), 'names': model.names}  # model metadata
    LOGGER.info(f"\n{colorstr('PyTorch:')} starting from {file} with output shape {shape} ({file_size(file):.1f} MB)")

设置模型为评估模式,并进行两次干运行以确保模型被正确初始化。

这段代码是在导出YOLOv5模型的过程中执行的,主要用于设置模型的评估模式、处理相关参数并进行干跑操作,这样做的目的是确保模型能够正常工作并准备好进行导出。以下是逐步分解和详细解释:

  1. model.eval():

    • 这行代码将模型设置为评估模式(evaluation mode)。在此模式下,模型不会应用诸如 dropout 和 batch normalization 等技术,这些技术通常只在训练模式下使用。这一步确保了评估时模型的行为是正确的。
  2. for k, m in model.named_modules()::

    • 这里使用 model.named_modules() 方法遍历模型中的每个命名子模块,其中 k 是模块的名称,m 是模块的实例。这使得我们可以访问模型内部结构并根据需要做出修改。
  3. if isinstance(m, Detect)::

    • 这行代码检查当前模块 m 是否是 Detect 类型的实例。Detect 是YOLOv5中用于进行物体检测的模块,如果是,则会进行以下的配置。
  4. m.inplace = inplace:

    • inplace 是一个参数,它决定了模型是否应该在原地进行检测,即是否应该在现有的张量上修改数据。通过将 inplace 赋值给 m.inplace,设置该检测模块的行为。
  5. m.dynamic = dynamic:

    • dynamic 参数用于决定模型在推理时是否使用动态输入形状。如果设置为 True,模型将可以使用不同的输入大小。
  6. m.export = True:

    • 这行代码将 m.export 设置为 True。这通常用于在导出模型时启用某些功能或特性,使得模型可以以适当的格式导出。
  7. for _ in range(2): y = model(im):

    • 这里进行两次“干跑”(dry runs)。通过传入输入数据 im 至模型 model 进行前向传播,以确保模型能正常运行。此步骤可以帮助确认模型结构的有效性,确保接口没有问题。
  8. if half and not coreml::

    • 检查是否需要将数据转换为半精度(FP16),且不适用于 CoreML。这是为了提高推理速度和减少内存使用。在确保 im 和 model 都被转换为 FP16 之后,代码进行如下处理:
  9. im, model = im.half(), model.half():

    • 将输入图像 im 和模型 model 都转换为 FP16 格式。
  10. shape = tuple((y[0] if isinstance(y, tuple) else y).shape):

    • 获取模型输出 y 的形状。如果输出是元组类型,则取第一个元素的形状(通常 YOLOv5 可能返回多个输出),否则直接获取输出的形状。最终将形状转为元组格式。
  11. metadata = {'stride': int(max(model.stride)), 'names': model.names}:

    • 创建一个词典 metadata,其中包含模型的一些元数据。stride 指的是最大步幅,用于决定特征图的分辨率,names 则是模型可以识别的类名。
  12. LOGGER.info(f"\n{colorstr('PyTorch:')} starting from {file} with output shape {shape} ({file_size(file):.1f} MB)"):

    • 这行代码使用 LOGGER 记录信息,表明导出过程已开始,并提供相关文件和输出形状的信息。

这段代码的主要功能是配置并准备 YOLOv5 模型的评估设置,以便后续导出。它确保模型以正确的模式运行,处理了一些模型特性(如 inplace 和 dynamic),并通过干跑验证了模型的兼容性。最后,它记录了导出过程中的一些重要信息,如输入文件和输出形状,帮助开发者了解导出状态。

3.6 导出模型:

if jit:  # TorchScript
    f[0], _ = export_torchscript(model, im, file, optimize)
if engine:  # TensorRT
    f[1], _ = export_engine(model, im, file, half, dynamic, simplify, workspace, verbose)
...

根据所需的导出格式调用不同的导出函数(如 TorchScript、ONNX、TensorRT 等)。

这段代码负责将训练好的YOLOv5模型导出为不同的格式,以供后续使用。这些格式包括TorchScript、TensorRT、ONNX、OpenVINO、CoreML、TensorFlow(包括SavedModel、GraphDef、TFLite和Edge TPU)以及PaddlePaddle。

以下是代码的逐步分解和详细解释:

  1. 初始化导出文件名列表

    f = [''] * len(fmts)  # exported filenames
    

    这里创建一个与支持的导出格式数量相同的列表f,初始化时每个元素都是空字符串,用于存放导出后的文件名。

  2. 过滤警告

    warnings.filterwarnings(action='ignore', category=torch.jit.TracerWarning)  # suppress TracerWarning
    

    该行代码用于忽略PyTorch中的TracerWarning,避免在模型导出过程中产生过多的警告信息。

  3. TorchScript导出

    if jit:  # TorchScript
        f[0], _ = export_torchscript(model, im, file, optimize)
    

    如果请求导出TorchScript格式,则调用export_torchscript函数,并将结果存储在f的第一个位置。

  4. TensorRT导出

    if engine:  # TensorRT required before ONNX
        f[1], _ = export_engine(model, im, file, half, dynamic, simplify, workspace, verbose)
    

    如果请求导出TensorRT格式,则调用export_engine,确保在导出ONNX之前先完成TensorRT的导出。

  5. ONNX和OpenVINO导出

    if onnx or xml:  # OpenVINO requires ONNX
        f[2], _ = export_onnx(model, im, file, opset, dynamic, simplify)
    if xml:  # OpenVINO
        f[3], _ = export_openvino(file, metadata, half)
    

    如果请求导出ONNX格式,调用export_onnx函数;如果需要OpenVINO格式,则在导出ONNX之后调用export_openvino函数。

  6. CoreML导出

    if coreml:  # CoreML
        f[4], _ = export_coreml(model, im, file, int8, half)
    

    如果请求导出CoreML格式,则调用export_coreml函数。

  7. TensorFlow系列格式导出

    if any((saved_model, pb, tflite, edgetpu, tfjs)):  # TensorFlow formats
        assert not tflite or not tfjs, 'TFLite and TF.js models must be exported separately, please pass only one type.'
        assert not isinstance(model, ClassificationModel), 'ClassificationModel export to TF formats not yet supported.'
        f[5], s_model = export_saved_model(model.cpu(), ...)
        ...
    
    • 首先,检查是否请求导出任何TensorFlow相关格式。如果是,则首先进行一些检查,确保不能同时导出TFLite和TF.js模型,并且不支持将分类模型导出为TF格式。
    • 接着,调用export_saved_model导出为TensorFlow的保存模型格式,保存路径同时在f[5]中记录。
  8. 导出GraphDef和TF.js

    if pb or tfjs:  # pb prerequisite to tfjs
        f[6], _ = export_pb(s_model, file)
    if tflite or edgetpu:
        ...
        if tfjs:
            f[9], _ = export_tfjs(file)
    
    • 如果请求导出GraphDef格式或TF.js格式,首先调用export_pb确保GraphDef模型可以导出为TF.js使用。
    • 如果请求导出TFLite格式,则调用export_tflite函数完成导出,并添加相应的元数据。
  9. PaddlePaddle格式导出

    if paddle:  # PaddlePaddle
        f[10], _ = export_paddle(model, im, file, metadata)
    

    最后,如果请求导出为PaddlePaddle格式,则调用export_paddle函数完成导出。

这段代码主要功能是将YOLOv5模型导出为多种格式,以便能够在不同环境和平台下使用。支持的导出格式包括TorchScript、TensorRT、ONNX、OpenVINO、CoreML、TensorFlow的多个变体(如SavedModel、GraphDef、TFLite 和 Edge TPU)、及PaddlePaddle。通过这些导出,用户可以灵活选择适合自己应用场景的模型格式。

3.7 输出结果:

 # Finish
    f = [str(x) for x in f if x]  # filter out '' and None
    if any(f):
        cls, det, seg = (isinstance(model, x) for x in (ClassificationModel, DetectionModel, SegmentationModel))  # type
        dir = Path('segment' if seg else 'classify' if cls else '')
        h = '--half' if half else ''  # --half FP16 inference arg
        s = "# WARNING ⚠️ ClassificationModel not yet supported for PyTorch Hub AutoShape inference" if cls else \
            "# WARNING ⚠️ SegmentationModel not yet supported for PyTorch Hub AutoShape inference" if seg else ''
        LOGGER.info(f'\nExport complete ({time.time() - t:.1f}s)'
                    f"\nResults saved to {colorstr('bold', file.parent.resolve())}"
                    f"\nDetect:          python {dir / ('detect.py' if det else 'predict.py')} --weights {f[-1]} {h}"
                    f"\nValidate:        python {dir / 'val.py'} --weights {f[-1]} {h}"
                    f"\nPyTorch Hub:     model = torch.hub.load('ultralytics/yolov5', 'custom', '{f[-1]}')  {s}"
                    f"\nVisualize:       https://netron.app")
    return f  # return list of exported files/dirs

最后,记录导出的完成消息,并返回导出的文件列表。

这段代码的主要功能是收尾处理,完成模型导出后,生成相关的日志信息,并返回导出的文件列表。以下是对代码的逐步分解和详细解释:

  1. 过滤空值

    f = [str(x) for x in f if x]  # filter out '' and None
    

    这行代码使用列表推导式将列表 f 中的非空字符串和非 None 值提取出来。最终,f 将只包含有效的路径字符串。

  2. 检查导出文件是否存在

    if any(f):
    

    这里检查 f 列表是否包含任何文件路径。如果列表中存在有效的导出文件,就会进入接下来的代码块。

  3. 获取模型类型

    cls, det, seg = (isinstance(model, x) for x in (ClassificationModel, DetectionModel, SegmentationModel))  # type
    

    使用 isinstance 检查模型的类型,判断模型是否属于分类模型(ClassificationModel)、检测模型(DetectionModel)或分割模型(SegmentationModel)。这将返回三个布尔值,分别存储在 clsdet 和 seg 变量中。

  4. 确定导出目录

    dir = Path('segment' if seg else 'classify' if cls else '')
    

    根据模型类型选择相应的目录路径,如果是分割模型,则使用 segment 目录;如果是分类模型,则使用 classify 目录;否则,使用空路径。

  5. 设置半精度标志

    h = '--half' if half else ''  # --half FP16 inference arg
    

    这里根据 half 变量的值设置 FP16(半精度)推理的命令行参数。

  6. 生成警告信息

    s = "# WARNING ⚠️ ClassificationModel not yet supported for PyTorch Hub AutoShape inference" if cls else \
        "# WARNING ⚠️ SegmentationModel not yet supported for PyTorch Hub AutoShape inference" if seg else ''
    

    根据模型类型生成相应的警告信息。如果当前模型是分类模型或分割模型,则生成相应的警告字符串,否则为空。

  7. 日志信息输出

    LOGGER.info(f'\nExport complete ({time.time() - t:.1f}s)'
                f"\nResults saved to {colorstr('bold', file.parent.resolve())}"
                f"\nDetect:          python {dir / ('detect.py' if det else 'predict.py')} --weights {f[-1]} {h}"
                f"\nValidate:        python {dir / 'val.py'} --weights {f[-1]} {h}"
                f"\nPyTorch Hub:     model = torch.hub.load('ultralytics/yolov5', 'custom', '{f[-1]}')  {s}"
                f"\nVisualize:       https://netron.app")
    

    这段代码记录导出完成的时间、保存结果的目录、模型的检测和验证命令的示例以及如何在 PyTorch Hub 中加载模型的示例。还包括如何在网格应用程序中可视化模型的链接。

  8. 返回导出文件列表

    return f  # return list of exported files/dirs
    

    最后,返回导出的文件或目录的列表。

这段代码主要负责在模型导出完成后进行日志记录和最终的整理工作。它检查导出的文件,确定模型的类别,生成适当的命令行使用示例,并在终端或日志中输出相关信息。这为用户提供了清晰的导出成功反馈和后续操作的指引。

3.8 总结

这段代码的主要功能是实现YOLOv5模型的导出,支持多种格式,包括TorchScript、ONNX、TensorRT、CoreML等。它能够根据用户的指定参数(如所需的导出格式、模型的设备类型、是否使用半精度等)对模型进行配置并进行导出。通过此函数,用户可以将训练好的YOLOv5模型转换为多种兼容格式,以便在不同平台和应用中进行推理或部署。

四、使用说明

# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
"""
Export a YOLOv5 PyTorch model to other formats. TensorFlow exports authored by https://github.com/zldrobit

Format                      | `export.py --include`         | Model
---                         | ---                           | ---
PyTorch                     | -                             | yolov5s.pt
TorchScript                 | `torchscript`                 | yolov5s.torchscript
ONNX                        | `onnx`                        | yolov5s.onnx
OpenVINO                    | `openvino`                    | yolov5s_openvino_model/
TensorRT                    | `engine`                      | yolov5s.engine
CoreML                      | `coreml`                      | yolov5s.mlmodel
TensorFlow SavedModel       | `saved_model`                 | yolov5s_saved_model/
TensorFlow GraphDef         | `pb`                          | yolov5s.pb
TensorFlow Lite             | `tflite`                      | yolov5s.tflite
TensorFlow Edge TPU         | `edgetpu`                     | yolov5s_edgetpu.tflite
TensorFlow.js               | `tfjs`                        | yolov5s_web_model/
PaddlePaddle                | `paddle`                      | yolov5s_paddle_model/

Requirements:
    $ pip install -r requirements.txt coremltools onnx onnx-simplifier onnxruntime openvino-dev tensorflow-cpu  # CPU
    $ pip install -r requirements.txt coremltools onnx onnx-simplifier onnxruntime-gpu openvino-dev tensorflow  # GPU

Usage:
    $ python export.py --weights yolov5s.pt --include torchscript onnx openvino engine coreml tflite ...

Inference:
    $ python detect.py --weights yolov5s.pt                 # PyTorch
                                 yolov5s.torchscript        # TorchScript
                                 yolov5s.onnx               # ONNX Runtime or OpenCV DNN with --dnn
                                 yolov5s_openvino_model     # OpenVINO
                                 yolov5s.engine             # TensorRT
                                 yolov5s.mlmodel            # CoreML (macOS-only)
                                 yolov5s_saved_model        # TensorFlow SavedModel
                                 yolov5s.pb                 # TensorFlow GraphDef
                                 yolov5s.tflite             # TensorFlow Lite
                                 yolov5s_edgetpu.tflite     # TensorFlow Edge TPU
                                 yolov5s_paddle_model       # PaddlePaddle

TensorFlow.js:
    $ cd .. && git clone https://github.com/zldrobit/tfjs-yolov5-example.git && cd tfjs-yolov5-example
    $ npm install
    $ ln -s ../../yolov5/yolov5s_web_model public/yolov5s_web_model
    $ npm start
"""

这段代码的主要功能是导出 YOLOv5 模型(由 Ultralytics 开发)到多种格式,以便在不同的平台和框架上进行推理。下面逐步分解和详细解释代码:

  1. 文件头和元数据

    # YOLOv5 🚀 by Ultralytics, GPL-3.0 license
    

    这行是代码的版权声明,表明该代码是由 Ultralytics 开发,并且使用 GPL-3.0 许可协议规范。

  2. 文档字符串

    """
    Export a YOLOv5 PyTorch model to other formats. TensorFlow exports authored by https://github.com/zldrobit
    

    以上是用来解释这个文件的目的:其功能是将一个 YOLOv5 的 PyTorch 模型导出为多种格式。

  3. 导出格式和命令行参数: 接下来的部分列出了支持的导出格式,相关的命令行参数和输出文件格式,包括:

    • PyTorch (yolov5s.pt)
    • TorchScript (yolov5s.torchscript)
    • ONNX (yolov5s.onnx)
    • OpenVINO (yolov5s_openvino_model/)
    • TensorRT (yolov5s.engine)
    • CoreML (yolov5s.mlmodel)
    • TensorFlow SavedModel (yolov5s_saved_model/)
    • TensorFlow GraphDef (yolov5s.pb)
    • TensorFlow Lite (yolov5s.tflite)
    • TensorFlow Edge TPU (yolov5s_edgetpu.tflite)
    • TensorFlow.js (yolov5s_web_model/)
    • PaddlePaddle (yolov5s_paddle_model/)

    这个表格为使用者提供了清晰的参数选项与对应的输出文件。

  4. 依赖项

    Requirements:
        $ pip install -r requirements.txt coremltools onnx onnx-simplifier onnxruntime openvino-dev tensorflow-cpu  # CPU
        $ pip install -r requirements.txt coremltools onnx onnx-simplifier onnxruntime-gpu openvino-dev tensorflow  # GPU
    

    这部分描述了导出模型所需要的 Python 包,用户需要根据其硬件选择 CPU 或 GPU 版本进行安装。

  5. 使用方法

    Usage:
        $ python export.py --weights yolov5s.pt --include torchscript onnx openvino engine coreml tflite ...
    

    这一部分说明了如何使用此脚本执行模型导出。使用者需要指定模型的权重文件和所需导出的格式。

  6. 推理说明: 这个部分提供了在不同框架下使用已导出的模型进行推理的示例命令,比如使用 PyTorch、ONNX、TensorFlow 等进行检测或推理。

  7. TensorFlow.js 示例

    TensorFlow.js:
        $ cd .. && git clone https://github.com/zldrobit/tfjs-yolov5-example.git && cd tfjs-yolov5-example
        $ npm install
        $ ln -s ../../yolov5/yolov5s_web_model public/yolov5s_web_model
        $ npm start
    

    该部分说明如何使用 TensorFlow.js 导入并运行模型,提供了具体的命令步骤。

总体而言,这段代码主要用于提供一个工具,该工具允许用户将训练好的 YOLOv5 PyTorch 模型导出为多种格式,以便在广泛的平台上进行推理。支持的输出格式包括 PyTorch、ONNX、TensorFlow等,方便用户在不同的框架和硬件上灵活使用模型。通过明确的命令行选项和依赖说明,该代码对用户具有良好的可操作性和扩展性。

五、依赖库 

import argparse
import contextlib
import json
import os
import platform
import re
import subprocess
import sys
import time
import warnings
from pathlib import Path

import pandas as pd
import torch
from torch.utils.mobile_optimizer import optimize_for_mobile

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
if platform.system() != 'Windows':
    ROOT = Path(os.path.relpath(ROOT, Path.cwd()))  # relative

from models.experimental import attempt_load
from models.yolo import ClassificationModel, Detect, DetectionModel, SegmentationModel
from utils.dataloaders import LoadImages
from utils.general import (LOGGER, Profile, check_dataset, check_img_size, check_requirements, check_version,
                           check_yaml, colorstr, file_size, get_default_args, print_args, url2file, yaml_save)
from utils.torch_utils import select_device, smart_inference_mode

MACOS = platform.system() == 'Darwin'  # macOS environment

这段代码导入了一系列必要的库,并初始化了一些与YOLOv5模型导出功能相关的设置。接下来,我将逐步分解并详细解释代码的各个部分。

  1. 导入库及模块:

    import argparse
    import contextlib
    import json
    import os
    import platform
    import re
    import subprocess
    import sys
    import time
    import warnings
    from pathlib import Path
    

    这些导入语句引入了标准库和第三方库,用于实现命令行参数解析、文件操作、系统状态检查、正则表达式、进程控制、时间处理等功能。

  2. 导入第三方库:

    import pandas as pd
    import torch
    from torch.utils.mobile_optimizer import optimize_for_mobile
    

    此部分引入了 pandas库(用于数据处理)和 PyTorch(深度学习框架),以及其中的mobile_optimizer模块(用于优化移动设备推理)。

  3. 获取当前文件路径及根目录:

    FILE = Path(__file__).resolve()
    ROOT = FILE.parents[0]  # YOLOv5 root directory
    

    FILE 变量获取当前脚本的绝对路径,而 ROOT 变量获取其父目录,作为YOLOv5项目的根目录。

  4. 更新系统路径:

    if str(ROOT) not in sys.path:
        sys.path.append(str(ROOT))  # add ROOT to PATH
    

    如果根目录不在系统路径中,则将其添加,以便可以导入项目中的模块。

  5. 处理非Windows环境:

    if platform.system() != 'Windows':
        ROOT = Path(os.path.relpath(ROOT, Path.cwd()))  # relative
    

    如果当前系统不是Windows,则将根目录转换为相对路径。这样做的目的是为了在非Windows环境中保持路径的一致性。

  6. 导入YOLOv5相关模块:

    from models.experimental import attempt_load
    from models.yolo import ClassificationModel, Detect, DetectionModel, SegmentationModel
    

    这些导入语句引入YOLOv5的模型定义,其中包括分类模型、检测模型和分割模型等。

  7. 导入数据加载和通用工具函数:

    from utils.dataloaders import LoadImages
    from utils.general import (LOGGER, Profile, check_dataset, check_img_size, check_requirements, 
                                check_version, check_yaml, colorstr, file_size, get_default_args, 
                                print_args, url2file, yaml_save)
    from utils.torch_utils import select_device, smart_inference_mode
    

    此部分引入与数据加载、日志记录、工具函数、设备选择和推理模式相关的功能模块。这些工具函数和模块将被用于在模型导出过程中提供高效的支持。

  8. 检测macOS环境:

    MACOS = platform.system() == 'Darwin'  # macOS environment
    

    此行代码判断当前操作系统是否为macOS,并将结果存储在 MACOS 变量中。

这段代码的主要功能是为之后的YOLOv5模型导出提供必要的准备工作。具体来说,它做了以下几件事情:

  • 导入并配置所需的库和模块:提供了数据处理、图像加载、模型定义等功能的支持。
  • 设置项目根目录:确保能够正确引用和导入项目中的模块。
  • 适配不同操作系统:确保在不同操作系统(如Windows和macOS)下的路径管理一致性。
  • 引入工具函数:为后续的模型导出和推理过程准备一系列通用工具,这样在进行模型导出时,可以高效地处理常见任务(如数据加载、日志记录等)。

综上所述,这段代码为YOLOv5模型导出的实现奠定了基础,确保了环境的正确配置与模块的可用性。

六、公共函数

6.1 导出格式

def export_formats():
    # YOLOv5 export formats
    x = [
        ['PyTorch', '-', '.pt', True, True],
        ['TorchScript', 'torchscript', '.torchscript', True, True],
        ['ONNX', 'onnx', '.onnx', True, True],
        ['OpenVINO', 'openvino', '_openvino_model', True, False],
        ['TensorRT', 'engine', '.engine', False, True],
        ['CoreML', 'coreml', '.mlmodel', True, False],
        ['TensorFlow SavedModel', 'saved_model', '_saved_model', True, True],
        ['TensorFlow GraphDef', 'pb', '.pb', True, True],
        ['TensorFlow Lite', 'tflite', '.tflite', True, False],
        ['TensorFlow Edge TPU', 'edgetpu', '_edgetpu.tflite', False, False],
        ['TensorFlow.js', 'tfjs', '_web_model', False, False],
        ['PaddlePaddle', 'paddle', '_paddle_model', True, True],]
    return pd.DataFrame(x, columns=['Format', 'Argument', 'Suffix', 'CPU', 'GPU'])
  1. 函数定义

    • def export_formats()::定义一个名为 export_formats 的函数,该函数没有参数。
  2. 注释

    • # YOLOv5 export formats:这一行是对函数的描述,说明该函数用于定义 YOLOv5 模型的导出格式。
  3. 格式数据的定义

    • x = [...]:创建一个名为 x 的列表,其中每个元素是一个列表,包含有关不同导出格式的信息。
    • 每个子列表的内容如下:
      • 第一个元素是字符串,表示格式的名称(例如 'PyTorch'、'ONNX' 等)。
      • 第二个元素也是一个字符串,表示导出时使用的命令行参数(例如 'torchscript'、'onnx' 等)。
      • 第三个元素是文件扩展名,用于保存导出的文件(例如 '.pt'、'.onnx' 等)。
      • 第四个元素是布尔值,指示该格式是否支持 CPU。
      • 第五个元素也是布尔值,指示该格式是否支持 GPU。
  4. 返回值

    • return pd.DataFrame(x, columns=['Format', 'Argument', 'Suffix', 'CPU', 'GPU']):将上述定义的列表 x 转换为一个 Pandas DataFrame,并且为每列指定列名(['Format', 'Argument', 'Suffix', 'CPU', 'GPU'])。最后,返回这个 DataFrame。

该函数的主要功能是定义和返回 YOLOv5 模型可以导出的各种格式的信息。函数创建的 DataFrame 包含每种格式的名称、命令行参数、文件扩展名以及是否支持 CPU 和 GPU 的指示。这使得管理和使用不同模型导出格式变得更加方便和直观,适用于后续的模型导出操作。

6.2 导出检测

def try_export(inner_func):
    # YOLOv5 export decorator, i..e @try_export
    inner_args = get_default_args(inner_func)

    def outer_func(*args, **kwargs):
        prefix = inner_args['prefix']
        try:
            with Profile() as dt:
                f, model = inner_func(*args, **kwargs)
            LOGGER.info(f'{prefix} export success ✅ {dt.t:.1f}s, saved as {f} ({file_size(f):.1f} MB)')
            return f, model
        except Exception as e:
            LOGGER.info(f'{prefix} export failure ❌ {dt.t:.1f}s: {e}')
            return None, None

    return outer_func

这段代码定义了一个装饰器函数 try_export(inner_func),用于处理 YOLOv5 模型的导出过程。以下是代码的逐步分解和详细解释:

  1. 装饰器定义:

    def try_export(inner_func):
    
    • 这个函数接受一个参数 inner_func,这是一个被装饰的函数,通常是一个导出模型的实际实现函数。
  2. 获取默认参数:

    inner_args = get_default_args(inner_func)
    
    • 使用 get_default_args(inner_func) 函数获取 inner_func 的默认参数。这通常是为了获取函数的一些元数据,比如参数的前缀 prefix
  3. 定义外部函数:

    def outer_func(*args, **kwargs):
    
    • 在 try_export 内部定义 outer_func,它接收任何参数并将其转发给被装饰的函数。这个函数将处理实际的调用和错误处理。
  4. 调用被装饰的函数:

    try:
        with Profile() as dt:
            f, model = inner_func(*args, **kwargs)
    
    • 使用 try 语句块来安全地调用 inner_func。通过 with Profile() as dt: 开启一个性能监测上下文,记录该过程的执行时间。
    • inner_func(*args, **kwargs) 被调用并返回两个结果:导出文件的路径 f 和模型 model
  5. 成功日志记录:

    LOGGER.info(f'{prefix} export success ✅ {dt.t:.1f}s, saved as {f} ({file_size(f):.1f} MB)')
    
    • 如果导出成功,记录一条日志,显示导出成功的消息、花费的时间和文件大小。
  6. 异常处理:

    except Exception as e:
        LOGGER.info(f'{prefix} export failure ❌ {dt.t:.1f}s: {e}')
        return None, None
    
    • 如果在执行 inner_func 时出现任何异常,将捕获这些异常并记录失败的消息。返回值为 (None, None),表示导出失败。
  7. 返回外部函数:

    return outer_func
    
    • 最后,try_export 返回 outer_func,这使得被装饰的函数现在具有错误处理和日志记录的功能。

这段代码的主要功能是提供一个装饰器 try_export,用于处理 YOLOv5 模型导出过程中的错误和性能监测。它通过以下方式完成其功能:

  • 任何被装饰的导出函数在运行时,如果发生异常,都会被捕获并记录到日志中。
  • 在成功导出时,它记录导出时间和文件大小,以便开发者了解导出过程的表现。
  • 该装饰器隐藏了具体的异常处理和日志记录逻辑,使得被装饰的导出函数代码更加简洁。

6.3 导出为torchscript格式

def export_torchscript(model, im, file, optimize, prefix=colorstr('TorchScript:')):
    # YOLOv5 TorchScript model export
    LOGGER.info(f'\n{prefix} starting export with torch {torch.__version__}...')
    f = file.with_suffix('.torchscript')

    ts = torch.jit.trace(model, im, strict=False)
    d = {"shape": im.shape, "stride": int(max(model.stride)), "names": model.names}
    extra_files = {'config.txt': json.dumps(d)}  # torch._C.ExtraFilesMap()
    if optimize:  # https://pytorch.org/tutorials/recipes/mobile_interpreter.html
        optimize_for_mobile(ts)._save_for_lite_interpreter(str(f), _extra_files=extra_files)
    else:
        ts.save(str(f), _extra_files=extra_files)
    return f, None

代码export_torchscript的作用是将YOLOv5模型导出为TorchScript格式,可以用于在生产环境中高效地运行模型。下面是对代码的逐步分解和详细解释:

  1. 函数定义

    def export_torchscript(model, im, file, optimize, prefix=colorstr('TorchScript:')):
    
    • model:要导出的YOLOv5模型。
    • im:用于追踪模型的输入示例(张量)。
    • file:保存导出模型的文件路径。
    • optimize:一个布尔值,指示是否对模型进行优化以适应移动设备。
    • prefix:用于日志的信息前缀,默认值为'TorchScript:'。
  2. 日志信息

    LOGGER.info(f'\n{prefix} starting export with torch {torch.__version__}...')
    
    • 使用LOGGER记录导出开始的消息,包括使用的Torch版本。
  3. 生成输出文件名

    f = file.with_suffix('.torchscript')
    
    • 将输入文件的后缀更改为.torchscript,即将导出的文件命名为file.torchscript
  4. 追踪模型

    ts = torch.jit.trace(model, im, strict=False)
    
    • 使用torch.jit.trace方法为给定输入张量im创建模型的TorchScript表示。strict=False表示在追踪时不进行严格的检查。
  5. 创建元数据

    d = {"shape": im.shape, "stride": int(max(model.stride)), "names": model.names}
    
    • 准备一个字典d,包含输入图像的形状、模型的最大步幅和模型的名字。这些信息将在导出时附加到文件中。
  6. 额外文件

    extra_files = {'config.txt': json.dumps(d)}  # torch._C.ExtraFilesMap()
    
    • 将先前创建的字典d转换为JSON格式,作为config.txt文件附加到TorchScript导出中。这个文件包含关于模型的一些配置参数。
  7. 条件优化

    if optimize:  
        optimize_for_mobile(ts)._save_for_lite_interpreter(str(f), _extra_files=extra_files)
    else:
        ts.save(str(f), _extra_files=extra_files)
    
    • 如果optimize为真,则调用optimize_for_mobile对模型进行优化并保存为轻量级解释器格式。
    • 否则,直接将模型保存为TorchScript格式,并附加任何额外的文件。
  8. 返回值

    return f, None
    
    • 返回导出文件的路径和None。这里第二个返回值可能预留以供将来扩展使用。

该函数的主要功能是将YOLOv5模型导出为TorchScript格式,适用于生产环境中的高效推理。它支持在导出时对模型进行优化,以适应移动设备。函数通过追踪提供的输入示例创建模型的TorchScript表示,并附加了额外的配置信息,确保导出的模型易于使用和维护。

6.4 导出为onnx格式

def export_onnx(model, im, file, opset, dynamic, simplify, prefix=colorstr('ONNX:')):
    # YOLOv5 ONNX export
    check_requirements('onnx')
    import onnx

    LOGGER.info(f'\n{prefix} starting export with onnx {onnx.__version__}...')
    f = file.with_suffix('.onnx')

    output_names = ['output0', 'output1'] if isinstance(model, SegmentationModel) else ['output0']
    if dynamic:
        dynamic = {'images': {0: 'batch', 2: 'height', 3: 'width'}}  # shape(1,3,640,640)
        if isinstance(model, SegmentationModel):
            dynamic['output0'] = {0: 'batch', 1: 'anchors'}  # shape(1,25200,85)
            dynamic['output1'] = {0: 'batch', 2: 'mask_height', 3: 'mask_width'}  # shape(1,32,160,160)
        elif isinstance(model, DetectionModel):
            dynamic['output0'] = {0: 'batch', 1: 'anchors'}  # shape(1,25200,85)

    torch.onnx.export(
        model.cpu() if dynamic else model,  # --dynamic only compatible with cpu
        im.cpu() if dynamic else im,
        f,
        verbose=False,
        opset_version=opset,
        do_constant_folding=True,
        input_names=['images'],
        output_names=output_names,
        dynamic_axes=dynamic or None)

    # Checks
    model_onnx = onnx.load(f)  # load onnx model
    onnx.checker.check_model(model_onnx)  # check onnx model

    # Metadata
    d = {'stride': int(max(model.stride)), 'names': model.names}
    for k, v in d.items():
        meta = model_onnx.metadata_props.add()
        meta.key, meta.value = k, str(v)
    onnx.save(model_onnx, f)

    # Simplify
    if simplify:
        try:
            cuda = torch.cuda.is_available()
            check_requirements(('onnxruntime-gpu' if cuda else 'onnxruntime', 'onnx-simplifier>=0.4.1'))
            import onnxsim

            LOGGER.info(f'{prefix} simplifying with onnx-simplifier {onnxsim.__version__}...')
            model_onnx, check = onnxsim.simplify(model_onnx)
            assert check, 'assert check failed'
            onnx.save(model_onnx, f)
        except Exception as e:
            LOGGER.info(f'{prefix} simplifier failure: {e}')
    return f, model_onnx

这段代码的功能是将YOLOv5模型导出为ONNX格式,以便于在不同的平台上进行推理。下面是逐步的分解和详细解释:

  1. 函数定义

    def export_onnx(model, im, file, opset, dynamic, simplify, prefix=colorstr('ONNX:')):
    
    • model: 需要导出的YOLOv5模型。
    • im: 输入数据,通常是一个张量,代表一批图像。
    • file: 导出后保存的文件路径。
    • opset: ONNX操作集版本。
    • dynamic: 布尔值,指示是否采用动态形状导出。动态形状可以让模型在推理时接收不同尺寸的输入。
    • simplify: 布尔值,指示是否在导出后简化模型。
    • prefix: 记录日志时使用的前缀。
  2. 检测ONNX要求

    check_requirements('onnx')
    import onnx
    
    • 使用check_requirements函数确认已安装ONNX库,如果没有则抛出异常。
  3. 日志记录

    LOGGER.info(f'\n{prefix} starting export with onnx {onnx.__version__}...')
    
    • 记录开始导出的日志信息。
  4. 设置输出文件路径

    f = file.with_suffix('.onnx')
    
  5. 定义输出名称

    output_names = ['output0', 'output1'] if isinstance(model, SegmentationModel) else ['output0']
    
    • 如果模型是分割模型,则输出两个名称,否则只输出一个。
  6. 动态形状处理

    if dynamic:
        dynamic = {'images': {0: 'batch', 2: 'height', 3: 'width'}}  # shape(1,3,640,640)
        ...
    
    • 根据模型类型和动态标志设置相应的模型输出的动态维度。
  7. 导出模型

    torch.onnx.export(
        model.cpu() if dynamic else model,
        im.cpu() if dynamic else im,
        f,
        verbose=False,
        opset_version=opset,
        do_constant_folding=True,
        input_names=['images'],
        output_names=output_names,
        dynamic_axes=dynamic or None)
    
    • 使用torch.onnx.export函数将模型导出为ONNX格式,针对动态形状和输入/输出名称进行了设置。
  8. 模型检查

    model_onnx = onnx.load(f)
    onnx.checker.check_model(model_onnx)
    
    • 加载导出的ONNX模型并进行检查以确保模型格式正确。
  9. 添加元数据

    d = {'stride': int(max(model.stride)), 'names': model.names}
    for k, v in d.items():
        meta = model_onnx.metadata_props.add()
        meta.key, meta.value = k, str(v)
    onnx.save(model_onnx, f)
    
    • 记录模型的元数据,包括步幅和类名,并将这些信息保存回模型文件中。
  10. 模型简化

    if simplify:
        ...
    
    • 如果simplify标志为真,则尝试简化模型。简化可以减少模型的复杂性,提高推理的速度。
  11. 返回导出的文件和模型

    return f, model_onnx
    
    • 返回导出的ONNX文件路径和加载的ONNX模型对象。

这段代码的主要功能是将YOLOv5模型导出为ONNX格式,为不同的推理平台(如TensorFlow、ONNX Runtime等)提供支持。它包括检查依赖关系、处理动态输入形状、设置输出信息、检查模型有效性、添加元数据以及可选的模型简化等步骤。通过这些步骤,用户能够有效地将训练好的模型转化为可用于生产和部署的格式,便于在多种硬件和框架中运行。

 

6.5 导出为openvino格式

def export_openvino(file, metadata, half, prefix=colorstr('OpenVINO:')):
    # YOLOv5 OpenVINO export
    check_requirements('openvino-dev')  # requires openvino-dev: https://pypi.org/project/openvino-dev/
    import openvino.inference_engine as ie

    LOGGER.info(f'\n{prefix} starting export with openvino {ie.__version__}...')
    f = str(file).replace('.pt', f'_openvino_model{os.sep}')

    cmd = f"mo --input_model {file.with_suffix('.onnx')} --output_dir {f} --data_type {'FP16' if half else 'FP32'}"
    subprocess.run(cmd.split(), check=True, env=os.environ)  # export
    yaml_save(Path(f) / file.with_suffix('.yaml').name, metadata)  # add metadata.yaml
    return f, None

这段代码是一个用于将YOLOv5模型导出为OpenVINO格式的函数。下面是对该代码的逐步分解和详细解释:

函数定义

def export_openvino(file, metadata, half, prefix=colorstr('OpenVINO:')):
  • 参数说明

    • file: 指定待导出的模型文件路径。

    • metadata: 模型的元数据,通常用于模型描述或配置。

    • half: 布尔值,指示是否使用半精度(FP16)进行导出。

    • prefix: 可选参数,用于定义日志信息的前缀,默认为"OpenVINO:"。

检查依赖

check_requirements('openvino-dev')
  • 调用check_requirements函数确保当前环境中已安装OpenVINO开发包(openvino-dev)。如果没有,会提示安装。

导入OpenVINO库

import openvino.inference_engine as ie
  • 导入OpenVINO推理引擎库,以便在后续的代码中使用。

开始导出日志

LOGGER.info(f'\n{prefix} starting export with openvino {ie.__version__}...')
  • 记录信息日志,显示正在开始导出过程,并指明使用的OpenVINO版本。

设置输出路径

f = str(file).replace('.pt', f'_openvino_model{os.sep}')
  • 生成输出路径f。将输入文件名的扩展名.pt替换成_openvino_model/,以便用来存放导出的模型文件。

构建导出命令

cmd = f"mo --input_model {file.with_suffix('.onnx')} --output_dir {f} --data_type {'FP16' if half else 'FP32'}"
  • 创建命令行指令,用于调用OpenVINO的模型优化工具(mo)。

    • --input_model: 指定输入模型,这里是使用.onnx格式的模型文件。

    • --output_dir: 指定输出目录。

    • --data_type: 根据half参数决定使用FP16还是FP32数据类型。

执行导出命令

subprocess.run(cmd.split(), check=True, env=os.environ)
  • 使用subprocess.run()执行上面构建的命令。check=True意味着如果命令返回非零状态将抛出异常,env=os.environ确保使用当前环境变量。

保存元数据

yaml_save(Path(f) / file.with_suffix('.yaml').name, metadata)
  • 将给定的metadata保存到指定路径的YAML文件中,以便随模型一起提供额外信息。

返回值

return f, None
  • 函数返回导出的文件路径fNone(第二个返回值未被使用)。

这段代码的主要功能是将YOLOv5模型导出为OpenVINO格式。通过调用OpenVINO的模型优化工具,它支持两种数据类型(FP16或FP32)的选择,同时还保存了模型的元数据,以确保模型的使用和后续推理过程具有足够的上下文信息。此功能是模型部署过程中的一部分,便于在不同硬件上进行更优化的推理。

 

6.6 导出为PaddlePaddle格式 

def export_paddle(model, im, file, metadata, prefix=colorstr('PaddlePaddle:')):
    # YOLOv5 Paddle export
    check_requirements(('paddlepaddle', 'x2paddle'))
    import x2paddle
    from x2paddle.convert import pytorch2paddle

    LOGGER.info(f'\n{prefix} starting export with X2Paddle {x2paddle.__version__}...')
    f = str(file).replace('.pt', f'_paddle_model{os.sep}')

    pytorch2paddle(module=model, save_dir=f, jit_type='trace', input_examples=[im])  # export
    yaml_save(Path(f) / file.with_suffix('.yaml').name, metadata)  # add metadata.yaml
    return f, None

 

这段代码定义了一个名为 export_paddle 的函数,其主要目的是将 YOLOv5 模型导出为 PaddlePaddle 格式。以下是对此段代码的逐步解释:

  1. 函数定义和参数:

    def export_paddle(model, im, file, metadata, prefix=colorstr('PaddlePaddle:')):
    
    • model: 需要导出的 PyTorch 模型。
    • im: 用于示例的输入图像,通常是一个张量。
    • file: 保存导出模型的文件路径。
    • metadata: 模型的元数据(如类别名称等)。
    • prefix: 日志前缀,用于输出中标识导出过程。
  2. 依赖检查:

    check_requirements(('paddlepaddle', 'x2paddle'))
    
    • 调用 check_requirements 函数以确保所需的库(paddlepaddle 和 x2paddle)已经安装。如果未安装,函数将抛出异常。
  3. 导入必要的库:

    import x2paddle
    from x2paddle.convert import pytorch2paddle
    
    • 导入 x2paddle 模块及其内部的 pytorch2paddle 函数,用于执行库间的模型转换。
  4. 日志记录:

    LOGGER.info(f'\n{prefix} starting export with X2Paddle {x2paddle.__version__}...')
    
    • 使用 LOGGER 输出日志,指示导出过程开始,并显示 x2paddle 的版本。
  5. 构建保存路径:

    f = str(file).replace('.pt', f'_paddle_model{os.sep}')
    
    • 根据提供的文件路径修改文件名,准备保存 PaddlePaddle 格式的模型文件。将文件扩展名从 .pt 替换为 _paddle_model,并添加系统特定的目录分隔符。
  6. 模型转换:

    pytorch2paddle(module=model, save_dir=f, jit_type='trace', input_examples=[im])  # export
    
    • 调用 pytorch2paddle 函数,将 PyTorch 模型转换为 PaddlePaddle 格式,并保存到指定的目录。
    • jit_type='trace' 表示通过追踪输入与模型的关系进行导出。
    • input_examples 参数提供示例输入数据以帮助转换。
  7. 保存元数据:

    yaml_save(Path(f) / file.with_suffix('.yaml').name, metadata)  # add metadata.yaml
    
    • 使用 yaml_save 函数将模型的元数据保存为 YAML 格式,文件名与模型文件一致,便于后期查看。
  8. 返回结果:

    return f, None
    
    • 返回导出模型文件的路径 f 和 None(表示没有额外的模型对象返回)。

该代码段的主要功能是将 YOLOv5 的 PyTorch 模型导出为 PaddlePaddle 格式,便于在 PaddlePaddle 框架中进行推理和部署。它检查所需的库,记录导出过程的日志,处理模型转换,以及保存与模型相关的元数据,确保导出过程顺利进行并符合规范。

6.7 导出为CoreML格式

def export_coreml(model, im, file, int8, half, prefix=colorstr('CoreML:')):
    # YOLOv5 CoreML export
    check_requirements('coremltools')
    import coremltools as ct

    LOGGER.info(f'\n{prefix} starting export with coremltools {ct.__version__}...')
    f = file.with_suffix('.mlmodel')

    ts = torch.jit.trace(model, im, strict=False)  # TorchScript model
    ct_model = ct.convert(ts, inputs=[ct.ImageType('image', shape=im.shape, scale=1 / 255, bias=[0, 0, 0])])
    bits, mode = (8, 'kmeans_lut') if int8 else (16, 'linear') if half else (32, None)
    if bits < 32:
        if MACOS:  # quantization only supported on macOS
            with warnings.catch_warnings():
                warnings.filterwarnings("ignore", category=DeprecationWarning)  # suppress numpy==1.20 float warning
                ct_model = ct.models.neural_network.quantization_utils.quantize_weights(ct_model, bits, mode)
        else:
            print(f'{prefix} quantization only supported on macOS, skipping...')
    ct_model.save(f)
    return f, ct_model

 

这段代码是一个功能函数,用于将YOLOv5模型导出到CoreML格式,以便在Apple的设备上进行推理。下面是对代码的逐步分解和详细解释:

  1. 函数定义和参数

    def export_coreml(model, im, file, int8, half, prefix=colorstr('CoreML:')):
    
    • model:要导出的YOLOv5模型。
    • im:输入图像,用于模型跟踪和转换。
    • file:输出文件路径,不带扩展名。
    • int8:布尔值,指示是否使用INT8量化。
    • half:布尔值,指示是否使用FP16半精度。
    • prefix:日志信息的前缀,用于标识输出格式。
  2. 检查依赖项

    check_requirements('coremltools')
    import coremltools as ct
    
    • 该行调用check_requirements函数,确保coremltools库已安装,然后将其导入为ct
  3. 日志记录

    LOGGER.info(f'\n{prefix} starting export with coremltools {ct.__version__}...')
    
    • 记录开始导出的信息,包括使用的coremltools版本。
  4. 设置输出路径

    f = file.with_suffix('.mlmodel')
    
    • 创建输出文件路径,将其扩展名设置为.mlmodel,这是CoreML模型的标准扩展名。
  5. TorchScript模型转换

    ts = torch.jit.trace(model, im, strict=False)
    
    • 使用TorchScript将PyTorch模型转换为可追踪脚本,允许它被导出。这里im是示例输入。
  6. CoreML转换

    ct_model = ct.convert(ts, inputs=[ct.ImageType('image', shape=im.shape, scale=1 / 255, bias=[0, 0, 0])])
    
    • 将TorchScript模型转换为CoreML模型。inputs参数指定输入的形状、缩放因子和偏置。
  7. 量化设置

    bits, mode = (8, 'kmeans_lut') if int8 else (16, 'linear') if half else (32, None)
    
    • 根据int8half参数确定量化的精度。bits表示位数,mode指定量化模式。
  8. 量化处理

    if bits < 32:
        if MACOS:
            with warnings.catch_warnings():
                warnings.filterwarnings("ignore", category=DeprecationWarning)
                ct_model = ct.models.neural_network.quantization_utils.quantize_weights(ct_model, bits, mode)
        else:
            print(f'{prefix} quantization only supported on macOS, skipping...')
    
    • 如果选择了量化,并且运行环境是macOS,则进行模型权重的量化处理。如果不是macOS,则跳过量化并输出警告信息。
  9. 保存CoreML模型

    ct_model.save(f)
    return f, ct_model
    
    • 最后,将生成的CoreML模型保存到文件中,并返回文件路径和模型对象。

上述代码的主要功能是将YOLOv5的PyTorch模型转换为CoreML格式,以便在苹果设备上使用。它通过TorchScript对模型进行追踪,然后使用coremltools库进行转换,并支持INT8和FP16量化以优化模型性能。转换完成后,模型将以.mlmodel格式保存,使其可以方便地在iOS和macOS应用中使用。

6.8 导出为TensorRT格式

def export_engine(model, im, file, half, dynamic, simplify, workspace=4, verbose=False, prefix=colorstr('TensorRT:')):
    # YOLOv5 TensorRT export https://developer.nvidia.com/tensorrt
    assert im.device.type != 'cpu', 'export running on CPU but must be on GPU, i.e. `python export.py --device 0`'
    try:
        import tensorrt as trt
    except Exception:
        if platform.system() == 'Linux':
            check_requirements('nvidia-tensorrt', cmds='-U --index-url https://pypi.ngc.nvidia.com')
        import tensorrt as trt

    if trt.__version__[0] == '7':  # TensorRT 7 handling https://github.com/ultralytics/yolov5/issues/6012
        grid = model.model[-1].anchor_grid
        model.model[-1].anchor_grid = [a[..., :1, :1, :] for a in grid]
        export_onnx(model, im, file, 12, dynamic, simplify)  # opset 12
        model.model[-1].anchor_grid = grid
    else:  # TensorRT >= 8
        check_version(trt.__version__, '8.0.0', hard=True)  # require tensorrt>=8.0.0
        export_onnx(model, im, file, 12, dynamic, simplify)  # opset 12
    onnx = file.with_suffix('.onnx')

    LOGGER.info(f'\n{prefix} starting export with TensorRT {trt.__version__}...')
    assert onnx.exists(), f'failed to export ONNX file: {onnx}'
    f = file.with_suffix('.engine')  # TensorRT engine file
    logger = trt.Logger(trt.Logger.INFO)
    if verbose:
        logger.min_severity = trt.Logger.Severity.VERBOSE

    builder = trt.Builder(logger)
    config = builder.create_builder_config()
    config.max_workspace_size = workspace * 1 << 30
    # config.set_memory_pool_limit(trt.MemoryPoolType.WORKSPACE, workspace << 30)  # fix TRT 8.4 deprecation notice

    flag = (1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH))
    network = builder.create_network(flag)
    parser = trt.OnnxParser(network, logger)
    if not parser.parse_from_file(str(onnx)):
        raise RuntimeError(f'failed to load ONNX file: {onnx}')

    inputs = [network.get_input(i) for i in range(network.num_inputs)]
    outputs = [network.get_output(i) for i in range(network.num_outputs)]
    for inp in inputs:
        LOGGER.info(f'{prefix} input "{inp.name}" with shape{inp.shape} {inp.dtype}')
    for out in outputs:
        LOGGER.info(f'{prefix} output "{out.name}" with shape{out.shape} {out.dtype}')

    if dynamic:
        if im.shape[0] <= 1:
            LOGGER.warning(f"{prefix} WARNING ⚠️ --dynamic model requires maximum --batch-size argument")
        profile = builder.create_optimization_profile()
        for inp in inputs:
            profile.set_shape(inp.name, (1, *im.shape[1:]), (max(1, im.shape[0] // 2), *im.shape[1:]), im.shape)
        config.add_optimization_profile(profile)

    LOGGER.info(f'{prefix} building FP{16 if builder.platform_has_fast_fp16 and half else 32} engine as {f}')
    if builder.platform_has_fast_fp16 and half:
        config.set_flag(trt.BuilderFlag.FP16)
    with builder.build_engine(network, config) as engine, open(f, 'wb') as t:
        t.write(engine.serialize())
    return f, None
  1. 函数定义

    def export_engine(model, im, file, half, dynamic, simplify, workspace=4, verbose=False, prefix=colorstr('TensorRT:')):
    

    该函数用于将YOLOv5模型导出为TensorRT引擎格式。参数包括:

    • model:待导出的YOLOv5模型。
    • im:输入图像的张量。
    • file:输出文件名。
    • half:指示是否使用FP16精度。
    • dynamic:指示模型是否支持动态输入形状。
    • simplify:指示是否简化模型。
    • workspace:TensorRT的工作区大小,默认为4GB。
    • verbose:是否启用详细日志。
    • prefix:日志前缀,默认为'TensorRT:'。
  2. 设备检查

    assert im.device.type != 'cpu', 'export running on CPU but must be on GPU, i.e. `python export.py --device 0`'
    

    确保输入图像在GPU上,而不是CPU。

  3. 导入TensorRT库

    try:
        import tensorrt as trt
    except Exception:
        if platform.system() == 'Linux':
            check_requirements('nvidia-tensorrt', cmds='-U --index-url https://pypi.ngc.nvidia.com')
        import tensorrt as trt
    

    尝试导入TensorRT库,如果导入失败,并且是Linux系统,检查所需的TensorRT版本。

  4. 处理TensorRT版本

    if trt.__version__[0] == '7':
        ...
    else:
        check_version(trt.__version__, '8.0.0', hard=True)
        ...
    

    根据TensorRT的版本处理不同的导出流程。对于版本7,调整模型的anchor_grid;对于版本8及以上,直接导出ONNX模型。

  5. 导出ONNX模型

    export_onnx(model, im, file, 12, dynamic, simplify)
    

    调用export_onnx函数,将模型导出为ONNX格式(opset版本为12)。

  6. 构建TensorRT引擎

    LOGGER.info(f'\n{prefix} starting export with TensorRT {trt.__version__}...')
    assert onnx.exists(), f'failed to export ONNX file: {onnx}'
    f = file.with_suffix('.engine')
    

    记录日志并确认ONNX文件存在,准备输出TensorRT引擎文件。

  7. 创建TensorRT构建器和配置

    builder = trt.Builder(logger)
    config = builder.create_builder_config()
    config.max_workspace_size = workspace * 1 << 30
    

    创建TensorRT构建器、配置其工作区大小。

  8. 创建网络

    flag = (1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH))
    network = builder.create_network(flag)
    parser = trt.OnnxParser(network, logger)
    if not parser.parse_from_file(str(onnx)):
        raise RuntimeError(f'failed to load ONNX file: {onnx}')
    

    创建网络并解析ONNX文件,确保其有效。

  9. 检查输入和输出

    inputs = [network.get_input(i) for i in range(network.num_inputs)]
    outputs = [network.get_output(i) for i in range(network.num_outputs)]
    

    获取网络的输入和输出,并记录它们的形状和数据类型。

  10. 动态输入配置

    if dynamic:
        ...
        profile = builder.create_optimization_profile()
        ...
        config.add_optimization_profile(profile)
    

    如果支持动态输入,根据输入的不同形状创建优化配置。

  11. 构建和保存引擎

    with builder.build_engine(network, config) as engine, open(f, 'wb') as t:
        t.write(engine.serialize())
    return f, None
    

    使用配置构建TensorRT引擎,并将其序列化为文件。

此代码定义了一个export_engine()函数,主要功能是将训练好的YOLOv5模型导出为TensorRT格式,以便在推理过程中提高性能。它首先确保输入图像在GPU上,并导入TensorRT库。接着,根据TensorRT版本分别处理导出过程,首先导出模型为ONNX格式,然后构建TensorRT引擎并配置其输入输出。最后,保存生成的TensorRT引擎文件,以便后续使用。该函数的设计能够处理不同的TensorRT版本和动态输入情况,为模型推理提供高效的解决方案。

6.9 导出为TensorFlow的SavedModel格式

def export_saved_model(model,
                       im,
                       file,
                       dynamic,
                       tf_nms=False,
                       agnostic_nms=False,
                       topk_per_class=100,
                       topk_all=100,
                       iou_thres=0.45,
                       conf_thres=0.25,
                       keras=False,
                       prefix=colorstr('TensorFlow SavedModel:')):
    # YOLOv5 TensorFlow SavedModel export
    try:
        import tensorflow as tf
    except Exception:
        check_requirements(f"tensorflow{'' if torch.cuda.is_available() else '-macos' if MACOS else '-cpu'}")
        import tensorflow as tf
    from tensorflow.python.framework.convert_to_constants import convert_variables_to_constants_v2

    from models.tf import TFModel

    LOGGER.info(f'\n{prefix} starting export with tensorflow {tf.__version__}...')
    f = str(file).replace('.pt', '_saved_model')
    batch_size, ch, *imgsz = list(im.shape)  # BCHW

    tf_model = TFModel(cfg=model.yaml, model=model, nc=model.nc, imgsz=imgsz)
    im = tf.zeros((batch_size, *imgsz, ch))  # BHWC order for TensorFlow
    _ = tf_model.predict(im, tf_nms, agnostic_nms, topk_per_class, topk_all, iou_thres, conf_thres)
    inputs = tf.keras.Input(shape=(*imgsz, ch), batch_size=None if dynamic else batch_size)
    outputs = tf_model.predict(inputs, tf_nms, agnostic_nms, topk_per_class, topk_all, iou_thres, conf_thres)
    keras_model = tf.keras.Model(inputs=inputs, outputs=outputs)
    keras_model.trainable = False
    keras_model.summary()
    if keras:
        keras_model.save(f, save_format='tf')
    else:
        spec = tf.TensorSpec(keras_model.inputs[0].shape, keras_model.inputs[0].dtype)
        m = tf.function(lambda x: keras_model(x))  # full model
        m = m.get_concrete_function(spec)
        frozen_func = convert_variables_to_constants_v2(m)
        tfm = tf.Module()
        tfm.__call__ = tf.function(lambda x: frozen_func(x)[:4] if tf_nms else frozen_func(x), [spec])
        tfm.__call__(im)
        tf.saved_model.save(tfm,
                            f,
                            options=tf.saved_model.SaveOptions(experimental_custom_gradients=False) if check_version(
                                tf.__version__, '2.6') else tf.saved_model.SaveOptions())
    return f, keras_model

这个函数的主要功能是将YOLOv5模型导出为TensorFlow的SavedModel格式,下面是逐步分解和详细解释:

  1. 函数定义与参数

    def export_saved_model(model,
                           im,
                           file,
                           dynamic,
                           tf_nms=False,
                           agnostic_nms=False,
                           topk_per_class=100,
                           topk_all=100,
                           iou_thres=0.45,
                           conf_thres=0.25,
                           keras=False,
                           prefix=colorstr('TensorFlow SavedModel:')):
    
    • model: 需要导出的YOLOv5模型。
    • im: 输入的图像批量,该形状将用于确定模型的输入大小。
    • file: 导出模型的目标文件路径。
    • dynamic: 是否使用动态轴(主要用于ONNX导出)来允许不同批大小的输入。
    • 其他参数 tf_nmsagnostic_nmstopk_per_classtopk_alliou_thresconf_thres 用于控制TensorFlow中的非极大值抑制(NMS)的具体行为。
    • keras: 布尔值,指示是否将模型保存为Keras格式。
  2. 导入TensorFlow库

    try:
        import tensorflow as tf
    except Exception:
        check_requirements(f"tensorflow{'' if torch.cuda.is_available() else '-macos' if MACOS else '-cpu'}")
        import tensorflow as tf
    
    • 尝试导入TensorFlow,如果失败,则检查要求并导入适合当前系统的平台版本的TensorFlow。
  3. 日志信息

    LOGGER.info(f'\n{prefix} starting export with tensorflow {tf.__version__}...')
    
    • 记录当前TensorFlow版本的信息,用于输出。
  4. 设置导出文件路径和图像形状

    f = str(file).replace('.pt', '_saved_model')
    batch_size, ch, *imgsz = list(im.shape)  # BCHW
    
    • 根据输入图像im的形状获取批大小和通道数。这里假设图像的形状为 BCHW (批大小, 通道数, 高, 宽)
  5. 初始化TensorFlow模型

    tf_model = TFModel(cfg=model.yaml, model=model, nc=model.nc, imgsz=imgsz)
    
    • 创建一个新的TFModel实例,它将载入YOLOv5模型配置、模型权重及输入图像大小。
  6. 生成示例输入

    im = tf.zeros((batch_size, *imgsz, ch))  # BHWC order for TensorFlow
    
    • 创建一个形状为 (batch_size, 高, 宽, 通道数) 的全0张量,作为TensorFlow模型的输入示例。
  7. 模型预测

    _ = tf_model.predict(im, tf_nms, agnostic_nms, topk_per_class, topk_all, iou_thres, conf_thres)
    
    • 进行一次预测以确保模型准备好进行导出。
  8. 定义TensorFlow输入与输出

    inputs = tf.keras.Input(shape=(*imgsz, ch), batch_size=None if dynamic else batch_size)
    outputs = tf_model.predict(inputs, tf_nms, agnostic_nms, topk_per_class, topk_all, iou_thres, conf_thres)
    
    • 定义模型的输入,以及将输入传给tf_model并获得输出。
  9. 构建Keras模型

    keras_model = tf.keras.Model(inputs=inputs, outputs=outputs)
    keras_model.trainable = False
    keras_model.summary()
    
    • 创建一个Keras模型,设置为不可训练,调用summary以打印模型信息。
  10. 保存模型

    if keras:
        keras_model.save(f, save_format='tf')
    else:
        ...
    
    • 如果keras为真,将模型直接保存为Keras格式。否则,将模型转换为SavedModel格式进行保存。
  11. 冻结模型

    spec = tf.TensorSpec(keras_model.inputs[0].shape, keras_model.inputs[0].dtype)
    m = tf.function(lambda x: keras_model(x))  # full model
    ...
    tf.saved_model.save(tfm, f, options=...)
    
    • 创建一个TensorFlow函数以冻结模型并保存为SavedModel格式,允许在后续推理中使用。

这段代码的主要功能是将YOLOv5模型导出为TensorFlow的SavedModel格式。它通过将模型的输入和输出重新定义为TensorFlow的标准格式,提供了在TensorFlow框架中使用YOLOv5进行推理的能力。该过程包括模型初始化、生成输入、模型预测、构建Keras模型以及最终的模型保存,确保导出后的模型能够在各种应用中被有效使用。

 

6.10 导出为TensorFlow Lite格式

def export_tflite(keras_model, im, file, int8, data, nms, agnostic_nms, prefix=colorstr('TensorFlow Lite:')):
    # YOLOv5 TensorFlow Lite export
    import tensorflow as tf

    LOGGER.info(f'\n{prefix} starting export with tensorflow {tf.__version__}...')
    batch_size, ch, *imgsz = list(im.shape)  # BCHW
    f = str(file).replace('.pt', '-fp16.tflite')

    converter = tf.lite.TFLiteConverter.from_keras_model(keras_model)
    converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS]
    converter.target_spec.supported_types = [tf.float16]
    converter.optimizations = [tf.lite.Optimize.DEFAULT]
    if int8:
        from models.tf import representative_dataset_gen
        dataset = LoadImages(check_dataset(check_yaml(data))['train'], img_size=imgsz, auto=False)
        converter.representative_dataset = lambda: representative_dataset_gen(dataset, ncalib=100)
        converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
        converter.target_spec.supported_types = []
        converter.inference_input_type = tf.uint8  # or tf.int8
        converter.inference_output_type = tf.uint8  # or tf.int8
        converter.experimental_new_quantizer = True
        f = str(file).replace('.pt', '-int8.tflite')
    if nms or agnostic_nms:
        converter.target_spec.supported_ops.append(tf.lite.OpsSet.SELECT_TF_OPS)

    tflite_model = converter.convert()
    open(f, "wb").write(tflite_model)
    return f, None

这段代码是用于将Keras模型导出为TensorFlow Lite格式的函数,具体功能是将训练好的YOLOv5模型转换为适合在移动设备上运行的轻量级格式。下面逐步分解并详细解释代码:

  1. 函数定义及参数

    def export_tflite(keras_model, im, file, int8, data, nms, agnostic_nms, prefix=colorstr('TensorFlow Lite:')):
    
    • keras_model:待转换的Keras模型。
    • im:输入数据,用于确定模型的输入形状。
    • file:输出文件的路径,包含模型名称。
    • int8:表示是否进行INT8量化(数据类型压缩),以减少模型大小和提高推理速度。
    • data:数据集路径,用于准备代表性数据集。
    • nmsagnostic_nms:表示是否将非极大值抑制(NMS)应用于模型。
    • prefix:用于日志记录的前缀。
  2. 导入TensorFlow

    import tensorflow as tf
    
  3. 日志记录

    LOGGER.info(f'\n{prefix} starting export with tensorflow {tf.__version__}...')
    

    记录开始导出过程的信息。

  4. 输入数据处理

    batch_size, ch, *imgsz = list(im.shape)  # BCHW
    

    将输入数据的尺寸解析为批量大小(batch_size)、通道数(ch)及图像大小(imgsz)。

  5. 指定输出文件名

    f = str(file).replace('.pt', '-fp16.tflite')
    

    将输出文件名设置为以-fp16.tflite结尾,表示该模型支持FP16格式。

  6. 创建TensorFlow Lite转换器

    converter = tf.lite.TFLiteConverter.from_keras_model(keras_model)
    

    利用指定的Keras模型创建一个TFLite转换器。

  7. 设置转换器参数

    converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS]
    converter.target_spec.supported_types = [tf.float16]
    converter.optimizations = [tf.lite.Optimize.DEFAULT]
    

    设置支持的操作和数据类型,并使用默认优化策略。

  8. INT8量化支持

    if int8:
        from models.tf import representative_dataset_gen
        dataset = LoadImages(check_dataset(check_yaml(data))['train'], img_size=imgsz, auto=False)
        converter.representative_dataset = lambda: representative_dataset_gen(dataset, ncalib=100)
        ...
    

    int8为真时,支持INT8量化:

    • 导入用于生成代表性数据集的函数。
    • 加载训练数据集并设置用于量化的数据集。
    • 指定转换器的输入和输出类型为tf.uint8(即无符号8位整数)。
  9. 添加非极大值抑制(NMS)支持

    if nms or agnostic_nms:
        converter.target_spec.supported_ops.append(tf.lite.OpsSet.SELECT_TF_OPS)
    

    如果需要NMS,则将相应的操作添加到支持列表中。

  10. 模型转换与保存

    tflite_model = converter.convert()
    open(f, "wb").write(tflite_model)
    
    • 通过转换器转换模型为TensorFlow Lite格式。
    • 将转换后的模型写入到指定的文件中。
  11. 返回输出文件名

    return f, None
    

    返回生成的TensorFlow Lite模型文件的路径。

该函数export_tflite的主要功能是将Keras训练好的YOLOv5模型转换为TensorFlow Lite格式,以便于在移动设备等资源受限的环境中进行高效推理。它支持FP16和INT8量化,能够根据输入数据自动调整模型的形状,并且可以为模型添加非极大值抑制(NMS)。最终,它将转换后的模型保存为.tflite文件,供后续使用。

 

6.11 导出为 Edge TPU所需的 格式

def export_edgetpu(file, prefix=colorstr('Edge TPU:')):
    # YOLOv5 Edge TPU export https://coral.ai/docs/edgetpu/models-intro/
    cmd = 'edgetpu_compiler --version'
    help_url = 'https://coral.ai/docs/edgetpu/compiler/'
    assert platform.system() == 'Linux', f'export only supported on Linux. See {help_url}'
    if subprocess.run(f'{cmd} >/dev/null', shell=True).returncode != 0:
        LOGGER.info(f'\n{prefix} export requires Edge TPU compiler. Attempting install from {help_url}')
        sudo = subprocess.run('sudo --version >/dev/null', shell=True).returncode == 0  # sudo installed on system
        for c in (
                'curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -',
                'echo "deb https://packages.cloud.google.com/apt coral-edgetpu-stable main" | sudo tee /etc/apt/sources.list.d/coral-edgetpu.list',
                'sudo apt-get update', 'sudo apt-get install edgetpu-compiler'):
            subprocess.run(c if sudo else c.replace('sudo ', ''), shell=True, check=True)
    ver = subprocess.run(cmd, shell=True, capture_output=True, check=True).stdout.decode().split()[-1]

    LOGGER.info(f'\n{prefix} starting export with Edge TPU compiler {ver}...')
    f = str(file).replace('.pt', '-int8_edgetpu.tflite')  # Edge TPU model
    f_tfl = str(file).replace('.pt', '-int8.tflite')  # TFLite model

    cmd = f"edgetpu_compiler -s -d -k 10 --out_dir {file.parent} {f_tfl}"
    subprocess.run(cmd.split(), check=True)
    return f, None

 

面是对提供代码的逐步分解和详细解释,以及对此代码的总结。

  1. 函数定义

    def export_edgetpu(file, prefix=colorstr('Edge TPU:')):
    

    该函数被定义为 export_edgetpu,其目的是将 YOLOv5 模型导出为 Edge TPU 可用的格式。函数接受两个参数:file 是要导出的模型文件,prefix 是用于日志输出的前缀,默认值为 'Edge TPU:'。

  2. 命令和帮助链接

    cmd = 'edgetpu_compiler --version'
    help_url = 'https://coral.ai/docs/edgetpu/compiler/'
    

    cmd 变量包含要运行的命令,以获取 Edge TPU 编译器的版本。help_url 提供了相关的文档地址。

  3. 操作系统检查

    assert platform.system() == 'Linux', f'export only supported on Linux. See {help_url}'
    

    该行用来确保代码在 Linux 操作系统上运行,因为 Edge TPU 编译器目前仅支持 Linux。

  4. 编译器检查

    if subprocess.run(f'{cmd} >/dev/null', shell=True).returncode != 0:
    

    这是在检查 Edge TPU 编译器是否已安装。如果该命令的返回值不为 0,表示未找到编译器。

  5. 日志输出与安装尝试

    LOGGER.info(f'\n{prefix} export requires Edge TPU compiler. Attempting install from {help_url}')
    sudo = subprocess.run('sudo --version >/dev/null', shell=True).returncode == 0
    

    如果编译器未安装,则记录日志信息,并检查系统上是否有 sudo 权限,以便进行安装。

  6. 安装 Edge TPU 编译器

    for c in (
            'curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -',
            'echo "deb https://packages.cloud.google.com/apt coral-edgetpu-stable main" | sudo tee /etc/apt/sources.list.d/coral-edgetpu.list',
            'sudo apt-get update', 'sudo apt-get install edgetpu-compiler'):
        subprocess.run(c if sudo else c.replace('sudo ', ''), shell=True, check=True)
    

    如果没有编译器,将依次执行一系列命令来安装 Edge TPU 编译器,包括添加 GPG 密钥、更新源列表和安装编译器。

  7. 获取编译器版本

    ver = subprocess.run(cmd, shell=True, capture_output=True, check=True).stdout.decode().split()[-1]
    

    运行之前定义的命令以获得安装的 Edge TPU 编译器的版本。

  8. 日志输出开始导出

    LOGGER.info(f'\n{prefix} starting export with Edge TPU compiler {ver}...')
    

    记录开始使用 Edge TPU 编译器出口的日志信息。

  9. 文件命名

    f = str(file).replace('.pt', '-int8_edgetpu.tflite')
    f_tfl = str(file).replace('.pt', '-int8.tflite')
    

    分别为生成的 Edge TPU 模型文件和 TFLite 文件命名。

  10. 编译命令与执行

    cmd = f"edgetpu_compiler -s -d -k 10 --out_dir {file.parent} {f_tfl}"
    subprocess.run(cmd.split(), check=True)
    

    使用编译器将 TFLite 模型文件编译为适用于 Edge TPU 的格式,并将其输出到指定目录。

  11. 返回值

    return f, None
    

    返回生成的 Edge TPU 文件名,第二个返回值为 None

该代码块的主要功能是将 YOLOv5 模型导出为 Edge TPU 所需的格式,具体步骤包括:

  • 检查操作系统和 Edge TPU 编译器的安装。
  • 根据需要安装 Edge TPU 编译器。
  • 运行编译命令将模型转换为适合 Edge TPU 的格式并生成对应的文件。

最终返回导出的文件路径,以供进一步使用。这使得 YOLOv5 模型能够在 Edge TPU 设备上实现高效推理,充分发挥其性能优势。

6.12 导出为可在浏览器中使用的 TensorFlow.js格式

def export_tfjs(file, prefix=colorstr('TensorFlow.js:')):
    # YOLOv5 TensorFlow.js export
    check_requirements('tensorflowjs')
    import tensorflowjs as tfjs

    LOGGER.info(f'\n{prefix} starting export with tensorflowjs {tfjs.__version__}...')
    f = str(file).replace('.pt', '_web_model')  # js dir
    f_pb = file.with_suffix('.pb')  # *.pb path
    f_json = f'{f}/model.json'  # *.json path

    cmd = f'tensorflowjs_converter --input_format=tf_frozen_model ' \
          f'--output_node_names=Identity,Identity_1,Identity_2,Identity_3 {f_pb} {f}'
    subprocess.run(cmd.split())

    json = Path(f_json).read_text()
    with open(f_json, 'w') as j:  # sort JSON Identity_* in ascending order
        subst = re.sub(
            r'{"outputs": {"Identity.?.?": {"name": "Identity.?.?"}, '
            r'"Identity.?.?": {"name": "Identity.?.?"}, '
            r'"Identity.?.?": {"name": "Identity.?.?"}, '
            r'"Identity.?.?": {"name": "Identity.?.?"}}}', r'{"outputs": {"Identity": {"name": "Identity"}, '
            r'"Identity_1": {"name": "Identity_1"}, '
            r'"Identity_2": {"name": "Identity_2"}, '
            r'"Identity_3": {"name": "Identity_3"}}}', json)
        j.write(subst)
    return f, None
def export_tfjs(file, prefix=colorstr('TensorFlow.js:')):
  • 定义函数:该函数的目的是将模型导出为 TensorFlow.js 格式。它接受两个参数:file代表模型文件路径,prefix是日志信息的前缀,默认值为'TensorFlow.js:'。
    check_requirements('tensorflowjs')
  • 检查依赖:调用 check_requirements 函数检查是否安装了 tensorflowjs 库,如果没有安装则会抛出错误。
    import tensorflowjs as tfjs
  • 导入库:导入 TensorFlow.js 的 Python 接口。
    LOGGER.info(f'\n{prefix} starting export with tensorflowjs {tfjs.__version__}...')
  • 日志记录:记录开始导出的信息,包括 TensorFlow.js 的版本号。
    f = str(file).replace('.pt', '_web_model')  # js dir
  • 生成输出目录:将输入的文件名中的 .pt 后缀替换为 _web_model,这将作为输出目录。
    f_pb = file.with_suffix('.pb')  # *.pb path
  • 生成冻结图路径:生成一个新的文件路径,用于保存 TensorFlow 的 PB 格式的冻结图,以便后续转换。
    f_json = f'{f}/model.json'  # *.json path
  • 生成 JSON 路径:定义最终模型的 JSON 文件路径,这个文件将包含模型的元信息。
    cmd = f'tensorflowjs_converter --input_format=tf_frozen_model ' \
          f'--output_node_names=Identity,Identity_1,Identity_2,Identity_3 {f_pb} {f}'
  • 命令构建:构建一个命令行字符串,用于调用 tensorflowjs_converter 进行模型格式转换。指定了输入格式为冻结模型,并定义了输出节点的名称。
    subprocess.run(cmd.split())
  • 运行命令:使用 subprocess.run 执行上面构建的命令,真正开始模型的导出过程。
    json = Path(f_json).read_text()
  • 读取 JSON 文件:读取刚刚创建的 JSON 文件的内容,以便进行后续的处理。
    with open(f_json, 'w') as j:  # sort JSON Identity_* in ascending order
  • 打开 JSON 文件:以写模式打开 JSON 文件,这里准备修改文件内容。
        subst = re.sub(
            r'{"outputs": {"Identity.?.?": {"name": "Identity.?.?"}, '
            r'"Identity.?.?": {"name": "Identity.?.?"}, '
            r'"Identity.?.?": {"name": "Identity.?.?"}, '
            r'"Identity.?.?": {"name": "Identity.?.?"}}}', r'{"outputs": {"Identity": {"name": "Identity"}, '
            r'"Identity_1": {"name": "Identity_1"}, '
            r'"Identity_2": {"name": "Identity_2"}, '
            r'"Identity_3": {"name": "Identity_3"}}}', json)
  • 正则表达式替换:这个正则替换用于将 JSON 中的输出节点的名称进行排序,使得输出结果更具一致性和可读性。
        j.write(subst)
  • 写入修改后的内容:将处理后的 JSON 内容写入文件。
    return f, None
  • 返回结果:函数最终返回输出目录路径和 None,表示导出成功。

这个 export_tfjs 函数的主要功能是将 YOLOv5 模型导出为可在浏览器中使用的 TensorFlow.js 格式。它首先检查所需的库是否存在,然后构建转换命令并执行,最后还对输出的 JSON 文件做了处理,确保输出节点名称的排序和一致性。这使得在 Web 应用中加载和使用模型更加便利。

 

6.13 导出为 TensorFlow Lite模型添加相关的元数据

def add_tflite_metadata(file, metadata, num_outputs):
    # Add metadata to *.tflite models per https://www.tensorflow.org/lite/models/convert/metadata
    with contextlib.suppress(ImportError):
        # check_requirements('tflite_support')
        from tflite_support import flatbuffers
        from tflite_support import metadata as _metadata
        from tflite_support import metadata_schema_py_generated as _metadata_fb

        tmp_file = Path('/tmp/meta.txt')
        with open(tmp_file, 'w') as meta_f:
            meta_f.write(str(metadata))

        model_meta = _metadata_fb.ModelMetadataT()
        label_file = _metadata_fb.AssociatedFileT()
        label_file.name = tmp_file.name
        model_meta.associatedFiles = [label_file]

        subgraph = _metadata_fb.SubGraphMetadataT()
        subgraph.inputTensorMetadata = [_metadata_fb.TensorMetadataT()]
        subgraph.outputTensorMetadata = [_metadata_fb.TensorMetadataT()] * num_outputs
        model_meta.subgraphMetadata = [subgraph]

        b = flatbuffers.Builder(0)
        b.Finish(model_meta.Pack(b), _metadata.MetadataPopulator.METADATA_FILE_IDENTIFIER)
        metadata_buf = b.Output()

        populator = _metadata.MetadataPopulator.with_model_file(file)
        populator.load_metadata_buffer(metadata_buf)
        populator.load_associated_files([str(tmp_file)])
        populator.populate()
        tmp_file.unlink()

这段代码的主要功能是为 TensorFlow Lite (.tflite) 模型添加元数据。元数据可以包含关于模型的附加信息,如输入输出的说明,有助于模型的管理和使用。以下是对代码逐步分解和详细解释:

  1. 函数定义:

    def add_tflite_metadata(file, metadata, num_outputs):
    

    该函数接受三个参数:

    • file:待添加元数据的 .tflite 文件路径。
    • metadata:要添加的元数据内容,通常为字典或字符串形式。
    • num_outputs:模型输出的数量,用于设置输出的元数据。
  2. 抑制导入错误:

    with contextlib.suppress(ImportError):
    

    该上下文管理器用于抑制(忽略)在导入模块时可能发生的 ImportError,如果 tflite_support 模块不存在,代码将继续执行。

  3. 导入所需模块:

    from tflite_support import flatbuffers
    from tflite_support import metadata as _metadata
    from tflite_support import metadata_schema_py_generated as _metadata_fb
    

    从 TensorFlow Lite 支持库中导入处理元数据所需的模块。

  4. 创建临时元数据文件:

    tmp_file = Path('/tmp/meta.txt')
    with open(tmp_file, 'w') as meta_f:
        meta_f.write(str(metadata))
    

    将传入的 metadata 写入一个临时文件 (/tmp/meta.txt)。

  5. 构建模型元数据结构:

    model_meta = _metadata_fb.ModelMetadataT()
    label_file = _metadata_fb.AssociatedFileT()
    label_file.name = tmp_file.name
    model_meta.associatedFiles = [label_file]
    

    创建一个 ModelMetadataT 实例,并添加关联文件(即之前创建的临时文件)。

  6. 设置子图元数据:

    subgraph = _metadata_fb.SubGraphMetadataT()
    subgraph.inputTensorMetadata = [_metadata_fb.TensorMetadataT()]
    subgraph.outputTensorMetadata = [_metadata_fb.TensorMetadataT()] * num_outputs
    model_meta.subgraphMetadata = [subgraph]
    

    创建子图的元数据,定义输入输出的张量元数据。这里使用了 num_outputs 来确定输出的数量。

  7. 构建 FlatBuffer:

    b = flatbuffers.Builder(0)
    b.Finish(model_meta.Pack(b), _metadata.MetadataPopulator.METADATA_FILE_IDENTIFIER)
    metadata_buf = b.Output()
    

    使用 FlatBuffers 库构造元数据并最终生成一个字节缓冲区(metadata_buf),用于后续存储。

  8. 填充模型文件的元数据:

    populator = _metadata.MetadataPopulator.with_model_file(file)
    populator.load_metadata_buffer(metadata_buf)
    populator.load_associated_files([str(tmp_file)])
    populator.populate()
    

    创建一个元数据填充器,加载先前生成的元数据缓冲区和关联文件,然后将它们添加到指定的 .tflite 文件中。

  9. 删除临时文件:

    tmp_file.unlink()
    

    最后,删除临时创建的文件,以保持环境的整洁。

这段代码的主要功能是为 TensorFlow Lite 模型添加相关的元数据,包含了以下几个关键步骤:

  • 创建一个临时文件以存储元数据。
  • 构建符合 TensorFlow Lite 规范的模型元数据结构,定义输入和输出。
  • 使用 FlatBuffers 将元数据序列化后附加到 .tflite 模型文件中。
  • 最后,清理临时文件。

这种方法可以帮助开发者更好地管理和使用 TensorFlow Lite 模型,为进一步的推理和部署提供更丰富的信息支持。

PaddlePaddle是一个开源的深度学习平台,可以用于构建和训练深度学习模型。如果你想使用PaddlePaddle,可以通过源码编译的方式来安装。首先,你需要在Git Bash中执行以下两条命令来将PaddlePaddle的源码克隆到本地,并进入Paddle目录: ``` git clone https://github.com/PaddlePaddle/Paddle.git cd Paddle ``` 接下来,你可以根据自己的需求进行编译。如果你使用的是Windows系统,可以使用源码编译来安装符合你需求的PaddlePaddle版本。具体的编译步骤可以参考官方文档中的Windows下源码编译部分\[2\]。 如果你想在docker镜像中编译PaddlePaddle,可以使用以下命令启动docker镜像并进行编译。如果你需要编译CPU版本,可以使用以下命令: ``` sudo docker run --name paddle-test -v $PWD:/paddle --network=host -it hub.baidubce.com/paddlepaddle/paddle:latest-dev /bin/bash ``` 如果你需要编译GPU版本,可以使用以下命令: ``` sudo nvidia-docker run --name paddle-test -v $PWD:/paddle --network=host -it hub.baidubce.com/paddlepaddle/paddle:latest-dev /bin/bash ``` 以上是关于使用源码编译PaddlePaddle的一些基本步骤和命令。你可以根据自己的需求和操作系统选择适合的方式来安装PaddlePaddle。 #### 引用[.reference_title] - *1* *2* *3* [《PaddlePaddle从入门到炼丹》一——新版本PaddlePaddle的安装](https://blog.csdn.net/qq_33200967/article/details/83052060)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值