WARNING
🧪 Beta公测版本提示:教程主体已完成,正在优化细节,欢迎大家提Issue反馈问题或建议。
s22 多模态模型 — exercise.py 练习指南
练习目标
通过补全三个 TODO 任务,掌握多模态学习的三个核心组件:
- InfoNCE 对比损失 —— CLIP 的训练目标
- 余弦相似度与跨模态检索 —— 图文匹配的数学基础
- 简单图像搜索引擎 —— 将理论转化为应用
预备知识
- CLIP 的对称 InfoNCE 损失:
- 图像方向:
- 文本方向:
- 余弦相似度:
任务清单
TODO 1:实现 InfoNCE 对比损失(infonce_loss 函数)
任务:输入 L2 归一化的图像和文本嵌入,输出对称对比损失。
实现步骤:
S = image_embeddings @ text_embeddings.T—— 计算相似度矩阵 (N, N)logits = S / temperature—— 温度缩放- 图像方向:
, pythonnumerator_image = np.exp(np.diag(logits)) # (N,) denominator_image = np.sum(np.exp(logits), axis=1) # (N,) L_image = -np.mean(np.log(numerator_image / denominator_image)) - 文本方向(对称):对
logits.T做同样操作pythonlogits_text = S.T / temperature numerator_text = np.exp(np.diag(logits_text)) denominator_text = np.sum(np.exp(logits_text), axis=1) L_text = -np.mean(np.log(numerator_text / denominator_text)) loss = (L_image + L_text) / 2
关键理解:
- 对角线
是匹配的图文对(正样本),每行的其他元素是负样本 - 对称设计确保两个编码器都学习对齐——图→文和文→图两个方向
的作用: 很小, 远大于 ,增强了正样本的优势
预期输出:
随机情况下的理论值: -log(1/3) = 1.0986
完美对齐时的理论最小值: 接近 0(取决于 τ)
损失范围: [约0.001, 约1.099]
损失越小 → 图文对齐越好TODO 2:实现余弦相似度与跨模态匹配
任务 2a:实现 cosine_similarity(vec_a, vec_b) —— 返回 (M, N) 相似度矩阵。
数学:
实现步骤:
dot_product = vec_a @ vec_b.T—— 点积矩阵 (M, N)norm_a = np.linalg.norm(vec_a, axis=1)—— 每行的 L2 范数 (M,)norm_b = np.linalg.norm(vec_b, axis=1)—— 每行的 L2 范数 (N,)similarity = dot_product / (norm_a[:, None] * norm_b[None, :])—— 广播除
注意:norm_a[:, None] 将 (M,) 变为 (M, 1),norm_b[None, :] 将 (N,) 变为 (1, N),相乘得到 (M, N)。
任务 2b:实现 find_best_match(query_embedding, candidate_embeddings, candidate_labels, top_k)
实现步骤:
query_reshaped = query_embedding.reshape(1, -1)—— (d,) → (1, d)similarities = cosine_similarity(query_reshaped, candidate_embeddings)—— (1, N)similarities_flat = similarities.flatten()—— (N,)top_indices = np.argsort(-similarities_flat)[:top_k]—— 降序取前 k- 构造
[(candidate_labels[i], similarities_flat[i]) for i in top_indices]
预期输出:
余弦相似度矩阵: 狗图像→狗文1 (最高) > 狗图像→猫文 (低)
狗图像正确匹配了狗文本 ✓
Top-3 匹配文本:
1. 「一只金毛犬」相似度最高
2. 「一只可爱的狗」次之
3. 「一只橘猫」相似度明显更低TODO 3:构建简单的 CLIP 图像搜索引擎(SimpleImageSearchEngine 类)
任务 3a:实现 add_image(embedding, metadata)
python
def add_image(self, embedding, metadata):
normalized_emb = self._normalize(embedding) # L2 归一化
self.image_embeddings.append(normalized_emb) # 存入向量列表
self.image_metadata.append(metadata) # 存入元数据列表任务 3b:实现 search_by_text(query_embedding, top_k, min_similarity)
实现步骤:
query_normalized = self._normalize(query_embedding)—— (d,)emb_matrix = np.stack(self.image_embeddings, axis=0)—— (N, d)similarities = emb_matrix @ query_normalized—— 内积=余弦相似度 (N,)top_indices = np.argsort(-similarities)[:top_k]- 构造结果,过滤掉
< min_similarity的:pythonresults = [] for idx in top_indices: if similarities[idx] >= min_similarity: results.append({"metadata": self.image_metadata[idx], "similarity": float(similarities[idx])})
关键设计:
- 向量已 L2 归一化,因此
emb_matrix @ query_normalized直接得到余弦相似度 np.argsort(-similarities)实现降序排序min_similarity阈值过滤不相关结果
预期输出:
索引规模: 10 张图片
查询: 狗的文本描述
Top-5 搜索结果:
金毛犬.jpg (dog) - 相似度最高
哈士奇.jpg (dog) - 接近
柯基.jpg (dog) - 接近
... (其他类别图片相似度更低)
以图搜图:
查询图片本身获得最高相似度(≈ 1.0)✓完成后的验证
全部三个 TODO 通过测试后,如果安装了 CLIP 模型和 sklearn,运行 python code/demo.py 观察:
- 零样本分类如何不需要任何训练就识别图像
- 图文相似度排序是否正确匹配语义
- PCA 可视化中图文嵌入的空间分布
源码位置
clone 后打开(相对仓库根目录):
docs/applied/systems/multimodal/code/exercise.py