一、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
。下面是对代码的逐步分解和详细解释:
-
def main(opt):
- 定义了一个名为
main
的函数,接收一个参数opt
。opt
是一个包含用户输入选项的对象(一般是在命令行中传递的参数)。
- 定义了一个名为
-
for opt.weights in (opt.weights if isinstance(opt.weights, list) else [opt.weights]):
- 这行代码是一个循环,目的是处理
opt.weights
。 opt.weights
可能是一个列表,也可能是一个单一的值:- 如果
opt.weights
是一个列表(即模型权重文件的列表),则直接使用这个列表。 - 如果
opt.weights
不是列表(即只有一个权重文件),则将其转换为包含单个元素的列表形式[opt.weights]
。
- 如果
- 这样处理的目的是确保在后续的操作中,无论用户输入的是单个权重文件还是多个权重文件,都可以统一处理。
- 这行代码是一个循环,目的是处理
-
run(**vars(opt))
- 这里调用了之前定义的
run
函数,并用**vars(opt)
将opt
对象的所有属性转换为关键字参数传递给run
。 vars(opt)
返回一个字典,包含opt
对象的所有属性及其值,**
运算符用于将字典解包为关键字参数。
- 这里调用了之前定义的
-
if __name__ == "__main__":
- 这是Python约定俗成的代码块,用来判断当前模块是否是主程序(即直接运行的模块)。
- 如果是主程序,那么执行以下代码。
-
opt = parse_opt()
- 调用
parse_opt
函数,这个函数的作用是解析命令行传入的参数,并返回一个包含这些参数的对象,通常是用来获取用户输入的设置。
- 调用
-
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
的函数,其主要功能是解析命令行参数并返回这些参数的值。以下是对代码逐步分解和详细解释:
-
引入 argparse 模块:
parser = argparse.ArgumentParser()
这行代码创建了一个参数解析器的实例,
argparse.ArgumentParser()
是用于处理命令行参数的标准模块。 -
定义参数: 下面的几行代码使用
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
。
-
解析参数:
opt = parser.parse_args()
这行代码解析命令行参数,并将解析结果存储在
opt
变量中。 -
打印参数:
print_args(vars(opt))
使用
print_args
函数打印参数的字典表示形式,vars(opt)
将opt
对象转换为字典,显示所有参数及其值。 -
返回参数:
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模型导出功能的一个部分,采用了参数化的方式来定义众多配置项。以下是对每个参数的详细解释:
-
data=ROOT / 'data/coco128.yaml':指定数据集配置文件的路径。
coco128.yaml
通常包含关于数据集的信息,如图像路径、类别等。 -
weights=ROOT / 'yolov5s.pt':指定要加载的模型权重文件的路径。
yolov5s.pt
是YOLOv5模型的PyTorch权重文件。 -
imgsz=(640, 640):定义输入图像的尺寸,表示图像的高度和宽度。在YOLOv5中,图像需要是正方形的,以便于处理。
-
batch_size=1:设定每次处理的图像数量,常用于批量推理。
-
device='cpu':指定要使用的计算设备。可以是CPU或CUDA设备(例如'0','1',等表示GPU)。
-
include=('torchscript', 'onnx'):指定要导出的模型格式。在这里,表示将模型导出为TorchScript和ONNX格式。
-
half=False:指示是否使用半精度浮点数(FP16)进行导出,通常加速推理。
-
inplace=False:决定是否在模型检测过程中使用就地操作,即是否直接修改输入数据。
-
keras=False:指示是否使用Keras框架。
-
optimize=False:表示是否将TorchScript模型进行优化以适应移动设备。
-
int8=False:指示是否进行量化以实现INT8精度,通常用于CoreML或TensorFlow模型。
-
dynamic=False:指定是否使用动态轴,适用于ONNX、TensorFlow和TensorRT模型,使得模型可以接收不同大小的输入。
-
simplify=False:指示是否简化ONNX模型,以降低复杂度和提高推理速度。
-
opset=12:指示ONNX的算子版本,版本号通常需要与不同的框架兼容。
-
verbose=False:设定TensorRT的日志详细程度,如果为True,会输出更详细的日志信息。
-
workspace=4:指定TensorRT的工作空间大小,单位为GB,影响最大内存使用。
-
nms=False:表示是否在TensorFlow模型中添加非极大值抑制(NMS),用于处理检测结果。
-
agnostic_nms=False:指示是否使用不区分类别的非极大值抑制(NMS)。
-
topk_per_class=100:在执行NMS时,对于每个类别保留前100个检测结果。
-
topk_all=100:在执行NMS时,对于所有类别保留的最多前100个检测结果。
-
iou_thres=0.45:定义交并比(IoU)阈值,用于NMS算法,通常设定在0到1之间,值越高则越严格。
-
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
)。这些元素是可用的导出格式的参数(如torchscript
,onnx
等)。
flags = [x in include for x in fmts]
- 这是另一个列表推导式,创建一个布尔列表
flags
。它检查fmts
中的每个元素是否存在于include
列表中,返回的列表中每个对应的元素为True
或False
。
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模型权重文件的路径。
该代码段主要实现了以下功能:
- 记录当前时间,用于性能测量;
- 将用户输入的导出格式转换为小写,以统一处理;
- 获取支持的导出格式,并验证用户输入的格式是否有效;
- 根据用户输入构建一个布尔值列表,表示各个导出格式的启用状态;
- 处理模型权重输入,支持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模型,并根据指定的设备和条件进行相应的处理。下面是逐行分解和详细解释:
-
device = select_device(device)
- 这一行调用了
select_device
函数,目的是根据传入的设备参数选择并返回合适的设备。常见的设备类型包括CPU和GPU。这个函数会检查设备是否可用,并返回一个可以用来进行模型推理的设备对象。
- 这一行调用了
-
if half:
- 这个条件语句检查变量
half
的值。若half
为真,表示用户希望使用半精度浮点数(FP16)格式,这种格式通常用于GPU上,以加速模型推理过程并节省内存。
- 这个条件语句检查变量
-
assert device.type != 'cpu' or coreml, '--half only compatible with GPU export, i.e. use --device 0'
- 这一行是一个断言语句,用于确保如果使用半精度(FP16),则当前设备不能是CPU,或者必须使用CoreML。换句话说,只有在GPU上才能使用半精度导出。如果这个条件不满足,程序会抛出一个错误,提示用户。
-
assert not dynamic, '--half not compatible with --dynamic, i.e. use either --half or --dynamic but not both'
- 另一个断言语句,用于确保不能同时使用动态轴(dynamic)和半精度(FP16)。也就是说,用户必须选择这两者之一来导出模型。如果两者都为真,会抛出错误。
-
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
- Checks:这是一个检查部分,目的是确保输入的图像尺寸
imgsz
是有效的。 - *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'
- if optimize:这部分代码检查是否设置了
optimize
标志。如果设置了,表示用户希望对模型进行优化。 - assert device.type == 'cpu':这行代码确保当前设备是 CPU。如果用户在使用 GPU 设备时试图启用优化,则会引发 AssertionError,并显示提示信息,说明优化不兼容 CUDA 设备。
# Input
gs = int(max(model.stride)) # grid size (max stride)
- # Input:这是标识输入部分的注释。
- gs = int(max(model.stride)):这里获取模型的最大步幅(stride),并将其转换为整数。步幅决定了网络的分辨率和处理速度,通常在目标检测模型中,与网络是如何处理输入图像的有关,尤其是如何将输入图像分割成网格进行处理。
imgsz = [check_img_size(x, gs) for x in imgsz] # verify img_size are gs-multiples
- 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
- 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模型的过程中执行的,主要用于设置模型的评估模式、处理相关参数并进行干跑操作,这样做的目的是确保模型能够正常工作并准备好进行导出。以下是逐步分解和详细解释:
-
model.eval()
:- 这行代码将模型设置为评估模式(evaluation mode)。在此模式下,模型不会应用诸如 dropout 和 batch normalization 等技术,这些技术通常只在训练模式下使用。这一步确保了评估时模型的行为是正确的。
-
for k, m in model.named_modules():
:- 这里使用
model.named_modules()
方法遍历模型中的每个命名子模块,其中k
是模块的名称,m
是模块的实例。这使得我们可以访问模型内部结构并根据需要做出修改。
- 这里使用
-
if isinstance(m, Detect):
:- 这行代码检查当前模块
m
是否是Detect
类型的实例。Detect
是YOLOv5中用于进行物体检测的模块,如果是,则会进行以下的配置。
- 这行代码检查当前模块
-
m.inplace = inplace
:inplace
是一个参数,它决定了模型是否应该在原地进行检测,即是否应该在现有的张量上修改数据。通过将inplace
赋值给m.inplace
,设置该检测模块的行为。
-
m.dynamic = dynamic
:dynamic
参数用于决定模型在推理时是否使用动态输入形状。如果设置为 True,模型将可以使用不同的输入大小。
-
m.export = True
:- 这行代码将
m.export
设置为 True。这通常用于在导出模型时启用某些功能或特性,使得模型可以以适当的格式导出。
- 这行代码将
-
for _ in range(2): y = model(im)
:- 这里进行两次“干跑”(dry runs)。通过传入输入数据
im
至模型model
进行前向传播,以确保模型能正常运行。此步骤可以帮助确认模型结构的有效性,确保接口没有问题。
- 这里进行两次“干跑”(dry runs)。通过传入输入数据
-
if half and not coreml:
:- 检查是否需要将数据转换为半精度(FP16),且不适用于 CoreML。这是为了提高推理速度和减少内存使用。在确保
im
和model
都被转换为 FP16 之后,代码进行如下处理:
- 检查是否需要将数据转换为半精度(FP16),且不适用于 CoreML。这是为了提高推理速度和减少内存使用。在确保
-
im, model = im.half(), model.half()
:- 将输入图像
im
和模型model
都转换为 FP16 格式。
- 将输入图像
-
shape = tuple((y[0] if isinstance(y, tuple) else y).shape)
:- 获取模型输出
y
的形状。如果输出是元组类型,则取第一个元素的形状(通常 YOLOv5 可能返回多个输出),否则直接获取输出的形状。最终将形状转为元组格式。
- 获取模型输出
-
metadata = {'stride': int(max(model.stride)), 'names': model.names}
:- 创建一个词典
metadata
,其中包含模型的一些元数据。stride
指的是最大步幅,用于决定特征图的分辨率,names
则是模型可以识别的类名。
- 创建一个词典
-
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。
以下是代码的逐步分解和详细解释:
-
初始化导出文件名列表:
f = [''] * len(fmts) # exported filenames
这里创建一个与支持的导出格式数量相同的列表
f
,初始化时每个元素都是空字符串,用于存放导出后的文件名。 -
过滤警告:
warnings.filterwarnings(action='ignore', category=torch.jit.TracerWarning) # suppress TracerWarning
该行代码用于忽略PyTorch中的TracerWarning,避免在模型导出过程中产生过多的警告信息。
-
TorchScript导出:
if jit: # TorchScript f[0], _ = export_torchscript(model, im, file, optimize)
如果请求导出TorchScript格式,则调用
export_torchscript
函数,并将结果存储在f
的第一个位置。 -
TensorRT导出:
if engine: # TensorRT required before ONNX f[1], _ = export_engine(model, im, file, half, dynamic, simplify, workspace, verbose)
如果请求导出TensorRT格式,则调用
export_engine
,确保在导出ONNX之前先完成TensorRT的导出。 -
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
函数。 -
CoreML导出:
if coreml: # CoreML f[4], _ = export_coreml(model, im, file, int8, half)
如果请求导出CoreML格式,则调用
export_coreml
函数。 -
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]
中记录。
-
导出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
函数完成导出,并添加相应的元数据。
- 如果请求导出GraphDef格式或TF.js格式,首先调用
-
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
最后,记录导出的完成消息,并返回导出的文件列表。
这段代码的主要功能是收尾处理,完成模型导出后,生成相关的日志信息,并返回导出的文件列表。以下是对代码的逐步分解和详细解释:
-
过滤空值:
f = [str(x) for x in f if x] # filter out '' and None
这行代码使用列表推导式将列表
f
中的非空字符串和非None
值提取出来。最终,f
将只包含有效的路径字符串。 -
检查导出文件是否存在:
if any(f):
这里检查
f
列表是否包含任何文件路径。如果列表中存在有效的导出文件,就会进入接下来的代码块。 -
获取模型类型:
cls, det, seg = (isinstance(model, x) for x in (ClassificationModel, DetectionModel, SegmentationModel)) # type
使用
isinstance
检查模型的类型,判断模型是否属于分类模型(ClassificationModel
)、检测模型(DetectionModel
)或分割模型(SegmentationModel
)。这将返回三个布尔值,分别存储在cls
、det
和seg
变量中。 -
确定导出目录:
dir = Path('segment' if seg else 'classify' if cls else '')
根据模型类型选择相应的目录路径,如果是分割模型,则使用
segment
目录;如果是分类模型,则使用classify
目录;否则,使用空路径。 -
设置半精度标志:
h = '--half' if half else '' # --half FP16 inference arg
这里根据
half
变量的值设置 FP16(半精度)推理的命令行参数。 -
生成警告信息:
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")
这段代码记录导出完成的时间、保存结果的目录、模型的检测和验证命令的示例以及如何在 PyTorch Hub 中加载模型的示例。还包括如何在网格应用程序中可视化模型的链接。
-
返回导出文件列表:
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 开发)到多种格式,以便在不同的平台和框架上进行推理。下面逐步分解和详细解释代码:
-
文件头和元数据:
# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
这行是代码的版权声明,表明该代码是由 Ultralytics 开发,并且使用 GPL-3.0 许可协议规范。
-
文档字符串:
""" Export a YOLOv5 PyTorch model to other formats. TensorFlow exports authored by https://github.com/zldrobit
以上是用来解释这个文件的目的:其功能是将一个 YOLOv5 的 PyTorch 模型导出为多种格式。
-
导出格式和命令行参数: 接下来的部分列出了支持的导出格式,相关的命令行参数和输出文件格式,包括:
- 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/
)
这个表格为使用者提供了清晰的参数选项与对应的输出文件。
- PyTorch (
-
依赖项:
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 版本进行安装。
-
使用方法:
Usage: $ python export.py --weights yolov5s.pt --include torchscript onnx openvino engine coreml tflite ...
这一部分说明了如何使用此脚本执行模型导出。使用者需要指定模型的权重文件和所需导出的格式。
-
推理说明: 这个部分提供了在不同框架下使用已导出的模型进行推理的示例命令,比如使用 PyTorch、ONNX、TensorFlow 等进行检测或推理。
-
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模型导出功能相关的设置。接下来,我将逐步分解并详细解释代码的各个部分。
-
导入库及模块:
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
此部分引入了 pandas库(用于数据处理)和 PyTorch(深度学习框架),以及其中的mobile_optimizer模块(用于优化移动设备推理)。
-
获取当前文件路径及根目录:
FILE = Path(__file__).resolve() ROOT = FILE.parents[0] # YOLOv5 root directory
FILE
变量获取当前脚本的绝对路径,而ROOT
变量获取其父目录,作为YOLOv5项目的根目录。 -
更新系统路径:
if str(ROOT) not in sys.path: sys.path.append(str(ROOT)) # add ROOT to PATH
如果根目录不在系统路径中,则将其添加,以便可以导入项目中的模块。
-
处理非Windows环境:
if platform.system() != 'Windows': ROOT = Path(os.path.relpath(ROOT, Path.cwd())) # relative
如果当前系统不是Windows,则将根目录转换为相对路径。这样做的目的是为了在非Windows环境中保持路径的一致性。
-
导入YOLOv5相关模块:
from models.experimental import attempt_load from models.yolo import ClassificationModel, Detect, DetectionModel, SegmentationModel
这些导入语句引入YOLOv5的模型定义,其中包括分类模型、检测模型和分割模型等。
-
导入数据加载和通用工具函数:
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环境:
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'])
-
函数定义:
def export_formats():
:定义一个名为export_formats
的函数,该函数没有参数。
-
注释:
# YOLOv5 export formats
:这一行是对函数的描述,说明该函数用于定义 YOLOv5 模型的导出格式。
-
格式数据的定义:
x = [...]
:创建一个名为x
的列表,其中每个元素是一个列表,包含有关不同导出格式的信息。- 每个子列表的内容如下:
- 第一个元素是字符串,表示格式的名称(例如 'PyTorch'、'ONNX' 等)。
- 第二个元素也是一个字符串,表示导出时使用的命令行参数(例如 'torchscript'、'onnx' 等)。
- 第三个元素是文件扩展名,用于保存导出的文件(例如 '.pt'、'.onnx' 等)。
- 第四个元素是布尔值,指示该格式是否支持 CPU。
- 第五个元素也是布尔值,指示该格式是否支持 GPU。
-
返回值:
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 模型的导出过程。以下是代码的逐步分解和详细解释:
-
装饰器定义:
def try_export(inner_func):
- 这个函数接受一个参数
inner_func
,这是一个被装饰的函数,通常是一个导出模型的实际实现函数。
- 这个函数接受一个参数
-
获取默认参数:
inner_args = get_default_args(inner_func)
- 使用
get_default_args(inner_func)
函数获取inner_func
的默认参数。这通常是为了获取函数的一些元数据,比如参数的前缀prefix
。
- 使用
-
定义外部函数:
def outer_func(*args, **kwargs):
- 在
try_export
内部定义outer_func
,它接收任何参数并将其转发给被装饰的函数。这个函数将处理实际的调用和错误处理。
- 在
-
调用被装饰的函数:
try: with Profile() as dt: f, model = inner_func(*args, **kwargs)
- 使用
try
语句块来安全地调用inner_func
。通过with Profile() as dt:
开启一个性能监测上下文,记录该过程的执行时间。 inner_func(*args, **kwargs)
被调用并返回两个结果:导出文件的路径f
和模型model
。
- 使用
-
成功日志记录:
LOGGER.info(f'{prefix} export success ✅ {dt.t:.1f}s, saved as {f} ({file_size(f):.1f} MB)')
- 如果导出成功,记录一条日志,显示导出成功的消息、花费的时间和文件大小。
-
异常处理:
except Exception as e: LOGGER.info(f'{prefix} export failure ❌ {dt.t:.1f}s: {e}') return None, None
- 如果在执行
inner_func
时出现任何异常,将捕获这些异常并记录失败的消息。返回值为(None, None)
,表示导出失败。
- 如果在执行
-
返回外部函数:
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格式,可以用于在生产环境中高效地运行模型。下面是对代码的逐步分解和详细解释:
-
函数定义:
def export_torchscript(model, im, file, optimize, prefix=colorstr('TorchScript:')):
model
:要导出的YOLOv5模型。im
:用于追踪模型的输入示例(张量)。file
:保存导出模型的文件路径。optimize
:一个布尔值,指示是否对模型进行优化以适应移动设备。prefix
:用于日志的信息前缀,默认值为'TorchScript:'。
-
日志信息:
LOGGER.info(f'\n{prefix} starting export with torch {torch.__version__}...')
- 使用
LOGGER
记录导出开始的消息,包括使用的Torch版本。
- 使用
-
生成输出文件名:
f = file.with_suffix('.torchscript')
- 将输入文件的后缀更改为
.torchscript
,即将导出的文件命名为file.torchscript
。
- 将输入文件的后缀更改为
-
追踪模型:
ts = torch.jit.trace(model, im, strict=False)
- 使用
torch.jit.trace
方法为给定输入张量im
创建模型的TorchScript表示。strict=False
表示在追踪时不进行严格的检查。
- 使用
-
创建元数据:
d = {"shape": im.shape, "stride": int(max(model.stride)), "names": model.names}
- 准备一个字典
d
,包含输入图像的形状、模型的最大步幅和模型的名字。这些信息将在导出时附加到文件中。
- 准备一个字典
-
额外文件:
extra_files = {'config.txt': json.dumps(d)} # torch._C.ExtraFilesMap()
- 将先前创建的字典
d
转换为JSON格式,作为config.txt
文件附加到TorchScript导出中。这个文件包含关于模型的一些配置参数。
- 将先前创建的字典
-
条件优化:
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格式,并附加任何额外的文件。
- 如果
-
返回值:
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格式,以便于在不同的平台上进行推理。下面是逐步的分解和详细解释:
-
函数定义:
def export_onnx(model, im, file, opset, dynamic, simplify, prefix=colorstr('ONNX:')):
model
: 需要导出的YOLOv5模型。im
: 输入数据,通常是一个张量,代表一批图像。file
: 导出后保存的文件路径。opset
: ONNX操作集版本。dynamic
: 布尔值,指示是否采用动态形状导出。动态形状可以让模型在推理时接收不同尺寸的输入。simplify
: 布尔值,指示是否在导出后简化模型。prefix
: 记录日志时使用的前缀。
-
检测ONNX要求:
check_requirements('onnx') import onnx
- 使用
check_requirements
函数确认已安装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) ...
- 根据模型类型和动态标志设置相应的模型输出的动态维度。
-
导出模型:
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格式,针对动态形状和输入/输出名称进行了设置。
- 使用
-
模型检查:
model_onnx = onnx.load(f) onnx.checker.check_model(model_onnx)
- 加载导出的ONNX模型并进行检查以确保模型格式正确。
-
添加元数据:
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)
- 记录模型的元数据,包括步幅和类名,并将这些信息保存回模型文件中。
-
模型简化:
if simplify: ...
- 如果
simplify
标志为真,则尝试简化模型。简化可以减少模型的复杂性,提高推理的速度。
- 如果
-
返回导出的文件和模型:
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
-
函数返回导出的文件路径
f
和None
(第二个返回值未被使用)。
这段代码的主要功能是将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 格式。以下是对此段代码的逐步解释:
-
函数定义和参数:
def export_paddle(model, im, file, metadata, prefix=colorstr('PaddlePaddle:')):
model
: 需要导出的 PyTorch 模型。im
: 用于示例的输入图像,通常是一个张量。file
: 保存导出模型的文件路径。metadata
: 模型的元数据(如类别名称等)。prefix
: 日志前缀,用于输出中标识导出过程。
-
依赖检查:
check_requirements(('paddlepaddle', 'x2paddle'))
- 调用
check_requirements
函数以确保所需的库(paddlepaddle
和x2paddle
)已经安装。如果未安装,函数将抛出异常。
- 调用
-
导入必要的库:
import x2paddle from x2paddle.convert import pytorch2paddle
- 导入
x2paddle
模块及其内部的pytorch2paddle
函数,用于执行库间的模型转换。
- 导入
-
日志记录:
LOGGER.info(f'\n{prefix} starting export with X2Paddle {x2paddle.__version__}...')
- 使用
LOGGER
输出日志,指示导出过程开始,并显示x2paddle
的版本。
- 使用
-
构建保存路径:
f = str(file).replace('.pt', f'_paddle_model{os.sep}')
- 根据提供的文件路径修改文件名,准备保存 PaddlePaddle 格式的模型文件。将文件扩展名从
.pt
替换为_paddle_model
,并添加系统特定的目录分隔符。
- 根据提供的文件路径修改文件名,准备保存 PaddlePaddle 格式的模型文件。将文件扩展名从
-
模型转换:
pytorch2paddle(module=model, save_dir=f, jit_type='trace', input_examples=[im]) # export
- 调用
pytorch2paddle
函数,将 PyTorch 模型转换为 PaddlePaddle 格式,并保存到指定的目录。 jit_type='trace'
表示通过追踪输入与模型的关系进行导出。input_examples
参数提供示例输入数据以帮助转换。
- 调用
-
保存元数据:
yaml_save(Path(f) / file.with_suffix('.yaml').name, metadata) # add metadata.yaml
- 使用
yaml_save
函数将模型的元数据保存为 YAML 格式,文件名与模型文件一致,便于后期查看。
- 使用
-
返回结果:
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的设备上进行推理。下面是对代码的逐步分解和详细解释:
-
函数定义和参数:
def export_coreml(model, im, file, int8, half, prefix=colorstr('CoreML:')):
model
:要导出的YOLOv5模型。im
:输入图像,用于模型跟踪和转换。file
:输出文件路径,不带扩展名。int8
:布尔值,指示是否使用INT8量化。half
:布尔值,指示是否使用FP16半精度。prefix
:日志信息的前缀,用于标识输出格式。
-
检查依赖项:
check_requirements('coremltools') import coremltools as ct
- 该行调用
check_requirements
函数,确保coremltools
库已安装,然后将其导入为ct
。
- 该行调用
-
日志记录:
LOGGER.info(f'\n{prefix} starting export with coremltools {ct.__version__}...')
- 记录开始导出的信息,包括使用的
coremltools
版本。
- 记录开始导出的信息,包括使用的
-
设置输出路径:
f = file.with_suffix('.mlmodel')
- 创建输出文件路径,将其扩展名设置为
.mlmodel
,这是CoreML模型的标准扩展名。
- 创建输出文件路径,将其扩展名设置为
-
TorchScript模型转换:
ts = torch.jit.trace(model, im, strict=False)
- 使用TorchScript将PyTorch模型转换为可追踪脚本,允许它被导出。这里
im
是示例输入。
- 使用TorchScript将PyTorch模型转换为可追踪脚本,允许它被导出。这里
-
CoreML转换:
ct_model = ct.convert(ts, inputs=[ct.ImageType('image', shape=im.shape, scale=1 / 255, bias=[0, 0, 0])])
- 将TorchScript模型转换为CoreML模型。
inputs
参数指定输入的形状、缩放因子和偏置。
- 将TorchScript模型转换为CoreML模型。
-
量化设置:
bits, mode = (8, 'kmeans_lut') if int8 else (16, 'linear') if half else (32, None)
- 根据
int8
和half
参数确定量化的精度。bits
表示位数,mode
指定量化模式。
- 根据
-
量化处理:
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,则跳过量化并输出警告信息。
-
保存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
-
函数定义:
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:'。
-
设备检查:
assert im.device.type != 'cpu', 'export running on CPU but must be on GPU, i.e. `python export.py --device 0`'
确保输入图像在GPU上,而不是CPU。
-
导入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版本。
-
处理TensorRT版本:
if trt.__version__[0] == '7': ... else: check_version(trt.__version__, '8.0.0', hard=True) ...
根据TensorRT的版本处理不同的导出流程。对于版本7,调整模型的anchor_grid;对于版本8及以上,直接导出ONNX模型。
-
导出ONNX模型:
export_onnx(model, im, file, 12, dynamic, simplify)
调用
export_onnx
函数,将模型导出为ONNX格式(opset版本为12)。 -
构建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引擎文件。
-
创建TensorRT构建器和配置:
builder = trt.Builder(logger) config = builder.create_builder_config() config.max_workspace_size = workspace * 1 << 30
创建TensorRT构建器、配置其工作区大小。
-
创建网络:
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文件,确保其有效。
-
检查输入和输出:
inputs = [network.get_input(i) for i in range(network.num_inputs)] outputs = [network.get_output(i) for i in range(network.num_outputs)]
获取网络的输入和输出,并记录它们的形状和数据类型。
-
动态输入配置:
if dynamic: ... profile = builder.create_optimization_profile() ... config.add_optimization_profile(profile)
如果支持动态输入,根据输入的不同形状创建优化配置。
-
构建和保存引擎:
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格式,下面是逐步分解和详细解释:
-
函数定义与参数:
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_nms
,agnostic_nms
,topk_per_class
,topk_all
,iou_thres
,conf_thres
用于控制TensorFlow中的非极大值抑制(NMS)的具体行为。 keras
: 布尔值,指示是否将模型保存为Keras格式。
-
导入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。
-
日志信息:
LOGGER.info(f'\n{prefix} starting export with tensorflow {tf.__version__}...')
- 记录当前TensorFlow版本的信息,用于输出。
-
设置导出文件路径和图像形状:
f = str(file).replace('.pt', '_saved_model') batch_size, ch, *imgsz = list(im.shape) # BCHW
- 根据输入图像
im
的形状获取批大小和通道数。这里假设图像的形状为BCHW (批大小, 通道数, 高, 宽)
。
- 根据输入图像
-
初始化TensorFlow模型:
tf_model = TFModel(cfg=model.yaml, model=model, nc=model.nc, imgsz=imgsz)
- 创建一个新的
TFModel
实例,它将载入YOLOv5模型配置、模型权重及输入图像大小。
- 创建一个新的
-
生成示例输入:
im = tf.zeros((batch_size, *imgsz, ch)) # BHWC order for TensorFlow
- 创建一个形状为
(batch_size, 高, 宽, 通道数)
的全0张量,作为TensorFlow模型的输入示例。
- 创建一个形状为
-
模型预测:
_ = tf_model.predict(im, tf_nms, agnostic_nms, topk_per_class, topk_all, iou_thres, conf_thres)
- 进行一次预测以确保模型准备好进行导出。
-
定义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
并获得输出。
- 定义模型的输入,以及将输入传给
-
构建Keras模型:
keras_model = tf.keras.Model(inputs=inputs, outputs=outputs) keras_model.trainable = False keras_model.summary()
- 创建一个Keras模型,设置为不可训练,调用
summary
以打印模型信息。
- 创建一个Keras模型,设置为不可训练,调用
-
保存模型:
if keras: keras_model.save(f, save_format='tf') else: ...
- 如果
keras
为真,将模型直接保存为Keras格式。否则,将模型转换为SavedModel
格式进行保存。
- 如果
-
冻结模型:
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
格式,允许在后续推理中使用。
- 创建一个TensorFlow函数以冻结模型并保存为
这段代码的主要功能是将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模型转换为适合在移动设备上运行的轻量级格式。下面逐步分解并详细解释代码:
-
函数定义及参数:
def export_tflite(keras_model, im, file, int8, data, nms, agnostic_nms, prefix=colorstr('TensorFlow Lite:')):
keras_model
:待转换的Keras模型。im
:输入数据,用于确定模型的输入形状。file
:输出文件的路径,包含模型名称。int8
:表示是否进行INT8量化(数据类型压缩),以减少模型大小和提高推理速度。data
:数据集路径,用于准备代表性数据集。nms
和agnostic_nms
:表示是否将非极大值抑制(NMS)应用于模型。prefix
:用于日志记录的前缀。
-
导入TensorFlow:
import tensorflow as tf
-
日志记录:
LOGGER.info(f'\n{prefix} starting export with tensorflow {tf.__version__}...')
记录开始导出过程的信息。
-
输入数据处理:
batch_size, ch, *imgsz = list(im.shape) # BCHW
将输入数据的尺寸解析为批量大小(
batch_size
)、通道数(ch
)及图像大小(imgsz
)。 -
指定输出文件名:
f = str(file).replace('.pt', '-fp16.tflite')
将输出文件名设置为以
-fp16.tflite
结尾,表示该模型支持FP16格式。 -
创建TensorFlow Lite转换器:
converter = tf.lite.TFLiteConverter.from_keras_model(keras_model)
利用指定的Keras模型创建一个TFLite转换器。
-
设置转换器参数:
converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS] converter.target_spec.supported_types = [tf.float16] converter.optimizations = [tf.lite.Optimize.DEFAULT]
设置支持的操作和数据类型,并使用默认优化策略。
-
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位整数)。
-
添加非极大值抑制(NMS)支持:
if nms or agnostic_nms: converter.target_spec.supported_ops.append(tf.lite.OpsSet.SELECT_TF_OPS)
如果需要NMS,则将相应的操作添加到支持列表中。
-
模型转换与保存:
tflite_model = converter.convert() open(f, "wb").write(tflite_model)
- 通过转换器转换模型为TensorFlow Lite格式。
- 将转换后的模型写入到指定的文件中。
-
返回输出文件名:
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
面是对提供代码的逐步分解和详细解释,以及对此代码的总结。
-
函数定义:
def export_edgetpu(file, prefix=colorstr('Edge TPU:')):
该函数被定义为
export_edgetpu
,其目的是将 YOLOv5 模型导出为 Edge TPU 可用的格式。函数接受两个参数:file
是要导出的模型文件,prefix
是用于日志输出的前缀,默认值为 'Edge TPU:'。 -
命令和帮助链接:
cmd = 'edgetpu_compiler --version' help_url = 'https://coral.ai/docs/edgetpu/compiler/'
cmd
变量包含要运行的命令,以获取 Edge TPU 编译器的版本。help_url
提供了相关的文档地址。 -
操作系统检查:
assert platform.system() == 'Linux', f'export only supported on Linux. See {help_url}'
该行用来确保代码在 Linux 操作系统上运行,因为 Edge TPU 编译器目前仅支持 Linux。
-
编译器检查:
if subprocess.run(f'{cmd} >/dev/null', shell=True).returncode != 0:
这是在检查 Edge TPU 编译器是否已安装。如果该命令的返回值不为 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
权限,以便进行安装。 -
安装 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 密钥、更新源列表和安装编译器。
-
获取编译器版本:
ver = subprocess.run(cmd, shell=True, capture_output=True, check=True).stdout.decode().split()[-1]
运行之前定义的命令以获得安装的 Edge TPU 编译器的版本。
-
日志输出开始导出:
LOGGER.info(f'\n{prefix} starting export with Edge TPU compiler {ver}...')
记录开始使用 Edge TPU 编译器出口的日志信息。
-
文件命名:
f = str(file).replace('.pt', '-int8_edgetpu.tflite') f_tfl = str(file).replace('.pt', '-int8.tflite')
分别为生成的 Edge TPU 模型文件和 TFLite 文件命名。
-
编译命令与执行:
cmd = f"edgetpu_compiler -s -d -k 10 --out_dir {file.parent} {f_tfl}" subprocess.run(cmd.split(), check=True)
使用编译器将 TFLite 模型文件编译为适用于 Edge TPU 的格式,并将其输出到指定目录。
-
返回值:
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) 模型添加元数据。元数据可以包含关于模型的附加信息,如输入输出的说明,有助于模型的管理和使用。以下是对代码逐步分解和详细解释:
-
函数定义:
def add_tflite_metadata(file, metadata, num_outputs):
该函数接受三个参数:
file
:待添加元数据的 .tflite 文件路径。metadata
:要添加的元数据内容,通常为字典或字符串形式。num_outputs
:模型输出的数量,用于设置输出的元数据。
-
抑制导入错误:
with contextlib.suppress(ImportError):
该上下文管理器用于抑制(忽略)在导入模块时可能发生的 ImportError,如果
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
从 TensorFlow Lite 支持库中导入处理元数据所需的模块。
-
创建临时元数据文件:
tmp_file = Path('/tmp/meta.txt') with open(tmp_file, 'w') as meta_f: meta_f.write(str(metadata))
将传入的
metadata
写入一个临时文件 (/tmp/meta.txt
)。 -
构建模型元数据结构:
model_meta = _metadata_fb.ModelMetadataT() label_file = _metadata_fb.AssociatedFileT() label_file.name = tmp_file.name model_meta.associatedFiles = [label_file]
创建一个
ModelMetadataT
实例,并添加关联文件(即之前创建的临时文件)。 -
设置子图元数据:
subgraph = _metadata_fb.SubGraphMetadataT() subgraph.inputTensorMetadata = [_metadata_fb.TensorMetadataT()] subgraph.outputTensorMetadata = [_metadata_fb.TensorMetadataT()] * num_outputs model_meta.subgraphMetadata = [subgraph]
创建子图的元数据,定义输入输出的张量元数据。这里使用了
num_outputs
来确定输出的数量。 -
构建 FlatBuffer:
b = flatbuffers.Builder(0) b.Finish(model_meta.Pack(b), _metadata.MetadataPopulator.METADATA_FILE_IDENTIFIER) metadata_buf = b.Output()
使用 FlatBuffers 库构造元数据并最终生成一个字节缓冲区(
metadata_buf
),用于后续存储。 -
填充模型文件的元数据:
populator = _metadata.MetadataPopulator.with_model_file(file) populator.load_metadata_buffer(metadata_buf) populator.load_associated_files([str(tmp_file)]) populator.populate()
创建一个元数据填充器,加载先前生成的元数据缓冲区和关联文件,然后将它们添加到指定的 .tflite 文件中。
-
删除临时文件:
tmp_file.unlink()
最后,删除临时创建的文件,以保持环境的整洁。
这段代码的主要功能是为 TensorFlow Lite 模型添加相关的元数据,包含了以下几个关键步骤:
- 创建一个临时文件以存储元数据。
- 构建符合 TensorFlow Lite 规范的模型元数据结构,定义输入和输出。
- 使用 FlatBuffers 将元数据序列化后附加到 .tflite 模型文件中。
- 最后,清理临时文件。
这种方法可以帮助开发者更好地管理和使用 TensorFlow Lite 模型,为进一步的推理和部署提供更丰富的信息支持。