用YOLO11做了个人车检测项目,全过程记录
1. 为什么选YOLO11做这个小项目
你有没有过这样的想法:想快速验证一个目标检测想法,但又不想被环境配置、依赖冲突、CUDA版本这些事拖住手脚?我也有。上个月想做个简单的“人+车”检测demo,用来识别自家小区门口的通行情况,原本打算从零搭环境,结果光是PyTorch和torchvision的版本对齐就折腾了两天。
直到发现这个预装好的YOLO11镜像——它不是半成品,也不是只跑通demo的玩具,而是一个开箱即用、结构完整、可调试可扩展的计算机视觉开发环境。Jupyter能直接写实验代码,SSH支持远程调试,训练脚本、标注工具、数据转换流程全都有现成路径。更重要的是,它基于Ultralytics最新稳定分支(ultralytics-8.3.9),模型定义清晰,API统一,不像有些老版本还要手动改detect.py里的后处理逻辑。
这不是一个“教你怎么从源码编译YOLO”的教程,而是一份真实项目视角的流水账:从打开镜像那一刻起,到最终看到带框的检测结果图,中间踩过的坑、绕过的弯、省下的时间,我都记下来了。
2. 环境准备:三分钟进入开发状态
2.1 镜像启动与基础访问
镜像启动后,你会获得两个标准入口:
- Jupyter Lab:默认监听
:8888端口,Token在日志里直接打印,复制粘贴就能进。界面干净,左侧文件树直接映射到容器内/workspace/目录,所有代码、数据、模型都在这里。 - SSH服务:监听
:22端口,用户名root,密码inscode(首次登录后建议修改)。适合运行长时间训练或需要终端交互的操作。
提示:Jupyter适合快速试错和可视化,SSH适合批量处理和后台训练。我习惯在Jupyter里写好
train_det.py,然后切到SSH用nohup python train_det.py &丢到后台跑,不卡浏览器。
2.2 项目目录结构一目了然
进入容器后执行ls -R | grep ":$" | sed -e 's/:$//' -e 's/[^-][^\/]*\//--/g' -e 's/^/ /' -e 's/-/|/',能看到清晰的工程骨架:
. ├── ultralytics-8.3.9/ # 官方Ultralytics主库(已patch适配YOLO11) ├── resources/ # 你的项目资产区 │ ├── images/det/ # 图片数据根目录 │ │ ├── json/ # Labelme原始json标注 │ │ └── datasets/ # 转换后的YOLO格式数据集(train/val/test) │ ├── config/ # 配置中心 │ │ ├── data/yolo11-det.yaml # 数据集路径与类别定义 │ │ └── model/yolo11-det.yaml # 模型结构定义(含backbone+head) │ └── weights/det/ # 训练权重保存位置(自动创建) ├── tool/ # 实用脚本包 │ ├── tool_json2label_det.py # json → txt标签转换器 │ └── tool_det2datasets.py # 划分训练/验证集 └── train_det.py # 主训练脚本(你将亲手写的第一个文件)这个结构不是随意组织的,而是和Ultralytics官方推荐方式完全对齐。这意味着:
你写的代码未来迁移到其他服务器或CI环境时,几乎不用改路径;
查看官方文档时,参数说明和你本地的yaml字段一一对应;
出现报错时,错误堆栈能准确定位到resources/config/下的具体文件。
3. 数据准备:5张图起步,也能跑通全流程
3.1 标注:用Labelme画框,别纠结完美
很多人卡在第一步——“我得先搞1000张高质量标注图”。其实大可不必。这个项目我只用了5张手机随手拍的小区路口照片:2张白天人多车少,2张傍晚车流密集,1张阴天逆光。重点不是数量,而是覆盖多样性。
安装Labelme只要一条命令:
pip3 install labelme启动后,把5张原图放进resources/images/det/json/,然后一张张打开,用矩形框标出所有人和车。注意两点:
- 类别名必须严格写成
person和car(小写,无空格),这和后续yaml里的names字段完全绑定; - 每张图保存为同名
.json文件(如IMG_001.jpg→IMG_001.json),方便后续脚本自动匹配。
小技巧:Labelme右下角有“自动保存”开关,打开后每画完一个框就存一次,不怕误关窗口。
3.2 转换:一行命令,JSON变YOLO格式
YOLO系列不吃JSON,只认.txt格式的标签文件:每行代表一个目标,格式为类别索引 中心x(归一化) 中心y(归一化) 宽度(归一化) 高度(归一化)。
执行转换脚本:
cd /workspace python tool/tool_json2label_det.py --input_dir resources/images/det/json/ --output_dir resources/images/det/datasets/labels/它会自动读取所有json,计算每个框的归一化坐标,并生成对应.txt文件,放在labels/目录下。你会发现:
IMG_001.txt里可能有3行(2个人+1辆车);- 所有数值都是0~1之间的小数,比如
0 0.423 0.671 0.152 0.284; - 如果某张图没标任何目标,就不会生成对应txt——这点很关键,避免训练时报“标签文件缺失”。
3.3 划分:让模型学会“举一反三”
YOLO训练需要明确的train/和val/目录。我们用另一个脚本完成划分:
python tool/tool_det2datasets.py \ --image_dir resources/images/det/json/ \ --label_dir resources/images/det/datasets/labels/ \ --output_dir resources/images/det/datasets/ \ --train_ratio 0.8它会:
① 把5张图随机打乱;
② 取4张放入images/train/和labels/train/;
③ 剩下1张放入images/val/和labels/val/;
④ 同时生成images/test/(空目录,留作后续扩展)。
注意:
images/和labels/下的子目录名(train/val)必须和yolo11-det.yaml里定义的train:、val:字段完全一致,否则model.train()会找不到数据。
4. 模型配置:抄作业也要懂原理
4.1 数据配置文件:yolo11-det.yaml
这是整个训练的“地图”,必须亲手写,不能跳过。创建文件:resources/config/data/yolo11-det.yaml
内容如下(仅需改两处路径):
# 数据根目录(相对于当前yaml文件的位置) path: ../ultralytics-yolo11/resources/images/det/datasets/images train: train val: val test: test # 类别定义:索引必须从0开始,顺序不能错 names: 0: person 1: car关键点解析:
path是相对路径,指向images/父目录,所以train:实际路径是.../images/train/;names顺序决定模型输出的类别ID:预测结果中0永远是person,1永远是car;- 这个文件不涉及模型结构,只管“数据在哪、叫什么”,非常轻量。
4.2 模型结构文件:复用官方yolo11n-det.yaml
镜像已内置标准模型定义。你不需要重写backbone,只需确认:resources/config/model/yolo11-det.yaml文件存在,且内容与参考博文中的yaml一致(含C3k2、SPPF、C2PSA等模块)。
它定义了:
- 输入尺寸(默认640×640);
- 网络深度与宽度缩放系数(
n表示nano级,适合小数据集); - 检测头输出层(Segment层支持实例分割,但我们只用检测功能)。
为什么选yolo11n?因为5张图+2类的任务,大模型会过拟合。nano版参数少、收敛快,在CPU上也能训——这点对个人项目太友好了。
5. 训练执行:从第一行代码到loss下降
5.1 编写train_det.py:专注业务逻辑
新建文件train_det.py,内容精简到15行:
from ultralytics import YOLO, settings # 设置权重和日志保存路径(避免默认跑到~/.ultralytics) settings.update({ "runs_dir": "./runs", "weights_dir": "./resources/weights/det" }) def main(): # 加载模型结构 + 预训练权重(yolo11n.pt是镜像自带的) model = YOLO("resources/config/model/yolo11-det.yaml").load("weights/det/yolo11n.pt") # 开始训练:小数据集,降低batch_size,延长耐心值 results = model.train( data="resources/config/data/yolo11-det.yaml", epochs=300, # 小数据集,300轮足够 patience=50, # 连续50轮val_loss不降则停 batch=1, # CPU训练,batch=1最稳妥 imgsz=416, # 比640小,更快,对小图更友好 workers=2, # 数据加载进程数 optimizer='AdamW', # 比SGD更稳 lr0=1e-3, # 初始学习率 cos_lr=True, # 余弦退火,防震荡 project='runs/detect', name='exp_person_car', exist_ok=True ) if __name__ == "__main__": main()5.2 运行与观察:看懂控制台每一行输出
在SSH中执行:
cd /workspace && python train_det.py你会看到类似这样的输出:
Epoch GPU_mem box_loss cls_loss dfl_loss Instances Size 1/300 1.2G 0.8211 0.5423 0.4128 12 416 2/300 1.2G 0.7532 0.4981 0.3876 14 416 ...重点关注三列:
box_loss:定位框回归误差,下降最快;cls_loss:分类误差,初期波动大,后期应趋稳;Instances:本轮参与训练的目标总数,验证数据加载是否正常。
如果
Instances一直是0,说明路径配置错了;如果box_loss卡在0.8以上不降,检查标注框是否严重偏离目标(Labelme里框太大/太小都会导致)。
训练300轮后,最佳权重会自动保存在:runs/detect/exp_person_car/weights/best.pt
6. 推理验证:让模型真正“看见”
6.1 写predict_det.py:三步出图
新建predict_det.py:
from ultralytics import YOLO import cv2 # 加载训练好的最佳权重 model = YOLO("runs/detect/exp_person_car/weights/best.pt") # 对验证集所有图片推理(自动遍历目录) results = model.predict( source="resources/images/det/datasets/images/val", imgsz=416, conf=0.35, # 置信度阈值,0.35比默认0.25更严格,减少误检 iou=0.6, # NMS阈值,防止同一目标多个框 save=True, # 自动保存带框图到 runs/detect/predict/exp/ project="runs/detect", name="predict_person_car", exist_ok=True ) # 打印检测统计(可选) for r in results: boxes = r.boxes print(f"{r.path}: {len(boxes)} objects detected")运行后,打开runs/detect/predict_person_car/,你会看到:
IMG_005.jpg→IMG_005.jpg(带绿色person框+蓝色car框);predictions.json(记录每个框的坐标、类别、置信度);results.csv(汇总所有图片的检测数量)。
6.2 效果怎么看:不只看mAP,更要看“像不像人”
对只有5张图的小项目,不要迷信mAP。我用三个朴素标准判断效果:
- 框得准不准:person框是否紧贴人体轮廓(不是包住整个身子),car框是否贴合车体(不是连带马路一起框);
- 不漏不错:验证图里有3个人,模型标出3个person框,且没有把路灯当person;
- 速度快不快:在CPU上单图推理<0.8秒,意味着后续可部署到树莓派。
我的结果:
白天图:person框精准,car框略松(因车体反光);
傍晚图:因光线弱,car框置信度降到0.42,但加conf=0.35后仍保留;
阴天图:1个person漏检(被雨伞遮挡),属合理失败。
这就是真实场景——没有100%准确,但有明确改进方向:下一步加几张遮挡样本微调。
7. 经验总结:给后来者的5条硬核建议
7.1 路径是魔鬼,绝对路径是朋友
所有yaml里的path、代码里的source=,都建议用绝对路径。我在Jupyter里用os.getcwd()打印当前路径,再拼接,比相对路径少90%的路径错误。
7.2 小数据集,别碰大数据参数
batch=1不丢脸,CPU上batch=2就OOM;imgsz=416比640快40%,对小图精度影响<1%;patience=50是底线,300轮里前100轮loss震荡很正常。
7.3 标注质量 > 标注数量
5张高质量标注(框紧、类别准、无歧义) > 50张模糊标注。Labelme里按Ctrl+D可快速删除最后一个框,别怕返工。
7.4 权重保存路径,自己管
Ultralytics默认把权重存到~/.ultralytics/,但镜像里这个路径可能被限制。用settings.update({"weights_dir": "./weights"})强制指定,避免找不着best.pt。
7.5 验证集要“像测试集”
我把唯一一张阴天图放进val/,就是因为它最接近我未来想检测的真实场景。验证集不是“剩下的图”,而是“最想考的题”。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。