☰
Windows10纯CPU跑YOLOv5:PyCharm环境搭建与推理调优
2026/10/27 20:58:30 网站建设 项目流程

1. 先把预期摆正:CPU 跑 YOLOv5 到底能干什么

windows10+cpu+pycharm跑yolov5 这个组合,这几年被问到的频率一点没降。原因很现实:大多数人手里只有一台办公本或者台式机,没有独立显卡,但又想亲手把目标检测跑起来看看效果。我先说结论——这套组合完全能跑通,检测精度和 GPU 版一模一样,差的是速度,不是结果。yolov5s 在 640 输入尺寸下,一颗普通的四核八线程移动端处理器,单张图推理大概 200 到 400 毫秒;换成 yolov5n 能压到 100 毫秒出头。这个速度做离线批处理、做 demo 演示、做标注辅助完全够用,做实时视频流就吃力了。

我见过太多人卡在第一步:照着某篇教程敲pip install -r requirements.txt,然后发现装上来的是带 CUDA 的 torch,或者版本对不上,报一堆看不懂的错,最后放弃。实际上这套环境真正的门槛不在 YOLOv5 本身,而在依赖版本之间的相互咬合。PyTorch 版本、numpy 版本、Python 版本、OpenCV 版本,四个东西只要有一个错位,就会在某个你想不到的地方炸掉。

所以这篇东西的定位很明确:给手上只有 Windows10 机器、习惯用 PyCharm 写代码、想认真把 YOLOv5 跑起来(而不是复制粘贴一把梭)的人看。我会把环境搭建的每一步为什么这么做讲清楚,把参数怎么调、调到什么程度算合理讲清楚,也会把我自己踩过的那些报错原样放出来。新手照着做能跑通,有基础的可以跳过前面直接看性能压榨和排错那几节。

1.1 这套组合的真实适用场景

先把话说透,省得你花两天搭完环境发现方向不对。CPU 版本的 YOLOv5 适合三类活:一是离线批量处理,比如你有一批图片或者录好的视频,需要把里面的目标框出来、导出坐标,跑一晚上也无所谓;二是算法验证和学习,你想搞清楚 YOLOv5 的预处理怎么做的、后处理 NMS 怎么写的、anchor 怎么匹配的,这些跟用什么硬件毫无关系;三是轻量级部署前的功能验证,先在 CPU 上把流程跑顺,确认输入输出格式没问题,再考虑上其他加速方案。

不适合的也很明确:需要每秒处理 15 帧以上的实时场景、需要训练几千张图以上的数据集、需要跑 yolov5l 或 yolov5x 这种大模型。这几件事在纯 CPU 上不是"慢一点",是"慢到没有意义"。我做过一个粗略统计,CPU 上训练一个 200 张图的小数据集,yolov5n、320 尺寸、20 个 epoch,大概要花掉四十分钟到一个半小时,取决于你的处理器。同样的活换一张入门级独显,五分钟内结束。所以训练这块,我后面会给一个"够用就行"的取巧方案,而不是硬刚。

1.2 为什么选 PyCharm 而不是命令行一把梭

命令行python detect.py --source xxx当然更快,但你一旦开始改代码、加断点、看变量,命令行就废了。PyCharm 在这套流程里的价值有三个:第一,解释器管理,你可以给每个项目单独指定虚拟环境,不会污染系统 Python;第二,运行配置,参数可以存下来,下次点一下绿色三角就复现,不用每次翻命令历史;第三,调试能力,YOLOv5 的detect.py里non_max_suppression那个函数,你想看看 NMS 前后候选框数量差多少,打个断点比 print 一百次快得多。

代价是 PyCharm 会引入两个额外变量:工作目录和运行参数填在哪。这两点恰恰是新手最容易翻车的地方,报错形式还特别迷惑——比如明明文件就在那儿,却提示路径不存在。后面第 3 节我会专门讲这块。

1.3 硬件基线与性能预估怎么算

不用瞎猜你的机器行不行,做个简单估算就行。yolov5s 在 640×640 输入下的前向计算量大约是 16 GFLOPs(乘加算两次的话是 32 GOPS 左右,业界习惯按前者说)。一颗主流的四核处理器,单精度浮点理论峰值大概在 200 到 400 GFLOPS 之间,但卷积运算实际能榨出来的效率通常只有 15% 到 25%,也就是 40 到 80 GFLOPS 的有效算力。用 16 除以 60,得到 0.27 秒,和实测的 200 到 400 毫秒基本吻合。这个估算方法你可以套到任何模型上:用模型的 GFLOPs 除以你机器的有效算力,就是理论单张耗时,再加 30% 的预处理和后处理开销。

内存方面不用太担心,yolov5s 的权重文件才 14MB 左右,推理峰值内存占用在 1GB 上下,8GB 内存的机器完全够。真正吃内存的是训练时的 DataLoader 多进程,这个后面会讲。

还有一点要提前说:YOLOv5 的官方权重是 80 类通用类别,人、车、动物、日常物品这些。你要是想检测自己业务里的特定目标(比如某种零件、某种包装),要么拿它做预标注再人工修,要么自己微调。CPU 上能做微调,但方法和 GPU 上不太一样,第 5 节细说。

2. 环境搭建:把坑一次性挖完

环境这块我打算讲得啰嗦一点,因为九成的失败都发生在这里。整个流程的核心原则只有一条:先确定 Python 版本,再单独装 CPU 版 PyTorch,最后装剩下的依赖,顺序不能乱。很多人失败就是因为在 torch 还没装好的情况下直接跑requirements.txt,pip 会自动去拉一个默认版本的 torch,而默认版本在 Windows 上通常是带 CUDA 的,几百兆下下来发现用不了,或者版本和后面的 numpy 打架。

2.1 Python 版本怎么选,别追新

YOLOv5 对 Python 的最低要求是 3.8,往上都行,但我不建议用太新的。3.12 刚出来那阵子,不少依赖包还没有预编译好的 wheel,pip 会尝试从源码编译,Windows 上没有 C++ 编译工具链直接报错,你得装几个 G 的 Visual Studio Build Tools,非常折腾。

我的建议是Python 3.10 或 3.11,这两个版本生态最成熟,几乎所有包都有现成的 wheel。安装的时候有一个细节必须注意:安装界面上那个 "Add Python to PATH" 一定要勾上,否则后面在 PyCharm 里找解释器会多绕一圈。安装路径不要带中文和空格,这是 Windows 上的老规矩了,很多库对非 ASCII 路径处理得很糟糕。

装完之后开个终端验证一下,python --version能输出版本号就说明 PATH 配好了。如果输出了 Microsoft Store 的提示,说明系统里有个占位的假 python,去"设置 → 应用执行别名"里把两个 python 的开关关掉。

提示:机器上如果已经装了好几个 Python,别急着卸载,容易把别的项目搞崩。用虚拟环境隔离就完事了,每个项目一个环境,互不干扰。

2.2 虚拟环境的创建与 CPU 版 PyTorch 的正确装法

虚拟环境用 venv 还是 conda 都行,我个人偏向 venv,因为它跟着 Python 自带,不额外装东西,PyCharm 对它的识别也最干脆。在项目根目录下开终端:

python -m venv venv venv\Scripts\activate

激活之后命令行前面会出现(venv)字样,这时候你装的任何包都只影响这个目录。接下来是最关键的一步,装 CPU 版 PyTorch。一定不要直接pip install torch,要走官方的 CPU 专用索引:

pip install torch==2.5.1 torchvision==0.20.1 --index-url https://download.pytorch.org/whl/cpu

为什么锁 2.5.1 这个版本?因为 2.6 之后的 torch 把torch.load的weights_only参数默认值改成了True,而 YOLOv5 加载.pt权重文件走的就是这个接口,会直接抛出一个WeightsUnpicklingError。官方仓库后续版本修了这个问题,但如果你拉的是稍旧的代码,就会撞上。锁在 2.5.x 是最省心的做法。当然你也可以留在新版本,把代码里相关调用改成weights_only=False,但那就不是"跑通"而是"改代码"了,第一次上手没必要给自己加难度。

装完验证一下,这段代码能跑通就说明 CPU 版装对了:

import torch print(torch.__version__) print(torch.cuda.is_available()) # 必须是 False print(torch.backends.mkldnn.is_available()) # CPU 加速后端,最好是 True

cuda.is_available()返回 False 不是坏事,恰恰说明你装的是 CPU 版,不会在运行时去尝试初始化显卡然后报错。mkldnn是 Intel 的一层深度神经网络加速库,PyTorch 在 CPU 上做卷积默认会调它,返回 True 意味着你的推理速度能好不少。

2.3 依赖清单:哪些必须锁版本,哪些不用管

拿到 YOLOv5 代码之后,requirements.txt里那一堆包不用无脑全装。我做了一张表,把每个包的作用和版本注意事项列清楚:

包名作用版本建议备注
torch / torchvision推理与训练核心torch 2.5.1 + tv 0.20.1必须走 CPU 索引单独装
numpy数组运算基础锁 < 2.0numpy 2.x 与旧版 torch 二进制不兼容
opencv-python图像读写与绘制4.9.x别和 headless 版同时装
PyYAML解析模型与数据配置≥ 6.0老版本在 Win 上要编译
tqdm进度条最新即可纯 Python,无风险
matplotlib结果可视化最新即可中文显示需要额外配字体
pandas / seaborn训练结果统计绘图最新即可只训练时用得上
requests / Pillow下载权重、图像处理最新即可无特殊要求
scipy部分后处理与评估最新即可无特殊要求

装的时候有个技巧:先把 torch 装好,再执行pip install -r requirements.txt,pip 看到 torch 已经满足就会跳过,不会去覆盖。如果它还是要重装,加个--no-deps先装别的,再单独补。

pip install "numpy<2.0" "opencv-python==4.9.0.80" pip install pyyaml tqdm matplotlib pandas seaborn requests pillow scipy

opencv-python和opencv-python-headless这两个包提供同一批模块但文件不同,同时安装会导致cv2目录被覆盖,报一些莫名其妙的AttributeError。你可以在装完后跑一下pip list | findstr opencv确认只有一个。

2.4 权重文件:别让首次运行卡在下载上

YOLOv5 的detect.py在找不到权重时会自动去远端拉取。这一步在网速不好的时候能卡十分钟,而且失败之后重试逻辑不友好,容易让人以为代码有问题。我的做法是手动下载好放进去,文件名保持原样(yolov5n.pt、yolov5s.pt这些),放在项目根目录,然后参数里直接写文件名。

选哪个权重看你的场景:yolov5n最轻,速度最快,精度略低,是 CPU 上的首选;yolov5s是精度和速度的平衡点,也是最常被拿来做 baseline 的;yolov5m往上在 CPU 上就不太现实了,单张图接近一秒,跑视频会明显卡顿。我第一次跑的时候图省事下了yolov5x,一张图等了快四秒,那个体验直接劝退。

3. PyCharm 里的工程配置:让脚本跑在正确的目录下

PyCharm 这一层的坑和 Python 本身的坑是两码事,它属于"代码没错但就是跑不起来"的类型,特别消耗耐心。核心就三件事:解释器指向哪个环境、工作目录设成什么、运行参数填在哪里。搞明白这三样,后面就顺了。

3.1 解释器与工作目录的绑定关系

打开项目之后先做一件事:File → Settings → Project → Python Interpreter,点右上角齿轮选Add,找到你刚才创建的venv\Scripts\python.exe。选完之后看一眼包列表,能看到 torch 就说明绑对了。这一步如果选错,比如指到了系统 Python,那你之前在虚拟环境里装的包一个都用不上,会报ModuleNotFoundError: No module named 'torch',很多人这时候会去pip install torch,结果装到系统环境里,越搞越乱。

工作目录(Working directory)的设置在运行配置里,默认是项目根目录,一般不用改。但如果你从别处复制了一个运行配置过来,它可能指向一个完全不存在的路径,表现就是--source里写的相对路径解析不到,报"未找到文件"。判断方法很简单:在脚本开头加一行print(os.getcwd()),看输出是不是项目根目录。

3.2 运行参数怎么填才不折腾

PyCharm 的运行配置里有个Script parameters输入框,你平时在命令行里敲的那些参数原样贴进去就行:

--weights yolov5s.pt --source data/images --img 640 --conf-thres 0.25 --device cpu --save-txt

注意几点。参数之间用空格分隔,不是逗号;路径里有空格的话要用引号包起来;--device cpu这个一定要写,不写的话脚本会去探测显卡,在纯 CPU 机器上虽然最终会回退到 CPU,但中间会打一堆警告信息,看着心烦。如果你后续想换输入源,只改--source那一项,其他参数存着不动,这就是运行配置比命令行舒服的地方。

有一个细节值得单独说:新版 PyCharm 在运行配置里对参数的解析是原样传递的,但它不会帮你做 shell 层面的转义。所以路径里如果有反斜杠,最好统一换成正斜杠,Windows 的 API 对正斜杠是兼容的,能省掉不少转义上的麻烦。

3.3 用断点看清预处理和后处理的真实开销

跑通之后,我建议你花二十分钟做一次断点调试,位置选两个:detect.py里letterbox调用之后,和non_max_suppression返回之后。前一个看的是模型实际吃进去的张量形状,后一个看的是 NMS 前后的候选框数量对比。

我给个参考数据:一张 1280×720 的图,经过 letterbox 缩放加填充后变成 640×640×3 的张量,归一化到 0 到 1;原始 YOLOv5s 输出是三个尺度,分别是 80×80、40×40、20×20,每个格子 3 个 anchor,总共 25200 个候选框,经过置信度过滤后通常剩一两百个,再过 NMS 一般剩几个到几十个。你要是发现 NMS 之前剩下的框特别多(比如上千个),那说明--conf-thres设得太低了,纯属给后处理添负担。

注意:调试模式下跑推理会慢好几倍,别拿调试的耗时当性能基准,测速一律用正常运行模式。

4. 推理实操:从第一张图到批量处理

环境通了,接下来就是把参数调到一个"能看又不太慢"的状态。这一节我会把detect.py的关键参数一条条拆开讲,尤其是那几个对 CPU 性能影响巨大的。

4.1 参数逐条拆解与实测耗时对照

参数默认值CPU 场景建议影响
--weightsyolov5s.ptyolov5n.pt / yolov5s.pt模型越大越慢,精度越高
--sourcedata/images图片目录 / 视频文件 / 00 表示摄像头,CPU 上不建议
--img640320 或 416面积按平方缩小,320 约为 640 的四分之一
--conf-thres0.250.3 ~ 0.4提高可减少后处理候选框
--iou-thres0.450.45密集目标可提到 0.5
--device空(自动)cpu显式指定,避免探测警告
--classes全部按需过滤只保留关心的类别,减少绘制开销
--augment关闭保持关闭开启等于推理多次取并集,CPU 上慢数倍
--save-txt关闭按需开启导出归一化坐标,方便后续处理
--half关闭必须关闭半精度在 CPU 上不支持,开了会报错

实测数据我列一组参考值,机器是四核八线程的移动端处理器,输入 640:

模型单张耗时峰值内存备注
yolov5n90 ~ 150 ms约 600 MB批处理首选
yolov5s220 ~ 400 ms约 900 MB精度明显更好
yolov5m700 ~ 1000 ms约 1.4 GB视频场景基本不可用

把--img从 640 降到 320,耗时大约降到原来的四分之一到三分之一,但小目标的召回会明显下降。如果你的目标在画面里占比超过三分之一,320 完全够;如果检测的是画面里远处的车牌、零件之类的小东西,那还是老实上 640,速度慢点就慢点。

4.2 输入尺寸、批大小、线程数三个旋钮怎么配合

这三个参数是联动的,单独调一个往往没效果。先看线程数:PyTorch 在 CPU 上默认会开和逻辑核心数相同的线程,但超线程在卷积运算里经常帮倒忙,因为两个逻辑核共享一个物理核的浮点单元,抢资源反而降低效率。我的经验是把线程数设成物理核心数,比如四核八线程就设 4。

import torch torch.set_num_threads(4) # 按物理核心数设,不是逻辑核心数

测一下不同设置的实际耗时,你会发现差别能到 15% 到 25%。这个调用要放在模型加载之前才生效。

再看批大小。detect.py本身没有暴露 batch 参数,它是一张一张处理的。如果你需要处理几万张图,自己写个脚本攒 batch 更划算——批大小 4 在 CPU 上通常比单张累加要快 20% 左右,因为矩阵运算的并行度利用得更充分。但批大小别超过 8,内存会顶不住,而且收益迅速衰减。

至于输入尺寸,除了一开始就定好,还有一个折中做法:先用 640 跑一遍,只对置信度在 0.1 到 0.4 之间的模糊样本再用 640 复核。不过说实话,CPU 上这么搞省下来的时间还不够你写代码的,老老实实定一个尺寸就行。

4.3 用自己的图片跑:目录结构和输出在哪

--source传一个目录,脚本会把里面所有图片都过一遍;传单个文件就只处理那一张;传视频文件会逐帧处理并合成输出视频,这个过程在 CPU 上会非常慢,十分钟的视频可能要跑一小时以上。

输出默认落在runs/detect/exp,每跑一次自动递增成exp2、exp3。这个设计挺贴心,但跑多了会攒一堆目录,建议在参数里加--project runs/mydetect --name test01,自己指定位置和名字。加上--save-txt之后,每张图会对应一个.txt,内容是类别索引 中心x 中心y 宽 高,全部归一化到 0 到 1,这是 YOLO 系列的标准格式,直接能拿去当标注用。

用自己数据的时候有个坑要提:路径里带中文或者特殊字符,cv2.imread会返回 None,然后脚本在后续处理时抛一个毫无关联的错误。解决办法有两个,一是把路径改成纯英文,二是如果实在改不了(比如数据在别人给的文件夹里),在读取的地方换成cv2.imdecode(np.fromfile(path, dtype=np.uint8), -1)。我一般是直接改路径,省事。

4.4 一个小脚本:批量处理并汇总结果

官方脚本一次只能输出图片,如果你要的是结构化数据(比如每张图检测到几个人、几个车),自己写一层包装会舒服很多:

import os import csv import torch import cv2 from pathlib import Path torch.set_num_threads(4) model = torch.hub.load('.', 'custom', path='yolov5s.pt', source='local') model.conf = 0.3 model.iou = 0.45 model.to('cpu') rows = [] for p in sorted(Path('data/mine').glob('*.jpg')): img = cv2.imread(str(p)) if img is None: print('读取失败,跳过:', p) continue res = model(img, size=640) df = res.pandas().xyxy[0] for _, r in df.iterrows(): rows.append([p.name, r['name'], round(r['confidence'], 3), int(r['xmin']), int(r['ymin']), int(r['xmax']), int(r['ymax'])]) with open('result.csv', 'w', newline='', encoding='utf-8-sig') as f: w = csv.writer(f) w.writerow(['file', 'label', 'conf', 'x1', 'y1', 'x2', 'y2']) w.writerows(rows) print('共导出', len(rows), '条检测结果')

这里用的是torch.hub.load接口,它把预处理、推理、NMS 全封装好了,代码短,适合快速验证。缺点是不方便改中间流程,想深入定制还是得走detect.py那一套。另外source='local'这个参数很重要,不加的话它会去远端拉模型定义,断网环境直接失败。

5. 在 CPU 上训练的可行性方案

先说结论:能训,但只适合小数据量微调,不适合从零训练。原因不是内存,是时间。反向传播的计算量大约是前向的两到三倍,再加上数据加载和增强,同样一张图在训练时耗掉的时间是推理的四五倍。一个 2000 张图的数据集、20 个 epoch,CPU 上大概要跑十几个小时。

5.1 什么样的任务适合 CPU 微调

判断标准有三条,满足两条以上就可以试试:类别数少(一到三类)、样本量小(几百张)、目标和预训练类别的形态接近(比如都是车辆、都是人形)。因为这种情况下模型不需要学太多新东西,只需要在已有的特征上做微调,收敛很快,几十个 epoch 就能出效果。

反过来,如果你的任务是检测显微镜下的细胞、电路板上的焊点这种和自然图像差异极大的目标,那 CPU 微调会非常痛苦,因为特征需要从头学,需要的迭代次数多得多。这种场景我建议先用预训练模型做一遍推理,看看能检出多少——有时候零样本的效果就已经够做预标注了,人工修一修比训练快。

5.2 冻结层、小模型、小尺寸三件套

CPU 训练的核心思路是"能省则省"。具体三招:

第一招,换最小的模型。yolov5n参数量只有 1.9M 左右,是yolov5s的四分之一,训练速度提升显著。精度会降一些,但小数据集上本来也用不上大模型的容量。

第二招,冻结骨干网络。YOLOv5 的train.py有个--freeze参数,传 10 表示冻结前 10 层(也就是主干特征提取部分),只训练检测头。这样反向传播只走一小部分网络,速度快很多,而且小数据集上还能防止过拟合。

第三招,降输入尺寸 + 降批大小。--img 320 --batch-size 4,显存不用管,但内存要看着点,尤其是多进程 DataLoader 开起来之后。

python train.py --weights yolov5n.pt --data mydata.yaml --epochs 50 ^ --img 320 --batch-size 4 --workers 2 --device cpu ^ --freeze 10 --cache ram --project runs/train --name cpu_exp1

--workers 2这个值在 Windows 上要特别注意。DataLoader 的多进程在 Windows 上用的是 spawn 方式启动,开销比 Linux 大,设太大会让每个 epoch 开始前卡半天。设 0 也行,就是数据加载会变成瓶颈。我一般在 2 到 4 之间试,看哪个快。--cache ram是把这个小数据集整个读进内存,几百张图完全放得下,能省掉反复读盘的时间,这个参数在小数据集上收益非常明显。

5.3 一次小样本训练的实际记录

我用一个 300 张图、两个类别的数据集试过一次,机器是六核处理器。配置是yolov5n、img 320、batch 4、freeze 10、epochs 60、workers 2、cache ram。实际耗时:前 10 个 epoch 每个大约 50 秒,后面稳定在 35 秒左右(因为缓存生效了),全程大约 40 分钟。最终 mAP@0.5 从预训练权重的 0.31 涨到 0.87,效果是能用的。

过程中遇到两个问题值得记一下。一是--workers 2在开始第一个 epoch 之前会有一个明显的停顿,大概十几秒,这不是卡死,是在启动子进程,别急着按 Ctrl+C。二是中途内存占用会缓慢上涨,跑到三十几个 epoch 的时候涨到了 6GB 左右,8GB 的机器要留够余量,最好别同时开浏览器。

提示:CPU 训练建议把--noval加上,跳过每个 epoch 结束后的验证环节,能省掉 20% 左右的时间。等训练完再单独跑一次val.py看指标。

6. 提速方案:把 PyTorch 换成 ONNX Runtime

如果你对速度有进一步的要求,又不打算换硬件,那最划算的一步是把模型导出成 ONNX,然后用 ONNX Runtime 推理。我实测下来,同样的 yolov5s、同样的 640 输入,ONNX Runtime 比原生 PyTorch 快 1.6 到 2.2 倍,具体倍数跟处理器架构有关,支持 AVX2 的处理器收益更大。

6.1 导出的正确姿势与常见报错

导出命令本身很简单:

pip install onnx onnxruntime onnxsim python export.py --weights yolov5s.pt --include onnx --img 640 --batch 1 --device cpu

几个参数必须注意:--batch 1固定批大小,动态批会让某些优化失效;--img必须和你推理时用的一致,ONNX 模型的输入尺寸是写死的;--device cpu避免导出脚本去探测显卡。导出的文件默认叫yolov5s.onnx,大小和.pt差不多。

常见的报错是onnxsim那一步失败,提示某些算子不支持。这个简化是可选的,主要是把计算图里冗余的部分合并掉,能再快 5% 到 10%。跑不通的话直接在--include里只写onnx,跳过简化也能用。

导出之后建议做一次数值校验:同一张图分别用 PyTorch 和 ONNX 跑,对比输出的框坐标,差异应该在千分之一以内。差太多说明导出有问题,通常是算子映射导致的,得回去查。

6.2 ONNX Runtime 推理的核心骨架

用 ONNX 推理需要自己写预处理和后处理,比detect.py麻烦一些,但代码不长。预处理的关键是 letterbox:保持长宽比缩放,短边补到目标尺寸,这样才和训练时的处理一致:

import cv2 import numpy as np import onnxruntime as ort def letterbox(img, new=640, color=(114, 114, 114)): h, w = img.shape[:2] r = min(new / h, new / w) nh, nw = int(round(h * r)), int(round(w * r)) resized = cv2.resize(img, (nw, nh), interpolation=cv2.INTER_LINEAR) canvas = np.full((new, new, 3), color, dtype=np.uint8) top, left = (new - nh) // 2, (new - nw) // 2 canvas[top:top + nh, left:left + nw] = resized return canvas, r, left, top sess = ort.InferenceSession('yolov5s.onnx', providers=['CPUExecutionProvider']) inp_name = sess.get_inputs()[0].name img = cv2.imread('test.jpg') canvas, ratio, dx, dy = letterbox(img, 640) x = canvas[:, :, ::-1].transpose(2, 0, 1) # BGR 转 RGB,HWC 转 CHW x = np.ascontiguousarray(x, dtype=np.float32) / 255.0 x = x[None] # 加 batch 维度 pred = sess.run(None, {inp_name: x})[0] # 形状 (1, 25200, 85)

后处理部分要做两件事:把(1, 25200, 85)里置信度低于阈值的行滤掉,再对剩下的框做 NMS。这部分逻辑和 PyTorch 版完全一样,只是用 numpy 写。写的时候有个细节:ONNX 输出的坐标是基于 640×640 输入的,要还原回原图尺寸得先减掉填充量再除以缩放比例,这一步忘了的话框会整体偏移。

6.3 实测对比与选择建议

同一台机器、同一个模型、同一批 100 张图,测下来:

推理后端平均单张耗时首帧加载耗时部署复杂度
PyTorch(原生)约 280 ms1.5 s低,代码直接用
ONNX Runtime约 140 ms0.6 s中,需写前后处理
OpenVINO(若有对应硬件)约 110 ms1.2 s中高,需转换工具链

如果你只是偶尔跑跑,PyTorch 那套就够,没必要为了省一半时间多写两百行代码。如果是要做成一个反复调用的工具或者服务,那花半天换成 ONNX 是值得的,尤其是处理量大的时候,这个差距会累积得很明显。

7. 常见问题与排查速查表

这一节是我攒下来的报错记录,基本都是第一次跑的时候会撞上的。按报错信息查表,能省掉大量搜索时间。

7.1 报错速查表

报错关键词真实原因处理办法
WeightsUnpicklingErrortorch 2.6+ 默认 weights_only=True降到 torch 2.5.1,或改代码传 False
A module compiled using NumPy 1.xnumpy 2.x 与 torch 二进制不兼容pip install "numpy<2.0"
WinError 1455 页面文件太小DataLoader 多进程内存不足降--workers、降--batch-size、加大虚拟内存
ModuleNotFoundError: torchPyCharm 解释器选错重新绑定到 venv 里的 python.exe
cv2.error: !_src.empty()图片路径含中文或文件损坏改英文路径,或换 imdecode 读
AssertionError: Torch not compiled with CUDA参数里写了--device 0改成--device cpu
AttributeError: module 'cv2' has no attribute ...两个 opencv 包打架卸载 headless 版,只留一个
--half相关报错CPU 不支持半精度去掉该参数
cannot identify image file权重文件下载不完整删掉重新下,核对文件大小
Permission denied写入 runs 目录目录被占用或只读换--project指定新目录

7.2 几个不报错但很要命的细节

有些问题不抛异常,只是结果不对或者速度异常,排查起来更费劲。

第一个,自动安装依赖把环境搞坏。YOLOv5 的部分版本在启动时会检查依赖,发现版本不符就自动 pip install。这个行为在联网环境下可能把你的 CPU 版 torch 升级成别的版本,或者把 numpy 拉到 2.x,然后你下次运行就崩了。较新的代码提供了环境变量来关闭这个行为(设置YOLOV5_AUTOINSTALL为 false),老版本的话,最稳妥的办法是跑之前先看一眼pip list,跑之后再对比一次,发现被改了就知道凶手是谁。

第二个,权重文件放错位置。脚本找权重是按"当前工作目录 → 项目根目录"的顺序找的。你在 PyCharm 里工作目录设成子文件夹,权重放在根目录,它就找不到,然后尝试去下载,网速慢就一直卡着,看起来像死机。判断方法很简单,看终端里有没有出现下载进度条。

第三个,置信度阈值设得太低导致 NMS 成为瓶颈。我见过有人把--conf-thres设成 0.01,想"多检出一些",结果一张图剩三千多个候选框,NMS 的耗时比前向计算还长。合理范围是 0.25 到 0.4,真要提升召回,应该从模型和输入尺寸下手,而不是无限压低阈值。

第四个,--augment这个开关的隐性成本。它表示测试时增强,会对同一张图做翻转、缩放等多种变换各跑一遍再合并结果。在 GPU 上代价可以接受,在 CPU 上等于把耗时乘以三到四倍。很多人是从教程里抄的参数,压根不知道开了这个,然后抱怨慢。

第五个,模型只加载一次还是每张图加载一次。如果你的代码写成循环里调torch.hub.load,那每张图都要重新读一次权重,光加载就一两秒。正确做法是把模型加载提到循环外面,循环里只调推理。这个错误非常低级但非常常见,尤其是在改造别人代码的时候。

提示:排查性能问题的时候,养成在关键节点打时间戳的习惯。预处理耗时、推理耗时、后处理耗时分别打出来,一眼就能看出瓶颈在哪,比猜快多了。

7.3 关于"跑通"之后该往哪走

环境通了、图能检出了,后面其实有三条路可选。往应用方向走,就是把检测结果接进你的业务流程,比如自动分类、自动计数、生成报表;往精度方向走,就是整理自己的数据集做微调,这条路绕不开标注,标注工具可以用 labelImg 这类本地工具,格式选 YOLO 的 txt;往性能方向走,就是换后端、剪枝、量化,前面讲的 ONNX 只是第一步。

我个人在实际操作中的体会是,绝大多数人真正需要的其实止步于第二步——用一个能跑起来的模型做预标注,把人工标注的工作量砍掉一大半,这件事在纯 CPU 上完全成立。速度慢不要紧,晚上挂着跑就行。反而是那些一上来就追求实时、追求部署的想法,在没有显卡的情况下容易陷入反复折腾环境却始终没产出的循环。

最后分享一个小技巧:把你调好用的那套运行配置导出成文件存起来(PyCharm 的运行配置可以复制粘贴成模板),配合一份记录着各个包精确版本的pip freeze > requirements-lock.txt。下次换机器或者重装系统,照着恢复,十分钟就能回到可用状态。我这两年换过三次工作机,全靠这份快照,一次没在环境上翻过车。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询