计算机视觉实战:从“拍脑袋”到线上跑通,我踩过的坑和攒下的经验

RAG小工匠
2025-12-15 07:09
阅读 702

嗨,大家好!我是阿哲,一个刚晋升半年的技术组长——没错,就是那种名义上开始“带人”,实际上还在给 PR 打标签、半夜被 PagerDuty 叫醒修 Bug 的初级 Leader。之前五年一直在一线写前端,但最近这两年,团队业务越来越“AI 化”,老板一句“我们要做智能体验”,我就被迫从 Vue 和 React 的舒适圈里爬出来,硬着头皮啃起了 OpenCV、PyTorch、YOLO 这些以前只在知乎标题里见过的词。

说来惭愧,去年双11前,产品经理拿着一份 PPT 跑过来:“用户上传商品图太乱了,能不能自动识别图中有没有‘真人试穿’?有就打个标签,提升点击率!”我当时嘴上答应得飞快:“没问题,CV 小事一桩!”心里却慌得一批——毕竟我上一次接触图像处理,还是大学时用 MATLAB 给 Lena 图加高斯模糊……

但没办法,技术组长嘛,总不能当众说“我不会”。于是,这场“计算机视觉实战项目”就这么硬着头皮开干了。今天这篇博客,不讲理论,不堆公式,就聊聊我们怎么从零搞出一个能上线的图像分类服务,中间踩了哪些坑,又怎么被面试题反向教育了一波。


需求很朴素,现实很骨感

先说清楚业务场景:我们的电商 App 允许用户上传穿搭照片,但有些图是纯商品平铺,有些是真人上身。运营希望自动区分这两类,给“真人试穿”图加个徽章,据说能提升 15% 的转化(数据来源:产品经理的直觉)。

听起来很简单?不就是二分类吗?但现实立马给我上了一课:

  • 用户上传的图五花八门:有的逆光、有的模糊、有的只露半条腿;
  • 有些图里既有真人又有平铺商品(比如模特坐在地上摆拍);
  • 线上 QPS 要求不高(峰值约 50),但延迟必须 <500ms,否则影响发帖流程;
  • 最要命的是——没有标注数据。对,你没看错,产品提需求时连 10 张样例图都没给全。

我一度想直接回一句:“要不你们先招个 CV 算法工程师?”但转念一想,这不正是技术组长该扛的事儿吗?咬咬牙,自己上。


第一步:别造轮子,先找现成的“轮子厂”

作为前端出身的老油条,我的第一反应不是手撸 CNN,而是:有没有现成模型能微调?

经过一番调研(其实就是 GitHub + Hugging Face 狂搜),最终锁定两个方向:

  1. MobileNetV2:轻量、快,适合移动端部署,TF Lite 支持好;
  2. YOLOv8n-cls:Ultralytics 新出的分类版 YOLO,虽然名字带“检测”,但分类任务也支持,而且预训练权重效果惊艳。

我们内部做了个小 benchmark,在自建的 500 张测试集上跑:

模型 准确率 推理时间 (CPU) 模型大小 是否支持 ONNX 导出
MobileNetV2 (ImageNet 预训练) 78.2% 120ms 14MB
YOLOv8n-cls (COCO 预训练) 85.6% 95ms 10MB
ResNet50 (ImageNet) 83.1% 210ms 98MB

结论很明显:YOLOv8n-cls 又快又准,还小。虽然它主打目标检测,但分类头其实很干净,微调起来也方便。关键是——它的训练脚本写得像前端工程一样规范,连 requirements.txt 都配好了,感动哭。

🙃 吐槽一句:有些开源项目 README 写得比代码还长,结果跑起来缺依赖、路径写死、GPU 内存爆炸……YOLOv8 这套工具链真的香,建议所有算法工程师都学学什么叫“工程友好”。


数据:没有标注?那就自己标!

接下来最痛苦的环节来了:标注数据

我们从历史用户上传图里随机抽了 3000 张,然后——我和实习生两人,花了整整三天,用 LabelImg 手动打标签。每天晚上盯着屏幕看“腿”看到眼花,一度怀疑自己是不是在做某种奇怪的内容审核。

但你以为这就完了?No!测试时发现,模型在“坐姿真人”上表现极差——因为训练集中 90% 是站立照。赶紧补标 500 张坐姿、蹲姿、躺姿……甚至还有宠物试穿(真的有用户给狗穿衣服拍照!)。

教训:数据分布决定模型上限。别信“少量样本也能 fine-tune”,除非你的任务极其简单。


训练 & 调优:那些让我凌晨三点骂街的坑

训练过程看似 smooth,实则暗流涌动。

坑 1:数据增强翻车

一开始我狂加数据增强:旋转 ±30°、随机裁剪、色彩抖动……结果验证集准确率反而从 85% 掉到 76%。后来发现——过度增强破坏了关键语义。比如把“真人”旋转 30° 后,腿部结构变形,模型学偏了。

解决方案:只保留最温和的增强:

augmentations = A.Compose([
    A.Resize(224, 224),
    A.HorizontalFlip(p=0.5),
    A.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])

去掉旋转、裁剪、亮度调整,准确率立马回升。

坑 2:学习率太高,loss 飞天

用默认 lr=0.01 训练,第一个 epoch loss 直接 NaN。查了半天,发现 YOLOv8 默认配置是针对大 batch size 的(比如 64),而我们只有 16(穷,GPU 只有 16G)。

调整后

# yolov8n-cls.yaml
lr0: 0.001
lrf: 0.01
batch: 16
imgsz: 224

配合 CosineAnnealingLR,loss 终于稳稳下降。

坑 3:线上推理慢得像乌龟

本地测试 95ms,部署到生产环境(4 核 CPU + 无 GPU)直接飙到 800ms!差点被 SRE 拉去喝茶。

排查发现:OpenCV 读图 + PyTorch 推理 + 后处理 三段耗时叠加。优化方案:

  1. cv2.IMREAD_UNCHANGED 替代默认读取,减少颜色空间转换;
  2. 模型转 ONNX + ONNX Runtime(CPU 推理提速 2.3 倍);
  3. 预热模型:启动时先 run 一次 dummy input,避免 JIT 编译拖慢首请求。

最终线上 P99 延迟压到 420ms,勉强过关。


关键代码:从训练到部署的一条龙

这里贴几个核心片段,省得你们再踩我踩过的坑。

微调 YOLOv8 分类模型

from ultralytics import YOLO

# 加载预训练模型
model = YOLO('yolov8n-cls.pt')

# 开始训练(数据集目录需按 YOLO 格式组织)
results = model.train(
    data='./datasets/fashion_cls',  # images/train/, images/val/
    epochs=50,
    imgsz=224,
    batch=16,
    name='fashion_v1'
)

ONNX 导出 + 推理

# 导出 ONNX(注意 opset 版本)
model.export(format='onnx', imgsz=224, opset=12)

# ONNX Runtime 推理
import onnxruntime as ort
import numpy as np

ort_session = ort.InferenceSession("fashion_v1.onnx")
input_name = ort_session.get_inputs()[0].name

def predict(image: np.ndarray) -> str:
    # image: (224, 224, 3) BGR
    input_tensor = image.transpose(2, 0, 1)[None, ...].astype(np.float32) / 255.0
    input_tensor = (input_tensor - np.array([0.485, 0.456, 0.406])[:, None, None]) \
                   / np.array([0.229, 0.224, 0.225])[:, None, None]
    
    outputs = ort_session.run(None, {input_name: input_tensor})
    probs = outputs[0][0]
    return "human" if probs[1] > 0.7 else "product"  # 阈值可调

💡 小技巧:阈值别死设 0.5!我们通过 ROC 曲线分析,发现 0.7 时 precision 更高,误标“平铺图为真人”的情况大幅减少。


面试题?不,这是我们的日常

说到这儿,突然想起最近面试候选人时问的一个题:

“如果线上模型准确率突然下降,你会怎么排查?”

以前我觉得这是道标准八股文,直到上周五晚上 11 点,监控报警:真人识别准确率暴跌至 62%

我打开 Grafana 一看,发现新上传的图里大量出现“镜子自拍”——用户对着镜子拍,图里有两个“人”,但我们的模型只见过单人图。更糟的是,有些图背景是浴室瓷砖,模型误判为“平铺商品纹理”。

紧急应对

  1. 临时提高阈值到 0.85,宁可漏杀不误杀;
  2. 快速收集 200 张镜面图,加入训练集 retrain;
  3. 上线灰度:先对 10% 流量生效,观察 24 小时。

三天后,准确率重回 84%。这次事故让我深刻意识到:CV 模型不是一劳永逸的,它需要持续的数据反馈闭环

所以现在我面试时,会追加一句:“如果你来做这个项目,会设计怎样的监控和回滚机制?”——能答出“bad case 自动采集 + 人工复核 pipeline”的,基本就过了。


总结:技术组长的“跨界”心得

回头看这个项目,从被产品经理“忽悠”入坑,到最终上线稳定运行三个月,我最大的感悟不是“CV 很难”,而是:

  1. 别怕跨界:前端懂点 CV,后端了解点 MLOps,才能在 AI 时代不被淘汰。我们组现在要求每个成员每年至少深入一个非本职领域。
  2. 工程化 > 算法精度:95% 准确率但延迟 2s 的模型,不如 85% 但 200ms 的。线上系统永远在 trade-off。
  3. 数据是爹:再牛的算法,喂垃圾数据也是垃圾。建立数据采集-标注-验证的流程,比调参重要十倍。

顺便,这个项目上线后,转化率确实涨了 12.3%(运营终于给了真实数据),老板请我们吃了顿火锅。虽然过程中熬过夜、骂过娘、想过转行送外卖,但看到用户发帖时自动带上那个小小的“真人试穿”徽章,心里还是有点小骄傲的。

最后,如果你也在做类似的 CV 落地项目,欢迎留言交流!或者……缺前端+CV hybrid 人才?我们团队正在扩招(手动狗头)。


写于一个远程办公的周五晚上,窗外下着雨,键盘旁边是第三杯美式。刚 merge 了修复 CI 的 PR,终于可以摸鱼写博客了。

评论 0

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝