简介:多模态大语言模型(MLLM)近两年迅速成为人工智能领域的热点方向。这份PPT重点回应大语言模型无法处理视觉等多模态输入的局限,系统梳理了MLLM的演进脉络,适合AI研究者、算法工程师以及对多模态技术感兴趣的进阶学习者。资源为单个PPTX文件,大小仅7.13MB,内容涵盖背景介绍、经典架构拆解(视觉编码器、连接器与大语言模型)、训练方法(模态对齐与指令微调)、评测标准(常规任务Benchmark与专门Benchmark)及未来展望。其中特别介绍了基于CLIP预训练ViT的视觉编码器、连接器使用MLP或Q-Former的投影方式,以及通过提高输入分辨率增强模型能力的两类思路。通过学习可快速建立MLLM核心知识框架,掌握GPT-4V、Gemini-Pro等代表性模型的实现思路与开源探索。目前已有502人学习,是一份兼顾广度与深度的多模态大语言模型入门与进阶资料。
1. 多模态大语言模型领域进展分享:从“能看会听”到“能操作”
当你拿到“多模态大语言模型领域进展分享.pptx”这个标题,真正要做的不是过一遍模型列表,而是把过去一年里这个领域最重要的变化讲清楚。多模态大语言模型(MLLM)已经不只是“看图写话”,而是把图像、视频、音频、界面截图统一压缩成token序列,再交给自回归语言模型做推理和决策。对一线工程师来说,感知层怎么接、微调时动哪一层、评估时如何辨别“看见”和“猜到”、部署时怎么压token成本,这些问题比记住几个SOTA名字更重要。这篇分享会按照架构、微调、评估、部署四条线往下走,帮助准备做技术选型或复现实验的团队少走弯路。
2. 多模态大语言模型架构演进的三个关键点:连接器、视觉编码器与统一分词
2.1 连接器:从线性投影、Q-Former到动态分辨率
多模态大语言模型的骨架通常由一个视觉编码器、一个连接器和一个基座语言模型组成。连接器是视觉信号进入语言模型的咽喉,决定了视觉信息以什么样的密度和格式被读入。最早一批工作(LLaVA-1.0)采用线性层把视觉特征映射到词向量维度,训练简单,但很快发现线性映射对空间位置和细节的保留不足;LLaVA-1.5改用两层MLP后,同等数据下的指标上涨接近3个点,这之后MLP连接器成为开源项目默认配置。
Q-Former路线走的是“压缩读出”:用一组固定数量的query通过交叉注意力从视觉特征中抽取信息,再把query输出映射到语言模型。它的优点是视觉token数量固定且少,对推理显存友好;缺点是训练难度更高,query不知道该“看”哪里的问题经常出现。到2024年,动态分辨率成为新的主线,LLaVA-NeXT和Qwen2-VL都把输入图像分成若干高分辨率块,每块独立编码后再与全局图一起进入连接器。这个改动对文档、表格、小目标检测是质变,视觉token数量也从固定576变成几百到上千,模型需要重新学习“局部块之间的位置关系”。
选型的时候我一般会先画一个表格,把不同连接器的代价摆出来。下面的对比可以作为内部技术评审的底稿:
| 连接器方案 | 代表模型 | 视觉token数量 | 适合场景 |
|---|---|---|---|
| 线性投影 | LLaVA-1.0 | 固定256 | 简单图像分类、caption |
| 两层MLP | LLaVA-1.5 | 固定576 | 通用图文问答 |
| Q-Former / Perceiver | BLIP-2、Flamingo | 固定32/64 | 低显存场景,跨模态检索 |
| 像素分块动态分辨率 | LLaVA-NeXT、Qwen2-VL | 随分辨率增加 | 文档解析、OCR、遥感图像 |
连接器越复杂,往往对训练数据的规模和分布越敏感。固定token数量的方案虽然损失细节,但在数据量较小的业务里更容易训稳定;动态分辨率方案则适合你手里已经积攒了高分辨率业务图片,并且能接受推理耗时上升。
2.2 视觉编码器要不要解冻:CLIP、SigLIP与高分辨率适配
视觉编码器决定模型“能看到什么”。CLIP类模型用图文对比学习对齐文本和视觉,但224×224的输入分辨率让它在OCR和位置关系上偏弱。SigLIP使用sigmoid对比损失,不再受batch内负样本数量限制,在同样的CLIP主干下能拿到更好的细粒度特征。如果你是在复现多模态融合论文,我一般建议先跑一遍CLIP-L/14作为基线,因为它和所有常见连接器、微调框架都兼容,排查问题的资料最多。
高分辨率是视觉编码器要迈的一道坎。常用的做法有三种:对原图做多尺度切块后分别过编码器;在CLIP主干后插入小型高分辨率Adapter;或者直接把编码器输入分辨率提升并配合位置编码插值。三者都会增加计算量,但收益并不线性。实际调参时,优先把分辨率的提升放在模型的中间层而不是第一层,因为视觉主干的前几层负责边缘纹理,对分辨率不敏感,最后一两层的语义特征才需要看到更多像素。
视觉编码器能不能解冻,是微调中最容易被问错的问题。我观察到大量失败案例是因为同时解冻了视觉塔和连接器,导致模型的语言能力被视觉梯度和文本梯度交替拉扯。底线做法是:第一冻结视觉塔,只训练连接器和LLM的LoRA;第二如果OCR能力还是不够,再解冻视觉塔最后2层或高分辨率Adapter,学习率设为LLM的十分之一。这个规则在LLaVA、Qwen2-VL和InternVL上都适用,也便于在实验记录里横向对照。
2.3 统一分词:图像、视频、音频都变成token序列
统一分词是“多模态大语言模型”和早期“视觉问答模型”的本质区别。图像经过视觉编码器和连接器变成视觉token,文本经过分词器变成文本token,它们被拼进同一个序列,共享同一套因果注意力和输出层。这样模型在生成下一个token时,能同时看到前文文字和图片局部信息,跨模态推理变成了一种“序列预测”。
视频和音频入场后,token序列变得更长。视频按时间轴抽帧,每帧产生一组视觉token,再叠加帧索引的时间位置编码;音频则先做log-mel谱图或预训练编码器输出,再把特征压成token。目前工程上更稳的视频方案仍然是“抽帧+图像塔”,音频能做到“听声识别环境、理解语气”的模型也越来越多,但很多还是套用图像塔。多模态时序数据融合方法里,真正被验证有效的不是把每一帧全部塞进去,而是先由外部模块检测关键帧,再让大模型处理压缩后的关键帧序列。
统一分词给位置编码扩展提出了新要求。原生长度为4K的LLM,突然输入2000个视觉token和一段长文本,位置编码必须往外推。常见做法有线性插值、NTK-Aware缩放、YaRN。我简单对比过,NTK-Aware在短序列上损失最小,但是对超长文本的扩展不够;YaRN能扩展到更长场景,但需要微调适应。多模态场景推荐先做NTK-Aware,然后单独在长视觉序列数据上做少量LoRA纠正,不要让统一分词直接暴露原始RoPE的位置盲区。
2.4 用Transformers在本地跑通最小推理代码
下面的代码用Qwen2-VL系列模型做一次本地推理,验证视觉编码器、连接器和语言模型整体链路是否正常。这也是做模型选型的第一步。
import torch from PIL import Image from transformers import Qwen2VLForConditionalGeneration, Qwen2VLProcessor model_id = "Qwen/Qwen2-VL-7B-Instruct" processor = Qwen2VLProcessor.from_pretrained(model_id) model = Qwen2VLForConditionalGeneration.from_pretrained( model_id, torch_dtype=torch.bfloat16, device_map="auto" ) image = Image.open("./table.png").convert("RGB") messages = [ {"role": "user", "content": [ {"type": "image"}, {"type": "text", "text": "请说出这张表格第二行第三列的数字"}, ]} ] prompt = processor.apply_chat_template( messages, tokenize=False, add_generation_prompt=True ) inputs = processor( text=prompt, images=[image], return_tensors="pt" ).to(model.device) with torch.no_grad(): output = model.generate( **inputs, max_new_tokens=512, do_sample=False, temperature=0.0, top_p=None, ) answer = processor.decode( output[0][inputs.input_ids.shape[1]:], skip_special_tokens=True ) print(answer)代码说明:processor.apply_chat_template会把多模态消息转成包含特殊token的文本,<image>占位符随后被processor替换成图像特征对应的输入。max_new_tokens=512给长表格答案留出余地,但如果问题简单可以降到128,降低生成时间。do_sample=False和temperature=0.0是文档抽取类任务的推荐配置,因为采样会给OCR结果带来随机性。
注意这里的输入图片不要包含EXIF旋转信息,很多图像库会忽略旋转参数导致图像内容实际是倒着的。如果显存不够,把torch_dtype=torch.bfloat16换成4bit加载需要额外传入load_in_4bit=True并安装bitandbytes。同时要留意transformers版本,Qwen2-VL这类新模型要求较新的依赖,如果遇到Positional Embedding相关报错,先升级transformers再排查,不要直接去改模型代码。
3. 多模态大语言模型的微调与对齐:数据、LoRA与偏好优化
3.1 微调数据怎么攒:多模态指令数据、数据集下载与负样本
微调数据是决定模型“听不听话”的第一因素。目前能直接下载的开源多模态指令数据,主要来自HuggingFace和ModelScope,常见的有LLaVA生成的LLaVA-Instruct-150K/665K,以及面向图表推理的WikiTableQuestions转换集。这些数据规模大约从几万到几十万条,条目格式通常是“图片路径+多轮对话”。如果是做中文业务,只靠英文数据不够,通常还要加入表格OCR、票据、网页截图等合成数据一起混合。
多模态数据集下载回来不能直接进训练。第一步做图片去重,很多在线数据集里同一张图片会出现几十次,重复的视觉样本会让LoRA过拟合到特定画面。第二步统一分辨率:连接器通常期望图片长边是某个像素级倍数,太低或太高的图片都会触发不同分支的预处理,造成视觉token分布不一致。第三步检查文本质量,看图回答里大量“无法回答”“图片模糊”这类答案会让模型学会偷懒拒答。
负样本经常被忽略。要让模型知道“图片里没有的东西不要说有”,数据集里必须出现这样的样本:图片中没有cat,但问题问“图片里是否有cat”,答案是no。你可以从正样本中随机换图构造,但要注意新的图片里不能刚好包含同类别物体。负样本比例建议控制在总样本的10%到20%,太高会让模型过度保守。
| 数据集类型 | 常用来源 | 建议用途 | 注意事项 |
|---|---|---|---|
| 通用指令数据 | LLaVA-Instruct | 通用图文问答 | 以英文为主,需补充中文 |
| 文档OCR数据 | 开源合成数据集 | 表格、票据、截图 | 分辨率要贴近业务 |
| 偏好数据对 | 自构建 | DPO训练 | 需人工或强模型标注 |
| 负样本数据 | 自构建 | 幻觉治理 | 控制比例在10%-20% |
3.2 多模态微调的最小微调单位:连接器、LoRA与视觉塔的取舍
“多模态微调最小微调单位”是最近经常出现在论文和复盘里的提法。它不是指一个rank,而是指一组必须同步更新的模块。以LLaVA类模型为例,最少需要更新的组合是“连接器+语言模型的LoRA”。如果只更新连接器,模型能感知图像,但语义表达能力仍停留在基座模型水平,复杂指令容易答不到点上;如果只更新LoRA,连接器产出的特征和词向量空间不匹配,loss下降快但验证指标一直上不去。
LoRA rank的选择要看视觉token数量和任务复杂度。图像token少、分类任务,rank在16到32之间就够;文档解析、小目标检测这类需要高频视觉细节的任务,rank可以到64,lora_alpha设为rank的2倍左右。学习率方面,全参微调常用2e-5,LoRA微调可以从1e-4起步,但要注意和连接器的学习率解耦。我一般用AdamW和余弦学习率,连接器的学习率即使和LoRA相同,也会给它更早的warm-up,让视觉特征先稳定下来再更新LoRA。
Q-LoRA适合显存受限的本地开发机。它把基座模型量化到4bit,在量化后的残差上插LoRA,7B模型微调的峰值显存大约是12GB到16GB。要注意的是,量化后的模型在反向传播中会引入额外噪声,因此learning_rate要比正常LoRA降低20%到30%,否则loss容易震荡。另一种常见误用是同时加载FlashAttention和4bit量化,部分依赖组合会导致注意力掩码错位,表现为图片没输入但模型能答出问题。
3.3 多模态偏好优化:从RLHF到DPO
微调阶段只能让模型学会形式,不能从两个答案里选更优的那个。传统RLHF需要多卡同时跑策略模型、参考模型和奖励模型,普通团队很难稳定。DPO则通过构建偏好对,直接计算策略模型在chosen和rejected上的log概率差作为损失,省掉了奖励模型和强化学习环境,这让多模态偏好优化变成一个普通二分类训练。
构造多模态偏好对时,输入包含图片、问题和两个回答。两个回答必须是同一目标分布下的候选,最常见的是先让当前模型用不同temperature采样多次,再用强模型打分排序。得分时要注意不能只看文本流畅度,还要看图是否被真正引用。更好的做法是把强模型的打分拆成“图文一致性”和“指令遵循”两个维度分别加权。
DPO的β参数默认0.1,但多模态任务建议从0.05开始试。β越小,模型对偏好对越不敏感,适合数据量少、噪声多的场景;β越大,模型被偏好对锁定得越紧,容易产生“套话式回答”。训练中除了DPO loss,通常还会保留10%到20%的纯SFT数据,防止偏好数据把模型带偏。
3.4 用PEFT + LoRA微调一个多模态模型的最小脚本
下面以LLaVA-HF模型为例,给出一个可放到单卡上跑的LoRA微调骨架。代码里保留数据预处理、LoRA配置和Trainer三部分,labels的mask处理按常见做法实现。
import torch from PIL import Image from datasets import load_dataset from transformers import ( LlavaForConditionalGeneration, LlavaProcessor, TrainingArguments, Trainer ) from peft import LoraConfig, get_peft_model model_id = "llava-hf/llava-1.5-7b-hf" processor = LlavaProcessor.from_pretrained(model_id) model = LlavaForConditionalGeneration.from_pretrained( model_id, torch_dtype=torch.bfloat16, device_map="auto", ) lora_config = LoraConfig( r=32, lora_alpha=64, target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"], lora_dropout=0.05, bias="none", task_type="CAUSAL_LM", ) model = get_peft_model(model, lora_config) def preprocess(sample): image = Image.open(sample["image"]).convert("RGB") user_text = f"USER: <image>\n{sample['question']}\nASSISTANT:" answer = sample["answer"] + processor.tokenizer.eos_token user_enc = processor(text=user_text, images=image, return_tensors="pt") answer_ids = processor.tokenizer( answer, add_special_tokens=False, return_tensors="pt" )["input_ids"] input_ids = torch.cat([user_enc["input_ids"][0], answer_ids[0]]) labels = torch.cat([ torch.full_like(user_enc["input_ids"][0], -100), answer_ids[0], ]) return { "input_ids": input_ids, "labels": labels, "attention_mask": torch.ones_like(input_ids), "pixel_values": user_enc["pixel_values"][0], } def collate_fn(batch): input_ids = torch.nn.utils.rnn.pad_sequence( [x["input_ids"] for x in batch], batch_first=True, padding_value=processor.tokenizer.pad_token_id, ) labels = torch.nn.utils.rnn.pad_sequence( [x["labels"] for x in batch], batch_first=True, padding_value=-100, ) pixel_values = torch.stack([x["pixel_values"] for x in batch]) attention_mask = (input_ids != processor.tokenizer.pad_token_id).long() return { "input_ids": input_ids, "labels": labels, "attention_mask": attention_mask, "pixel_values": pixel_values, } dataset = load_dataset("json", data_files="train.jsonl")["train"] dataset = dataset.map(preprocess, remove_columns=dataset.column_names) args = TrainingArguments( output_dir="./mm-lora-run", per_device_train_batch_size=1, gradient_accumulation_steps=8, learning_rate=1e-4, lr_scheduler_type="cosine", warmup_ratio=0.03, num_train_epochs=1, bf16=True, logging_steps=20, save_strategy="steps", save_steps=200, ) trainer = Trainer( model=model, args=args, train_dataset=dataset, data_collator=collate_fn, ) trainer.train()代码逻辑:先用processor把“用户问题+图片占位符”编码为输入序列,再把答案编码后拼在序列后面。labels把用户问题对应的token全部置为-100,只让模型学习预测assistant部分;pixel_values只与图片占位符对应,顺序不能错乱。
参数说明:target_modules中包含了LLM的所有attention和MLP投影,适合第一版实验;r=32对7B模型是居中设置,后续可以做rank 16和64的对比。per_device_train_batch_size=1是因为单张图片的视觉token很多,梯度累积8步等效batch size为8,显存压力不大。learning_rate=1e-4是LoRA常见的起点,如果loss震荡就降到5e-5,同时把warmup_ratio提高到0.1。训练数据文件train.jsonl每个样例需要包含image、question、answer三个字段,图片路径建议写绝对路径。
4. 多模态大语言模型的评估与“幻觉”治理:从POPE到视觉锚定
4.1 评估基准选型:MMMU、MathVista、GQA与POPE
多模态大语言模型的评估容易被人忽略。MMMU看起来全面,但很多题目通过文本背景知识就能蒙对,模型并没有真正“看见”图片。MathVista能用手写数字、几何图形这些细节暴露视觉编码器的短板,但它计入分数的题往往依赖OCR解析。GQA侧重场景图中的属性与关系,适合评估空间位置。POPE只问“图片里是否存在某物体”,是评估物体幻觉的轻量探针。
| 基准 | 测评内容 | 常见误用 |
|---|---|---|
| MMMU | 多学科大学知识+视觉理解 | 将平均分当作视觉能力 |
| MathVista | 数学图表、几何、计数 | 忽略OCR下界的干扰 |
| GQA | 场景图属性和关系 | 类别不均衡导致虚高 |
| POPE | 物体存在性幻觉 | 负样本与正样本分布不均 |
实际业务评估,我会把四个基准的结果和内部业务测试集的结果放在一起对比。如果模型在MMMU上分数高,但在内部截图问答上很差,多半是连接器分辨率不够,而不是语言模型能力问题。如果POPE负样本的yes率超过15%,幻觉治理就要提上日程。评估跑完之后,还应该输出一个“按问题类型拆分”的得分,而不是只汇报总分。
4.2 幻觉的来源:文本先验、视觉压缩与解码策略
多模态幻觉本质上是语言模型在做“没有视觉证据的合理猜测”。模型在训练语料里见过大量“键盘附近有鼠标”的搭配,因此当图片里只有键盘时,仍可能在回答中提到鼠标。这种语言先验在复杂视觉场景里会被放大,因为模型发现文本路径更容易预测。降低温度或关闭采样能减少随机性,但并不能降低先验猜测,只是让模型更“自信”地重复高频共现。
视觉压缩是另一个根源。动态分辨率目前已能保留局部细节,但连接器在压缩过程中仍会丢掉颜色、空间比例等低层信息。视频模型中的关键帧抽取机制不适用静态图片,所以一张密集文档里的表格边框、空单元格,经常被当成噪声压掉。POPE检测不到这种细粒度错误,因为它的负样本是“物体类别”,不是“表格结构”。
解码策略也在其中起作用。当生成token的概率分布很平坦时,模型会随temperature升高而随机挑选词汇,容易产生幻觉;分布很尖锐时又容易复制训练样本中的常见表述。理想做法是温度随视觉注意力变化:当视觉注意力集中时温度略高,视觉注意力涣散时温度降到接近0。不过目前这块更多停留在论文里,工程上很少做动态温度,除非你已经在边缘设备上有固定负载。
4.3 用POPE检测幻觉:一个可直接跑的脚本
POPE的核心是构建一组“存在性提问”,统计模型在正负样本上的回答分布。下面的脚本把真实物体和反事实物体分别测试,输出准确率和幻觉率:
from PIL import Image from transformers import pipeline pipe = pipeline( "image-to-text", model="llava-hf/llava-1.5-7b-hf", device=0 ) targets = ["cat", "remote control", "sofa"] negatives = ["elephant", "toothbrush", "oven"] def ask(obj): prompt = f"USER: <image>\nIs there a {obj} in the image? Answer yes or no.\nASSISTANT:" out = pipe(Image.open("test.png"), prompt=prompt, max_new_tokens=8) answer = out[0]["generated_text"][len(prompt):].strip().lower() return answer.startswith("yes") yes_real = sum(ask(t) for t in targets) / len(targets) yes_fake = sum(ask(n) for n in negatives) / len(negatives) print(f"real recall: {yes_real:.2f}, hallucination rate: {yes_fake:.2f}")代码先用pipeline加载LLaVA模型,然后对图片上的三类物体和目标外物体分别提问。max_new_tokens=8只留出“yes”或“no”的空间,过长输出会影响统计。切片[len(prompt):]只取新生成的文本,避免问题文本里的“yes”干扰判断。负样本类别不要与真实物体语义太近,否则模型很可能因为它们常和关键词共现而误答。
参数调整上,真实物体列表要来自图片标注;如果没有标注,可以先让模型描述图片,再用描述中的名词去问。幻觉率超过15%说明模型对“看不见”的物体缺乏拒答能力。该脚本也可以在多张图片上跑后汇总,但每张图片的负样本集合要变化,不要用同一组负样本,否则模型可能记住这组词。
4.4 视觉锚定与解码校正:识别“空想”并抑制
视觉锚定的思想是“生成必须绑定视觉证据”。实现上有一个低成本的近似:在模型生成时拿到各层对视觉token的注意力权重,如果当前生成token对视觉token的平均注意力非常低,就说明模型在依赖语言先验。工程上可以在generate中设置output_attentions=True,但对长序列会消耗额外显存。更实用的做法是在服务端做一个二次校验:把模型输出的实体列表与图片中的物体标签召回结果做交集,交集之外的实体标记为低置信度。
解码校正如果要在生成过程中做,可以用视觉对比解码:同时让模型看原图和模糊图,将模糊图的logit从原图的logit中减去,保留那些确实由原图带来的视觉信号。对OCR和颜色类问题有效,对纯文本常识问题会过修正。所以需要先判断问题类型,只在涉及视觉细节的请求中开启。
最后是数据策略:在微调数据里加入“图片信息不足”的样本,让模型学会回答“图片中没有足够证据”。这类拒答训练数据不需要很多,1000到2000条就能形成稳定的行为模式,但需要保证问题问的对象在常见类别里,避免模型对所有不确定问题都拒答。
5. 多模态大语言模型的部署与Agent化:成本控制关键在token
5.1 用vLLM启动多模态服务:三个必调参数
本地部署大语言模型跑多模态任务,常见选择是vLLM。它把视觉编码器和语言模型统一调度,支持连续批处理,吞吐比transformers原生推理高一截。一个可用的服务启动命令如下:
python -m vllm.entrypoints.openai.api_server \ --model Qwen/Qwen2-VL-7B-Instruct \ --dtype bfloat16 \ --max-model-len 8192 \ --limit-mm-per-prompt 'image=3' \ --enforce-eager--max-model-len 8192要覆盖图片视觉token和回答token的总长,设置过小会导致“图片超出上下文长度”报错;--limit-mm-per-prompt image=3限制单次请求最多带3张图,避免一张请求把整个batch卡死;--enforce-eager关闭CUDA Graph,多模态动态shape下更稳定。服务起来后,用OpenAI客户端传image_url即可。
5.2 多模态Agent的观察、规划与工具调用
多模态Agent把视觉理解变成了“观察-规划-行动”闭环。常用做法是让模型每步输出一个结构化JSON,例如点击坐标、OCR区域或检索query。系统提示里只给动作schema,不给示例代码;示例代码会导致模型输出多余解释。生成时关闭采样并设置max_tokens=256,因为Agent的一步动作往往很短,长输出反而会延迟环境反馈。
在Agent里,图片token会被反复输入多次,多轮操作会产生重复计算。可以将同一张截图的pixel_values缓存起来,不同轮次只更新文本部分,减少视觉塔重复前向。这个缓存在多模态Agent的迭代中能省下大约30%到40%的prefill时间。
5.3 一个具体技巧:用注意力分数裁掉低价值视觉token
视觉token是成本和显存的主要开销。高分辨率图片进来,哪怕不生成内容,prefill阶段也要处理上千个token。我常用的一招是裁剪低注意力视觉token。思路是先让模型完整过一遍图片,只统计各层平均注意力在视觉token上的分布;保留注意力得分最高的前70%位置,把其他visual token从输入序列中剪掉,再做第二轮生成。这个技巧适合网页截图、扫描件这类信息冗余高的场景。
import torch def prune_by_attention(attn_scores, visual_positions, keep_ratio=0.7): # attn_scores: [layers, heads, query_len, key_len] avg = attn_scores.mean(dim=(0, 1)) # 对层和头取平均 visual_attn = avg[:, visual_positions].mean(dim=0) # 视觉token的最终注意力 keep_count = max(1, int(len(visual_positions) * keep_ratio)) keep_idx = visual_attn.topk(keep_count).indices keep_pos = sorted([visual_positions[i] for i in keep_idx]) return keep_pos这个函数返回保留下来的视觉token位置,之后按位置重新构造input_ids和pixel_values,并替换<image>占位符的编码。裁剪比例从30%起调,不要一上来就砍掉一半,否则文字密集区域会先受害。文本部分永远不裁剪,只裁视觉token,避免破坏Agent上下文的连续性。
本文还有配套的精品资源,点击获取