LLM时代,LLaVA代码解读1——encoder部分

本文详细解读了LLAVA框架中clip_encoder.py和builder.py中的关键函数,包括初始化、模型加载、特征选择和视觉塔构建,着重介绍了如何构造基于CLIP的VIT视觉模型及其组成部分的使用方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

说明

本文所有观点和代码解读来自于搜索引擎以及GPT生成的内容,并且加上自己的理解后综合表达,并不一定正确,非常希望大佬能指导提出问题,也希望大家共同进步学习!

概要

主要针对LLAVA中model的multimodal_encoder中的两个python源代码解读

  • clip_encoder.py
  • builder.py

代码解读

clip_encoder.py

该程序主要是用于构建一整个基于CLIP的VIT视觉模型,是整个LLAVA模型视觉框架的构建,重要部分在于内部的CLIPVisionTower类

init函数
    def __init__(self, vision_tower, args, delay_load=False):
        super().__init__()

        self.is_loaded = False  # 初始没有加载模型 用于判断是否加载好了模型

        self.vision_tower_name = vision_tower # 传参设置视觉模块名称
        self.select_layer = args.mm_vision_select_layer 
        # 这行代码从args对象中获取mm_vision_select_layer属性,并将其赋值给select_layer实例变量。这可能是用来选择模型中的特定层 比如为-2的时候,可能就是加载输出层的前一层
        
        self.select_feature = getattr(args, 'mm_vision_select_feature', 'patch') # 设置有两个可选择的 一个是patch 另一个是cls_patch 

        if not delay_load: # 默认调用load_model来加载模型
            self.load_model()
        else:
            self.cfg_only = CLIPVisionConfig.from_pretrained(self.vision_tower_name)
load_model函数
    def load_model(self):
    	# 这个好理解,一个是 img的处理模块 一个是vit视觉模块,llava中并不训练vit,因此vit的梯度为False 最后设置is_load为true代表已经加载完毕了
    	# 整个模型调用transformer中的CLIP库加载模型。
        self.image_processor = CLIPImageProcessor.from_pretrained(self.vision_tower_name) 
        self.vision_tower = CLIPVisionModel.from_pretrained(self.vision_tower_name)
        self.vision_tower.requires_grad_(False)

        self.is_loaded = True
feature_select函数
    def feature_select(self, image_forward_outs):
    	# image_feature是图像的特征,源码中  select_layer = -2(finetune.sh中mm_vision_select_layer 默认值为-2 代表的是拿到了img经过VIT的计算后的倒数第二层的特征值)
    	# 具体来说应该是 分类的前一个头,对应于VIT的原文中,应该是MLP的输出结果 也有可能是transformer encoder的输出结果。
        image_features = image_forward_outs.hidden_states[self.select_layer]
        # 如果是patch 那么不要第一个特征,应该是cls(VIT中第一个token做的是cls)
        if self.select_feature == 'patch':
            image_features = image_features[:, 1:]
        # cls_patch 是完全保留的
        elif self.select_feature == 'cls_patch':
            image_features = image_features
        else:
            raise ValueError(f'Unexpected select feature: {self.select_feature}')
        return image_features
forward函数
# 注意这里设置为不需要梯度计算,因为llava不训练整个clip的vit
    @torch.no_grad()
    def forward(self, images):
        if type(images) is list: # 如果输入图像是list时
            image_features = []
            for image in images: # 遍历
            # 计算前向输出结果 并且增加维度,应该是表示 批次顺序的维度
                image_forward_out = self.vision_tower(image.to(device=self.device, dtype=self.dtype).unsqueeze(0), output_hidden_states=True)
            # 拿到特征
                image_feature = self.feature_select(image_forward_out).to(image.dtype)
                image_features.append(image_feature)
        else:
        # 单一图片 直接计算这张图片, 并且拿到feature
            image_forward_outs = self.vision_tower(images.to(device=self.device, dtype=self.dtype), output_hidden_states=True)
            image_features = self.feature_select(image_forward_outs).to(images.dtype)

        return image_features

其余的函数较为简单,没有说明的必要

builder.py

builder.py主要用于调用上面的multimodal_encoder中的clipvisiontower,从而构建一个llava的视觉encoder,只有一个函数时 build_vision_tower

build_vision_tower函数
def build_vision_tower(vision_tower_cfg, **kwargs):
	# 根据参数传递来选择vision_tower
    vision_tower = getattr(vision_tower_cfg, 'mm_vision_tower', getattr(vision_tower_cfg, 'vision_tower', None)) 
    is_absolute_path_exists = os.path.exists(vision_tower)
    if is_absolute_path_exists or vision_tower.startswith("openai") or vision_tower.startswith("laion"):
    	# 在这调用用于构建一个vision_tower对象
        return CLIPVisionTower(vision_tower, args=vision_tower_cfg, **kwargs)

    raise ValueError(f'Unknown vision tower: {vision_tower}')
### LLava 架构概述 LLaVa(Large Language and Vision Assistant)是一种多模态人工智能模型,其核心目标在于融合文本和图像两种数据形式,从而实现更深层次的理解与交互[^1]。以下是关于该架构的具体概念、设计以及组成部分。 #### 多模态处理的核心理念 LLaVa 被设计用于同时接收并解析来自语言和视觉的信息流。通过这种方式,它不仅能够单独理解文字或图片的内容,还能够在两者之间建立关联,生成更加精准且上下文敏感的回答。 #### 结构组成详解 整个 LLaVa 模型由三大主要部分构成: 1. **Language Model (LLM)** 此处采用了 Vicuna 这一开源大型语言模型作为基础框架。Vicuna 已经经过大量训练,在自然语言理解和生成方面表现出色,并具备良好的指令跟随特性[^2]。 2. **Vision Encoder** 使用 CLIP(具体版本为 ViT-L/14),负责将输入的图像转化为高维特征向量即 visual features。这些提取出来的特征代表了原始图像中的重要信息点。 3. **Projection Layer** Projection 层的作用至关重要——它会把上述得到的 visual features 投影至 language embedding space 中去匹配相应的语义空间位置。这一过程使得原本独立存在的两类信息得以统一表示,便于后续联合建模操作。 ```python class LLaVaModel(nn.Module): def __init__(self, llm_model, vision_encoder, projection_layer): super(LLaVaModel, self).__init__() self.llm = llm_model # e.g., Vicuna instance self.vision_encoder = vision_encoder # e.g., CLIP with ViT-L/14 backbone self.projection = projection_layer def forward(self, text_input_ids, image_features): # Process textual input through the LLM lang_output = self.llm(text_input_ids) # Encode images using Vision Encoder encoded_images = self.vision_encoder(image_features) # Map visual features to language embedding space via Projection layer projected_visuals = self.projection(encoded_images) # Combine both modalities' outputs here... combined_representation = torch.cat((lang_output.last_hidden_state, projected_visuals), dim=1) return combined_representation ``` 此代码片段展示了如何构建一个简单的 LLaVa 类似结构,其中包含了三个关键组件及其相互作用方式。 ---
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值