1 写在前面
最近在做二次元商品项目,遇到了拍照识别商品的需求。一开始完全不懂,只能边学边做。这篇文章记录了学习和实践过程,希望能给同样在探索图片识别技术的朋友一些参考。
2 学习图片相似度的基础知识
在开始动手之前,我花了不少时间去理解图片相似度是怎么回事。
2.1 从向量开始理解
最初不理解图片和向量的关系,后来发现就是把图片转换成数字。计算机视觉中,图片需要转换为数学形式才能处理。向量化是将图片转换为高维数值向量的过程,每个向量包含图片的特征信息。
图片向量通常包含512维、1024维或更高维度的特征,每一维代表图片的某个特征属性,如颜色、纹理、形状等。通过向量化,图片相似度问题转化为向量相似度问题。
说明:为了便于理解,上图简化为3维向量展示。实际应用中,每张图片会被转换为512维或1024维的高维向量,包含颜色、纹理、形状、边缘等数百个特征维度。
2.2 向量化算法的学习
这部分是我学习曲线最陡峭的地方。CNN(卷积神经网络)听起来很高大上,但当我深入了解后发现,它的核心思想其实很直观。
卷积层是CNN的核心组件,它通过一系列可学习的滤波器(卷积核)在图片上滑动,提取不同的特征。
每个卷积核就像一个特征检测器,通过多层卷积的堆叠,CNN模型能够从简单的像素信息中逐步提取出高层的语义特征。
强烈推荐 Image Kernels Explained Visually,你可以实时看到不同卷积核(模糊、锐化、边缘检测等)对图片的处理效果。
在实际应用中,我选择了市面上常用的预训练模型ResNet。使用ResNet-50作为特征提取器,能够将输入的图片转换为一个2048维的特征向量。
下面是一个简单的代码示例,展示如何使用ResNet提取图片特征:
import torch
from torchvision.models import resnet50, ResNet50_Weights
from torchvision import transforms
from PIL import Image
# 加载预训练模型,去掉分类层
model = resnet50(weights=ResNet50_Weights.IMAGENET1K_V1)
model = torch.nn.Sequential(*list(model.children())[:-1])
model.eval()
# 预处理图片
transform = transforms.Compose([
transforms.Resize((224, 224)),
transforms.ToTensor(),
])
# 提取特征
image = Image.open('小卡.png')
image_tensor = transform(image).unsqueeze(0)
features = model(image_tensor)
print(f"特征向量维度: {features.shape}") # 输出: (1, 2048, 1, 1)
2.3 向量数据库的学习
在图片相似度检索中,传统关系型数据库无法胜任。向量数据库专门为高维向量的相似度搜索而优化,能在千万级数据中快速响应,这是传统数据库无法比拟的。
在实际应用中,我选择了Milvus作为向量数据库。除了Milvus,市面上还有其他不错的选择:
- Pinecone: 云原生向量数据库服务
- Weaviate: 支持多模态搜索的向量数据库
- Qdrant: 高性能的向量搜索引擎
2.4 HNSW索引算法的理解
在深入了解向量数据库时,我发现了一个关键技术:HNSW(Hierarchical Navigable Small World)索引。需要说明的是,HNSW并不保证100%精确的结果,它是一种近似最近邻搜索算法,通过牺牲少量精度来换取巨大的性能提升。
如上图所示,HNSW构建了多层图结构:
- 顶层(Layer 2):少量节点,红色路径快速缩小搜索范围
- 中层(Layer 1):节点适中,进一步细化搜索
- 底层(Layer 0):包含所有数据点,进行精确搜索
搜索过程沿着红色路径:从顶层入口快速跳跃到目标区域,然后逐层下降细化搜索。这种"先粗后细"的策略实现了亚秒级的相似度检索。
2.5 相似性度量:从语义理解到数学计算
在学习过程中,我发现了一个很有意思的转换:如何将人类对图片相似性的主观判断转换为计算机可以处理的数学问题。
经过网上搜索和学习,我找到了几种常用的向量距离计算算法,并做了对比分析:
算法名称 | 计算原理 | 特点 | 适用场景 |
---|---|---|---|
余弦相似度(Cosine) | 计算向量夹角余弦值 | 不受向量长度影响,关注方向性 | 文本相似度、图片特征匹配 |
欧几里得距离(L2) | 计算两点间直线距离 | 几何意义明确,高维空间易受维度诅咒影响 | 低维数据、精确匹配 |
曼哈顿距离(L1) | 计算坐标轴方向距离之和 | 计算简单,对异常值鲁棒,不考虑对角线距离 | 网格化数据、城市距离 |
内积相似度(IP) | 计算向量内积 | 计算高效,同时考虑方向和大小,受向量长度影响 | 推荐系统、特征权重敏感场景 |
汉明距离(Hamming) | 计算不同位置的数量 | 适合二进制数据,仅限等长序列 | 二进制特征、错误检测 |
通过这种"语义→向量→数学计算"的转换,我们成功地将人类的视觉判断能力赋予了计算机系统,实现了从主观的"像不像"到客观的数值计算。
3 解决实际应用问题
3.1 目标检测的必要性
在实际测试中,我发现用户上传的图片往往包含大量无关信息。比如识别小卡时,图片中还有桌子、手机等干扰元素。直接对整张图片向量化,识别效果很差。
需要先把商品从复杂背景中"抠"出来,再进行相似度计算。经过调研,我选择了YOLO(You Only Look Once)作为目标检测的解决方案,YOLO能够在保证准确性的同时实现实时检测。
下面是一个简单的YOLO目标检测代码示例:
from ultralytics import YOLO
model = YOLO('yolov8n.pt')
# 检测图片中的物体
results = model('小卡.png')
# 显示结果
results[0].show()
3.2 数据标注的挑战
我们的业务场景比较特殊,主要是二次元相关物品(明星小卡、手办、徽章等),通用模型识别不了,只能自己标注数据。
选择了Label Studio作为标注工具:
- 界面直观,学习成本低
- 支持团队协作
- 导出格式兼容YOLO
# 1. 安装Label Studio
pip install label-studio
# 2. 启动服务
label-studio start
# 3. 浏览器访问 http://localhost:8080
# 首次使用需要注册账号
标注了大概200张图片后,发现这活儿比想象中累多了,简直是"鼠标点到手抽筋"😂 标注工作完成后就可以进行数据导出了,为后续模型训练提供基础。
3.3 训练实践经验
有了标注数据后,就可以开始训练了。说实话,之前看到YOLO训练的教程都觉得很复杂,各种参数配置让人头大。但实际动手后发现,核心代码其实很简洁:
from ultralytics import YOLO
# 准备训练和验证数据集(整理图片和标注文件)
prepare_dataset()
# 开始训练
model = YOLO('yolov8n.pt')
results = model.train(
data='dataset/dataset.yaml', # 数据集配置文件
epochs=200, # 训练轮数
imgsz=640, # 输入图片尺寸
batch=32, # 批次大小
device=0, # GPU设备号(0=第一块GPU, 'cpu'=使用CPU)
patience=20 # 早停轮数
)
第一次运行的时候心情还挺忐忑的,毕竟只有200多张图片,不知道能不能训练出什么效果。结果让我挺意外的:
- 训练速度比想象中快,RTX 4090跑了2分多钟就完成了
- 最终mAP达到了97.6%,这个数字看起来还不错
- 模型文件只有6MB左右,很轻量
关于训练速度:一开始用CPU训练,慢得我怀疑程序卡死了😅 后来花几块钱租了个GPU,嘿,2分钟搞定!果然贫穷限制了想象力...
关于mAP指标:mAP可以理解为检测准确率,97.6%意味着模型在识别小卡方面表现很优秀,90%以上就算是实用级别了。
训练完成后,迫不及待地测试了一下效果。写了个简单的检测脚本:
from ultralytics import YOLO
model = YOLO('best.pt')
results = model('test_model_image.jpg')
results[0].show() # 显示检测结果
看到检测框准确地框出了小卡,那一刻还是挺有成就感的。虽然数据量不大,但至少证明了这个思路是可行的。
第一次训练的几个收获:
- 数据质量比数量重要 - 200张精心标注的图片效果已经不错
- GPU确实快 - 相比CPU训练,速度提升明显
- YOLO真的很好用 - 几行代码就能完成整个训练流程
4 未来规划
学完了这些技术,脑子里已经有了一个完整系统的雏形。虽然现在还停留在理论和实验阶段,但我觉得可以分享一下我的规划思路。
4.1 理想中的系统架构
我画了一个理想化的系统流程图,把前面学到的技术都串联起来:
整个系统分成四个核心环节:
- 模型训练环节:通过Label Studio标注数据,训练出专门的YOLO检测模型
- 数据预处理环节:用训练好的模型批量处理存量商品图片,提取特征向量存入向量数据库
- 实时检索环节:用户上传图片时,先用YOLO检测出商品区域,再提取特征进行相似度搜索
- 反馈优化环节:收集用户反馈,分析错误样本,持续优化模型效果
这个架构看起来挺完美的,但实际落地肯定会遇到各种意想不到的问题。
4.2 当前进展与挑战
坦率地说,目前这些都还是纸上谈兵。虽然各个技术环节我都单独跑通了,但要组装成一个稳定可用的系统,还有很多工作要做:
技术挑战:
- 数据质量瓶颈:200张标注数据还是太少,需要更多样化的训练样本
- 训练参数调优:学习率、批次大小、数据增强策略等超参数需要反复试验,这个过程很耗时间和算力
- 模型泛化:目前只在小卡上测试过,其他商品类型的效果未知
- 边界case处理:模糊图片、遮挡严重、光线不足等极端情况下的识别准确率还需要提升
- 性能优化:向量检索在大规模数据下的响应速度还需要优化
每解决一个技术问题,就感觉离目标更近了一步。虽然现在还在实验室阶段,但我相信距离真正可用的产品不会太远。
到时候真实用户的使用场景肯定比我闭门造车想象的更复杂,也能发现很多现在想不到的问题。这才是最有价值的学习机会!
5 写在最后
这次的学习和实践让我对图片识别技术的应用有了更深的理解。虽然过程中遇到了很多困难,但边学边做的过程还是很有收获的。
这篇文章记录的只是我个人的学习过程和思考,肯定有很多不足的地方。如果有经验更丰富的朋友看到了,欢迎指正和交流!
我们也会持续分享更多的技术实践经验,希望能和大家一起进步。
关于作者:曹建涛,转转C2C&寄卖业务研发工程师
想了解更多转转公司的业务实践,欢迎点击关注下方公众号
转转研发中心及业界小伙伴们的技术学习交流平台,定期分享一线的实战经验及业界前沿的技术话题。 关注公众号「转转技术」(综合性)、「大转转FE」(专注于FE)、「转转QA」(专注于QA),更多干货实践,欢迎交流分享~