GLM-4v-9b多模态教程图文联合embedding生成、跨模态相似度计算与检索应用1. 引言为什么你需要关注图文联合embedding想象一下这个场景你是一家电商公司的运营每天要处理成千上万的商品图片和描述文案。你想快速找到所有“红色连衣裙”的图片但系统只能通过文字标签搜索很多图片没有打上“红色”或“连衣裙”的标签结果漏掉了一大半。或者你是一个内容创作者手头有一堆素材图片想找一张“表达孤独感的城市夜景”配图但光靠关键词搜索出来的都是“夜景”、“城市”完全不对味。这就是传统搜索的痛点——文字和图片是割裂的。而GLM-4v-9b带来的图文联合embedding技术正是为了解决这个问题。它能让机器像人一样同时理解图片的内容和文字的含义并把它们映射到同一个“语义空间”里进行比较。简单来说有了它你可以用一段文字描述直接搜到最匹配的图片用一张图片反向搜索到描述它的文字实现真正的“以图搜图”、“以文搜图”甚至“以图搜文”本教程将手把手带你玩转GLM-4v-9b的这项核心能力。即使你之前没接触过多模态模型跟着步骤走也能在半小时内搭建起自己的跨模态检索系统。2. 环境准备一条命令搞定部署开始之前我们先确保环境就绪。GLM-4v-9b的部署已经非常简化特别是通过CSDN星图这样的平台。2.1 基础环境要求你的机器需要满足以下条件操作系统Linux (Ubuntu 20.04 推荐) 或 Windows (WSL2)显卡至少24GB显存 (如RTX 4090) 用于FP16精度运行如果使用INT4量化9GB显存 (如RTX 4060 Ti 16GB) 即可内存32GB RAM 或以上磁盘空间50GB 可用空间 (用于存放模型权重和依赖)2.2 快速部署方案对于大多数开发者我推荐直接使用预置的Docker镜像或云平台服务避免环境配置的麻烦。方案一使用CSDN星图镜像最快如果你在CSDN星图平台可以直接搜索“GLM-4v-9b”镜像一键部署。平台已经预配置好了所有环境包括transformers库、vLLM加速等。方案二本地Docker部署如果你习惯本地开发可以使用官方提供的Docker镜像# 拉取镜像 docker pull registry.cn-beijing.aliyuncs.com/glm/glm-4v-9b:latest # 运行容器 docker run -it --gpus all \ -p 7860:7860 \ -v /path/to/your/data:/data \ registry.cn-beijing.aliyuncs.com/glm/glm-4v-9b:latest方案三手动安装适合定制化需求如果你需要更灵活的控制可以手动安装# 1. 创建虚拟环境 conda create -n glm4v python3.10 conda activate glm4v # 2. 安装PyTorch (根据你的CUDA版本选择) pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118 # 3. 安装transformers和视觉相关库 pip install transformers accelerate pillow pip install githttps://github.com/huggingface/transformers # 4. 安装vLLM用于加速推理可选但推荐 pip install vllm2.3 验证安装安装完成后用一段简单的代码测试环境是否正常import torch from transformers import AutoModel, AutoTokenizer # 检查CUDA是否可用 print(fCUDA available: {torch.cuda.is_available()}) print(fGPU count: {torch.cuda.device_count()}) if torch.cuda.is_available(): print(fCurrent GPU: {torch.cuda.get_device_name(0)}) print(fGPU memory: {torch.cuda.get_device_properties(0).total_memory / 1e9:.2f} GB)如果一切正常你会看到类似这样的输出CUDA available: True GPU count: 1 Current GPU: NVIDIA GeForce RTX 4090 GPU memory: 24.00 GB3. 核心概念图文embedding到底是什么在深入代码之前我们先花几分钟搞清楚几个关键概念。别担心我用最直白的方式解释。3.1 什么是embedding你可以把embedding理解成一种“数学指纹”。就像每个人有独特的指纹一样每段文字、每张图片经过模型处理都会生成一串数字通常是512或1024维的向量这串数字就是它的embedding。关键点在于语义相似的内容它们的embedding在数学空间里的距离也很近。举个例子“猫”的embedding和“猫咪”的embedding会很接近“狗”的embedding和“宠物”的embedding也比较接近一张猫的图片和“猫”这个文字的embedding也会很接近这就是跨模态检索的基础——不同模态图、文但语义相同的内容被映射到了相近的位置。3.2 GLM-4v-9b如何生成联合embeddingGLM-4v-9b的巧妙之处在于它用一个模型同时处理图片和文字图片处理通过视觉编码器ViT把图片转换成视觉特征文字处理通过语言模型把文字转换成文本特征特征融合通过交叉注意力机制让图片特征和文字特征相互“交流”统一输出最终输出一个融合了图文信息的联合embedding这个过程是端到端训练的意味着模型在学习过程中就学会了如何让图片和文字的embedding在同一个空间里对齐。3.3 为什么1120×1120分辨率很重要你可能会注意到GLM-4v-9b特别强调了1120×1120的高分辨率支持。这在实际应用中非常实用小字识别图表中的坐标轴标签、表格里的数字都能看清细节保留图片中的纹理、物体的细微特征不会丢失文档处理扫描的PDF、截图中的文字都能准确识别传统模型往往把图片缩放到224×224或384×384很多细节就糊掉了。GLM-4v-9b保持高分辨率输入让它在处理复杂图片时优势明显。4. 实战开始生成你的第一个图文embedding理论讲完了现在我们来动手实践。我会带你一步步生成图片和文字的embedding。4.1 加载模型和处理器首先我们需要加载GLM-4v-9b模型和对应的处理器import torch from PIL import Image from transformers import AutoModel, AutoTokenizer, AutoProcessor # 设置设备 device cuda if torch.cuda.is_available() else cpu # 加载模型和处理器 model_name THUDM/glm-4v-9b print(正在加载tokenizer...) tokenizer AutoTokenizer.from_pretrained(model_name, trust_remote_codeTrue) print(正在加载processor...) processor AutoProcessor.from_pretrained(model_name, trust_remote_codeTrue) print(正在加载模型...) model AutoModel.from_pretrained( model_name, torch_dtypetorch.float16, # 使用半精度减少显存占用 device_mapauto, # 自动分配到可用GPU trust_remote_codeTrue ) model.eval() # 设置为评估模式 print(模型加载完成)重要提示第一次运行时会下载模型权重约18GB请确保网络通畅和磁盘空间充足。如果下载慢可以考虑先下载到本地再加载。4.2 准备测试数据我们来准备一些测试用的图片和文字。你可以用自己的图片或者用我提供的示例import requests from io import BytesIO import matplotlib.pyplot as plt # 示例1一张猫的图片 cat_image_url https://images.unsplash.com/photo-1514888286974-6d03bde4ba4f cat_text 一只橘猫坐在窗台上晒太阳 # 示例2一张风景图片 landscape_image_url https://images.unsplash.com/photo-1501854140801-50d01698950b landscape_text 山间湖泊远处有雪山水面平静如镜 # 示例3一张食物图片 food_image_url https://images.unsplash.com/photo-1565958011703-44f9829ba187 food_text 一盘新鲜的沙拉有西红柿、黄瓜和生菜 def download_image(url): 下载图片并转换为PIL Image response requests.get(url) img Image.open(BytesIO(response.content)) return img # 下载图片 print(下载测试图片...) cat_img download_image(cat_image_url) landscape_img download_image(landscape_image_url) food_img download_image(food_image_url) # 显示图片可选 fig, axes plt.subplots(1, 3, figsize(15, 5)) axes[0].imshow(cat_img) axes[0].set_title(猫) axes[0].axis(off) axes[1].imshow(landscape_img) axes[1].set_title(风景) axes[1].axis(off) axes[2].imshow(food_img) axes[2].set_title(食物) axes[2].axis(off) plt.tight_layout() plt.show()4.3 生成图片embedding现在我们来生成图片的embedding。GLM-4v-9b提供了专门的方法来提取视觉特征def get_image_embedding(image, model, processor): 获取图片的embedding 参数: image: PIL Image对象 model: 加载好的GLM-4v-9b模型 processor: 对应的处理器 返回: embedding: 图片的embedding向量 # 使用处理器准备输入 inputs processor(imagesimage, return_tensorspt) # 将输入移动到GPU inputs {k: v.to(device) for k, v in inputs.items()} # 生成embedding with torch.no_grad(): # 不计算梯度加快推理速度 outputs model.get_image_features(**inputs) # 提取embedding并归一化重要 embedding outputs.last_hidden_state.mean(dim1) # 取平均池化 embedding torch.nn.functional.normalize(embedding, p2, dim1) # L2归一化 return embedding.cpu().numpy() # 转回numpy方便后续处理 # 为三张图片生成embedding print(生成图片embedding...) cat_img_emb get_image_embedding(cat_img, model, processor) landscape_img_emb get_image_embedding(landscape_img, model, processor) food_img_emb get_image_embedding(food_img, model, processor) print(f猫图片embedding形状: {cat_img_emb.shape}) print(f风景图片embedding形状: {landscape_img_emb.shape}) print(f食物图片embedding形状: {food_img_emb.shape})你会看到输出类似猫图片embedding形状: (1, 2048) 风景图片embedding形状: (1, 2048) 食物图片embedding形状: (1, 2048)这里的2048就是embedding的维度。每个图片都被转换成了一个2048维的向量。4.4 生成文字embedding同样地我们也可以为文字生成embeddingdef get_text_embedding(text, model, tokenizer): 获取文字的embedding 参数: text: 文本字符串 model: 加载好的GLM-4v-9b模型 tokenizer: 对应的tokenizer 返回: embedding: 文本的embedding向量 # 使用tokenizer编码文本 inputs tokenizer(text, return_tensorspt, paddingTrue, truncationTrue) # 将输入移动到GPU inputs {k: v.to(device) for k, v in inputs.items()} # 生成embedding with torch.no_grad(): outputs model.get_text_features(**inputs) # 提取embedding并归一化 embedding outputs.last_hidden_state.mean(dim1) # 取平均池化 embedding torch.nn.functional.normalize(embedding, p2, dim1) # L2归一化 return embedding.cpu().numpy() # 为三段文字生成embedding print(生成文字embedding...) cat_text_emb get_text_embedding(cat_text, model, tokenizer) landscape_text_emb get_text_embedding(landscape_text, model, tokenizer) food_text_emb get_text_embedding(food_text, model, tokenizer) print(f猫文字embedding形状: {cat_text_emb.shape}) print(f风景文字embedding形状: {landscape_text_emb.shape}) print(f食物文字embedding形状: {food_text_emb.shape})注意文字embedding的维度也是2048和图片embedding的维度一致。这是实现跨模态检索的关键——图片和文字在同一个向量空间里。5. 计算相似度让图片和文字“对话”有了embedding我们就可以计算相似度了。相似度越高说明内容越相关。5.1 理解相似度计算在embedding空间里我们通常用余弦相似度来衡量两个向量的相似程度。它的取值范围是[-1, 1]1完全相似方向相同0不相关垂直-1完全相反方向相反计算公式很简单两个向量的点积除以它们模长的乘积。5.2 实现相似度计算我们来写一个计算余弦相似度的函数import numpy as np def cosine_similarity(vec1, vec2): 计算两个向量的余弦相似度 参数: vec1, vec2: 两个向量形状为(1, n)或(n,) 返回: similarity: 余弦相似度范围[-1, 1] # 确保是1维向量 if len(vec1.shape) 1: vec1 vec1.flatten() if len(vec2.shape) 1: vec2 vec2.flatten() # 计算点积 dot_product np.dot(vec1, vec2) # 计算模长 norm1 np.linalg.norm(vec1) norm2 np.linalg.norm(vec2) # 避免除零 if norm1 0 or norm2 0: return 0 # 计算余弦相似度 similarity dot_product / (norm1 * norm2) return similarity # 测试计算猫图片和猫文字的相似度 cat_similarity cosine_similarity(cat_img_emb, cat_text_emb) print(f猫图片 vs 猫文字描述 相似度: {cat_similarity:.4f}) # 计算猫图片和风景文字的相似度应该较低 cat_landscape_similarity cosine_similarity(cat_img_emb, landscape_text_emb) print(f猫图片 vs 风景文字描述 相似度: {cat_landscape_similarity:.4f})正常情况下的输出应该是猫图片 vs 猫文字描述 相似度: 0.85xx (较高接近1) 猫图片 vs 风景文字描述 相似度: 0.2xxx (较低接近0)5.3 批量计算相似度矩阵在实际应用中我们经常需要计算多个图片和多个文字之间的相似度。我们来写一个更实用的函数def compute_similarity_matrix(image_embeddings, text_embeddings): 计算图片embedding和文字embedding之间的相似度矩阵 参数: image_embeddings: 列表包含多个图片的embedding text_embeddings: 列表包含多个文字的embedding 返回: similarity_matrix: 相似度矩阵shape(n_images, n_texts) n_images len(image_embeddings) n_texts len(text_embeddings) # 初始化相似度矩阵 similarity_matrix np.zeros((n_images, n_texts)) # 计算每对图片和文字的相似度 for i in range(n_images): for j in range(n_texts): similarity_matrix[i, j] cosine_similarity( image_embeddings[i], text_embeddings[j] ) return similarity_matrix # 准备数据 image_embeddings [cat_img_emb, landscape_img_emb, food_img_emb] text_embeddings [cat_text_emb, landscape_text_emb, food_text_emb] image_names [猫图片, 风景图片, 食物图片] text_descriptions [猫文字, 风景文字, 食物文字] # 计算相似度矩阵 similarity_matrix compute_similarity_matrix(image_embeddings, text_embeddings) print(相似度矩阵:) print(行: 图片, 列: 文字) print( * 50) print(f{:15}, end) for text in text_descriptions: print(f{text:15}, end) print() for i, img_name in enumerate(image_names): print(f{img_name:15}, end) for j in range(len(text_descriptions)): print(f{similarity_matrix[i, j]:.4f} , end) print()你会看到一个3x3的矩阵对角线上的值应该最高图片和对应的文字描述非对角线上的值应该较低。6. 构建跨模态检索系统现在到了最实用的部分——用我们学到的知识构建一个真正的跨模态检索系统。6.1 系统设计思路一个完整的跨模态检索系统通常包括建库阶段处理所有图片生成embedding并存储检索阶段接收查询文字或图片计算相似度返回最匹配的结果排序阶段按相似度排序返回Top-K结果6.2 实现图片库管理我们先实现一个简单的图片库管理类import os import pickle from pathlib import Path from typing import List, Dict, Tuple import numpy as np class ImageRetrievalSystem: 跨模态图片检索系统 def __init__(self, model, processor, tokenizer): 初始化检索系统 参数: model: GLM-4v-9b模型 processor: 图片处理器 tokenizer: 文本tokenizer self.model model self.processor processor self.tokenizer tokenizer self.image_paths [] # 图片路径列表 self.image_embeddings [] # 图片embedding列表 self.image_metadata [] # 图片元数据可选 def add_image(self, image_path: str, image: Image.Image None, metadata: Dict None): 添加图片到库中 参数: image_path: 图片路径 image: PIL Image对象如果提供则直接使用 metadata: 图片元数据 # 加载图片如果未提供 if image is None: try: image Image.open(image_path).convert(RGB) except Exception as e: print(f无法加载图片 {image_path}: {e}) return False # 生成embedding try: embedding get_image_embedding(image, self.model, self.processor) except Exception as e: print(f生成embedding失败 {image_path}: {e}) return False # 保存到库中 self.image_paths.append(image_path) self.image_embeddings.append(embedding) self.image_metadata.append(metadata or {}) print(f已添加图片: {image_path} (embedding形状: {embedding.shape})) return True def add_image_directory(self, directory_path: str, extensions: List[str] None): 批量添加目录中的所有图片 参数: directory_path: 目录路径 extensions: 支持的图片扩展名默认为[.jpg, .jpeg, .png, .bmp] if extensions is None: extensions [.jpg, .jpeg, .png, .bmp, .webp] directory Path(directory_path) if not directory.exists(): print(f目录不存在: {directory_path}) return image_files [] for ext in extensions: image_files.extend(directory.glob(f*{ext})) image_files.extend(directory.glob(f*{ext.upper()})) print(f找到 {len(image_files)} 张图片) added_count 0 for img_path in image_files: if self.add_image(str(img_path)): added_count 1 print(f成功添加 {added_count} 张图片到库中) def search_by_text(self, query_text: str, top_k: int 5): 用文字搜索图片 参数: query_text: 查询文本 top_k: 返回最相似的K个结果 返回: results: 列表每个元素为(图片路径, 相似度, 元数据) # 生成查询文本的embedding query_embedding get_text_embedding(query_text, self.model, self.tokenizer) # 计算相似度 similarities [] for img_emb in self.image_embeddings: sim cosine_similarity(query_embedding, img_emb) similarities.append(sim) # 获取Top-K结果 similarities np.array(similarities).flatten() top_indices np.argsort(similarities)[-top_k:][::-1] # 从高到低排序 # 构建结果 results [] for idx in top_indices: results.append(( self.image_paths[idx], similarities[idx], self.image_metadata[idx] )) return results def search_by_image(self, query_image: Image.Image, top_k: int 5): 用图片搜索图片以图搜图 参数: query_image: 查询图片 top_k: 返回最相似的K个结果 返回: results: 列表每个元素为(图片路径, 相似度, 元数据) # 生成查询图片的embedding query_embedding get_image_embedding(query_image, self.model, self.processor) # 计算相似度 similarities [] for img_emb in self.image_embeddings: sim cosine_similarity(query_embedding, img_emb) similarities.append(sim) # 获取Top-K结果 similarities np.array(similarities).flatten() top_indices np.argsort(similarities)[-top_k:][::-1] # 构建结果 results [] for idx in top_indices: results.append(( self.image_paths[idx], similarities[idx], self.image_metadata[idx] )) return results def save_index(self, filepath: str): 保存索引到文件 参数: filepath: 保存路径 data { image_paths: self.image_paths, image_embeddings: self.image_embeddings, image_metadata: self.image_metadata } with open(filepath, wb) as f: pickle.dump(data, f) print(f索引已保存到: {filepath}) def load_index(self, filepath: str): 从文件加载索引 参数: filepath: 索引文件路径 with open(filepath, rb) as f: data pickle.load(f) self.image_paths data[image_paths] self.image_embeddings data[image_embeddings] self.image_metadata data[image_metadata] print(f已加载索引包含 {len(self.image_paths)} 张图片)6.3 使用检索系统现在我们来实际使用这个检索系统# 初始化检索系统 retrieval_system ImageRetrievalSystem(model, processor, tokenizer) # 添加一些测试图片这里用之前下载的图片 # 在实际应用中你可以添加自己的图片目录 retrieval_system.add_image(cat_image.jpg, cat_img, {category: animal}) retrieval_system.add_image(landscape_image.jpg, landscape_img, {category: scenery}) retrieval_system.add_image(food_image.jpg, food_img, {category: food}) # 用文字搜索图片 print(\n 文字搜索测试 ) query_text 一只可爱的宠物 results retrieval_system.search_by_text(query_text, top_k2) print(f查询: {query_text}) print(搜索结果:) for i, (path, score, metadata) in enumerate(results): print(f {i1}. {Path(path).name} (相似度: {score:.4f}, 类别: {metadata.get(category, N/A)})) # 用图片搜索图片以图搜图 print(\n 图片搜索测试 ) # 用猫图片搜索 results retrieval_system.search_by_image(cat_img, top_k2) print(查询: 猫图片) print(搜索结果:) for i, (path, score, metadata) in enumerate(results): print(f {i1}. {Path(path).name} (相似度: {score:.4f}, 类别: {metadata.get(category, N/A)}))6.4 实际应用示例电商商品搜索让我们看一个更实际的例子——电商商品图片搜索# 模拟一个电商商品库 def setup_ecommerce_example(): 设置电商商品搜索示例 # 创建新的检索系统 ecommerce_system ImageRetrievalSystem(model, processor, tokenizer) # 模拟添加商品图片在实际应用中这里应该是真实的商品图片 # 我们使用一些示例图片URL来模拟 product_images [ { path: red_dress_1.jpg, url: https://images.unsplash.com/photo-1595777457583-95e059d581b8, metadata: {category: clothing, color: red, type: dress, price: 299} }, { path: blue_jeans_1.jpg, url: https://images.unsplash.com/photo-1542272604-787c3835535d, metadata: {category: clothing, color: blue, type: jeans, price: 199} }, { path: white_sneakers_1.jpg, url: https://images.unsplash.com/photo-1549298916-b41d501d3772, metadata: {category: shoes, color: white, type: sneakers, price: 599} }, { path: black_backpack_1.jpg, url: https://images.unsplash.com/photo-1553062407-98eeb64c6a62, metadata: {category: bags, color: black, type: backpack, price: 399} }, { path: red_dress_2.jpg, url: https://images.unsplash.com/photo-1591047139829-d91aecb6caea, metadata: {category: clothing, color: red, type: dress, price: 459} } ] print(正在构建电商商品库...) for product in product_images: try: # 下载图片 img download_image(product[url]) # 添加到系统 ecommerce_system.add_image( product[path], img, product[metadata] ) except Exception as e: print(f添加商品失败 {product[path]}: {e}) return ecommerce_system # 设置电商示例 ecommerce_system setup_ecommerce_example() # 测试搜索 print(\n 电商商品搜索测试 ) # 测试1搜索红色连衣裙 print(\n1. 搜索红色连衣裙:) results ecommerce_system.search_by_text(红色连衣裙, top_k3) for i, (path, score, metadata) in enumerate(results): print(f {i1}. {metadata.get(type, 未知)} - {metadata.get(color, 未知颜色)}色 f(价格: ¥{metadata.get(price, 0)}, 相似度: {score:.4f})) # 测试2搜索休闲鞋 print(\n2. 搜索休闲运动鞋:) results ecommerce_system.search_by_text(休闲运动鞋, top_k3) for i, (path, score, metadata) in enumerate(results): print(f {i1}. {metadata.get(type, 未知)} - {metadata.get(color, 未知颜色)}色 f(价格: ¥{metadata.get(price, 0)}, 相似度: {score:.4f})) # 测试3以图搜图用红色连衣裙图片搜索 print(\n3. 以图搜图红色连衣裙:) # 使用第一件红色连衣裙的图片进行搜索 red_dress_img download_image(https://images.unsplash.com/photo-1595777457583-95e059d581b8) results ecommerce_system.search_by_image(red_dress_img, top_k3) for i, (path, score, metadata) in enumerate(results): print(f {i1}. {metadata.get(type, 未知)} - {metadata.get(color, 未知颜色)}色 f(价格: ¥{metadata.get(price, 0)}, 相似度: {score:.4f}))这个示例展示了如何用GLM-4v-9b构建一个电商商品搜索系统。你可以看到即使用户输入的是自然语言描述如红色连衣裙系统也能找到相关的商品图片。7. 性能优化与实用技巧在实际应用中你可能会遇到性能问题。这里分享一些优化技巧7.1 批量处理加速如果你需要处理大量图片逐个处理会很慢。GLM-4v-9b支持批量处理def batch_get_image_embeddings(images, model, processor, batch_size4): 批量获取图片embedding 参数: images: PIL Image对象列表 model: GLM-4v-9b模型 processor: 图片处理器 batch_size: 批处理大小 返回: embeddings: embedding列表 embeddings [] # 分批处理 for i in range(0, len(images), batch_size): batch images[i:ibatch_size] # 准备批处理输入 inputs processor(imagesbatch, return_tensorspt, paddingTrue) inputs {k: v.to(device) for k, v in inputs.items()} # 批量生成embedding with torch.no_grad(): outputs model.get_image_features(**inputs) # 处理每个样本 batch_embeddings outputs.last_hidden_state for j in range(len(batch)): # 对每个样本取平均池化和归一化 emb batch_embeddings[j].mean(dim0, keepdimTrue) emb torch.nn.functional.normalize(emb, p2, dim1) embeddings.append(emb.cpu().numpy()) print(f已处理 {min(ibatch_size, len(images))}/{len(images)} 张图片) return embeddings7.2 使用向量数据库当图片数量很大时比如超过1万张用Python列表存储和搜索会变得很慢。这时应该使用专门的向量数据库# 使用FAISS向量数据库的示例 import faiss class FaissRetrievalSystem: 使用FAISS的检索系统 def __init__(self, model, processor, tokenizer, embedding_dim2048): self.model model self.processor processor self.tokenizer tokenizer self.embedding_dim embedding_dim # 创建FAISS索引 self.index faiss.IndexFlatIP(embedding_dim) # 使用内积余弦相似度 self.image_paths [] self.image_metadata [] def add_image(self, image_path, imageNone, metadataNone): 添加图片到FAISS索引 if image is None: image Image.open(image_path).convert(RGB) # 生成embedding embedding get_image_embedding(image, self.model, self.processor) # 添加到FAISS self.index.add(embedding.astype(float32)) self.image_paths.append(image_path) self.image_metadata.append(metadata or {}) return True def search_by_text(self, query_text, top_k5): 用文字搜索 # 生成文字embedding query_embedding get_text_embedding(query_text, self.model, self.tokenizer) # FAISS搜索 distances, indices self.index.search(query_embedding.astype(float32), top_k) # 构建结果 results [] for i, idx in enumerate(indices[0]): if idx len(self.image_paths): # 确保索引有效 # FAISS返回的是内积需要转换为余弦相似度 # 因为embedding已经归一化内积就是余弦相似度 similarity distances[0][i] results.append(( self.image_paths[idx], similarity, self.image_metadata[idx] )) return resultsFAISS可以处理百万级别的向量搜索速度极快。对于生产环境推荐使用FAISS、Milvus、Qdrant等专业的向量数据库。7.3 缓存机制生成embedding是比较耗时的操作特别是对于大图片。我们可以实现缓存机制import hashlib import json from pathlib import Path class CachedRetrievalSystem(ImageRetrievalSystem): 带缓存的检索系统 def __init__(self, model, processor, tokenizer, cache_dir.embedding_cache): super().__init__(model, processor, tokenizer) self.cache_dir Path(cache_dir) self.cache_dir.mkdir(exist_okTrue) def _get_cache_key(self, image_path): 生成缓存键文件路径修改时间 path Path(image_path) if not path.exists(): return None # 使用文件路径和修改时间作为缓存键 mtime path.stat().st_mtime key_str f{path.absolute()}:{mtime} return hashlib.md5(key_str.encode()).hexdigest() def add_image(self, image_path, imageNone, metadataNone): 带缓存的添加图片 # 检查缓存 cache_key self._get_cache_key(image_path) cache_file self.cache_dir / f{cache_key}.pkl if cache_file.exists(): # 从缓存加载 with open(cache_file, rb) as f: cached_data pickle.load(f) embedding cached_data[embedding] print(f从缓存加载: {image_path}) else: # 生成新的embedding if image is None: image Image.open(image_path).convert(RGB) embedding get_image_embedding(image, self.model, self.processor) # 保存到缓存 with open(cache_file, wb) as f: pickle.dump({embedding: embedding}, f) print(f生成并缓存: {image_path}) # 添加到系统 self.image_paths.append(image_path) self.image_embeddings.append(embedding) self.image_metadata.append(metadata or {}) return True8. 常见问题与解决方案在实际使用中你可能会遇到一些问题。这里总结了一些常见问题和解决方法8.1 显存不足问题问题处理大图片或批量处理时显存不足。解决方案使用量化模型GLM-4v-9b支持INT4量化显存占用从18GB降到9GB减小批处理大小将batch_size从4降到2或1降低图片分辨率虽然GLM-4v-9b支持1120×1120但对于简单图片可以适当降低使用CPU卸载对于非常大的模型可以使用accelerate库的CPU卸载功能# 使用INT4量化 from transformers import BitsAndBytesConfig quantization_config BitsAndBytesConfig( load_in_4bitTrue, bnb_4bit_compute_dtypetorch.float16, bnb_4bit_use_double_quantTrue, bnb_4bit_quant_typenf4 ) model AutoModel.from_pretrained( model_name, quantization_configquantization_config, # 添加量化配置 device_mapauto, trust_remote_codeTrue )8.2 相似度分数不理想问题某些图片和文字的相似度分数偏低即使看起来相关。解决方案检查embedding归一化确保所有embedding都进行了L2归一化调整相似度阈值根据实际数据调整匹配阈值使用更详细的文字描述提供更丰富、更准确的描述微调模型在自己的数据上微调模型需要训练数据8.3 处理速度慢问题生成embedding或搜索速度慢。解决方案使用vLLM加速GLM-4v-9b支持vLLM可以大幅提升推理速度预计算embedding对于静态图片库提前计算好所有embedding使用向量数据库对于大规模搜索使用FAISS等专用数据库异步处理对于实时性要求不高的场景使用异步队列处理8.4 中文支持问题问题中文描述搜索效果不如英文。解决方案使用中英混合描述GLM-4v-9b对中英文都支持良好可以混合使用确保tokenizer正确加载使用trust_remote_codeTrue参数检查文本编码确保中文文本正确编码没有乱码9. 总结通过本教程你应该已经掌握了GLM-4v-9b在图文联合embedding生成和跨模态检索方面的核心用法。让我们回顾一下关键要点9.1 核心收获理解了embedding的本质embedding是内容的数学指纹语义相似的内容在embedding空间里距离相近。掌握了GLM-4v-9b的核心能力这个模型不仅能理解图片和文字还能让它们在同一个语义空间里对齐这是跨模态检索的基础。学会了实际应用从生成embedding、计算相似度到构建完整的检索系统你现在可以自己实现一个跨模态搜索应用了。了解了优化技巧批量处理、向量数据库、缓存机制等技巧能帮你处理大规模数据。9.2 应用场景扩展GLM-4v-9b的图文联合embedding技术可以应用在很多场景电商平台商品图片搜索、相似商品推荐内容管理媒体库智能分类、自动打标签教育领域图文教材检索、习题匹配医疗影像病例图片与诊断报告关联安防监控嫌疑人图片与描述匹配9.3 下一步建议如果你想进一步深入尝试不同任务除了检索GLM-4v-9b还支持视觉问答、图像描述等任务优化性能在自己的数据集上微调模型提升特定领域的准确率构建完整应用将检索系统集成到Web应用或移动App中探索多模态RAG结合检索增强生成构建更智能的多模态问答系统9.4 最后的话多模态AI正在改变我们处理和理解信息的方式。GLM-4v-9b作为一个开源的高性能多模态模型让开发者能够以较低的成本构建强大的跨模态应用。记住技术本身不是目的解决实际问题才是。希望本教程能帮你快速上手将GLM-4v-9b的能力应用到你的项目中。如果在实践中遇到问题多查阅官方文档多在社区交流技术之路就是不断尝试和解决问题的过程。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。