Series · NLP · Chapter 11

自然语言处理(十一):多模态大模型

多模态大模型深度解析:CLIP的对比学习、BLIP-2的Q-Former桥接架构、LLaVA的视觉指令微调、Whisper语音识别、GPT-4V能力图谱以及MMBench/MME/MMMU评测体系——配可运行代码。

人不会一次只用一种感官理解世界。我们一边看图表一边读文字,一边听语调一边读表情,一边看截图一边讨论代码 bug。纯文本语言模型对这一切是又聋又瞎的。多模态大语言模型(Multimodal LLM, MLLM) 试图弥补这个鸿沟:把图像、音频、视频对齐到与语言模型相同的表示空间里。

过去四年的故事可以用三步讲清楚。CLIP(2021)证明,从网上抓 4 亿对带噪图文就足够学到一个统一向量空间,让图像和文本通过余弦相似度互相检索。BLIP-2(2023)让对齐变得便宜:冻结一个强力视觉编码器,冻结一个强力 LLM,只训练它们之间约 1.88 亿参数的"Q-Former"。LLaVA / GPT-4V(2023–2024)把桥接换成一行线性投影,再加上视觉指令微调——把图像 embedding 当作词 embedding 喂给 LLM,再用 GPT-4 生成的视觉对话数据微调。结果是单个模型既能读图表、调试截图,又能像聊天助手一样描述照片。

本文按这条主线把多模态 NLP 讲透:对比对齐背后的数学、可扩展的架构、音频侧的故事(Whisper)、GPT-4V 能做什么不能做什么,以及 MMBench / MME / MMMU 等评测如何正确解读——全部配可运行代码。

你将学到什么

  • CLIP:InfoNCE 对比损失、双编码器、零样本分类、温度 $\tau$ 的作用
  • BLIP-2:Q-Former 为什么有效、32 个可学习查询、两阶段预训练
  • LLaVA:视觉指令微调、为什么 4M 参数线性投影也能打、GPT-4 蒸馏指令数据的妙用
  • Whisper:log-mel 频谱输入、多语言编码器-解码器、特殊 token 控制、模型尺寸权衡
  • GPT-4V 与前沿 MLLM:能力矩阵、跨域推理、典型失败模式(计数、细粒度定位、幻觉)
  • 评测:MMBench、MME、MMMU、SEED-Bench、POPE 各测什么,以及如何诚实比较
  • 实战:CLIP 多模态检索索引、图像描述、VQA、Whisper 转写流水线

前置知识

  • Transformer 架构(第 4 部分
  • 预训练与指令微调(第 5 部分第 8 部分
  • 熟悉 PyTorch 和 Hugging Face 生态;了解 ViT/patch 概念有帮助但非必需

视觉-语言模型

CLIP:对比视觉-语言对齐

CLIP 双编码器架构与批内相似度矩阵

CLIP(Radford 等人,OpenAI 2021)同时训练两个编码器。图像编码器 $f_I$(ViT-L/14 或 ResNet)把图片 $x$ 映射成向量 $I = f_I(x) \in \mathbb{R}^{512}$。文本编码器 $f_T$(12 层 Transformer)把文本 $t$ 映射成同维度向量 $T = f_T(t)$。两者都做 L2 归一化,于是 $I \cdot T$ 就是余弦相似度。训练目标:让匹配对的相似度尽量高,其他都尽量低。

具体地,对一个 batch 中的 $N$ 对图文,定义相似度矩阵 $s_{ij} = (I_i \cdot T_j)/\tau$,CLIP 最小化对称的 InfoNCE 损失:

$$\mathcal{L} = -\frac{1}{2N}\sum_{i=1}^{N}\Bigg[\log\frac{e^{s_{ii}}}{\sum_{j} e^{s_{ij}}} + \log\frac{e^{s_{ii}}}{\sum_{j} e^{s_{ji}}}\Bigg].$$

两项分别是图→文与文→图的分类损失:以批内 $N$ 个候选做 softmax,对角线就是正确答案。温度 $\tau \approx 0.07$ 是可学习标量,初始化为 $\log(1/\tau) = \log 100$ 并裁剪以防崩溃。batch 越大负样本池越难:CLIP 训练 4 亿图文对时使用了跨上百块 GPU 的 32 768 batch。

由这一目标天然导出三个性质:

  1. 零样本分类:把每个候选标签写成 "a photo of a {label}",挑余弦相似度最高的那个即可,不需任何标注。
  2. 跨模态检索:图→文与文→图检索都退化为点积索引(FAISS / ScaNN)。
  3. 生成模型条件信号:DALL·E 2、Stable Diffusion 的文本编码器、各种 image-to-image 系统都用 CLIP embedding 做监督。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
from transformers import CLIPProcessor, CLIPModel
from PIL import Image
import torch

model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32").eval()
processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32")

image = Image.open("cat.jpg")
labels = ["a photo of a cat", "a photo of a dog",
          "a photo of a car", "a photo of a bird"]

with torch.no_grad():
    inputs = processor(text=labels, images=image,
                       return_tensors="pt", padding=True)
    out = model(**inputs)

# logits_per_image 已经按 1/τ 缩放
probs = out.logits_per_image.softmax(dim=-1)[0]
for label, p in zip(labels, probs):
    print(f"{p.item():>6.2%}  {label}")

干净的猫照片通常会在 "a photo of a cat" 上输出 >95%——而模型从未见过任何 "cat" 监督样本。

CLIP 的失败模式。CLIP 类似词袋:它会把 “a red cube on a blue sphere”“a blue cube on a red sphere” 混淆。它不擅长计数(两个 vs. 三个���、细粒度类别(约克夏 vs. 诺里奇梗),也不擅长读图中文字(需要带 OCR 的模型)。中文场景请用 Chinese-CLIP 或通义千问 VL——原始 WIT 数据集压倒性以英文为主。

BLIP-2:连接冻结的视觉与语言

CLIP 只对齐不生成。BLIP-2(Li 等人,ICML 2023)解决生成问题,同时把预训练成本压到极低。图像编码器(ViT-g/14, 10 亿参数)和 LLM(OPT-2.7B/6.7B 或 FlanT5)都冻结,只训练桥接模块——Q-Former

BLIP-2 架构:Q-Former 连接冻结 ViT 与冻结 LLM

Q-Former 是一个 12 层 Transformer,关键设计有两点:

  • 32 个可学习的 query embedding 作为输入——这与图像 token 没有任何关系,就是 32 个可训练向量。
  • 每层都有交叉注意力层,让 query 去关注冻结 ViT 输出的 patch 特征。query 因此从冻结的视觉特征中主动抽取与语言相关的信息。

经过 12 层后,32 个 query 变成 32 个"软视觉 token",再经线性投影对齐到 LLM 的 embedding 维度,以前缀形式拼接到文本。LLM 看它们就像普通词 embedding。

BLIP-2 分两阶段训练:

  1. 视觉-语言表示学习。Q-Former 只对接冻结的 ViT,用三个损失联合训练:图文对比(ITC,类似 CLIP)、图文匹配(ITM,硬负样本上的二分类)、图像条件文本生成(ITG,captioning 损失)。这一阶段教会 query “看懂"视觉内容。
  2. 视觉到语言生成。Q-Former 输出接到冻结 LLM,用标准语言模型损失在图像条件文本上训练。这一阶段教会 query “说 LLM 的语言”。

总可训练参数约 1.88 亿——OPT-6.7B 的约 1.4%。然而 BLIP-2 在 VQA-v2 与图像描述上的零样本表现可与端到端训练的更大模型相当。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
from transformers import Blip2Processor, Blip2ForConditionalGeneration
from PIL import Image
import torch

processor = Blip2Processor.from_pretrained("Salesforce/blip2-opt-2.7b")
model = Blip2ForConditionalGeneration.from_pretrained(
    "Salesforce/blip2-opt-2.7b",
    torch_dtype=torch.float16, device_map="auto",
)

image = Image.open("street.jpg")
prompt = "Question: What is happening in this photo? Answer:"
inputs = processor(image, text=prompt, return_tensors="pt").to("cuda", torch.float16)

with torch.no_grad():
    ids = model.generate(**inputs, max_new_tokens=60)
print(processor.batch_decode(ids, skip_special_tokens=True)[0].strip())

LLaVA:视觉指令微调

BLIP-2 巧妙地预训练了一个桥。LLaVA(Liu 等人,NeurIPS 2023)做了一件更激进的事:把桥换成单层线性投影(或 2 层 MLP),把精力投入到视觉指令跟随数据上。

LLaVA:视觉编码器 → 投影器 → LLM,及连接器对比

流程出乎意料地简单。取一个冻结的 CLIP ViT-L/14,得到 256 个 patch 特征 $Z_v \in \mathbb{R}^{256 \times 1024}$,过一个可学习的投影器 $W$ 映射到 LLM 的 4096 维 embedding 空间:

$$H_v = W \cdot Z_v.$$

把 $H_v$ 与用户文本 token embedding 拼接喂给 Vicuna,自回归预测助手回答。训练分两阶段:

  1. 特征对齐。只训练 $W$,数据是 558K 图文对(LAION/CC/SBU)。LLM 完全不变——只让投影器学会把 CLIP 特征映射到 LLM 能读懂的空间。
  2. 端到端视觉指令微调。解冻 LLM,与 $W$ 联合训练,数据是 158K 由 GPT-4 从 COCO 描述与边界框生成的图像-指令对。GPT-4 被引导生成三类对话:详细描述、对话问答、复杂推理。

单层线性投影最多 ~17M 参数,但在 MMBench 上 LLaVA-1.5(MLP-2 连接器)拿到 80.0——在自家擅长的基准上击败了更重的 Q-Former 设计。经验:对指令跟随 MLLM 来说,数据质量与端到端微调比连接器设计更重要

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# LLaVA-1.5 推理(Transformers 版)
from transformers import LlavaForConditionalGeneration, AutoProcessor
from PIL import Image
import torch

model_id = "llava-hf/llava-1.5-7b-hf"
processor = AutoProcessor.from_pretrained(model_id)
model = LlavaForConditionalGeneration.from_pretrained(
    model_id, torch_dtype=torch.float16, device_map="auto",
)

image = Image.open("ui_screenshot.png")
prompt = ("USER: <image>\n这个 UI 是干什么的?是否存在 bug?请具体说明。"
          "ASSISTANT:")

inputs = processor(text=prompt, images=image,
                   return_tensors="pt").to("cuda", torch.float16)
with torch.no_grad():
    out = model.generate(**inputs, max_new_tokens=200, do_sample=False)
print(processor.batch_decode(out, skip_special_tokens=True)[0])

视觉-语言任务:VQA、描述、检索

VQA、图像描述与跨模态检索三种任务流水线

三类经典任务支配着评测和大部分实际应用:

  • 视觉问答(VQA)。给图像和问题,输出答案。数据集:VQA-v2、OK-VQA(开放知识)、GQA(组合)、TextVQA(需要 OCR)。现代指令微调 MLLM 用单模型即可处理。
  • 图像描述(Captioning)。生成自然语言描述。数据集:COCO Captions、NoCaps、Flickr30K。num_beams=3length_penalty>1.0 通常在细节与流畅性间取得不错平衡。
  • 跨模态检索。给文本查询返回最相关图像(反之亦然)。退化为对归一化 CLIP embedding 做最近邻——生产系统用 FAISS-IVF 或 ScaNN,可扩展到十亿级。

图像描述

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from transformers import BlipProcessor, BlipForConditionalGeneration
from PIL import Image
import torch

class ImageCaptioner:
    def __init__(self, model_id="Salesforce/blip-image-captioning-large"):
        self.proc = BlipProcessor.from_pretrained(model_id)
        self.model = BlipForConditionalGeneration.from_pretrained(model_id).eval()

    @torch.no_grad()
    def caption(self, image_path, prefix=None,
                max_length=50, num_beams=3):
        img = Image.open(image_path).convert("RGB")
        # `prefix` 启用条件描述,例如 "A photo of"
        inputs = self.proc(img, text=prefix, return_tensors="pt") \
            if prefix else self.proc(img, return_tensors="pt")
        out = self.model.generate(
            **inputs, max_length=max_length,
            num_beams=num_beams, length_penalty=1.0, early_stopping=True,
        )
        return self.proc.decode(out[0], skip_special_tokens=True)

cap = ImageCaptioner()
print(cap.caption("dog.jpg"))                        # 无条件
print(cap.caption("dog.jpg", prefix="A photo of"))   # 条件

视觉问答

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
from transformers import BlipProcessor, BlipForQuestionAnswering
from PIL import Image
import torch

class VQA:
    def __init__(self, model_id="Salesforce/blip-vqa-base"):
        self.proc = BlipProcessor.from_pretrained(model_id)
        self.model = BlipForQuestionAnswering.from_pretrained(model_id).eval()

    @torch.no_grad()
    def answer(self, image_path, question, max_length=30):
        img = Image.open(image_path).convert("RGB")
        inputs = self.proc(img, question, return_tensors="pt")
        out = self.model.generate(**inputs, max_length=max_length)
        return self.proc.decode(out[0], skip_special_tokens=True)

vqa = VQA()
print(vqa.answer("market.jpg", "How many apples are on the table?"))
print(vqa.answer("market.jpg", "What color is the basket?"))

生产级 CLIP 检索

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import numpy as np
import torch
from PIL import Image
from transformers import CLIPProcessor, CLIPModel

class CLIPRetrieval:
    """简易 CLIP 图像索引。>1M 条目请改用 FAISS-IVF。"""

    def __init__(self, model_id="openai/clip-vit-base-patch32",
                 device=None):
        self.device = device or ("cuda" if torch.cuda.is_available() else "cpu")
        self.model = CLIPModel.from_pretrained(model_id).to(self.device).eval()
        self.proc = CLIPProcessor.from_pretrained(model_id)
        self.embeds = []   # list of np.ndarray, shape (512,)
        self.paths = []

    @torch.no_grad()
    def add_images(self, paths, batch_size=32):
        for i in range(0, len(paths), batch_size):
            batch_paths = paths[i:i + batch_size]
            imgs = [Image.open(p).convert("RGB") for p in batch_paths]
            inputs = self.proc(images=imgs, return_tensors="pt").to(self.device)
            emb = self.model.get_image_features(**inputs)
            emb = emb / emb.norm(dim=-1, keepdim=True)
            self.embeds.extend(emb.cpu().numpy())
            self.paths.extend(batch_paths)

    @torch.no_grad()
    def search_text(self, query, top_k=5):
        inputs = self.proc(text=[query], return_tensors="pt",
                           padding=True).to(self.device)
        q = self.model.get_text_features(**inputs)
        q = q / q.norm(dim=-1, keepdim=True)
        sims = np.vstack(self.embeds) @ q.cpu().numpy().T
        sims = sims.flatten()
        idx = np.argpartition(-sims, top_k)[:top_k]
        idx = idx[np.argsort(-sims[idx])]
        return [(self.paths[i], float(sims[i])) for i in idx]

1M 图像建议改用 FAISS 的 IVF-PQ 索引:512-d float32 raw 大约 2 GB,PQ 压缩后可降到 <100 MB。


音频:Whisper

语音是第二大非文本模态,Whisper(Radford 等人,ICML 2023)是开源主力。它就是普通的 encoder-decoder Transformer,在 68 万小时互联网弱监督多语言音频上训练,单模型同时承担转写、翻译、语种识别和语音活动检测。

Whisper 输入 log-mel 频谱与编码器-解码器

流水线:

  1. 音频重采样为 16 kHz 单声道,切成 30 秒 chunk。短的补静音,长的滑窗。
  2. chunk 转为 80 通道 log-mel 频谱,窗长 25 ms、跳长 10 ms——每 30 秒 chunk 得到 3000 帧。
  3. 两层 stride-2 Conv1D + GELU 降采样得到 1500 个音频 token,送入编码器(不同尺寸 12–32 层自注意力)。
  4. 解码器自回归,前缀加上控制 token:<|startoftranscript|> <|en|> <|transcribe|> <|notimestamps|>。把 <|transcribe|> 换成 <|translate|>,同一模型就能输出外语音频的英文翻译。

模型尺寸:tiny (39M), base (74M), small (244M), medium (769M), large (1.55B)。多数生产场景用 smallmedium GPU 推理可达 0.1–0.3× 实时延迟,常见英文 WER < 10%。重口音、code-switching 或强噪声场景再上 large-v3

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import whisper

class WhisperASR:
    def __init__(self, size="base", device="cuda"):
        self.model = whisper.load_model(size, device=device)

    def transcribe(self, audio_path, language=None, task="transcribe"):
        # task 可选 {"transcribe", "translate"}
        return self.model.transcribe(
            audio_path,
            language=language,           # None 表示自动识别
            task=task,
            word_timestamps=True,        # 输出 per-word 时间戳
            condition_on_previous_text=False,  # 长音频更稳
            no_speech_threshold=0.6,
            verbose=False,
        )

asr = WhisperASR(size="small")
result = asr.transcribe("meeting.mp3")
print(f"识别语种: {result['language']}")
for seg in result["segments"]:
    print(f"[{seg['start']:6.2f} - {seg['end']:6.2f}] {seg['text'].strip()}")

实战要点。长录音务必关闭 condition_on_previous_text——否则前段一旦幻觉会级联放大。要批吞吐请用 faster-whisper (CTranslate2 后端,4–8× 加速)。说话人分割(who-spoke-when)配 pyannote.audio


GPT-4V 与前沿 MLLM 全景

GPT-4V 能力矩阵与示例交互

GPT-4V(及其后续 GPT-4o、GPT-4.1)把图像视为与文本平起平坐的输入模态,开箱即用地处理:

  • OCR 与文档理解:读发票、表格、表单、截图。
  • 图表:抽数值、总结趋势、回答比较问题。
  • 示意图与流程图:解释 UML、系统图、手绘白板。
  • 代码与 UI:从代码截图调试、根据渲染页面建议 CSS 修改。
  • 视觉推理:多图比较、照片中考题分步解答、场景安全推理。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from openai import OpenAI
import base64, mimetypes

client = OpenAI()

def analyse_image(path: str, prompt: str, model: str = "gpt-4o") -> str:
    mime, _ = mimetypes.guess_type(path)
    with open(path, "rb") as f:
        b64 = base64.b64encode(f.read()).decode()
    resp = client.chat.completions.create(
        model=model,
        messages=[{
            "role": "user",
            "content": [
                {"type": "text", "text": prompt},
                {"type": "image_url",
                 "image_url": {"url": f"data:{mime};base64,{b64}",
                               "detail": "high"}},
            ],
        }],
        max_tokens=500,
        temperature=0.2,
    )
    return resp.choices[0].message.content

print(analyse_image("dashboard.png",
                    "总结关键指标,指出任何异常。"))

detail 参数("low" | "high" | "auto")权衡成本与分辨率——"high" 会把图像切成 512px 子块,使用约 10× 视觉 token。

值得了解的开源替代:LLaVA-1.6(基线好、易微调)、通义千问 VL-Max(中文与 OCR 强)、InternVL(grounding 强)、CogVLM(感知强)。可本地部署,控制成本或满足隐私合规。

MLLM 仍然普遍薄弱(跨厂商一致):

  • 计数:超过 5 个对象就不稳。
  • 细粒度空间推理:「哪支笔离杯子更近?」
  • 低分辨率密集文字:截图里的小字号。
  • 物体幻觉:诱导性 prompt 会让模型描述不存在的对象(“描述这只狗”——实则没有狗)。

视频理解

视频是 4D 张量(时、高、宽、通道),逐帧处理 token 数量爆炸。2024 年主流套路:

  1. 均匀采样帧:在剪辑中均匀取 $N \in \{8, 16, 32\}$ 帧。
  2. 每帧视觉编码:冻结 ViT 编码,可选搭配时间池化适配器(VideoLLaMA、Video-LLaVA),先在投影前混合相邻帧。
  3. 拼接:帧 token(或池化后的帧 token)拼到 LLM 上下文,通常加时序位置编码让模型知道顺序。

对于很长视频(几小时),用粗到细策略:先用 captioner 总结小窗口,再用 LLM 在文字摘要上检索定位需要细看的几秒。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import cv2
import numpy as np

def sample_frames(video_path, num_frames=8):
    """均匀采样为 PIL.Image 列表。"""
    from PIL import Image
    cap = cv2.VideoCapture(video_path)
    total = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
    if total <= 0:
        cap.release()
        raise RuntimeError(f"无法读取 {video_path}")
    indices = np.linspace(0, total - 1, num_frames).astype(int)
    frames = []
    for idx in indices:
        cap.set(cv2.CAP_PROP_POS_FRAMES, int(idx))
        ok, frame = cap.read()
        if not ok:
            continue
        frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        frames.append(Image.fromarray(frame))
    cap.release()
    return frames

像 Gemini 1.5 Pro 这样的模型支持原生视频输入,靠长上下文注意力流式处理小时级视频。


MLLM 评测:MMBench、MME、MMMU、SEED、POPE

多模态基准对比与能力雷达图

基准选择比一般人想的更重要。每个基准强调不同侧面,单一榜单数字会误导。

基准测什么形式备注
MMBench20 个能力维度:感知、推理、OCR、细粒度识别4 选 1 选择题“循环评测"轮换选项顺序,破除字母偏置。
MME14 个子任务:感知 (10) + 认知 (4)是/否问题总分为各项准确率之和(最高 2800)。“全说 yes” 容易作弊。
MMMU30 个学科大学级别题(医学、工程、艺术)开放式 + 选择题2024 最难——前沿模型仍未到 60%。
SEED-Bench空间关系、实例计数、场景理解、时序选择题包含视频子集(SEED-Bench v2)。
POPE物体幻觉探测物体存在与否真实与虚构物体配对,暴露幻觉。
TextVQA / DocVQAOCR 重的阅读理解开放式验证视觉塔能否真"读”。
RefCOCO短语 grounding(返回 bbox)区域预测空间精度诊断。

合理的评测报告至少包含:一个感知基准(MME、SEED)、一个推理基准(MMMU)、一个幻觉基准(POPE),以及一个针对实际部署场景的领域内测试集。


多模态应用工程经验

一些上线后才学到的硬经验:

  • 预处理要狠。把图像缩放到模型原生分辨率(CLIP: 224,LLaVA-1.5: 336,GPT-4V high-detail: 768)。更大输入并不会更好,只是浪费视觉 token。
  • 缓存图像 embedding。CLIP 编码远比每个查询都重新编码同图便宜——存 512 维向量复用。
  • 量化 LLM 那一半。LLaVA-1.5 的语言模型部分做 4-bit AWQ/GPTQ,VRAM 从 14 GB 降到约 5 GB,MMBench 损失 < 1 分。
  • prompt 要扎根。措辞极重要。“What color is the car?” 没问题;“What color is the car if there is one?” 在无车图上能将幻觉降低约 40%。永远允许模型显式说"不知道”
  • 当心 OCR 漂移。许多 MLLM 会先 OCR 图中文字再"信任"它——照片角落的对抗性文字能劫持回答。安全敏感部署务必清洗输入。

常见问题

CLIP / BLIP / LLaVA 怎么选?

CLIP 用于检索、零样本分类,以及任何只要固定 embedding 的系统。BLIP-2 适合在冻结 LLM、紧算力下做 VQA / captioning。LLaVA 风格指令微调 MLLM 用于对话式、跟随指令的多模态交互。

BLIP-2 为什么训练参数那么少?

昂贵的部分——10 亿参数 ViT 与 70 亿参数 LLM——都冻结。仅 1.88 亿参数的 Q-Former 在两者之间桥接。两阶段课程让桥保持专注:第一阶段在视觉中扎根,第二阶段对齐到 LLM 的词表。

微调需要多少数据?

预训练 MLLM(LLaVA、Qwen-VL)的领域适配,通常 5K–50K 高质量图像-指令对就够,配 LLM 的 LoRA 与投影器全训。从零预训练则需 10⁸–10⁹ 对。

如何把 VRAM 压到 24 GB 以内?

三件事配合:LLM 的 4-bit 量化(bitsandbytes 或 AWQ)、注意力投影上的 LoRA、梯度检查点。7B LLaVA 在单张 RTX 4090 上微调毫无压力。

怎样诚实地评测?

挑一个对齐你用例的基准(如文档场景选 DocVQA)、一个幻觉基准(POPE)、一个独立的内部测试集。至少报告三个基准——单一榜单数字会掩盖 trade-off。

CLIP 能用在中文上吗?

原始 OpenAI CLIP 95% 是英文。中文用 Chinese-CLIP(OFA-Sys)、通义千问 VLWukong-CLIP。它们 API 兼容,可即插即用。


系列导航

部分主题链接
10RAG 与知识增强系统<– 上一篇
11多模态大模型(本文)
12前沿技术与实战应用下一篇 –>

Liked this piece?

Follow on GitHub for the next one — usually one a week.

GitHub