边带娃边搞计算机视觉:深夜调参、产品需求与区块链的奇妙混搭

奇妙先知
2025-12-15 16:04
阅读 1038

凌晨两点,宝宝刚睡。我蹑手蹑脚地合上婴儿房门,打开 MacBook,终端窗口里 docker-compose up 的日志正在刷屏。这是我每天最奢侈的“自由时间”——娃睡了,老公也睡了,没人喊“妈妈”,也没人催 PRD 评审。作为一个全职妈妈兼算法工程师,我的生产力峰值不在白天,而在这种万籁俱寂的深夜。

坐标上海,租住在公司附近的老小区,通勤5分钟,但带娃+工作的时间管理比调度 Kubernetes 集群还复杂。上周五,产品经理又在群里@我:“这个视觉识别功能能不能加个区块链存证?双11前必须上线!” 我盯着消息愣了三秒,差点把泡面打翻在键盘上——计算机视觉 + 区块链 + JavaScript 前端?这组合听起来像是把火锅底料倒进咖啡机。

但没办法,谁让我是团队里唯一一个既写过 OpenCV 又碰过 Solidity 的“多面手”呢?于是,就有了这篇记录我如何在带娃间隙搞定这个“缝合怪”项目的技术复盘。


起因:产品说要“可信视觉”

事情得从去年双11说起。我们公司做的是一个二手奢侈品鉴定平台,用户上传包包、手表的照片,我们的 CV 模型判断真伪和成色。原本流程很简单:前端(React + TypeScript)上传图片 → 后端 Python API 调用模型 → 返回结果。一切岁月静好,直到风控部门发现有人批量伪造鉴定报告。

产品经理灵机一动(或者说拍脑袋):“咱们把每次鉴定的原始图像哈希值上链!这样用户无法篡改历史记录,还能作为法律证据。” 说完甩来一份 PRD,里面赫然写着:“需支持以太坊主网/侧链双通道,JavaScript SDK 集成,响应时间 ≤800ms。”

我当场就懵了。区块链?那玩意儿不是每笔交易都要等 15 秒确认吗? 而我们的 CV 接口平均响应才 300ms!这需求简直是在高速公路上骑共享单车追高铁。

但吐槽归吐槽,deadline 不等人。我只能一边给娃换尿布,一边在脑子里画架构图:如何在不拖垮现有 CV 流程的前提下,优雅地塞进区块链存证?


架构设计:解耦是唯一出路

我的第一反应就是:绝对不能同步上链! 用户上传一张图,等 15 秒才返回“鉴定成功”?怕不是想被差评冲垮服务器。

于是,我提出一个异步方案:

[用户上传] 
   ↓
[CV 模型实时分析] → [立即返回结果]
   ↓
[后台任务队列] → [计算图像哈希 + 元数据]
   ↓
[区块链写入服务] → [签名 & 提交交易]
   ↓
[链上状态回调] → [更新 DB 标记“已存证”]

这样,前端 JavaScript 完全无感——它只关心 CV 结果。区块链成了后台的“影子服务”。我把这个方案画在白板上,产品经理眯着眼看了半天,最后点头:“行吧,只要用户能看到‘本鉴定已上链’的小绿标就行。”

技术栈选择上:

  • CV 模型:YOLOv8 + ResNet50 组合,用于定位商品区域 + 材质/磨损分类
  • 区块链层:Polygon 侧链(Gas 费低,出块快),用 Web3.js 写入
  • 任务队列:Celery + Redis
  • 前端:React + ethers.js 显示存证状态

小插曲:运维小哥听说要用 Polygon,幽幽地说:“你们确定不是又要搞 NFT?” 我翻了个白眼:“这是正经业务,不是割韭菜!”


CV 模型优化:速度就是生命线

既然核心诉求是“快”,那模型本身必须精简。我们之前用的 YOLOv5s 在 1080Ti 上推理约 45ms,但考虑到未来可能部署到边缘设备(比如门店的 iPad),我决定试试 YOLOv8n(nano 版)

训练数据来自内部积累的 12 万张奢侈品图像,标注包括:

  • 商品类别(包/表/首饰)
  • 区域 bbox
  • 成色等级(1-5 星)

调参过程堪称“深夜修仙”:

  • Batch size 从 16 → 32,显存爆了,降回 24
  • 使用 Albumentations 做数据增强,重点模拟反光、阴影(奢侈品最爱打光)
  • 最后用 TensorRT 转成 FP16 引擎,推理速度直接干到 22ms
# yolov8_trt_infer.py 片段
import tensorrt as trt

def infer(engine, image):
    # ... 预处理
    context.execute_v2([image.nbytes, output.nbytes])
    # ... 后处理
    return boxes, scores, classes

实测对比(Tesla T4 GPU):

模型版本 平均推理时间 (ms) mAP@0.5
YOLOv5s 45 0.78
YOLOv8n 22 0.76
YOLOv8n + TRT 18 0.75

虽然 mAP 微跌 0.03,但速度提升 2.5 倍,完全值得。毕竟用户要的是“快准稳”,不是 SOTA 论文指标。


区块链集成:Web3.js 的坑与填

接下来是重头戏:把 CV 结果上链。

我选了 Polygon Mumbai 测试网先跑通流程。核心逻辑是:

  1. CV 任务完成后,生成唯一 ID 和图像哈希(SHA256)
  2. 构造交易:调用智能合约的 recordVerification(bytes32 id, string hash) 方法
  3. 用私钥签名并广播

JavaScript SDK 这边,我封装了一个 EvidenceService

// evidence.js
import { ethers } from 'ethers';

class EvidenceService {
  constructor() {
    this.contract = new ethers.Contract(
      CONTRACT_ADDRESS,
      ABI,
      new ethers.Wallet(PRIVATE_KEY, provider)
    );
  }

  async submit(hash) {
    const tx = await this.contract.recordVerification(
      ethers.utils.id(Date.now().toString()),
      hash
    );
    return tx.wait(); // 等待确认
  }
}

但问题来了:tx.wait() 默认等 1 个确认就要 2 秒! 这哪行?我赶紧改成异步回调:

// 改进版:不阻塞主线程
submitAsync(hash) {
  this.contract.recordVerification(id, hash)
    .then(tx => {
      console.log("Tx sent:", tx.hash);
      // 存 tx.hash 到 DB,后续轮询状态
    });
}

后端用 Celery 定时任务每 30 秒查一次交易状态,更新数据库字段 blockchain_status: pending → confirmed。前端通过 WebSocket 推送“已上链”通知。

吐槽:Web3.js 的文档真是反人类。有次我传错参数类型,报错信息居然是 invalid EIP-1559 transaction,查了三天才发现是 gasPrice 写成了字符串……


前端性能:别让 JS 拖后腿

虽然区块链是异步的,但前端仍要展示“存证中…”的状态。这里有个细节:图像上传 + CV 分析 + 区块链状态更新,三个异步操作要协调好

我用 React 的 useReducer 管理状态机:

const [state, dispatch] = useReducer(verifierReducer, {
  upload: 'idle',
  cv: 'idle',
  blockchain: 'idle'
});

// 当 CV 成功后,触发区块链提交
useEffect(() => {
  if (state.cv === 'success') {
    evidenceService.submitAsync(imageHash)
      .then(() => dispatch({ type: 'BLOCKCHAIN_START' }));
  }
}, [state.cv]);

关键优化点:

  • 图片压缩:前端用 canvas 把原图压到 1024px 宽,减少上传体积
  • 防抖提交:避免用户疯狂点击“重新鉴定”导致重复上链
  • 缓存结果:同一张图哈希相同,直接返回历史存证记录

实测在 iPhone 12 上,从点击上传到显示“鉴定完成+存证中”,总耗时 620ms,满足产品 ≤800ms 的 KPI(虽然产品经理根本不懂这个数字怎么来的 😅)。


真实线上事故:Gas 费暴涨的惊魂夜

一切看似顺利,直到上周三晚上——我正给宝宝读《好饿的毛毛虫》,手机突然狂震。告警:区块链写入服务失败率 90%!

登录 AWS 一看,Polygon Gas Price 从 30 Gwei 暴涨到 500 Gwei!我们的固定 gasPrice 设置瞬间失效,所有交易卡在 mempool。

当时真的想砸电脑。娃还在哭,线上用户疯狂投诉“为什么没上链”。我深吸一口气,快速做了三件事:

  1. 临时提高 gasPrice 到动态模式:provider.getFeeData()
  2. 加入重试机制:失败交易 5 分钟后重发
  3. 前端加提示:“网络拥堵,存证稍有延迟”
// 动态 gas 策略
const feeData = await provider.getFeeData();
const tx = await contract.recordVerification(id, hash, {
  gasLimit: 100000,
  maxFeePerGas: feeData.maxFeePerGas * 1.2, // 加点 buffer
});

凌晨 4 点,服务终于恢复。我在 Slack 留言:“建议产品下次提需求前,先问问 Polygon 节点愿不愿意配合。” 全组笑疯。


效果与反思:值不值得?

上线两周后,数据说话:

指标 优化前 优化后
CV 平均响应 320ms 210ms
区块链成功率 - 99.2%
用户留存提升 - +7.5%

更意外的是,法务部反馈已有 3 起纠纷通过链上存证快速解决。看来产品经理这次没完全瞎指挥。

但我也深刻体会到:技术方案必须为业务兜底,而不是炫技。 区块链在这里只是“信任增强器”,核心还是 CV 的准确性和速度。如果为了上链牺牲用户体验,那就是本末倒置。


写给 fellow 程序员妈妈的话

写这篇文章时,宝宝又醒了。我暂停打字去喂奶,回来发现终端还在跑测试——还好没崩。

很多人觉得带娃没法搞技术深度,但我反而觉得,育儿逼我学会极致的时间管理和优先级排序。以前我会纠结模型 mAP 是 0.76 还是 0.77,现在只问:“它能让用户少等 100ms 吗?”

如果你也在平衡家庭与代码,记住:你不需要完美,只需要 deliver value。深夜的每一行代码,都是对生活的温柔反击。

最后,那个“CV+区块链”的项目,我们内部叫它 “Mom’s Night Project” ——因为所有关键 commit,都诞生于娃睡后的寂静时光。

共勉。

评论 0

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