从Debug中学习MiniGPT4
一、代码及配置
话不多说,先附上我用的代码:
import osos.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"
os.environ['TRANSFORMERS_OFFLINE'] = '1'import argparse
import os
import randomimport numpy as np
import torch
import torch.backends.cudnn as cudnn
import gradio as grfrom transformers import StoppingCriteriaListfrom minigpt4.common.config import Config
from minigpt4.common.dist_utils import get_rank
from minigpt4.common.registry import registry
from minigpt4.conversation.conversation import Chat, CONV_VISION_Vicuna0, CONV_VISION_LLama2, StoppingCriteriaSub# imports modules for registration
from minigpt4.datasets.builders import *
from minigpt4.models import *
from minigpt4.processors import *
from minigpt4.runners import *
from minigpt4.tasks import *def parse_args():parser = argparse.ArgumentParser(description="Demo")parser.add_argument("--cfg-path", required=True, help="path to configuration file.")parser.add_argument("--gpu-id", type=int, default=0, help="specify the gpu to load the model.")parser.add_argument("--options",nargs="+",help="override some settings in the used config, the key-value pair ""in xxx=yyy format will be merged into config file (deprecate), ""change to --cfg-options instead.",)args = parser.parse_args()return argsdef setup_seeds(config):seed = config.run_cfg.seed + get_rank()random.seed(seed)np.random.seed(seed)torch.manual_seed(seed)cudnn.benchmark = Falsecudnn.deterministic = True# ========================================
# Model Initialization
# ========================================conv_dict = {'pretrain_vicuna0': CONV_VISION_Vicuna0,'pretrain_llama2': CONV_VISION_LLama2}print('Initializing Chat')
args = parse_args()
cfg = Config(args)model_config = cfg.model_cfg
model_config.device_8bit = args.gpu_id
model_cls = registry.get_model_class(model_config.arch)
model = model_cls.from_config(model_config).to('cuda:{}'.format(args.gpu_id))CONV_VISION = conv_dict[model_config.model_type]vis_processor_cfg = cfg.datasets_cfg.cc_sbu_align.vis_processor.train
vis_processor = registry.get_processor_class(vis_processor_cfg.name).from_config(vis_processor_cfg)stop_words_ids = [[835], [2277, 29937]]
stop_words_ids = [torch.tensor(ids).to(device='cuda:{}'.format(args.gpu_id)) for ids in stop_words_ids]
stopping_criteria = StoppingCriteriaList([StoppingCriteriaSub(stops=stop_words_ids)])chat = Chat(model, vis_processor, device='cuda:{}'.format(args.gpu_id), stopping_criteria=stopping_criteria)
print('Initialization Finished')USER_ANS_FORMAT = ''
# 新代码
# 上传图片
# 图片地址
gr_img = "F:\PKUMultimodal\MiniGPT4main\example1.png"
chat_state = CONV_VISION.copy()
img_list = []
# user_message = '[grounding] describe this image in detail'
USER_PROMPT_CUB_NOVEL = 'describe this image in detail'
user_message = '[grounding]' + USER_ANS_FORMAT + USER_PROMPT_CUB_NOVEL"""chat.upload_img(gr_img, chat_state, img_list)# 问问题
chat.ask(user_message, chat_state)
chat.encode_img(img_list)# 得到回答
llm_message = chat.answer(conv=chat_state,img_list=img_list,temperature=1.5,max_new_tokens=1000,max_length=2000)[0]
print(llm_message)"""from PIL import Image# === 图像预处理 ===
img_path = r"F:\PKUMultimodal\MiniGPT4main\example1.png"
raw_image = Image.open(img_path).convert("RGB")
image_tensor = vis_processor(raw_image).unsqueeze(0).to(model.device)# === 设置 prompt 和 answer ===
user_message = "[grounding] describe this image in detail"
gt_answer = "A detailed description of the image goes here." # 真实回答越贴近越好# === 伪造 chat state 和 img_list ===
chat_state = CONV_VISION.copy()
img_list = []# === 使用 Chat 模块封装输入 ===
chat.upload_img(raw_image, chat_state, img_list)
chat.encode_img(img_list)
chat.ask(user_message, chat_state)# === 这里我们手动构造符合模型 forward 要求的 samples ===
samples = {"image": image_tensor,"instruction_input": [chat_state.get_prompt()],"answer": [gt_answer],
}# === 模型推理并打印 loss ===
model.eval()
with torch.no_grad():outputs = model(samples)print("Loss:", outputs["loss"].item())
把命令行参数加入到Pycharm的步骤
这个还是要用命令行参数:
打开 PyCharm,打开你的项目。
在右上角,找到 Run/Debug Configurations 的下拉框(通常显示你当前运行的脚本名或者
<no configurations>
)。点击下拉箭头,选择 Edit Configurations...
在弹出的 Run/Debug Configurations 窗口,点击左上角的 + 号,选择 Python。
填写配置内容:
- Name:给这个配置起个名字,比如
Run MiniGPT4
- Script path:点击右侧的文件夹图标,选择你的 Python 脚本文件,比如
test.py
- Python interpreter:确保选择的是你对应的 conda 虚拟环境里的 Python 解释器(比如
minigptv
) - Parameters:这里填写你的命令行参数,比如
- css
- 复制编辑
--cfg-path eval_configs/minigpt4_eval.yaml --gpu-id 0
- Working directory:一般默认是项目根目录,可以确认一下。然后点击APPLY和OK就好了
二、Debug过程中对代码的深入理解
1. 关于parse的相关用法
parse, 中文意思是“分析(字符串、句子、数据)以便理解和处理”。如parse a sentence, parse aa JSON, parse arguments,用来表达“分析并转成结构化形式”。
这这里用于解析命令行参数
def parse_args():parser = argparse.ArgumentParser(description="Demo")parser.add_argument("--cfg-path", required=True, help="path to configuration file.")parser.add_argument("--gpu-id", type=int, default=0, help="specify the gpu to load the model.")parser.add_argument("--options",nargs="+",help="override some settings in the used config, the key-value pair ""in xxx=yyy format will be merged into config file (deprecate), ""change to --cfg-options instead.",)args = parser.parse_args()return args
2.这里的config配置
model:arch: minigpt4model_type: pretrain_llama2max_txt_len: 160end_sym: "</s>"low_resource: Trueprompt_template: '[INST] {} [/INST] 'ckpt: "F:\\PKUMultimodal\\MiniGPT4main\\pretrained_minigpt4_llama2_7b.pth"datasets:cc_sbu_align:vis_processor:train:name: "blip2_image_eval"image_size: 224text_processor:train:name: "blip_caption"run:task: image_text_pretrain
这里记一下,以防后面用到的时候还要找
3.初始化
(1)config初始化配置
这里的config是runner_config, model_config, dataset_config,evaluation_dataset_config, user_config五个的结合
配置名 | 作用描述 | 控制内容示例 |
---|---|---|
runner_config | 控制训练/测试主流程 | epochs, amp, output_dir, logging |
model_config | 控制模型结构和预训练权重加载 | arch, pretrained, precision |
dataset_config | 控制训练数据集加载 | 数据集名称、路径、预处理方式 |
evaluation_dataset_config | 控制推理/验证时使用的数据集 | image path, vis_processor, text_proc |
user_config | 用户自定义覆盖参数(命令行等) | 比如 model.precision=fp16 |
①OmegaConnf
它的作用是比 Python 的字典更灵活地管理配置文件,尤其是 .yaml
格式的深层嵌套配置。
它返回的是DictConfig类型,支持点号访问
cfg = OmegaConf.load("config.yaml")
print(cfg.model.name) # 相当于 cfg["model"]["name"]
②_build_opt_list
把嵌套的字典结构转换为“点式路径”(default一般是默认的意思)
def _build_opt_list(self, opts):opts_dot_list = self._convert_to_dot_list(opts)return OmegaConf.from_dotlist(opts_dot_list)
如:
opts = {"model": {"precision": "fp16","pretrained": True}
}
# 转换成:
opts_dot_list = ["model.precision=fp16","model.pretrained=True"
]
(2)Model初始化配置
①初始化所用的三个模型——vit、Qformer、llama
vit_model = cfg.get("vit_model", "eva_clip_g")q_former_model = cfg.get("q_former_model", "https://storage.googleapis.com/sfr-vision-language-research/LAVIS/models/BLIP2/blip2_pretrained_flant5xxl.pth")img_size = cfg.get("image_size")num_query_token = cfg.get("num_query_token")llama_model = cfg.get("llama_model")
checkpoint:
②interpolate
interpolate是指插值从而使位置嵌入与输入数据大小相同,内部依赖于torch.nn.functional.interpolate函数,它主要是用于对多维向量做上采样或下采样操作,形状一般为(N,C,H,W),这就要求orig_size和new_size要相匹配,否则会报错。
③用到的数据集构建方式
名称 | 含义 |
---|---|
cc_sbu_align | 是两个大规模图文对齐数据集 Conceptual Captions (CC) 和 SBU Captions 的组合(用于预训练的图文对齐)。MiniGPT-4 的训练数据就是这两个数据集拼接而来的版本。 |
在 minigpt4/datasets/builders/cc_sbu_align_builder.py 中 | 有定义如何构建该数据集类,包括图像路径、文本内容、预处理方式等。 |
4.model运行过程
①EncoderImage
vit:transform(形状变为3*224*224)
->x = self.proj(x).flatten(2).transpose(1, 2)(把图像划分为固定大小的patch(通过设置卷积核和步长大小相等),并把每个patch由三通道嵌入到embed_dim个通道,通过卷积实现)(1,256,1408)
#init
self.proj = nn.Conv2d(in_chans, embed_dim, kernel_size=patch_size, stride=patch_size)
#forward
x = self.proj(x).flatten(2).transpose(1, 2)
->添加cls_token(涵盖图片全局信息)+添加位置信息+dropout(1,257,1408)
cls_tokens = self.cls_token.expand(batch_size, -1, -1) # stole cls_tokens impl from Phil Wang, thanks
if self.pos_embed is not None:x = x + self.pos_embed
#位置初始化使用了截断的正态分布(Truncated Normal Distribution)来填充给定的 tensor。
#也就是说,和普通的正态分布不一样,它会将值 限制在区间 [a, b] 内,超出范围的会被重新采样。
#注意:这里的位置编码知识一个可学习的参数矩阵初始值,并不是像句子那样明确arange的位置,所以这么初始化没有问题
->进入block(block结构看上一篇)
值得注意的是这里虽然没有直接加位置参数,但是使用了相对位置索引,从而添加了位置感知信息的能力
if self.relative_position_bias_table is not None:relative_position_bias = \self.relative_position_bias_table[self.relative_position_index.view(-1)].view(self.window_size[0] * self.window_size[1] + 1,self.window_size[0] * self.window_size[1] + 1, -1) # Wh*Ww,Wh*Ww,nH#从可学习的 relative_position_bias_table 中,# 根据每对 token 的相对位置索引,取出偏置,并加到注意力矩阵中,用于增强模型的空间结构感知能力。relative_position_bias = relative_position_bias.permute(2, 0, 1).contiguous() # nH, Wh*Ww, Wh*Wwattn = attn + relative_position_bias.unsqueeze(0)
LN
Qformer(训练的时候有,但是这里没有)
(注意,到目前位置没有经过llama,图像处理只经过了Vit+Qformer)
里面新学到的参数的意思:num_beams用于控制beam_search的宽度,决定模型每一步保留多少个候选序列(类似于topk中的k),stopping_criteria定义什么时候停止生成过程,除了eos_tojen_id外还能增加更多复杂判断。
②LLama模型
将得到的文本特征+图片特征输入到LLama模型中进行生成,因为这个它调用的是LlamaForCausalLMOrig进行的是简单的生成函数,就不再过多介绍。