train.yaml
从零到一:我的第一个计算机视觉实战项目经历

去年年初,我作为技术负责人接手了一个看起来“轻量级”但实际极具挑战的项目:为一家零售企业开发一套基于摄像头的智能货架识别系统。简单来说,他们的核心诉求是通过门店部署的普通摄像头实时识别商品摆放情况,并自动记录哪些商品卖空、库存不足,同时生成补货建议。当时听起来好像就是一个标准的CV项目,目标检测+图像分类嘛,能有多难?结果这一趟下来,真让我从头到脚刷新了认知。
今天我想以第一人称的角度,把整个项目的过程和经验复盘一遍。这不是一篇教科书式的教程,也不是纸上谈兵的理论文章,而是我亲手踩坑、反复挣扎、不断优化后总结出来的实战心得。如果你也在做或准备做一个计算机视觉相关的项目,希望这篇文章对你有帮助。
一、项目背景与初始构想
客户是一家连锁便利店品牌,在全国有上百个门店。他们希望通过摄像头实现一种低成本的“数字巡检”,替代传统的纸质盘点和人工巡店方式。他们希望看到的是:
- 商品是否缺货
- 商品是否错摆(比如A品牌的水放到了B的区域)
- 货架陈列状态是否正常(是否有乱放、倒置等情况)
最初我们打算用现成的目标检测模型(YOLOv5)做识别,然后结合图像处理手段判断商品位置是否异常。听上去很合理,但真正动起手来才发现,现实远比训练集复杂得多。
二、遇到的第一个大坑:数据不真实、标注质量差
我们在项目初期拿到的数据是客户提供的1000张图片,拍摄于某个样板门店。这些图片上标注整齐、光线良好,几乎每张都像是拍广告用的。我们迅速跑通了一个基本流程:YOLOv5做目标检测、OpenCV做定位分析,一切看似顺利。
然而第一次去现场部署的时候,问题就来了。
- 光照变化极大:门店灯光白天和晚上不一样,部分角落背光明显。
- 镜头角度差异大:客户用的是不同型号的摄像头,安装高度也不同,导致视图视角差异很大。
- 遮挡严重:顾客频繁走动时,货架经常被遮挡。
- 标注数据偏差大:原始训练数据里很多标签不准确,甚至有些是手工标注错误的。
结果就是模型在样板数据上mAP有0.85,但在实际门店中基本不到0.5,很多商品压根儿没识别出来。
这是我第一次深刻意识到——训练数据的真实性和多样性决定了一半项目的成败。
三、解决方案设计与关键决策
面对这些问题,我们不得不重新调整整体思路。这里我重点说三个关键点:数据增强策略、模型微调方案和评估机制优化。
1. 数据采集与增强
我们没有继续依赖客户提供数据,而是带着一台笔记本电脑和几个员工一起去了门店,在营业时间下抓拍了上千张照片。注意,不是“摆拍”,而是真实运营场景下的连续拍摄,包含各种时间段、光照变化和人流干扰。
为了扩充数据,我们做了以下几件事:
- 使用 Albumentations 进行多种光照、模糊、旋转变换
- 利用 CutMix 和 Mosaic 增加样本多样性
- 对于少数类商品(如特定饮品),手动合成一些示例,确保模型不会漏掉
2. 模型选择与迁移学习
最终我们选用了 YOLOv8s,相比之前的YOLOv5,它的泛化能力更强,推理速度也足够应对边缘设备的部署需求。我们也尝试过EfficientDet、CenterNet等其他模型,但综合来看,YOLO系列更容易部署和维护。
模型方面我们采取了两阶段迁移学习法:
- 阶段一:使用COCO预训练权重 + 客户提供数据做初步微调
- 阶段二:将现场采集的数据进一步打标,加入模型训练中
此外,我们还对某些类别做了 loss 权重调整(比如易错检的饮料瓶和相似包装的商品),并采用 class-wise focal loss 来缓解类别不平衡问题。
3. 多任务输出设计
除了识别商品种类,我们还需要知道它们的位置是否异常。因此我们在网络输出层上扩展了一下,增加了一个“相对位置偏移”的回归头,用来辅助判断某个商品是否出现在正确区域。
这样做的好处是,不仅识别出商品名称,还能知道它是不是被放错了地方。
四、代码实践片段分享
下面是一个简化的YOLOv8微调训练配置文件示例(train.yaml):
train:
imgsz: 640
batch-size: 32
epochs: 100
weights: 'yolov8s.pt' # 初始权重
data: '../data/shelves.yaml' # 数据集配置
device: 0 # GPU编号
workers: 4 # 数据加载线程数
name: 'shelf-detection'

这是我们使用的训练损失函数的一部分(PyTorch伪代码):
from yolov8.models.yolo import DetectionModel
from yolov8.utils.loss import v8DetectionLoss
# 构建模型
model = DetectionModel(cfg='models/yolov8s.yaml')
loss_func = v8DetectionLoss(model)
# 在训练循环中:
for images, targets in dataloader:
predictions = model(images)
loss = loss_func(predictions, targets)
optimizer.zero_grad()
loss.backward()
optimizer.step()
对于边缘部署,我们使用ONNX格式进行模型导出,以支持多种平台推理:
yolo export model=runs/train/shelf-detection/weights/best.pt format=onnx
这部分可以配合OpenVINO或者TensorRT进行加速推理,在树莓派等设备上也能跑起来。
五、开发中的那些“坑”,我都踩过了
1. 光照太强,反光严重
我们在某家门店调试的时候发现摄像头画面中有大片白亮区域,导致目标检测全失效。后来查出是因为玻璃门反射阳光直射进镜头。解决办法是给摄像头装了个滤镜套件,并调整了曝光参数。
cap.set(cv2.CAP_PROP_EXPOSURE, -6) # 手动降低曝光
cap.set(cv2.CAP_PROP_GAIN, 0)
2. 标注数据混乱不堪
我们花了很多时间在验证数据标签的准确性。最终我们自己写了一个可视化标注工具,让业务方参与确认。虽然有点慢,但从源头解决问题更彻底。
3. 推理延迟影响体验
最开始模型是在本地GPU运行,但偶尔会有卡顿。后来我们做了异步处理,将视频流解码和推理分离,效果提升明显:
from threading import Thread
class AsyncInference:
def __init__(self, model):
self.model = model
self.result = None
self.running = False
def start(self, frame):
self.thread = Thread(target=self._infer, args=(frame,))
self.thread.start()
def _infer(self, frame):
self.running = True
self.result = self.model(frame)
self.running = False
六、项目成果与落地反馈
经过前后三个版本迭代,我们在六个门店试点部署成功:
- 平均识别准确率达到91%以上(IoU>0.5)
- 缺货识别响应延迟控制在3秒以内
- 系统自动生成补货建议,并推送至门店App
- 减少人工巡检工作量约60%
客户对这个结果非常满意,后续已经开始考虑拓展到所有门店,并将数据接入他们的ERP系统。
对我们团队而言,这次实战带来的最大收获不是技术和算法本身,而是一种工程思维:一个成功的CV项目不是靠精度指标堆出来的,而是从真实场景出发,从用户痛点入手,持续打磨细节的结果。
七、几点经验和建议
回顾整个项目过程,我总结了几条给正在或将要进入CV实战领域的朋友们的建议:
- 不要迷信公开数据集:很多开源数据集过于理想化,真实应用中需要你自己构建高质量数据。
- 重视数据清洗与标注管理:宁可多花时间整理数据,也不要把精力浪费在一个永远学不会的烂数据上。
- 模型不是万能的,业务理解才是核心:很多时候你不是在训练模型,而是在帮客户定义问题。
- 提前考虑部署环境:别等到模型训练好了才发现无法部署,性能瓶颈往往在边缘设备。
- 保持灵活与快速迭代的能力:CV项目容易出现预期之外的问题,快速验证比完美设计更重要。

另外,随着AI技术的发展,现在像AutoML、低代码训练框架也越来越成熟了,大家可以关注一下HuggingFace AutoTrain、Roboflow Train、Google Vertex AI Vision这些平台,能在一定程度上降低开发门槛。
最后的一句话
做项目就像爬山,你以为山顶近在眼前,其实还有两个山头要翻。但我一直相信一句话:“最好的学习机会,永远藏在那些让你痛苦的问题背后。”
如果你现在正面临困难,不妨坚持一下——也许下一个版本,就是转折点。
愿你在自己的项目旅程中越战越勇。

评论 0