LLaVA系列①——LLaVA的快速学习和简单调用(附详细代码+讲解)


引言:一篇 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)。

  1. 视觉编码器: 主要是 CLIP 的 ViT 模块。
  2. 对齐层: 图像到文本对齐的矩阵 W \mathbf W W
  3. 语言模型: 主要是 LLaMA文本模型。

在这里插入图片描述



● 【训练LLaVA的(微调)指令格式】如下图所示, X s y s t e m − m e s s a g e \mathbf X_{\mathtt{system-message}} Xsystemmessage 是一个系统指令,用于指明 LLaVA 的任务身份。其中, X v \mathbf {X_v} Xv 表示图像, X q \mathbf {X_q} Xq 表示问题, X a \mathbf {X_a} Xa 表示答案。

在这里插入图片描述
在这里插入图片描述



● 【额外补充LLaVA 的训练过程并不是只有 “指令微调” 这一个阶段,它其实进行了 两阶段的学习

  1. 阶段1——特征对齐: 采用 “图像-文本对” 生成的有关的对话数据(过滤后的CC数据集,总共595K,无上下文场景) 来训练 W \mathbf W W
  2. 阶段2——指令微调: 用(ChatGPT)生成指令微调数据(有上下文场景) 和 ScienceQA数据集(第一个大规模的多模态科学问题数据集)来训练 W \mathbf W W 和 语言模型。

2 LLaVA 的模型流程图

● 在了解 LLaVA 模型的流程图之前,我先介绍一下 大语言模型(LLMs) 的流程图,如下图所示。LLM(大语言模型) 主要分成两个部分:

  1. 分词器部分: 对输入的文本进行分词操作,得到 input_ids
  2. 语言模型部分: 先对 input_ids 进行 Embdding 操作,再传入语言模型(一般是 transformer layer)中,得到最终的输出(一般输出的是文本或者分类结果,当然也有其他类型)。

在这里插入图片描述


● 了解完 大语言模型(LLMs) 的流程图,我们再看 LLaVA 模型的流程图,如下图所示。MLLM(多模态大语言模型) 主要分成四个部分:

  1. 预处理: 对输入的文本进行分词操作,得到 input_ids;同时对输出的图像进行 裁剪、归一化 等操作(其中尺寸一般1标准化到336x336),得到 pixel_values(即图像的像素值数据,其中的 3 指的是图像的三通道)。
  2. 视觉编码:pixel_values 进行编码得到 图像的表示特征image_hidden_states(现在的这个特征的维度img_d_model 与 文本特征的维度text_d_model 是不一致),再通过 “投影层(我称之为 对齐层)” 得到最终的 待与文本表示特征融合的 图像表示特征projected hidden_states(这时的图像特征维度 与 文本特征维度 就是一致的了)。
  3. 文本编码:input_ids 进行 文本Embdding 操作,得到 文本的表示特征text hidden_states
  4. 融合+语言模型: 将 “图像表示特征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 教研室工位 💻

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一支王同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值