赞
踩
源码地址:https://github.com/OpenGVLab/InternVL/tree/main/internvl_chat/internvl/model/internvl_chat
用InternVisionConfig类来设置类,用于初始化模型
num_channels=3, patch_size=14, image_size=224, qkv_bias=False, hidden_size=3200, num_attention_heads=25, intermediate_size=12800, qk_normalization=True, num_hidden_layers=48, use_flash_attn=True, hidden_act='gelu', norm_type='rms_norm', layer_norm_eps=1e-6, dropout=0.0, drop_path_rate=0.0, attention_dropout=0.0, initializer_range=0.02, initializer_factor=0.1, **kwargs,
重要的参数:
1.num_channels:输入图像的颜色通道数量。这是一个与图像数据格式相关的重要参数。不同的图像格式具有不同的颜色通道数量。
以下是一些常见的分类和解释:
RGB 图像:
- 通道数:3
- 描述:RGB 图像使用三个颜色通道来表示颜色信息,即红色(Red)、绿色(Green)和蓝色(Blue)。这是最常见的图像格式,用于大多数彩色图像。
- 如何处理:RGB 图像中的每个像素由三个值组成,分别表示红、绿、蓝的强度。这三个值通常在 0 到 255 之间(对于 8 位图像)。
灰度图像:
- 通道数:1
- 描述:灰度图像只有一个通道,用于表示亮度信息,没有颜色信息。每个像素的值表示该点的灰度强度。
- 如何处理:灰度图像中的每个像素通常在 0 到 255 之间,0 表示黑色,255 表示白色。
RGBA 图像:
- 通道数:4
- 描述:RGBA 图像在 RGB 的基础上增加了一个透明度通道(Alpha 通道),用于表示像素的透明度。
- 如何处理:RGBA 图像中的每个像素由四个值组成,分别表示红、绿、蓝的强度和透明度。这四个值通常在 0 到 255 之间。
其他多通道图像:
- 通道数:任意数量
- 描述:某些专业领域(如医学成像、遥感)可能使用超过三个或四个通道的图像。例如,多光谱图像和超光谱图像可以有数十个甚至数百个通道,每个通道对应不同波段的光谱信息。
- 如何处理:这些图像中的每个像素由多个值组成,每个值表示在不同光谱波段的强度。处理这类图像通常需要特定的算法和工具。
2.patch_size:定义了输入图像被划分成的小块(patch)的大小。
patch_size
指的是将图像划分成的小块(patch)的边长。例如,如果patch_size
设置为 16,那么每个
patch 的大小就是 16x16 像素。为什么使用 Patch?
在基于 Transformer 的视觉模型中,输入图像会被划分成若干个不重叠的 patch,这些 patch
会被展平并线性嵌入到较高维度的向量中,随后作为 Transformer
的输入。这样做是为了将图像转换成类似于自然语言处理(NLP)中的词嵌入(word embedding)的形式,使得图像数据可以用
Transformer 模型进行处理。Patch Size 的影响
Patch 数量:
- 图像的分辨率是固定的,例如 224x224 像素。如果
patch_size
设置为 16,那么整个图像会被划分成 (224/16) x (224/16) = 14 x 14 = 196 个 patch。- 较小的 patch size 会导致更多的 patch,从而提供更细粒度的图像信息。
- 较大的 patch size 会导致较少的 patch,从而减少模型的计算复杂度,但可能丢失一些细节信息。
计算复杂度:
- 较大的 patch size 通常会降低模型的计算复杂度,因为需要处理的 patch 数量减少了。
- 较小的 patch size 会增加计算复杂度,但可能提高模型的细节捕捉能力。
特征提取:
- 较小的 patch size 可以更好地捕捉图像中的细微特征和局部模式。
- 较大的 patch size 更关注全局信息和较大范围的特征。
3.qk_normalization:指在自注意力机制中对查询(queries)和键(keys)进行归一化处理。
归一化 Queries 和 Keys 的原因
稳定数值计算:
- 在没有归一化的情况下,( Q ) 和 ( K ) 的值可能会很大,导致 ( QK^T ) 的值也会很大。这样,经过 softmax 后的值会变得非常尖锐(即,大部分权重集中在几个元素上)。
- 归一化可以将 ( Q ) 和 ( K ) 的值缩小到较小的范围,从而避免数值计算中的溢出问题,使计算更加稳定。
提高模型的性能和收敛速度:
- 归一化后的 ( Q ) 和 ( K ) 可以使注意力权重更加平滑和稳定,避免过度依赖某些特定的查询或键。
- 这有助于模型在训练过程中更快地收敛,并可能提高模型的整体性能。
避免极端权重:
- 未归一化的情况下,某些查询和键的点积可能会非常大,导致 softmax 函数在这些位置生成接近 1 的值,而在其他位置生成接近 0 的值。这样会导致模型的注意力机制过于集中在某些位置,忽略了其他重要的信息。
- 归一化有助于避免这种极端权重的情况,使得注意力机制更加平衡和合理。
归一化的实现
常见的归一化方法包括:
- 标准化:将每个向量减去其均值,并除以其标准差,使得数据符合标准正态分布。
- 单位向量归一化:将向量除以其 L2 范数,使其长度为 1。这种方法通常用于 self-attention 机制。
总结
对查询和键进行归一化可以稳定数值计算,提高模型的性能和收敛速度,并避免极端权重情况的发生。这种归一化处理在实际应用中已经证明是有效的,可以帮助模型在复杂的任务中表现得更好。
from_pretrained函数接受cls和pretrained_model_name_or_path参数,用于从预训练模型的路径或名称加载配置。
cls 是一个用于类方法中的通用参数名,指代调用该方法的类本身。
继承了上个VFM的配置,然后配置了LLM的参数。
1.use_backbone_lora:指定是否在模型的骨干网络中使用 LoRA(Low-Rank Adaptation)技术。
2.pad2square:是否将输入图像填充成正方形。
3.force_image_size:指定一个固定的图像尺寸。
4.downsample_ratio:在处理图像时,输入图像可以通过下采样来减少尺寸。这个参数指定下采样的比例,例如 0.5 表示将图像尺寸减半。
5.use_thumbnail:是否使用缩略图。
6.ps_version:像素混洗(pixel shuffle)的版本。
PixelShuffle 的基本概念
PixelShuffle
是一种将低分辨率特征图转换为高分辨率图像的上采样方法。它通过重新排列低分辨率特征图中的元素来生成高分辨率图像。这个过程通常用于图像超分辨率任务,即从低分辨率图像生成高分辨率图像。工作原理
PixelShuffle 的核心思想是将特征图的通道维度转换为空间维度。具体步骤如下:
低分辨率特征图:
- 输入一个形状为 ((N, C \times r^2, H, W)) 的低分辨率特征图,其中 (N) 是批量大小,(C) 是通道数,(H) 和 (W) 是特征图的高度和宽度,(r) 是上采样倍数。
通道转换:
- 将通道数 (C \times r^2) 拆分为 (r \times r) 个小块,每个小块的通道数为 (C)。
重新排列:
- 将这些小块重新排列到空间维度,生成一个新的特征图,形状为 ((N, C, H \times r, W \times r))。
通过这个过程,特征图的分辨率从 ((H, W)) 增加到 ((H \times r, W \times r)),实现上采样。
应用场景
PixelShuffle 主要用于以下场景:
- 图像超分辨率:提高图像分辨率,从低分辨率图像生成高分辨率图像。
- 上采样:在生成模型(如生成对抗网络,GAN)中用于生成高分辨率图像。
- 视频处理:提高视频帧的分辨率,实现视频的超分辨率。
modeling_intern_vit.py文件
InternVisionEmbeddings 视觉嵌入类
InternAttention 注意力机制
InternMLP MLP实现
InternVisionEncoderLayer 单层的encoder
InternVisionEncoder 完整的encoder
InternVisionModel 视觉模型类
直接给出模型类的注释
# 视觉模型类 class InternVisionModel(PreTrainedModel): main_input_name = 'pixel_values' config_class = InternVisionConfig _no_split_modules = ['InternVisionEncoderLayer'] # 参数定义,从预定义的嵌入类和encoder类加载 def __init__(self, config: InternVisionConfig): super().__init__(config) self.config = config self.embeddings = InternVisionEmbeddings(config) self.encoder = InternVisionEncoder(config) # 调整位置嵌入,适应不同尺寸的图像 def resize_pos_embeddings(self, old_size, new_size, patch_size): # 从嵌入类获取位置嵌入 pos_emb = self.embeddings.position_embedding _, num_positions, embed_dim = pos_emb.shape # 获取位置数量、嵌入维度 cls_emb = pos_emb[:, :1, :] # 取出第一个位置的嵌入,是分类嵌入,表示图像整体的信息 # 针对其他位置的嵌入,调整形状,变为(1, embed_dim, old_size // patch_size, old_size // patch_size) pos_emb = pos_emb[:, 1:, :].reshape(1, old_size // patch_size, old_size // patch_size, -1).permute(0, 3, 1, 2) # 使用 F.interpolate 函数对位置嵌入进行双三次插值,将其尺寸调整为 (new_size // patch_size, new_size // patch_size) pos_emb = F.interpolate(pos_emb.float(), size=new_size // patch_size, mode='bicubic', align_corners=False) # 上采样或下采样 # 还原位置嵌入形状 pos_emb = pos_emb.to(cls_emb.dtype).reshape(1, embed_dim, -1).permute(0, 2, 1) # 拼接分类嵌入和位置嵌入 pos_emb = torch.cat([cls_emb, pos_emb], dim=1) # 将新的位置嵌入赋值给模型的嵌入参数,并更新图像尺寸 self.embeddings.position_embedding = nn.Parameter(pos_emb) # 将一个张量转换为模型的可学习参数 self.embeddings.image_size = new_size logger.info('Resized position embeddings from {} to {}'.format(old_size, new_size)) # 返回图像嵌入 def get_input_embeddings(self): return self.embeddings # 将输入图像数据转换为特征表示 def forward( self, pixel_values: Optional[torch.FloatTensor] = None, # 输入图像的像素值,形状为 (batch_size, channels, height, width) output_hidden_states: Optional[bool] = None, # 是否输出所有层的隐藏状态 return_dict: Optional[bool] = None, # 是否返回字典格式的输出 pixel_embeds: Optional[torch.FloatTensor] = None, # 预先计算好的图像嵌入特征,形状为 (batch_size, sequence_length, hidden_size) ) -> Union[Tuple, BaseModelOutputWithPooling]: # 是否输出所有层的隐藏状态, output_hidden_states = ( output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states ) # 是否返回字典格式的输出 return_dict = return_dict if return_dict is not None else self.config.use_return_dict # 检查输入,要么输入像素值,要么输入嵌入 if pixel_values is None and pixel_embeds is None: raise ValueError('You have to specify pixel_values or pixel_embeds') # 优先选择图像嵌入,将其作为隐藏状态 if pixel_embeds is not None: hidden_states = pixel_embeds else: # 否则,检查图像像素值的形状,拿他生成嵌入,作为隐藏状态 if len(pixel_values.shape) == 4: hidden_states = self.embeddings(pixel_values) else: raise ValueError(f'wrong pixel_values size: { pixel_values.shape}') # 将隐藏状态输入到编码器 self.encoder 中,得到编码器的输出 encoder_outputs = self.encoder( inputs_embeds=hidden_states, output_hidden_states=output_hidden_states, return_dict=return_dict, ) # 从编码器输出中取出最后的隐藏状态 last_hidden_state,迭代取出第一个位置的输出,是池化的分类嵌入 last_hidden_state = encoder_outputs.last_hidden_state pooled_output = last_hidden_state[:, 0, :] # 如果 return_dict 为 False,返回元组格式的结果,包括最后的隐藏状态、池化输出和编码器的其他输出 if not return_dict: # + 运算符用于元组时,它的作用是连接两个元组 return (last_hidden_state, pooled_output) + encoder_outputs[1:] # (last_hidden_state, pooled_output, hidden_states, attentions) # 如果是字典形式的输出,就返回 BaseModelOutputWithPooling 对象,包含最后的隐藏状态、池化输出、隐藏状态和注意力。 return BaseModelOutputWithPooling( last_hidden_state=last_hidden_state, pooler_output=pooled_output, hidden_states=encoder_outputs.hidden_states, attentions=encoder_outputs.attentions, )
初始化了很多参数,主要还是VFM和LLM、MLP,还有是否对这两个模型使用Lora化,以减少训练的参数量。
输入图像的像素值、文本的id、图像标志、标签,返回loss、预测的概率等。
重点在于
input_embeds[selected] = input_embeds[selected] * 0.0 + vit_embeds.reshape(-1, C)
将图片和文本嵌入拼接。
自己实现的。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。