ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Atlas 300V 24G部署YOLO系列模型:从环境搭建到性能调优全解析

Atlas 300V 24G部署YOLO系列模型:从环境搭建到性能调优全解析 1. 先说清楚Atlas 300V 24G 到底是不是运算加速卡我发现最近后台被问得最多的一个问题就是“atlas 300v 24g 是运算加速卡吗”甚至有人在群里争论它和普通显卡的区别。这里直接给结论是而且它不是一般的运算加速卡它是华为昇腾专门为AI推理场景设计的NPU加速卡。我最早接触这块卡是在一个安防项目上当时客户要在边缘侧跑十几个路的YOLOv5做实时检测GPU供货紧张、功耗又压不住后来换了Atlas 300V 24G单卡就把活全干了。要理解这张卡先得把它的定位搞清楚。它和你在电脑里插的RTX系列游戏显卡完全是两回事。Atlas 300V Pro官方全称里带Pro我们平时喊300V是一款纯推理卡核心是一颗昇腾310P系列芯片板载24GB显存准确说是LPDDR4X主打的是高能效比、多路视频分析、大模型推理这类场景。它不能用来做训练至少在官方设计目标里没打算让你拿它训大模型它是用来“跑模型”的不是“炼模型”的。那它跟CPU比呢CPU也可以跑YOLO但一张Atlas 300V 24G的INT8算力在140 TOPS上下换算成CPU得多核并行才能勉强掰手腕而功耗一对比就差远了。这块卡典型功耗才70多瓦你拿一张同样能跑到这个算力水平的GPU功耗基本是它的三到五倍。省电、便宜、不挑供电这三点在机房部署时有多重要做过边缘项目的人都懂。所以如果你手里正好有这块卡或者正打算采购这篇博文就是写给你的。我会完整拆解从环境搭建、模型转换到推理代码、性能调优的全过程结合我在两个实际项目里踩过的坑把Atlas 300V 24G上部署YOLO系列模型这事彻底讲透。不吹不黑都是实操记录。2. 部署前的硬件理解与选型判断2.1 一张被名字耽误的推理卡Atlas 300V 24G这个名字里没有“GPU”也没有“显卡”字样很多人第一反应是这是不是华为出的一款“计算卡”或者“加速模块”。实际上它的确是一张标准的PCIe标准卡插在服务器的PCIe x16插槽上就能用不需要额外供电接口主板PCIe供电足够散热方式是风冷被动散热靠服务器机箱风道带走热量。这一点跟GPU不一样塔式工作站自己加装的话要注意机箱风道不能太闷不然温度会顶到90度然后降频。这张卡的硬件规格我整理了一份实测中比较关键的数据项目参数芯片昇腾310P双Die设计显存24GB LPDDR4X算力INT8约140 TOPSFP16约70 TFLOPS接口PCIe 4.0 x16功耗典型72W视频解码支持H.264/H.265硬件解码DVPP能力形态全高全长被动散热上表需要注意的就两点。第一24GB显存对于推理卡来说属于“大肚量”意味着你可以同时加载多个模型或者把一个较大的模型比如YOLOv7、YOLOv8的large版本直接塞进去不需要做太多模型裁剪。第二DVPP硬件解码单元是个隐藏福利它能直接从RTSP视频流里硬解H.264/H.265帧然后送给NPU做推理不需要CPU参与软解。如果你做视频检测项目这一项就能省下大量CPU资源。2.2 和GPU方案对比什么场景适合上Atlas我在决定用Atlas 300V之前其实做过一轮GPU和NPU方案的对比。这里直接把我的选型逻辑分享出来帮你省掉重复调研的时间。选GPU方案比如用RTX 4060或者Tesla T4的优势是软件生态成熟PyTorch直接能用YOLO系列的官方代码拉下来就能跑不需要改任何东西。缺点是成本高、功耗高、供货渠道参差不齐而且如果是做长期运行的服务一张GPU卡的风扇寿命和散热压力都是不可忽视的隐患。选Atlas 300V 24G则反过来软件生态需要稍微折腾一下但稳定性和能效比在推理场景里非常能打。而且昇腾的部署工具链CANN在近两年迭代很快用ACLAscend Computing Language接口做推理整体流程并不比TensorRT复杂多少。我的建议是只做离线批量推理、单路或几路视频流检测追求快速上手的直接上GPU没问题。做多路视频流并发分析、7x24小时长时间运行、对功耗和散热有硬性要求的选Atlas 300V 24G更合适。预算敏感、又要一定算力储备的Atlas 300V 24G的性价比也很可观。另外提醒一点Atlas 300V 24G有个隐藏优势是单卡能扛住高并发小目标检测。因为NPU的调度方式和GPU不同它对小batch的推理延迟优化得特别好实测下来单帧延迟非常稳定这对实时性要求高的场景很关键。3. 环境准备从裸机到能跑通YOLO的完整装机流程3.1 硬件检查与驱动固件安装先说一个很多人掉进去的坑拿到Atlas 300V 24G之后直接插上电脑就想跑结果系统认不到卡。原因很简单这张卡必须先装驱动和固件而且版本必须严格配套。我个人建议直接参考昇腾官方的CANN版本配套表不要自己乱搭配。安装之前先检查环境# 查看服务器是否识别到PCIe设备 lspci | grep -i ascend如果能看到类似Huawei Ascend Device的信息说明硬件层面已经识别到了。如果看不到大概率是PCIe插槽接触问题或者主板BIOS里需要开启PCIe 4.0。驱动和固件建议用root用户操作。华为官方提供的是一个Ascend HDK安装包hdk.run里面包含驱动driver和固件firmware直接运行即可# 解压获取hdk.run后执行先增加执行权限 chmod x Ascend-hdk-xxx.run ./Ascend-hdk-xxx.run --full --install安装完成后可以用npu-smi info命令查看卡的状态npu-smi info如果能看到卡的温度、芯片型号、显存大小等信息就说明驱动和固件都到位了。这里有个小细节npu-smi info如果提示找不到命令一般是因为/usr/local/sbin没加到PATH里手动加一下就行。3.2 CANN工具链安装与环境变量配置驱动固件装好后还需要装CANNCompute Architecture for Neural Networks工具包。CANN是昇腾的软件栈类比NVIDIA的CUDA ToolkitYOLO模型要在NPU上跑起来必须经过CANN的ATC工具转换成.om格式并且通过ACL接口调用。安装CANN很简单官方提供一个.run安装包chmod x Ascend-cann-toolkit_xxx.run ./Ascend-cann-toolkit_xxx.run --install默认安装路径是/usr/local/Ascend/ascend-toolkit/latest。安装完成后需要设置一堆环境变量。我建议直接写进~/.bashrcsource /usr/local/Ascend/ascend-toolkit/set_env.sh这个set_env.sh脚本会自动把CANN相关的lib、bin、include路径配好。不过我自己实际用下来有时候还是会有PYTHONPATH不对的问题所以我还是建议手动把下面这行补上export PYTHONPATH/usr/local/Ascend/ascend-toolkit/latest/python/site-packages:$PYTHONPATH环境配完验证一下是否可用python3 -c import acl; print(acl.__version__)如果能正常输出版本号说明ACL的Python接口已经就绪。到这一步你的机器就具备了在Atlas 300V 24G上部署YOLO的基本条件。注意CANN有两个大版本线老项目用的是5.x新项目推荐6.x及以上。我个人建议直接上7.0版本API保持一致但算子支持和性能优化更好。不同版本之间不要混用卸载重装前先清理干净/usr/local/Ascend下的残留文件。4. 核心环节YOLO模型转换与量化4.1 先从PyTorch导出ONNXAtlas的NPU不能直接跑PyTorch的.pt权重文件需要先把模型转成ONNX再通过ATC工具转成.om格式。这一步看似简单但其实有一堆细节搞不好就转出来的模型精度丢失或者直接转换失败。先讲pt转onnx。我在项目中用的是YOLOv5s官方提供的export.py脚本可以直接导出ONNXpython3 export.py --weights yolov5s.pt --include onnx --opset 11 --batch-size 1有两点必须强调。第一--opset建议用11或者12不要用更高的因为CANN的ATC工具对ONNX算子支持存在一个上界版本超出之后容易报不支持的算子错误。第二--batch-size这里有讲究。如果你后续推理需要动态batch这一步最好固定成1然后在ATC转换时通过--dynamic_batch_size参数来实现动态batch会更灵活。如果你用的是YOLOv8或者YOLOv5的改进版本比如加了注意力机制的变体光用官方export.py有时候会失败因为有些自定义算子ONNX不支持。备选方案是用torch.onnx.export手动导出并且把模型里不支持的算子替换掉或者用onnx-simplifier简化一下python3 -m onnxsim yolov5s.onnx yolov5s_sim.onnx这个简化的目的是把ONNX里一些冗余的Shape算子、常量节点清理掉。转出来的模型会不会失败很多时候就卡在这些看似不起眼的节点上。所以导出ONNX后一定要用onnx.checker.check_model或者直接可视化看一下计算图结构。4.2 ATC转OM参数不是随便填的拿到onnx之后核心动作就是调用ATC工具转成om。Atlas 300V 24G对应的soc_version是Ascend310P3注意不要填错填错会在转换阶段直接报不支持。我长期使用的转换命令整理成一个脚本方便复用atc --modelyolov5s_sim.onnx \ --framework5 \ --outputyolov5s_310p3 \ --soc_versionAscend310P3 \ --input_shapeimages:1,3,640,640 \ --insert_op_confaipp.cfg \ --output_typeFP32 \ --precision_modeforce_fp16逐个解释关键参数--framework5表示输入模型是ONNX这个数字千万别改我见过有人手滑改成别的值导致转换直接崩。--input_shape指定模型输入张量的名称、batch大小、通道数、宽高。这里的images:1,3,640,640对应YOLOv5官方代码里模型的输入名。--insert_op_conf是插入AIPPAI Preprocessing配置这是昇腾的一大杀器后面专门讲。--precision_modeforce_fp16是把模型转成半精度推理。对YOLO这类模型FP16精度损失几乎可以忽略但推理速度会有明显提升。有个经验性的建议如果你的模型尺寸是640x640且不打算动态尺寸建议固定输入shape这样能获得最好的性能因为NPU不需要为shape变化预留额外资源。如果确实需要动态shapeATC也支持atc --modelyolov5s_sim.onnx \ --framework5 \ --outputyolov5s_dynamic \ --soc_versionAscend310P3 \ --input_shapeimages:-1,3,-1,-1 \ --dynamic_dims1,640,640;1,1280,1280;8,640,640 \ --precision_modeforce_fp16这里--dynamic_dims枚举了几种可能的尺寸组合推理时通过ACL接口指定具体尺寸。说实话动态shape虽然方便但性能会比固定shape差一截。我的习惯是能用固定shape就尽量固定动态shape留给不得不用的场景。4.3 AIPP配置把预处理直接烧进模型里这是我觉得Atlas 300V 24G最值得讲的一个特性。在GPU的TensorRT流程里图像缩放、通道变换、归一化这些预处理通常是在GPU上用算子或者自己写的kernel实现的。但Atlas的ATC工具支持AIPPAI Preprocessing可以把这些预处理步骤直接合并进生成好的om模型里推理时输入只需要给原始图像数据NPU会在内部自动完成resize、减均值、除方差、通道重排等操作。这样做的好处非常直接CPU不用再浪费算力做图像预处理推理延迟也低了不少。以YOLOv5为例aipp.cfg大概长这样aipp_op { aipp_mode: static related_input_rank: 0 input_format: RGB888_U8 src_image_size_w: 640 src_image_size_h: 640 crop: 1 load_start_pos_h: 0 load_start_pos_w: 0 crop_size_w: 640 crop_size_h: 640 csc_switch: 1 rbuv_swap_switch: 0 rgba_swap_switch: 0 min_quant: 0 max_quant: 255 padding_value: 0 mean_chn_0: 0 mean_chn_1: 0 mean_chn_2: 0 }重点说几个参数。input_format: RGB888_U8表示输入图是RGB三通道、8位整型crop和crop_size_*表示是否先裁剪再推理mean_chn_0等是归一化用的均值如果有需要就填自己的值。用AIPP之后一个最大的好处是预处理完全不需要你在代码里操心了。你在推理代码里只需要把一帧图像的二进制数据直接传给ACL接口NPU自己完成所有事情。刚开始用的时候还有点不习惯后来发现这设计真省事。5. 推理代码实现pyACL调用NPU跑YOLO环境好了模型转好了接下来就是写推理代码。Atlas 300V 24G上跑YOLO最常用的接口是pyACL——CANN对Python的ACL绑定。这一节我把整个推理链路的代码骨架写出来并附上我在实际项目中踩过的坑。5.1 初始化与资源申请ACL的编程模型可以类比你在GPU上写CUDA的流程先初始化再申请设备资源然后加载模型最后执行推理。但在NPU上多了一个概念叫“Context”——相当于隔离不同任务运行环境的上下文。基础代码框架如下import acl import numpy as np # 初始化ACL ret acl.init() assert ret 0, fACL init failed: {ret} # 指定运行设备 ret acl.rt.set_device(0) assert ret 0, fset_device failed: {ret} # 创建Context context, ret acl.rt.create_context(0) assert ret 0, fcreate_context failed: {ret} # 申请运行内存 self._context context这里有个很容易踩的坑如果你在代码里先创建了numpy数组再初始化ACL有时候会报错“ACL memory is not initialized”。原因是ACL在acl.init()之前没有完成底层内存池的初始化后面对numpy数组做拷贝就可能出问题。我的习惯是程序入口处第一行就调用acl.init()并且在此期间不要做任何其他操作。5.2 模型加载与数据集准备模型加载需要用到acl.mdl相关接口。这里我踩过一个比较隐蔽的坑如果om文件路径相对路径不对加载时的报错信息其实很不直观会显示什么“load model from file failed”。建议直接用绝对路径并在加载前先检查文件是否存在。# 加载模型 model_id, ret acl.mdl.load_from_file(/path/to/yolov5s_310p3.om) assert ret 0, fload model failed: {ret} # 获取模型描述信息主要是输入输出tensor的shape和大小 model_desc acl.mdl.create_desc() ret acl.mdl.get_desc(model_desc, model_id) assert ret 0, fget_desc failed: {ret} # 获取模型输入输出的数量 input_size acl.mdl.get_num_inputs(model_desc) output_size acl.mdl.get_num_outputs(model_desc) print(finputs: {input_size}, outputs: {output_size})获取到输入输出数量后要为每个输入输出申请设备内存Device Memory。这一步非常关键昇腾的内存分为Host内存和Device内存推理的输入数据必须放到Device端而Host端的数据通过acl.rt.memcpy拷贝到Device端。如果直接在Device端使用numpy数组推理会报“invalid device pointer”之类的错。申请内存可以用acl.rt.mallocdevice_input_ptr, ret acl.rt.malloc(input_size, acl.const.MEMORY_NORMAL) device_output_ptr, ret acl.rt.malloc(output_size, acl.const.MEMORY_NORMAL)同时准备一个绑定输入输出内存的acl.mdl.create_dataset这些样板代码比较多我建议封装一个推理类把初始化、加载模型、内存分配统一管理。5.3 推理逻辑与结果解析推理本身其实很简单把图像数据塞进Device内存调用acl.mdl.execute就行# 假设input_data是已经转换好格式的numpy数组例如(1, 3, 640, 640)的float32数组 input_data np.ascontiguousarray(input_data, dtypenp.float32) # 把输入数据拷贝到设备端 ret acl.rt.memcpy( device_input_ptr, input_size, input_data.ctypes.data, input_data.nbytes, acl.const.MEMCPY_HOST_TO_DEVICE ) assert ret 0, fmemcpy to device failed: {ret} # 将设备内存绑定到输入dataset上伪代码实际需要逐个tensor绑定 # ... # 执行推理 ret acl.mdl.execute(model_id, input_dataset, output_dataset) assert ret 0, fmodel execute failed: {ret} # 把设备端输出拷贝回Host端 output_data np.zeros(output_size, dtypenp.float32) ret acl.rt.memcpy( output_data.ctypes.data, output_size, device_output_ptr, output_size, acl.const.MEMCPY_DEVICE_TO_HOST )执行完之后输出数据是模型输出的原始张量。对YOLOv5来说输出一般是形状为(1, 25200, 85)的张量其中25200是三个尺度加起来的所有anchor数量85是(cx, cy, w, h, obj_conf, class_conf_0~79)。后处理就是在numpy里对三维数组做解析筛选置信度、按类别做NMS。这方面的代码网上很多不展开。但我想说一个经验后处理建议直接在numpy上做不要转成Python list再循环否则在视频流高并发场景下后处理会成为新的性能瓶颈。你可以先用np.where筛选高置信度候选框再在少量候选框上做NMS循环这样效率会好很多。5.4 预处理要用DVPP不要用OpenCV硬扛如果你没用AIPP而是想在代码里做预处理我要大声劝一句在Atlas 300V 24G上做视频推理图像缩放和格式转换一定别用OpenCV在CPU上扛。看起来代码简单实际一压多路视频就露馅CPU占用率直接拉满推理延迟反而上去了。正确的做法是使用CANN的DVPP接口。DVPP是板载的硬件图像处理单元专门做resize、裁剪、颜色格式转换比如BGR转RGB或者YUV转RGB速度和CPU软解完全不在一个量级。pyACL里可以用acl.dvpp相关接口借助Python绑定实现# DVPP初始化伪代码需结合实际范例 dvpp acl.dvpp.DvppProcessor() # 传入输入图像数据, 设置resize目标尺寸 result dvpp.resize(input_data, width640, height640)不过DVPP的Python封装在不同CANN版本里差异较大文档也没有特别细致。我的建议是如果项目周期紧直接用AIPP方式把预处理烧进om模型代码里只需要把原始图像数据转成numpy数组传给ACL省心又高效。AIPP和DVPP本质上是在做类似的事情只不过AIPP是在模型内部就完成了DVPP是单独一个硬件单元做。在深度学习推理场景里AIPP对小白友好得多。6. 性能调优与实战数据6.1 一次实测数据对比我拿一张Atlas 300V 24G在CANN 7.0环境下部署YOLOv5s 640×640固定batch1AIPP预处理重复推理1000帧取平均值得到的数据大体是这样的模型输入尺寸单帧延迟吞吐量YOLOv5s640x640约3.5ms285 FPSYOLOv5m640x640约6.8ms147 FPSYOLOv8s640x640约4.2ms238 FPSYOLOv8m640x640约8.1ms123 FPS这些数字在不同的CANN版本和服务器配置下会有浮动但总体量级是稳定的。YOLOv5s在Atlas 300V 24G上跑出300 FPS左右这个成绩在边缘推理卡里相当能打。如果你想验证自己的部署是否正常可以用这个数据做一个参照。这里有个值得注意的点Atlas 300V 24G在batch1下的推理延迟约3~4ms意味着单路视频流即使按25FPS算模型推理本身也只占用约10%的时间预算剩余大量算力可以用来做多路并行。6.2 调优三板斧batch、多路并行、算子融合第一板斧是用足batch。在GPU上跑batch32是常规操作在NPU上其实也类似Atlas 300V 24G特别适合用大batch把算力打满。实测下来batch8时YOLOv5s的吞吐量能摸到600~700 FPS比batch1翻了两倍多。但batch并不是越大越好因为显存占用会线性增长而且某些算子在大batch下可能会触发更耗时的内存分配。我的建议是从batch1、4、8、16分别测一轮找到吞吐量拐点再定方案。第二板斧是多路并行。这个多路不是指代码里开多线程而是用昇腾的Stream流机制。在pyACL里可以通过acl.rt.create_stream创建多个执行流让不同的视频帧在不同的Stream上并行推理。这个过程类似GPU上的CUDA Stream需要一定的编程理解但对提高多路视频分析的整体吞吐量帮助很大。如果你只是做单路实时检测一个Stream就够用如果做16路甚至32路视频分析必须用多个Stream来分担负载。第三板斧是算子融合。ATC转换时有一些参数可以控制算子的融合策略比如设置了--op_precision_mode或者--enable_small_channel后某些卷积层、激活层、归一化层会被自动融合成更高效的组合。对于YOLOv5s这种结构相对常规的模型默认设置其实已经能取得不错的融合效果但如果你用的是一些不常见结构的模型手动检查和调整这些参数还是很有必要的。注意调优时不要只看推理时间一个指标要同时关注NPU利用率和内存占用。可以用npu-smi info实时查看卡的负载状态。如果NPU利用率一直很低说明瓶颈可能在预处理或后处理环节而不是推理本身。7. 常见问题与排查技巧实录7.1 问题速查表这部分内容我从两个项目里挑出最高频遇到的问题整理成表格方便以后遇到直接查。这些报错信息不一定字字一样但出现的位置和场景基本对应。报错/现象可能原因解决方法acl.mdl.load_from_file失败om文件路径错误或om与当前CANN版本不匹配检查绝对路径重新用当前版本CANN执行ATC转换atc报E10001类似的错误onnx模型中有不支持的算子用onnxsim简化检查opset版本用可视化工具查找出问题的算子节点npu-smi info看不到卡驱动未装好或PCIe识别异常重新安装HDK检查lspci确认服务器是否开启PCIe 4.0推理时输入shape不匹配onnx导出时的input shape和ATC input_shape不一致统一输入名和shape用onnx.load确认模型输入名推理结果全为0或NaN模型精度异常可能用了错误的AIPP配置检查aipp.cfg的mean/std以及--output_type是否正确推理速度远低于预期batch太小或未使用AIPP/DVPP预处理增大batch把预处理尽量从CPU挪到NPU硬件单元检查NPU利用率内存不足/申请内存失败多个模型同时加载24G显存被占满减少同时加载的模型数量使用acl.rt.mem_free手动释放不再用的内存7.2 三个让我印象深刻的排查经历第一个经历是YOLOv8部署时模型转om一直报“Resize算子不支持”。后来我排查了很久发现问题出在onnx导出时用了opset 17而CANN对高版本opset的支持不完整。把opset降到12重新导出一次就成功了。从那以后我所有YOLO变体导出onnx都固定用opset 11或12。第二个经历是推理结果偶尔出现“空目标”——同一段测试视频有时能检测到目标有时一帧都没有。排查了整整两天最后发现是AIPP配置里src_image_size_w和src_image_size_h填反了。如果你用的是YOLOv5输入是640x640这里一定要填宽在前、高在后填反了NPU虽然不报错但图像会被错误裁剪。从那以后我写完aipp.cfg都会先做一次静态图推理验证确认输出正常再进下一步。第三个经历是关于多路视频流并发时内存泄漏。早期版本我在每帧推理后没有释放输入输出的device内存跑两三个小时后内存占用一路飙升最终把卡搞到“假死”状态必须重启服务器才能恢复。后来我写了个监控脚本每推理1000帧打印一次内存占用一眼就看出了问题。在长稳运行的推理服务里内存申请和释放必须成对出现这个坑几乎每个人都会踩一次。7.3 独家避坑技巧除了上面的内容最后分享几个很难在文档里找到的小技巧。第一个技巧是关于acl.rt.memcpy的性能。在传输图像数据时如果用默认方式Host到Device的拷贝可能会阻塞住推理线程。可以尝试用异步拷贝接口acl.rt.memcpy_async把拷贝和上一次推理重叠起来从而提升有效吞吐。这属于比较进阶的优化但对视频流场景效果很明显。第二个技巧是在多模型切换时不要反复acl.mdl.load_from_file和acl.mdl.unload这样每次切换都会有较大的耗时。更好的做法是同时加载两个模型在代码里按需调用不同model_id执行推理。因为Atlas 300V 24G拥有24GB显存塞两三个常见检测模型完全不是问题。第三个技巧是关于日志排障。CANN的日志默认比较“啰嗦”但一旦遇到问题这些日志是非常有用的。建议把日志级别设成INFO或DEBUG保留最近的日志文件。排查问题时重点搜[ERROR]和[WARNING]大多数问题在日志里就能直接定位。第四个技巧如果要部署YOLOv5之外的模型比如YOLOv8的检测头结构有变化ATC转换时有些Head部分的算子比如DFL的解码可能不被原生支持。我的处理办法比较土但很有效要么在onnx导出时把head部分替换成标准的decode结构要么推理后取原始输出自己在Host端写DFL解码逻辑。两种方式我都试过第二种更稳因为它绕开了NPU上处理动态shape解码算子的限制。8. 写在最后的一点体会Atlas 300V 24G这块卡我用了大半年。从最初的环境配置折腾到最终稳定跑起多路视频检测过程中走了不少弯路但回过头看它确实是一张非常适合推理场景的卡算力够用、显存充足、功耗感人价格还比同级GPU友好很多。如果你正打算在它上面部署YOLO我最想提醒的就是三件事环境版本一定要配套、预处理尽量走AIPP或DVPP、内存申请释放必须成对。把这三点做好整个部署过程会顺畅很多。最后分享一个小技巧不管用什么模型模型转换完成后先拿一张固定图片做“冒烟测试”确认输出稳定、置信度正常之后再写业务代码。这样做的目的是把“模型转换问题”和“代码逻辑问题”隔离开排查时不会互相干扰。我自己所有项目都保持这个习惯省下来的调试时间比什么都值。
返回列表