计算机视觉项目上线后,我连夜改了三版SQL
去年双11前两周,我们组接了个“紧急需求”:给商品主图自动打标签,减少人工审核成本。产品经理画了个大饼:“AI提效嘛,现在不都这样?”——说得轻巧,他连OpenCV是啥都不知道。
作为DBA出身的后端开发,我对数据库有执念,但对CV(计算机视觉)真没多少经验。不过架不住老板一句“你逻辑强,学得快”,硬着头皮上了。坐标杭州,阿里网易这边卷得飞起,不学点新东西,简历都不好意思投。
顺便说一句,我平时用Mac写代码,Windows只留着测兼容性。结果这次偏偏要在Linux服务器上跑推理模型,还得适配CUDA,差点把我M1芯片的MacBook Air干烧了。
为啥不用现成API?因为贵,也因为慢
一开始想偷懒,直接调阿里云或百度的图像识别API。测试下来,单张图平均200ms延迟,费用还不低。我们日均要处理50万张图,光API调用费就顶半个团队工资。更别提网络抖动时,整个流程卡成PPT。
于是决定自建模型。目标很明确:本地部署、低延迟、高准确率、能对接现有MySQL商品表。
我翻了翻GitHub,发现Hugging Face上有不少预训练的ViT(Vision Transformer)和ResNet变种。选型时纠结了两天——ViT效果好但吃显存,ResNet50轻量但精度略逊。最后折中用了ResNet50 + 自定义分类头,毕竟我们不是做ImageNet竞赛,只是区分“服装/美妆/家电/食品”四大类加几十个子标签。
Claude Code 帮我少写了三天脏活
写数据预处理脚本时,我实在懒得手动写图像加载、缩放、归一化那一套。正好试了下Claude Code(Anthropic家的AI编程助手),输入:
“用Python写一个PyTorch DataLoader,从MySQL读取图片路径和标签,加载本地图片,resize到224x224,做标准化”
它秒回了一段带注释的代码,连torchvision.transforms的链式调用都写好了。虽然最后我改了几处(比如把Image.open换成cv2.imread避免RGBA通道问题),但省了至少半天调试时间。
AI提效这事,在这种重复性高的胶水代码上特别香。不过别指望它帮你设计算法——有一次让它“优化损失函数”,结果给我整了个带log(cos(x))的玩意儿,梯度直接爆炸,loss变成NaN,当时真的想砸电脑。
数据库才是真正的战场
模型训得再好,如果不能和业务系统无缝集成,就是玩具。
我们的商品信息存在MySQL里,表结构大概是这样的:
CREATE TABLE products (
id BIGINT PRIMARY KEY,
title VARCHAR(255),
main_image_url VARCHAR(512),
auto_tags JSON, -- 新增字段,存AI打的标签
updated_at TIMESTAMP
);
训练数据怎么来?最初的想法是从线上dump一批带人工标签的数据。但运维同事说:“你这查询要全表扫,IO太高,影响线上。” 我只好妥协,写了个分页脚本,每批查5000条,sleep 2秒,像个做坏事的小偷。
更坑的是图片存储——有些URL是内网地址,有些是OSS外链,还有些已经404了。数据清洗阶段,我写了段Rust脚本(最近在研究Rust,感觉内存安全真香)批量校验图片可访问性,比Python快了近3倍,还顺手练了tokio异步并发。
训练过程:GPU不够,玄学来凑
公司给的训练机只有16GB显存的RTX 3090。Batch size开到32就OOM。怎么办?
我祭出了DBA的老本行:优化I/O,减少内存压力。
- 用
TFRecord格式缓存预处理后的数据(虽然用PyTorch,但TFRecord读起来确实快) - 开启
pin_memory=True+num_workers=4 - 梯度累积(gradient accumulation)模拟大batch
训练脚本关键片段:
# 每8个step更新一次,等效batch_size=256
accum_steps = 8
optimizer.zero_grad()
for i, (images, labels) in enumerate(dataloader):
outputs = model(images.to(device))
loss = criterion(outputs, labels.to(device))
loss = loss / accum_steps # 缩放loss
loss.backward()
if (i + 1) % accum_steps == 0:
optimizer.step()
optimizer.zero_grad()
跑了3天,准确率卡在87%上不去。复盘发现:类别极度不平衡。食品类样本占60%,而“宠物用品”只有不到1%。于是重采样+ focal loss 双管齐下,最终验证集准确率冲到93.2%。
部署上线:从Flask到Rust API
最初用Flask搭了个REST API,结果压测时QPS才80,还经常502。运维翻白眼:“你这Python GIL锁死,不如回去写Java。”
痛定思痛,我用actix-web(Rust的高性能Web框架)重写了推理服务。得益于Rust的零成本抽象和异步运行时,同样一张V100,QPS干到了1200+,延迟稳定在30ms以内。
关键配置对比:
| 方案 | QPS | P99延迟 | 内存占用 | 维护成本 |
|---|---|---|---|---|
| Flask (Python) | 80 | 420ms | 1.2GB | 低 |
| FastAPI | 210 | 180ms | 1.5GB | 中 |
| actix-web (Rust) | 1200+ | 30ms | 380MB | 高(但值得) |
上线那天是周五晚上9点。测试同学疯狂刷接口,我在旁边盯着Grafana面板,心跳比loss曲线还抖。直到看到“成功率99.98%”,才敢去吃那碗凉透的片儿川。
效果与反思:AI提效,但别神话AI
上线一个月后,人工审核工作量减少了65%。运营小姐姐第一次夸我:“你们技术这次真靠谱!”
但也要泼点冷水:AI不是万能药。我们遇到几个典型bad case:
- 黑底白字的商品图被误判为“美妆”(因为训练集中口红图多是这种风格)
- 堆满零食的全家福图,只识别出“薯片”,漏了“饮料”“糖果”
- 动态图(GIF)只取第一帧,导致信息丢失
后续我们加了规则引擎兜底:比如标题含“冰箱”但模型输出“服装”,直接拦截复审。算法 + 规则 + 人审,才是工业级落地的常态。
给同行的建议
- 别忽视数据质量:垃圾进,垃圾出。花70%时间清洗和标注数据,不丢人。
- 数据库思维很重要:CV项目不是孤立模型,要考虑如何高效读写、如何版本管理标签、如何回溯错误。
- 小模型往往更实用:ResNet34可能比ViT-L/16更适合你的业务场景,别盲目追SOTA。
- 用对工具提效:Claude Code这类AI助手写样板代码真香,但核心逻辑还得自己把控。
- 监控必须到位:我们给每个预测结果加了
confidence_score,低于阈值自动告警,避免静默错误。
现在回头看,这个项目让我从“只会写JOIN的DBA”变成了能端到端搞AI系统的后端。虽然过程中踩了无数坑,但每次解决完问题,那种“老子又行了”的爽感,是调优一条SQL无法比拟的。
对了,如果你也在杭州,正在折腾CV or Rust,欢迎约咖啡(或者片儿川)。毕竟在这座码农密度爆表的城市,独行快,众行远。
最后送大家一句我在阿里学到的话:“AI提效的本质,是把人的经验转化为可复用的系统能力。”
别让算法成了黑盒,也别让数据库沦为摆设。共勉。

评论 0