引言:一篇 30
分钟快速学习和使用 LLaVA 的教程 ⭐️
LLaVA系列的下一篇文章链接:LLaVA系列②——从底层构建LLaVA并测试运行(附详细代码+讲解)
✅ NLP 研 2 选手的学习笔记
笔者简介:Wang Linyong,NPU,2023级,计算机技术
研究方向:文本生成、大语言模型
参考视频:https://www.bilibili.com/video/BV1nw4m1S7nZ
论文链接:https://arxiv.org/abs/2304.08485
1 LLaVA 的简介和原理
● 【模型名】LLaVA
,全称 Large Language and Vision Assistant,简单翻译一下是:“会处理语言和视觉的大助手” (← 个人的发癫翻译…)。原论文
● 【时代背景】2023年,指令微调在NLP领域已经取得比较大的进展(最典型的就是GPT)。但是在 “图像文本”的多模态任务 中,指令微调 还没有得到应用,同时也缺乏这样的 “指令微调的数据”。
● 【具体动机】在 BLIP-2(在2023年一个很厉害的多模态大语言模型) 的 limitation(限制)中提到,像它(BLIP系列)这样的模型,在 VQA(视觉问答任务)场景下,它是缺乏上下文学习能力的。因为它的训练数据其实就是一个一个的 “图像-文本对”,也就是说,一个图像对应一个文本,不存在上下文学习的场景(换句话说,b图像只和b文本有关,b图像不和 上一个图像a和文本a 有关,同时也不和 上一个图像c和文本c 有关)。
所以,LLaVA
的作者就想构造一个上下文学习的场景,以供 多模态大语言模型 来学习。
● 【动机的实现方法】那怎么构造这样的 “上下文学习的场景” 呢?正好,当时的 ChatGPT 出来了,作者就把 “图像的描述(Caption)+ 标注框(Bounding Box,也就是边界框)” 交给 ChatGPT,来生成一系列,从简单到复杂的问答对,如下图所示。需注意一点的是,作者并没有把图像也交给 ChatGPT,下图中放出来只是为了便于理解和展示。
● 【LLaVA模型介绍】LLaVA
主要由三部分构成,也就是下图中的:视觉编码器(Vision Encoder)、对齐层(Projection,我喜欢叫它对齐层,虽然直翻是“投影层”)、语言模型(Language Model)。
- 视觉编码器: 主要是 CLIP 的 ViT 模块。
- 对齐层: 图像到文本对齐的矩阵 W \mathbf W W。
- 语言模型: 主要是 LLaMA文本模型。
● 【训练LLaVA的(微调)指令格式】如下图所示,
X
s
y
s
t
e
m
−
m
e
s
s
a
g
e
\mathbf X_{\mathtt{system-message}}
Xsystem−message 是一个系统指令,用于指明 LLaVA
的任务身份。其中,
X
v
\mathbf {X_v}
Xv 表示图像,
X
q
\mathbf {X_q}
Xq 表示问题,
X
a
\mathbf {X_a}
Xa 表示答案。
● 【额外补充】LLaVA
的训练过程并不是只有 “指令微调” 这一个阶段,它其实进行了 两阶段的学习:
- 阶段1——特征对齐: 采用 “图像-文本对” 生成的有关的对话数据(过滤后的CC数据集,总共
595K
,无上下文场景) 来训练 W \mathbf W W。 - 阶段2——指令微调: 用(ChatGPT)生成指令微调数据(有上下文场景) 和 ScienceQA数据集(第一个大规模的多模态科学问题数据集)来训练 W \mathbf W W 和 语言模型。
2 LLaVA 的模型流程图
● 在了解 LLaVA
模型的流程图之前,我先介绍一下 大语言模型(LLMs) 的流程图,如下图所示。LLM(大语言模型) 主要分成两个部分:
- 分词器部分: 对输入的文本进行分词操作,得到
input_ids
。 - 语言模型部分: 先对
input_ids
进行 Embdding 操作,再传入语言模型(一般是 transformer layer)中,得到最终的输出(一般输出的是文本或者分类结果,当然也有其他类型)。
● 了解完 大语言模型(LLMs) 的流程图,我们再看 LLaVA
模型的流程图,如下图所示。MLLM(多模态大语言模型) 主要分成四个部分:
- 预处理: 对输入的文本进行分词操作,得到
input_ids
;同时对输出的图像进行 裁剪、归一化 等操作(其中尺寸一般1标准化到336x336),得到pixel_values
(即图像的像素值数据,其中的3
指的是图像的三通道)。 - 视觉编码: 对
pixel_values
进行编码得到 图像的表示特征image_hidden_states
(现在的这个特征的维度img_d_model 与 文本特征的维度text_d_model 是不一致),再通过 “投影层(我称之为 对齐层)” 得到最终的 待与文本表示特征融合的 图像表示特征projected hidden_states
(这时的图像特征维度 与 文本特征维度 就是一致的了)。 - 文本编码: 对
input_ids
进行 文本Embdding 操作,得到 文本的表示特征text hidden_states
。 - 融合+语言模型: 将 “图像表示特征
projected hidden_states
” 和 “文本的表示特征text hidden_states
” 进行跨模态融合(一般是通过交叉注意力融合),得到 融合表示特征hidden_states
,再将其传入语言模型(一般是 transformer layer),得到最终的输出(一般输出的是文本或者分类结果,当然也有其他类型)。
3 LLaVA 的简单调用
● 项目目录: 其中,下载在 model_bank
目录下的 models--llava-hf--llava-1.5-7b-hf
源自于 liuhaotian/llava-v1.5-7b。备注: 如果模型下载的很慢,可以参考博客 “《LLaVA系列②——从底层构建LlaVA并测试运行(附详细代码+讲解)》” 的 “2 下载 LLaVA 的视觉层和语言层的模型参数” 小节内容,使用下述的代码来下载:
export HF_ENDPOINT=https://hf-mirror.com
huggingface-cli download --resume-download liuhaotian/llava-v1.5-7b --local-dir ./model_bank/llava-v1.5-7b --local-dir-use-symlinks False
● simple_LlaVA
的完整代码:
from PIL import Image
import torch
from transformers import AutoProcessor, LlavaForConditionalGeneration, CLIPProcessor
# 原始模型叫做 "liuhaotian/llava-v1.5-7b", 我已经把它下载到本地目录 model_bank 中
model_name_or_path = "model_bank/llava-1.5-7b-hf"
device = "cuda:0" # 定义使用的设备(GPU)
model_cache = "model_bank" # 定义模型缓存路径
print("开始加载模型...")
# 从预训练路径加载模型,指定设备、缓存路径、数据类型等
model = LlavaForConditionalGeneration.from_pretrained(
pretrained_model_name_or_path=model_name_or_path,
device_map=device,
cache_dir=model_cache,
torch_dtype=torch.bfloat16,
local_files_only=True
)
print("模型加载完毕")
# 加载处理器(AutoProcessor),用于处理输入数据
processor = AutoProcessor.from_pretrained(pretrained_model_name_or_path=model_name_or_path, local_files_only=True)
print("processor 加载完毕")
# 定义输入的提示文本,包含用户和助手的对话格式
prompt = "USER: <image>\n What's the content of the image? ASSISTANT:"
# 定义图像文件的路径
url = "./image_data/王冰冰.png" # .image_data/test_image_1.jpg
# 打开图像文件
image = Image.open(fp=url)
# 使用处理器处理文本和图像,生成模型输入
inputs = processor(text=prompt, images=image, return_tensors="pt")
# 将输入数据移动到GPU上
for temp_key in inputs.keys():
inputs[temp_key] = inputs[temp_key].to("cuda:0")
# 使用模型生成输出,限制生成的最大token数为50
generate_ids = model.generate(**inputs, max_new_tokens=50)
# 将生成的token解码为文本,跳过特殊token并清理空格
response = processor.batch_decode(
generate_ids, skip_special_tokens=True, clean_up_tokenization_spaces=False
)[0]
# 打印模型的响应
print("\n\nresponse:\n", response)
● 输入图片:
● 输出结果: 还是蛮准确的!😄 😄
The image features a woman wearing a red jacket and holding a microphone. She appears to be a newscaster or reporter, possibly interviewing someone or presenting news.
翻译:照片中的女子身穿红色夹克,手持麦克风。她似乎是一名新闻播音员或记者,可能在采访某人或播报新闻。
4 参考资料
[1] 《多模态大模型LLaVA模型讲解——transformers源码解读》,感谢B站Up主:良睦路程序员
[2] 《靠谱女士的组会分享[多模态大模型之clip,blip,blip–2,llava]》,感谢B站Up主:拖学家
[3] 《【LLM多模态】LLava模型架构和训练过程 | CLIP模型》,感谢CSDN博主:山顶夕景
5 补充说明
● 若有写得不对的地方,或有疑问,欢迎评论交流。
● LLaVA系列的下一篇文章链接:LLaVA系列②——从底层构建LlaVA并测试运行(附详细代码+讲解)
⭐️ ⭐️ 写于2025年3月21日 22:55 教研室工位 💻