尧图精选

LLaVA(一)LLaVA 论文解:从视觉指令微调到多模态推理的完整链路拆解

🕒 发布时间:2026/10/2 12:11:19 📁 来源:尧图网络
1. 从论文到跑通LLaVA 视觉指令微调到底在解决什么问题LLaVA 全称 Large Language and Vision Assistant出自论文《Visual Instruction Tuning》它做的事情一句话概括把图像编码器输出的视觉特征通过一个线性投影层塞进语言模型的词嵌入空间再用 GPT-4 生成的指令跟随数据做两阶段微调让一个纯文本 LLM 学会看图说话。它适合谁适合想复现多模态对话模型、想搞懂视觉-语言对齐链路、或者想拿公开权重直接跑图像问答的开发者。我试过把它的结构拆开看最核心的其实就三个部件CLIP 预训练的 ViT-L/14 当视觉编码器 g(Xv)Vicuna 当语言模型 fϕ(.)中间一个可训练的线性层 W 做模态对齐。论文的公式写得很干净Xa f(concat([W·Zv, Hq])) f(concat([W·g(Xv), Hq]))。翻译成人话就是图像 Xv 先被编码成视觉特征 Zv再经线性层 W 变成 Hv指令 Xq 经 embedding 层变成 Hq两者拼接后送进语言模型输出回答 Xa。整个链路里真正被训练的只有 W阶段一和 WLLM阶段二视觉编码器全程冻结。论文的三大贡献值得记住一是提出了一套把图像-文本对转成指令跟随数据的 pipeline并开源了 LLaVA-Instruct-158K二是提出了这个极简的多模态框架三是构建了多模态指令跟随评测集。数据分三类Conversation 对话 58K、Detailed description 详细描述 23K、Complex reasoning 复杂推理 77K合计 158K 样本全部由 GPT-4 基于 COCO 的 caption 和 bounding box 生成。训练策略是两阶段阶段一冻结 LLM 和视觉编码器只训线性层 W用过滤后的 CC3MCC595K跑 1 个 epoch学习率 2e-3batch size 128目的是先把视觉特征和词嵌入对齐阶段二只冻结视觉编码器微调 LLM 权重和 W用 LLaVA-Instruct-158K 跑 3 个 epoch学习率 2e-5batch size 32。两阶段都用 Adam 加 cosine 衰减。理解了这个链路后面配置和排障才有依据——很多报错其实都出在投影层维度和 LLM hidden size 对不上或者图像 token 没拼进对话模板这两个点上。2. 复现前的环境准备与 TaoToken 统一 Key 配置在真正加载权重之前先把两件事办了本地推理环境以及一个能对照实验的多模态 API 通道。本地跑 LLaVA 需要 PyTorch、transformers、accelerate以及能放下 7B/13B 权重的显存7B fp16 大约 14GB13B 大约 26GB量化后能压到 8GB 以内。而对照实验这块我建议用 TaoToken 的统一 Key 去调多模态 API好处是同一个 Key 能横向对比不同视觉模型的输出省得为每个模型单独申请账号。TaoToken 的定位是统一的大模型 API 接入层官网在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 端点是 https://taotoken.net/api 。它的价值在于你本地跑 LLaVA 得到一组答案再用统一 Key 调云端多模态模型得到另一组答案两边对照就能判断是模型能力问题还是你的数据/配置问题。这对复现论文特别有用因为论文里的效果对比图本身就是多模型横向比的。拿 Key 的路径很直接进控制台 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 在 API Keys 页面 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 创建一个 Key。创建后先别急着写代码用模型对话页面 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite 手动发一张图试试确认 Key 和额度都正常。如果你后面要长期跑编码类 Agent 做数据构造脚本可以看下 Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。这里要强调一个概念TaoToken 是合规的 API 聚合接入层不是任何形式的网络中转工具你只需要把它当成一个 Key 调多个模型的普通服务来用。环境变量建议这样设避免 Key 硬编码进脚本export TAOTOKEN_API_KEYsk-你的key export TAOTOKEN_BASE_URLhttps://taotoken.net/api本地环境则用 conda 隔离防止和系统里的 torch 版本打架conda create -n llava python3.10 -y conda activate llava pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121 pip install transformers4.37.0 accelerate sentencepiece pillow版本这块踩过的坑是 transformers 太新会导致 LLaVA 的 modeling 文件 import 失败4.37 附近比较稳。显存不够就加--load-8bit或--load-4bit但量化会掉点对照实验时记得两边条件一致。3. 可复制配置视觉编码器与语言模型对齐的关键参数这一节给可直接复制的配置片段。LLaVA 的对齐核心是视觉特征维度 → LLM hidden size的投影配置写错就是维度不匹配报错。下面是一个最小化的模型配置 JSON路径放在configs/llava_7b.json字段和官方实现保持一致{ model_name_or_path: lmsys/vicuna-7b-v1.5, vision_tower: openai/clip-vit-large-patch14, mm_projector_type: linear, mm_vision_select_layer: -2, mm_vision_select_feature: patch, mm_hidden_size: 1024, hidden_size: 4096, tune_mm_mlp_adapter: true, freeze_vision_tower: true, freeze_backbone: true, bf16: true, image_aspect_ratio: pad }关键参数逐个说清楚。vision_tower选clip-vit-large-patch14它的输出 hidden size 是 1024对应mm_hidden_size。hidden_size是 Vicuna-7B 的 4096线性层 W 的 shape 就是[1024, 4096]这两个数必须和实际权重对上否则加载时报 size mismatch。mm_vision_select_layer: -2表示取 ViT 倒数第二层的特征论文里用的就是这一层不是最后一层。mm_projector_type: linear就是那个单线性层换成 mlp 是后续 LLaVA-1.5 的改法复现原论文保持 linear。阶段一和阶段二的训练超参也给你一份 TOML放在scripts/stage_args.toml[stage1] data_path data/cc595k.json epochs 1 learning_rate 2e-3 batch_size 128 tune_mm_mlp_adapter true freeze_backbone true freeze_vision_tower true [stage2] data_path data/llava_instruct_158k.json epochs 3 learning_rate 2e-5 batch_size 32 tune_mm_mlp_adapter true freeze_backbone false freeze_vision_tower true注意阶段二freeze_backbone false也就是解冻 LLM 权重这是和阶段一最大的区别。指令数据模板必须严格按论文格式image占位符要放在 human 的第一轮里system message 和STOP结束符不能省{ id: 000000033471, image: 000000033471.jpg, conversations: [ {from: human, value: What are the colors of the bus in the image?\nimage}, {from: gpt, value: The bus in the image is white and red.} ] }训练时只有 gpt 回复部分参与 loss 计算human 部分和图像 token 都要 mask 掉这点在数据 collator 里实现。如果你用 TaoToken 做对照多模态请求的配置长这样Base URL、Key、Model ID 三件套齐全import os, base64, requests base_url https://taotoken.net/api api_key os.environ[TAOTOKEN_API_KEY] model_id gpt-4o # 按控制台可用模型替换 with open(test.jpg, rb) as f: img_b64 base64.b64encode(f.read()).decode() resp requests.post( f{base_url}/v1/chat/completions, headers{Authorization: fBearer {api_key}}, json{ model: model_id, messages: [{ role: user, content: [ {type: text, text: What are the colors of the bus in the image?}, {type: image_url, image_url: {url: fdata:image/jpeg;base64,{img_b64}}} ] }] } ) print(resp.json()[choices][0][message][content])这套配置跑通后你本地 LLaVA 和云端模型的输入就是同一张图、同一个问题对照才有意义。4. 验证请求用公开权重跑通图像问答并对照结果配置就绪后先做一次最小验证确认权重加载和推理链路没问题。用官方公开的liuhaotian/llava-v1.5-7b权重结构同源便于验证命令行推理python -m llava.serve.cli \ --model-path liuhaotian/llava-v1.5-7b \ --image-file ./test.jpg \ --load-8bit正常输出会先打印加载日志然后进入交互你输入问题它返回答案。如果看到Loading vision tower...后卡住多半是权重在下载耐心等或提前用huggingface-cli download拉好。成功时你会看到类似USER: What are the colors of the bus in the image? ASSISTANT: The bus in the image is white and red.这就是论文里 Conversation 类数据的典型问答。接着做对照实验同一张图、同一个问题分别喂给本地 LLaVA 和 TaoToken 的多模态 API把两边答案并排记录。建议写个小脚本批量跑一组图输出成表格方便比对import json, subprocess questions [ What are the colors of the bus in the image?, Describe this image in detail., What skill set might someone need to perform such a frisbee trick? ] results [] for q in questions: local_ans run_local_llava(test.jpg, q) # 封装你的本地推理 api_ans run_taotoken(test.jpg, q) # 封装上面的 requests 调用 results.append({question: q, local: local_ans, api: api_ans}) with open(compare.json, w) as f: json.dump(results, f, ensure_asciiFalse, indent2)实测下来Detailed description 类问题最能看出差异本地 LLaVA 在细节丰富度上往往不如更大的云端模型但在是否遵循指令格式上因为它是专门用指令数据微调过的反而更稳定。Complex reasoning 类问题两边都容易翻车这时候重点看推理步骤是否连贯而不是只看最终答案对不对。把这三类问题的对照结果整理出来你就能判断自己的复现是否抓住了论文的核心——视觉指令微调带来的指令跟随能力而不是单纯的图像描述能力。验证阶段还有一个必做项确认图像 token 数量。LLaVA 用 ViT-L/14 的 patch 特征一张 336x336 的图会产生 576 个视觉 token24x24 patch这些 token 会占据上下文长度。如果你的对话很长又带图很容易超上下文表现为输出被截断或报长度错误。对照实验时把 max_tokens 设合理别让截断干扰判断。5. 本篇常见报错排查401、local proxy failed、reading choices、OAuth复现和对照过程中报错基本集中在几类逐个给排查路径。第一类调用 TaoToken 时返回 401 Unauthorized。这几乎都是 Key 的问题要么环境变量没生效echo $TAOTOKEN_API_KEY确认非空要么请求头写成了Authorization: sk-xxx少了Bearer前缀要么 Key 被复制时带了空格。正确写法是{Authorization: fBearer {api_key}}。如果确认 Key 没问题还是 401去控制台看下额度是否耗尽。第二类local proxy failed或连接超时。这类报错通常出现在你本地设置了 HTTP_PROXY/HTTPS_PROXY 环境变量导致请求被错误路由。先unset HTTP_PROXY HTTPS_PROXY再重试。注意这里说的是清理本地环境变量不是让你去配置任何网络工具TaoToken 的 API 端点直接访问即可。第三类Error reading choices或KeyError: choices。这说明返回体结构和你预期不符常见原因是请求体里 model 名写错服务端返回了错误对象而不是正常 completion。打印resp.status_code和resp.text看原始返回多半能看到model not found之类的提示。把 model_id 换成控制台里确认可用的名称即可。第四类OAuth 相关报错比如OAuth token expired或invalid_grant。如果你用的是某些 CLI 工具如 Claude Code 类客户端通过 OAuth 登录token 过期就会报这个。解决方式是重新走一遍授权流程或者改用 API Key 方式接入。用 TaoToken 时推荐直接用 API Key避免 OAuth 过期打断实验。如果你在配 Claude Code 这类工具Base URL 填https://taotoken.net/apiKey 填你的 sk-Model ID 填控制台可用模型三件套对齐就不会出 OAuth 问题。第五类本地加载权重报size mismatch for mm_projector。这是配置里mm_hidden_size或hidden_size和实际权重对不上。用torch.load打印权重里mm_projector.weight的 shape反推正确的维度改回 JSON 配置。7B 模型对应 409613B 对应 5120别混用。第六类图像问答输出乱码或重复。多半是image_aspect_ratio设错pad 和 anyres 处理方式不同复现原论文用 pad。另外确认图像预处理用了 CLIP 的 mean/std不是 ImageNet 的。6. 把链路用起来从论文复现到多模态 API 对照的下一步走到这里你已经把 LLaVA 论文的核心链路拆完了视觉编码器 g(Xv) 出特征 Zv线性层 W 投影成 Hv和指令 embedding Hq 拼接送进语言模型两阶段微调分别对齐特征和教会指令跟随。配置、数据模板、验证脚本、排障路径都齐了。接下来最有价值的动作是把这个链路变成你自己的实验台换不同的视觉编码器看对齐难度换不同的指令数据配比看哪类数据对推理提升最大或者用 TaoToken 的统一 Key 把云端多模态模型拉进来做基线对照。如果你要长期跑数据构造脚本、批量做对照实验甚至用 Agent 自动生成指令数据Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 会比按次调用更划算。接入细节和参数说明都在文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里遇到模型名或请求格式不确定时先查文档再改代码比盲目试错快得多。最后留一个实用习惯每次对照实验都把图像、问题、本地答案、云端答案、判定记成一行 JSON跑够几十组之后你对 LLaVA 的能力边界会有比读论文更具体的判断。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →