Skip to content
🔗 分享本题
查看我的学习进度 →

图文检索系统图解:离线编码图片并建立向量索引,在线编码文本后 ANN 召回并用跨模态模型精排

🧠 图解记忆:生产级图文检索先离线建立图片向量索引,在线把文本映射到同一空间,用 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(图文检索的对比学习范式)