从CLIP到LLaVA:多模态视觉大模型核心概念与代码实战
很多新手刚开始接触多模态视觉大模型时最头疼的往往不是模型本身有多复杂而是概念之间的关系太混乱。CLIP、LLaVA、BLIP、Qwen2.5-VL 这些名字频繁出现在各种文章里看起来都跟“看图说话”有关但彼此到底是什么关系很少有人能用一句话讲清楚。尤其是 CLIP 和 LLaVA一个被归为“视觉语言预训练模型”一个被归为“视觉语言大模型”听起来很像实际上走的是两条完全不同的路线。CLIP 解决的是“图像和文本能不能对齐”的问题LLaVA 解决的是“模型能不能看着图像把话说好”的问题。前者是基础能力后者是在这个能力之上长出来的对话能力。如果带着这个判断去学整条学习路径就会清晰很多。这篇文章会从 CLIP 出发先把“图文对齐”这条主线拆清楚再进入 LLaVA看它如何把 CLIP 的视觉编码能力和大语言模型的推理能力组合在一起。整个过程中不追求复杂的数学推导而是先把底层架构的骨架讲明白再给出可以实际运行的代码。读完你可以回答三个问题为什么 CLIP 能做图片标签分类和图文检索LLaVA 为什么能根据图片生成自然语言自己动手跑一个多模态视觉大模型需要准备什么、会遇到哪些坑。1. 这篇文章真正要解决的问题先聊一个现象。很多初学者看到“多模态视觉大模型”这个词第一反应是找教程然后收藏了一堆资料结果越看越乱。为什么会乱因为多模态这个方向包含的东西太多有做图文匹配的有做视觉问答的有做图像描述的有做视频理解的还有既能看图又能生成图片的。如果一开始就扎进细节很容易迷失。这篇文章想解决三个具体困惑。第一个困惑是概念混淆。CLIP 和 LLaVA 是最容易混淆的两个模型因为它们都处理图像和文本。很多人跑完 CLIP 的 demo又去跑 LLaVA 的 demo感觉差不多却说不清差异在哪。实际上CLIP 是一个“对齐模型”输出的是相似度分数LLaVA 是一个“生成模型”输出的是自然语言回答。二者解决的问题不同底层架构也不同只是 LLaVA 借用了 CLIP 的视觉编码器所以看起来有血缘关系。第二个困惑是“只会调用不理解原理”。Hugging Face 生态让模型调用变得非常容易几行代码就能跑起来。但代码一旦跑通很多人就停下来了遇到效果不好、显存不够、结果不符合预期时完全不知道从哪个方向排查。这就是因为缺少架构层面的理解。第三个困惑是缺少落地路径。多模态模型不是玩具在真实项目里可以用于图片标签分类、以文搜图、图库去重、视觉问答、内容审核辅助等场景。但怎么从 demo 走到项目有哪些坑需要什么资源这些信息往往散落在各处。这篇文章只聚焦一条主线CLIP 到 LLaVA。其他模型先不提避免干扰。代码基于 Hugging Face Transformers 生态同时会补充一些底层原理和工程建议。2. 多模态核心概念先把 CLIP 和 LLaVA 的位置搞清楚2.1 什么是多模态多模态Multi-Modal指的是模型可以同时处理多种类型的数据。视觉加文本是最常见的组合也是本文讨论的范畴。图像是一种模态文本是另一种模态多模态视觉大模型要解决的核心问题是这两种在结构上完全不同的数据如何在模型内部被统一理解。图像在计算机里是一堆像素值文本是一串离散的词元token二者天然不在一个语义空间。要让模型“看图说话”第一步是让图像和文本的特征能够对应起来这就是对齐第二步才是让模型基于对齐后的信息去做推理和生成。2.2 CLIP 是什么一次“图文对齐”的学习CLIP 的全称是 Contrastive Language-Image Pre-training也就是对比语言-图像预训练由 OpenAI 提出。它的核心思想是用对比学习的方式把图像和文本映射到同一个向量空间。训练过程可以通俗理解为模型拿到一批图像和一批文本描述需要判断哪句描述与哪张图匹配。比如图库里有“一只猫”“一条狗”“一辆车”三段描述模型看到一张猫的图片要尽量给“一只猫”更高的匹配分给另外两段描述更低的分。通过这种大量、自动构造的弱监督信号CLIP 学到了一组视觉编码器和一组文本编码器二者输出的向量在同一个空间里可以直接计算相似度。可以把它类比成一本“双语词典”只不过词典的两个侧面一个是图片一个是文本。训练完成后给定一张新图片CLIP 可以算出它与任意一段文字描述的匹配程度反过来给定一段文字也能在图库中找到最匹配的图片。CLIP 最革命性的地方在于零样本能力。传统图像分类必须先收集标注数据、训练分类头CLIP 不需要你只需要把候选类别写成文字描述比如“a photo of a cat”“a photo of a dog”模型就能直接把图片归类。这也是如今很多图片标签分类系统把 CLIP 当底座的原因。2.3 LLaVA 是什么在 CLIP 之上学会对话LLaVA 的全称是 Large Language and Vision Assistant即大型语言与视觉助手。它做的事情比 CLIP 更进一步不仅要知道图片里有猫还要能用自然语言回答“这只猫是什么颜色的”“它在做什么”“这张图传递了什么样的情绪”这类开放式问题。LLaVA 的架构由三部分组成视觉编码器通常使用 CLIP 的 ViT 模型负责把图像转换为视觉特征。投影层负责把视觉特征的维度映射到语言模型的词嵌入空间。大语言模型负责接收文本指令和图像特征生成回答。可以这样理解CLIP 是“眼睛”大语言模型是“大脑”投影层是连接眼睛和大脑的“神经通路”。LLaVA 的贡献在于它不是简单把这三个模型拼起来而是设计了一个两阶段训练方案让组合后的系统真正学会看图和对话。两阶段训练思路如下第一阶段是对齐预训练。冻结视觉编码器和语言模型只训练投影层用大量图像-文本对让视觉特征和文本特征对齐。这个阶段的目标是让“眼睛看到的东西”能被“大脑理解”。第二阶段是指令微调。冻结视觉编码器联合训练投影层和语言模型使用视觉指令数据训练模型。这个阶段的目标是让模型学会遵循用户指令根据图片内容回答开放式问题。LLaVA-1.5 里常见的组合是用 CLIP ViT-L/14 作为视觉编码器Vicuna 作为语言模型。这种设计思路已经成为很多多模态模型的参考范式。2.4 CLIP 与 LLaVA 的关系比较维度CLIPLLaVA核心任务图文对齐视觉语言对话输出形式图文匹配分数自然语言文本主要组件图像编码器 文本编码器图像编码器 投影层 LLM典型应用图片标签分类、图文检索、零样本分类视觉问答、图像描述、多轮对话训练方式对比学习两阶段训练对齐预训练 指令微调能否生成文本不能能两者不是替代关系而是递进关系。LLaVA 继承了 CLIP 的视觉编码能力这是它可以“看见”的基础但 LLaVA 并不需要 CLIP 的文本编码器因为真正负责语言理解的是它内部的大语言模型。所以你可以这样记CLIP 解决“看得懂”里的“匹配”LLaVA 解决“看得懂”之后的“说出来”。3. 环境准备与硬件要求3.1 硬件与操作系统要求多模态模型对算力有一定要求。CLIP-base 级别的模型在 GPU 上推理很快在 CPU 上也可以运行只是稍微慢一些LLaVA 这种 7B 级别的大模型建议至少准备 14GB 以上显存float16 精度下32GB 以上会更从容。如果显卡不够可以把模型放在 CPU 上跑体验一下推理流程但不要对速度抱太高期望。操作系统方面Windows 和 Ubuntu 都可以跑通下面的代码。如果长期做深度学习项目Ubuntu 24.04 这类 Linux 发行版会更顺手环境干净不容易出现路径和依赖的奇怪问题。3.2 Python 环境与依赖库推荐使用 Python 3.9 或 3.10。核心依赖包括 PyTorch、Transformers、Accelerate、Pillow 等。建议用 conda 或 venv 创建独立环境避免污染系统 Python。conda create -n multimodal python3.10 conda activate multimodal pip install torch transformers accelerate pillow也可以把依赖写入 requirements.txt 方便复现torch2.0 transformers4.40 accelerate pillow版本方面不要强求最新以实际安装时能正常运行为准。Transformers 库更新很快不同版本对 LLaVA 的支持情况有差异如果代码报错提示找不到某个类优先检查是不是 Transformers 版本太旧。4. CLIP 代码实战图文匹配、图片标签分类与特征提取4.1 加载 CLIP 模型下面代码使用 Hugging Face Transformers 加载 CLIP模型名是 openai/clip-vit-base-patch32。from transformers import CLIPProcessor, CLIPModel model CLIPModel.from_pretrained(openai/clip-vit-base-patch32) processor CLIPProcessor.from_pretrained(openai/clip-vit-base-patch32)第一次执行时Transformers 会自动从 Hugging Face Hub 下载模型权重。如果网络环境受限导致下载失败可以先把模型下载到本地再把 from_pretrained 里的参数换成本地路径。4.2 图文匹配测试准备一张图片和几段候选文本让 CLIP 判断图片与哪段文本最匹配。from PIL import Image import torch image Image.open(example.jpg) texts [a photo of a cat, a photo of a dog, a photo of a car] inputs processor(texttexts, imagesimage, return_tensorspt, paddingTrue) with torch.no_grad(): outputs model(**inputs) logits_per_image outputs.logits_per_image probs logits_per_image.softmax(dim-1) print(probs)输出结果是一个形状为[1, 3]的概率张量三个值分别对应三段候选文本的匹配概率总和为 1。概率最高的一项就是模型认为与图片最匹配的描述。这段代码的关键是理解 logits。logits 是模型输出的原始相似度分数它可以是任意实数不便于直接比较所以用 softmax 归一化成概率。实际项目里如果你只需要找最匹配的文本直接取 logits 的 argmax 即可不需要算 softmax。4.3 用 CLIP 做图片标签分类零样本CLIP 在图片标签分类场景中非常实用。不需要收集标注数据、不需要训练分类器只要提供候选标签即可。labels [cat, dog, car, tree] prompts [fa photo of a {label} for label in labels] inputs processor(textprompts, imagesimage, return_tensorspt, paddingTrue) with torch.no_grad(): outputs model(**inputs) logits outputs.logits_per_image probs logits.softmax(dim-1) pred_label labels[probs.argmax().item()] print(pred_label)这里真正容易踩坑的地方是 prompt 写法。同一个标签写成 “cat” 和写成 “a photo of a cat, which is a common household pet” 得到的分类效果可能差别很大。CLIP 训练时见过大量类似 “a photo of a {label}” 的自然语言描述所以这种模板通常效果稳定。项目里如果标签是中文可以先用翻译工具转成英文或者选择支持中文的 CLIP 变体。4.4 提取视觉和文本特征很多实际场景需要把图片和文本转成向量然后离线做检索。比如构建一个以文搜图的图片库先把所有图片的特征算好存入向量数据库线上查询时只计算文本特征然后做相似度检索。这种方式比每次实时计算要高效得多。import torch texts [a photo of a cat, a photo of a dog] inputs processor(texttexts, imagesimage, return_tensorspt, paddingTrue) with torch.no_grad(): image_features model.get_image_features(pixel_valuesinputs[pixel_values]) text_features model.get_text_features( input_idsinputs[input_ids], attention_maskinputs[attention_mask] ) # 归一化 image_features image_features / image_features.norm(dim-1, keepdimTrue) text_features text_features / text_features.norm(dim-1, keepdimTrue) similarity_matrix image_features text_features.T print(similarity_matrix)注意 CLIP 的特征向量在使用前需要归一化否则直接计算点积时向量模长会影响相似度分数导致结果偏向模长更大的特征。归一化之后点积等价于余弦相似度范围在 -1 到 1 之间更方便设置阈值。5. LLaVA 代码实战视觉对话推理5.1 加载 LLaVA 模型LLaVA 的模型权重比 CLIP 大得多以 llava-hf/llava-1.5-7b-hf 为例权重文件有数 GB加载时要量力而行。from transformers import AutoProcessor, LlavaForConditionalGeneration import torch from PIL import Image model LlavaForConditionalGeneration.from_pretrained( llava-hf/llava-1.5-7b-hf, torch_dtypetorch.float16, device_mapauto ) processor AutoProcessor.from_pretrained(llava-hf/llava-1.5-7b-hf)torch_dtypetorch.float16 表示用半精度加载显存占用大约是 float32 的一半。device_mapauto 让 Transformers 自动把模型分配到可用设备上如果有多张显卡也会自动分流。如果显存还是不够可以考虑 CPU 推理但速度会明显变慢。5.2 做一次视觉问答LLaVA 的输入和普通语言模型不同需要同时传入图片和 prompt并且在 prompt 中预留image占位符。image Image.open(example.jpg) prompt USER: image\nDescribe this image in detail.\nASSISTANT: inputs processor(textprompt, imagesimage, return_tensorspt) output model.generate(**inputs, max_new_tokens200, do_sampleFalse) answer processor.decode(output[0], skip_special_tokensTrue) print(answer)这里有两个细节值得注意。第一prompt 模板是固定的。image占位符会在预处理阶段被替换成图像的视觉特征嵌入USER 和 ASSISTANT 是对话角色的分隔标记。如果你去掉image模型根本不会读取图片又怎么能“看图说话”所以一旦发现 LLaVA 输出与图片内容无关首先检查 prompt 模板。第二generate 函数的参数会影响生成质量。max_new_tokens 控制回答的最大长度do_sampleFalse 表示贪心解码每次选概率最高的词适合追求稳定性的场景想要更多样化的回答可以设置 do_sampleTrue 并配合 temperature 参数。5.3 LLaVA 两阶段训练思路虽然直接跑推理很简单但理解训练思路才能真正明白 LLaVA 设计背后的逻辑。第一阶段是对齐预训练。先准备大量图像-文本对比如一张猫的图片配一句 “this is a cat” 的描述。训练时冻结视觉编码器和语言模型只更新投影层。这样做的目的是让视觉编码器输出的特征经过投影后能够和语言模型的输入空间对齐。打个比方眼睛和大脑都正常但中间缺一根连接线第一阶段就是在接这根线。第二阶段是指令微调。准备好视觉指令数据形式类似“图片 用户的提问 期望的回答”。训练时冻结视觉编码器联合更新投影层和语言模型。这样模型不仅能把视觉信息传过去还能学会按照用户的指令进行对话。新手不建议直接尝试训练 LLaVA因为数据构造和显存要求都比较高。更稳妥的路径是先用预训练模型做推理跑熟之后再读一点指令微调的源码理解整体流程。6. 运行结果与效果验证6.1 CLIP 的结果验证CLIP 图文匹配运行后预期输出是一个[1, 3]的概率张量。当你输入一张猫的图片和“cat / dog / car”三段文本时结果如果是概率集中在猫那一项说明模型判断正确。如果概率接近均匀分布比如 0.33、0.33、0.34说明模型对这张图和这几段描述没有明显倾向。这种情况通常有两种可能一是图片内容确实与候选描述关联不大二是候选描述太接近或太模糊模型分不出区别。可以换一张更典型的图片或者修改文本描述再试。6.2 LLaVA 的结果验证LLaVA 运行后输出是一段自然语言。正常情况下回答内容应该围绕图片展开比如描述场景、物体、动作等。如果回答与图片毫无关系优先检查三点prompt 中是否保留了image占位符。图片是否正确加载是否因路径错误而被预处理器当成空图。model 是否成功加载到 GPU如果加载失败可能回退到 CPU 但速度极慢容易让人误以为模型卡住。6.3 失败时第一步看什么多模态模型报错时不要盲目改代码先看日志如果是下载权重时卡住多半是网络问题可以配置镜像源或手动下载。如果是 CUDA out of memory优先减小 batch、换成 float16、或换更小的模型。如果是 Transformers 报错提示找不到类名或参数检查版本是否过旧。7. 常见问题与排查思路问题现象可能原因排查方式解决方案模型下载失败或卡住网络受限无法访问 Hugging Face Hub查看日志是否停在下载阶段配置镜像源或手动下载后放入本地路径CUDA out of memory模型过大、batch 太大、未使用半精度查看显存占用和报错栈顶信息使用 float16、减小 batch、换更小模型LLaVA 回答与图片无关prompt 模板错误、图片未正确加载检查 prompt 是否包含 image 占位符按官方模板正确编写 promptCLIP 分类效果差prompt 模板不佳、标签含义不清晰尝试不同 prompt 前缀和描述使用 “a photo of a {label}” 模板丰富标签描述CPU 推理非常慢模型太大CPU 算力有限观察生成耗时换成 GPU 环境或选择更小的模型Transformers 报错找不到类版本过旧查看报错信息中的模块名升级 transformers 到较新版本输出包含大量重复文本采样参数设置不当检查生成配置调整 repetition_penalty或改用贪心解码8. 多模态项目最佳实践与工程建议8.1 先分清任务类型再选模型很多项目失败不是因为模型不好而是模型选错。做图片标签分类、以文搜图、图库去重这类任务CLIP 是最经济的选择它不需要生成文本速度快、显存占用小。做视觉问答、图像描述、客服场景的“用户发图 模型回答问题”才需要 LLaVA 这类生成式模型。8.2 批量推理的设计思路如果需要给大量图片打标签逐张调用 CLIP 输入文本会浪费算力。更高效的做法是分两步先把所有图片的视觉特征批量提取出来存成向量再把候选标签的文本特征也提前算好。推理阶段只需要计算图片特征与文本特征矩阵的相似度一次就能得到全量图片的标签分布。8.3 部署时注意精度与显存选型大模型部署时fp32、fp16、bf16 这些浮点数格式是绕不开的话题。fp32 精度高但显存占用大fp16 能省一半显存但数值范围较小某些模型可能出现溢出bf16 动态范围更大在不少硬件上稳定性更好。实际部署时可以用 float16 或 bf16 加载模型效果不明显下降且显存占用更低。8.4 安全与合规边界多模态模型生成的内容不一定可靠。LLaVA 这类生成式模型可能会出现“幻觉”也就是模型自信地描述图片里并不存在的东西。用于生产环境时一定要增加内容审核环节。训练数据也需要确保合法授权不要使用来路不明的图片和文本数据。8.5 从 demo 到生产的路径先在小规模测试集上评估模型效果再考虑部署。评估时别只看一两个例子要统计不同类别、不同光照条件下的表现。确定效果可行后再引入量化、批处理、缓存等手段每一步都要做回归测试。多模态系统的稳定性建立在评估之上不是建立在模型本身有多强大之上。9. 总结与后续学习方向回到最初的问题CLIP 和 LLaVA 到底是什么关系CLIP 是让模型拥有“视觉与文本对齐”能力的基础模型LLaVA 是在 CLIP 视觉能力之上通过投影层和大语言模型组合出来的对话助手。一个负责匹配一个负责对话这就是多模态视觉大模型里最核心的一条主线。下一步建议按顺序做三件事先跑通 CLIP 的图文匹配和图片标签分类示例理解对比学习是怎么把两种模态拉到同一个向量空间的再去加载 LLaVA 做一次视觉问答体会生成式模型和匹配式模型的差异最后选一个感兴趣的垂直场景比如图库搜索或图片自动打标把模型真正用起来。如果想继续深入可以从三个方向往下挖对比学习损失函数的数学原理理解 CLIP 为什么能对齐大语言模型的预训练和指令微调机制理解 LLaVA 第二阶段的本质更多多模态模型比如 Qwen2.5-VL、BLIP、MiniGPT-4、InternVL 的实现差异。往工程方向走可以关注模型量化、推理加速、批量 Batch 推理以及 fp16、bf16 这类浮点格式在部署中的选型和坑。多模态视觉大模型并不是一个黑盒它只是把视觉编码、对齐、语言生成这几件事有顺序地组合起来。理解了 CLIP 到 LLaVA 这条主线再看其他多模态模型会发现它们大多是在同一个框架下替换了某个组件。把这个地基打牢后面学什么都快。