🧠 图解记忆:生产级图文检索先离线建立图片向量索引,在线把文本映射到同一空间,用 ANN 快速召回候选,再用跨模态匹配模型精排;点击图片可查看原图。
💡 答案要点
图文检索 = 输入文本找图片,或输入图片找文本
系统架构:
┌─────────────────────────────────────────────────────────┐
│ 图文检索系统 │
└─────────────────────────────────────────────────────────┘
离线索引:
图片库(100万张)
↓
CLIP 图像编码器
↓
图像向量(100万 × 512维)
↓
向量数据库(Milvus/Qdrant)
在线检索:
用户输入:"夕阳下的海滩"
↓
CLIP 文本编码器
↓
文本向量(1 × 512维)
↓
向量检索(余弦相似度)
↓
Top-K 图片实现步骤:
1. 离线索引(一次性):
展开 Python 代码示例(36 行)
python
import clip
import torch
from pymilvus import Collection
# 加载 CLIP 模型
device = "cuda"
model, preprocess = clip.load("ViT-B/32", device=device)
# 编码所有图片
image_paths = load_image_paths() # 100万张
batch_size = 256
all_features = []
for i in range(0, len(image_paths), batch_size):
batch_paths = image_paths[i:i+batch_size]
# 加载并预处理图片
images = [preprocess(Image.open(p)) for p in batch_paths]
images = torch.stack(images).to(device)
# 编码
with torch.no_grad():
features = model.encode_image(images)
features = features / features.norm(dim=-1, keepdim=True)
all_features.append(features.cpu())
all_features = torch.cat(all_features) # 100万 × 512
# 存入向量数据库
collection = Collection("image_vectors")
collection.insert([
image_paths,
all_features.tolist()
])
collection.create_index("image_vector", {"index_type": "IVF_FLAT", "metric_type": "IP"})2. 在线检索:
python
def search_images(query_text, top_k=10):
# 编码文本
text = clip.tokenize([query_text]).to(device)
with torch.no_grad():
text_features = model.encode_text(text)
text_features = text_features / text_features.norm(dim=-1, keepdim=True)
# 向量检索
results = collection.search(
data=text_features.cpu().tolist(),
anns_field="image_vector",
param={"metric_type": "IP", "nprobe": 10},
limit=top_k
)
# 返回结果
return [(r.id, r.distance) for r in results[0]]
# 使用
results = search_images("夕阳下的海滩", top_k=10)
# [(img_id_1, 0.89), (img_id_2, 0.85), ...]3. 反向检索(以图搜文):
python
def search_texts(query_image_path, candidate_texts, top_k=10):
# 编码图片
image = preprocess(Image.open(query_image_path)).unsqueeze(0).to(device)
with torch.no_grad():
image_features = model.encode_image(image)
image_features = image_features / image_features.norm(dim=-1, keepdim=True)
# 编码候选文本
texts = clip.tokenize(candidate_texts).to(device)
with torch.no_grad():
text_features = model.encode_text(texts)
text_features = text_features / text_features.norm(dim=-1, keepdim=True)
# 计算相似度
similarity = (image_features @ text_features.T).squeeze(0)
# 排序
top_indices = similarity.argsort(descending=True)[:top_k]
return [(candidate_texts[i], similarity[i].item()) for i in top_indices]优化策略:
1. 多模板 Ensemble:
python
templates = [
"a photo of {}",
"a picture of {}",
"{}",
]
query_texts = [t.format(query) for t in templates]
text_features = model.encode_text(clip.tokenize(query_texts))
text_features = text_features.mean(dim=0) # 平均多个模板2. 重排序(Rerank):
python
# 第一步:向量检索召回 100 个候选
candidates = collection.search(text_features, limit=100)
# 第二步:用 BLIP 计算精确相似度
rerank_scores = []
for img_id in candidates:
image = load_image(img_id)
# 用 BLIP 的 ITM(Image-Text Matching)
score = blip_itm(image, query_text)
rerank_scores.append(score)
# 重新排序
final_results = sorted(zip(candidates, rerank_scores),
key=lambda x: x[1], reverse=True)[:10]性能优化:
| 策略 | 效果 | 成本 |
|---|---|---|
| ANN 索引 | 10-100x 加速 | 1-2% 精度损失 |
| 量化(INT8) | 4x 显存节省 | <1% 精度损失 |
| GPU 推理 | 100x 加速 | 硬件成本 |
| 批处理 | 5-10x 吞吐 | 增加延迟 |
面试话术:
"图文检索的核心是向量相似度搜索。我用 CLIP 编码图文,用 Milvus 做 ANN 检索。100 万张图片,检索延迟 < 50ms。关键优化是 IVF 索引 + GPU 加速 + 多模板 ensemble。"
📚 参考:CLIP(图文检索的对比学习范式)
