Skip to content

Commit f755c72

Browse files
authored
docs(blog): add share document (#47)
* Create 17. 实训营心得分享 Signed-off-by: Sky-Runner-Z <[email protected]>
1 parent c92acb1 commit f755c72

File tree

1 file changed

+164
-0
lines changed

1 file changed

+164
-0
lines changed
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
# SPX-Algorithm:构建多模态搜索服务的一些心得
2+
3+
## 写在前面
4+
5+
也是快到结营阶段了,关于我在实训营期间负责的 SPX-Algorithm 这个项目模块的设计思路,想着写个分享记录一下这段时间的心得体会。这个项目说起来其实就是个图文搜索服务,但做下来发现里面还挺有意思的,尤其是在架构设计和工程实践方面踩了不少坑,也有一些收获。
6+
7+
## 项目背景和定位
8+
9+
SPX-Algorithm本质上是一个图像搜索推荐模块。用户输入文本描述,系统从图片库里找出最相关的结果。听起来简单,但实际做起来发现挑战不少。
10+
11+
传统的图片搜索主要靠标签匹配,但这种方式局限性很明显。图片的标签往往不够丰富,用户的表达又很灵活。所以我的目标很明确:用AI来理解文本和图片的语义,让搜索更智能。
12+
13+
## 整体架构思路
14+
15+
经过一段时间的摸索,最终采用了分层架构设计:
16+
17+
```text
18+
API层 ← 对外接口,简洁易用
19+
协调层 ← 编排各种算法模块
20+
服务层 ← 图文匹配 + 重排序
21+
数据层 ← 向量存储 + 用户反馈数据存储
22+
```
23+
24+
### 为什么选择分层架构?
25+
26+
1. **职责清晰**:每一层只关心自己的事情,降低耦合
27+
2. **易于扩展**:加新功能时只需要在对应层面改动
28+
3. **便于测试**:每层都可以独立测试和调试
29+
4. **未来团队协作**:不同人可以专注不同层面的开发
30+
31+
## 核心模块设计
32+
33+
### 图文匹配服务
34+
35+
这是算法实现的核心模块,负责理解用户查询并召回相关图片。
36+
37+
选择CLIP模型主要是因为它能把文本和图片映射到同一个语义空间。我们用的是OpenCLIP的ViT-B-32,512维向量输出。这个选择是经过权衡的:
38+
39+
- 效果足够好,能理解复杂的语义关系
40+
- 推理速度快,满足线上服务需求
41+
- 社区成熟,资料多坑少
42+
43+
向量存储用的Milvus,主要看中它对大规模向量检索的优化。
44+
45+
### 重排序服务
46+
47+
图文匹配召回的结果虽然相关,但排序质量还有提升空间。这时候就需要重排序来精调。
48+
49+
我们采用 LTR 的思路,基于用户的真实反馈来训练排序模型。整个设计围绕"用户反馈闭环"展开:
50+
51+
1. **反馈收集**:用户每次搜索后的点击行为都会被记录下来
52+
2. **训练数据构建**:从点击行为中提取pairwise训练样本,被选中的图片作为正样本,未选中的作为负样本
53+
3. **模型训练**:使用神经网络学习用户偏好模式
54+
4. **在线重排序**:训练好的模型对搜索结果进行重新排序
55+
56+
这种设计的好处是能够持续学习用户偏好,越用越准。但也有挑战,比如冷启动问题、反馈噪声处理等。
57+
58+
### 搜索协调器
59+
60+
为了统一管理多个算法服务,我们设计了SearchCoordinator。它的作用是编排整个搜索流程:
61+
62+
1. 调用图文匹配服务进行粗排召回
63+
2. 如果启用了重排序,调用重排序服务进行细排
64+
3. 返回最终的排序结果
65+
66+
这个设计让我们能够灵活控制算法流程,比如可以随时开关重排序功能,方便做A/B测试。
67+
68+
## API设计理念
69+
70+
在接口设计上,我们遵循了"对外简洁,对内丰富"的原则,主要分为三类接口:
71+
72+
### 资源管理接口 (`/v1/resource/`)
73+
74+
- `POST /add`:添加单个资源
75+
- `POST /batch`:批量添加资源
76+
- `POST /search`:基于文本的语义搜索
77+
- `POST /match`:基于图片的语义搜索
78+
79+
### 用户反馈接口 (`/v1/feedback/`)
80+
81+
- `POST /submit`:提交用户点击反馈
82+
- `GET /stats`:查看反馈统计信息
83+
- `GET /recent`:获取最近的反馈数据
84+
- `POST /train`:触发模型训练
85+
- `GET /model/status`:查看模型状态
86+
- `POST /model/enable|disable`:启用/禁用重排序
87+
88+
### 内部调试接口 (`/v1/internal/`)
89+
90+
- 向量数据管理:查看、搜索向量数据
91+
- 资源管理:列表查询、删除操作
92+
- 数据库状态:详细的系统状态信息
93+
94+
这样设计的好处是业务方使用简单(只需要关心resource接口),反馈收集独立成模块(便于数据科学团队使用),同时我们内部有足够的工具来监控和调试系统。
95+
96+
## 踩过的坑和解决方案
97+
98+
### SVG处理问题
99+
100+
我们的图片库有很多SVG格式的图片,但CLIP模型只能处理位图。最初直接用Pillow转换,发现效果很差——SVG的矢量信息丢失了。
101+
102+
后来改用CairoSVG来处理,质量明显提升。
103+
104+
这个经历让我意识到:**看似简单的问题往往有很多细节**
105+
106+
### 向量存储优化
107+
108+
最初我们把所有向量数据都放在内存里,随着数据量增长很快就撑不住了。
109+
110+
迁移到Milvus后,发现索引类型的选择很关键。我们试了IVF、HNSW等几种索引,最终选择HNSW是因为它在我们的数据规模下召回率和速度表现最均衡。
111+
112+
### 重排序特征设计
113+
114+
重排序模块我们改了好几版。最初用10维手工特征,包括余弦相似度、欧氏距离、向量模长等。后来发现CLIP向量已经归一化了,模长特征基本没用。
115+
116+
最终改成了神经网络方案,用1500多维的丰富特征,包括原始向量、统计特征等。虽然维度高了很多,但信息保留更完整。
117+
118+
### 配置管理混乱
119+
120+
项目初期配置管理比较随意,开发、测试、生产环境的配置经常搞混。后来统一用dataclass重新设计了配置系统,按环境分离,清爽了很多。
121+
122+
## 一些工程实践心得
123+
124+
### 日志和监控
125+
126+
每个关键模块都加了详细的日志,包括:
127+
128+
- 请求响应时间
129+
- 数据库操作耗时
130+
- 搜索结果统计
131+
- 异常情况记录
132+
133+
这些数据在出问题时特别有用,能快速定位根因。
134+
135+
### 错误处理
136+
137+
对于算法服务来说,容错很重要。我们的策略是:
138+
139+
- 单个服务出错不影响整体功能
140+
- 重排序失败时返回粗排结果
141+
- 向量服务异常时走降级逻辑
142+
143+
### 测试策略
144+
145+
除了常规的单元测试,我们还设计了端到端的功能测试。特别是对于机器学习模块,我们会构造一些已知结果的测试用例,确保算法逻辑正确。
146+
147+
## 未来的一些想法
148+
149+
1. **多模态融合**:除了CLIP,考虑加入其他视觉理解能力
150+
2. **个性化搜索**:结合用户历史行为做个性化排序
151+
3. **特征学习**:让模型能更好地适应用户偏好
152+
4. **效果评估**:建立更完善的线上效果评估体系
153+
154+
## 总结
155+
156+
做SPX-Algorithm这个项目最大的收获是:**好的架构设计比炫酷的算法更重要**
157+
158+
技术选型要务实,不要为了用新技术而用新技术。我们选择CLIP、Milvus这些相对成熟的方案,主要考虑的是稳定性和可维护性。
159+
160+
工程实践要细致,很多看起来简单的问题往往有很多坑。配置管理、错误处理、日志监控这些"脏活累活"往往决定了系统能不能稳定运行。
161+
162+
最后,用户反馈闭环很重要。我们通过收集用户的真实行为数据来持续优化算法效果,这比闭门造车要有效得多。
163+
164+
希望这些分享对大家有用。如果有问题欢迎交流讨论!

0 commit comments

Comments
 (0)