[CLIP-VIT-L + Qwen] 多模态大模型源码阅读 - 语言模型篇(2)

多模态学习笔记-语言模型篇(2)

参考repo:WatchTower-Liu/VLM-learning; url:vlm-learning

吐槽

今天的源码看的欲仙欲死,NTK(neural tangent kernel), rotary_position_embedding这些在之前的学习中完全闻所未闻,导致看的时候一脸懵逼,只能说不愧是Qwen大模型,各种sota的技术都用上了。就是看的有点费劲TAT~

学习心得

这次还是读源码,接着上一次的笔记(多模态源码阅读-1)接着讲,上一次讲了在第一次处理输入的序列数据时,去除掉序列数据input_ids中的image_token(也就是应当替换为图像数据的地方),并且将device设定为input_ids或者input_embeds挂载的设备(gpu or cpu),需要注意的是在前向传播时不能同时传入input_ids和input_embeds参数,只需传入其一即可。
下面来看看接下来的源码,还是前向传播部分(注意,这里的前向传播代码不是Qwen的原装代码,是为了多模态适配重写的代码)。

		output_attentions = (output_attentionsif output_attentions is not Noneelse self.config.output_attentions)output_hidden_states = (output_hidden_statesif output_hidden_states is not Noneelse self.config.output_hidden_states)use_cache = use_cache if use_cache is not None else self.config.use_cachereturn_dict = (return_dict if return_dict is not None else self.config.use_return_dict)

这段代码用来初始化output_attention, output_hidden_states和use_cache 三个参数,参数的含义分别如下:
output_attention: 用来确定是否输出注意力权重
output_hidden_states: 用来确定是否输出所有(每个时间步)的隐藏状态,而不是只输出最后一个时间步的隐藏状态。
use_cache: 是否使用缓存机制,通过缓存已计算的键值对信息来减少重复计算,加快模型的推理速度,具体来说,模型会在推理过程中逐步生成每个token,同时将计算得到的每个token对应的K和V缓存起来。当生成下一个token时,模型可以复用之前缓存的K和V,只对新token进行Attention计算,而无需重新计算整个序列的Attention,这样可以显著减少计算量,提高效率。
return_dict: 很好理解,返回值是否以字典形式返回。

		if input_ids is not None and inputs_embeds is not None:raise ValueError("You cannot specify both input_ids and inputs_embeds at the same time")elif input_ids is not None:input_shape = input_ids.size()input_ids = input_ids.view(-1, input_shape[-1]).contiguous()batch_size = input_ids.shape[0]elif inputs_embeds is not None:input_shape = inputs_embeds.size()[:-1]batch_size = inputs_embeds.shape[0]else:raise ValueError("You have to specify either input_ids or inputs_embeds")

如果我们同时传入了Input_ids和input_embeds就会报错,错误信息为不能同时指定input_ids和input_embeds, 如果input_ids不为空(此时Input_embeds为空),我们用size()函数获取input_ids的形状,通常为(batch_size, seq_len),利用view函数将input_ids转换为二维张量,contifuous()函数的目的是让Input_ids在内存中连续,这是使用view函数的前提条件(reshape函数就不需要,但是view函数对老代码的兼容性更好)。
如果我们传入了inputs_embeds,一般情况下它的形状是(batch_size, seq_len, embed_size),我们不需要它的embed_size维度,所以在获取input_shape的时候去除掉了最后一个维度, 其batch_szie就为第一个维度。
如果input_ids和input_embeds都未传入,报错,要求我们必须传入至少一个序列输入。

if images is not None and first_step:input_shape = input_shape[0], input_shape[-1] + self.otherConfig["image_context_length"]   ############

在推理或者训练的第一步时,我们需要对输入数据的形状进行处理,一般情况下,input_shape[0]为betch_szie,input_shape[-1]为seq_len,我们需要再seq_len维度加上图片的上下文序列长度,以便后续对图片特征和文字输入进行融合。

		if token_type_ids is not None:token_type_ids = token_type_ids.view(-1, input_shape[-1])if position_ids is not None:position_ids = position_ids.view(-1, input_shape[-1])

这里对token_type_ids和position_ids进行形状的调整,其中token_type用来区分不同模态的token, position_id则是transformer模型的基础,由于transformer模型的自注意力机制天然无法考虑序列的位置信息,它是并行处理序列输入的每一个元素,而RNN则是递归处理数据,天然可以记忆之前时间步的信息,因此我们需要position_id。我们确保token_type_id和position_id的最后一个维度都是seq_len + image_context_len,以便于后续的处理。

        if past_key_values is None:past_length = 0past_key_values = tuple([None] * len(self.h))else:if self.use_cache_quantization:past_length = past_key_values[0][0][0].size(2)else:past_length = past_key_values[0][0].size(-2)if position_ids is None:position_ids = torch.arange(past_length,input_shape[-1] + past_length,dtype=torch.long,device=device,)position_ids = position_ids.unsqueeze(0).view(-1, input_shape[-1])

如果没有缓存的键值对信息,我们将past_length置为0,代表我们目前尚未处理任何序列输入信息(假设use_cache),将past_key_valuies初始化为长度为注意力头数量(self.h)的元组,用来存储每个注意力头的key_value信息。
如果有缓存的键值对信息,并且启动了缓存量化,past_key_values[0][0][0]很抽象,这里我们知道它是取第一个注意力头的键张量的第三个维度就行。如果不启用缓存量化,同样是取第一个注意力头的键张量的倒数第二个维度。我们只需要知道这两个维度是past_len就行,底层深究会很麻烦。
如果没有传入position_ids,或者说我们目前不处于推理和训练的第一步,我们初始化一个positon_ids,起始位置在past_len之后,终止位置为past_len + (seq_len + image_context_len),这里加上past_len是为了保持长度不变,数据类型为torch.long,。
最后对position_id进行重新塑性,position_ids原本的size为(seq_len + image_context_len,),我们添加一个为1的维度(用unsqueeze(0)),并且将position_ids最后一个维度重塑为(seq_len + image_context_len),这里可能有点多此一举,但是为了代码的健壮性也无妨。
————————————————————————————————————————————
时间原因,今天先写到这里,明日再战fighting~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/52414.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

红黑树、B+Tree、B—Tree

红黑树 B-Tree 这三个通常都是把内存全部加载到内存里,然后再内存中进行处理的,数据量通常不会很大。 内存一般容量都在GB级别,比如说现在常见的4G、8G或者16G。 如果要处理的数据规模非常大,大到内存根本存不下的时候。这个时候…

如何简单判断某个port是否被防火墙block

在存储系统中经常遇到要上传一些code到存储中做升级,但是通过客户网络死活搞不定的情况,其实很多时候是由于客户内部有防火墙的设置,某些端口是被block屏蔽的。本文就介绍几个命令用来快速判断是否这些port被客户做了block。如果确认是被bloc…

Spring Boot 集成 swagger 3.0 指南

Spring Boot 集成 swagger 3.0 指南 一、Swagger介绍1.springfox-swagger 22.SpringFox 3.0.0 发布 二、Spring Boot 集成 swagger 3.01. 添加Maven依赖2. 创建配置类配置Swagger2.1 创建SwaggerConfig 配置类2.1 创建TestInfoConfig信息配置类 3. 在你的Controller上添加swagg…

【思源笔记】思源笔记配置S3同步

本文首发于 ❄️慕雪的寒舍 文章目录 1. 写在前面2. 什么是思源笔记的S3/WEBDAV同步?2.1. 说明2.2. 思源的同步配置和工作空间2.3. 什么是S3协议? 3. 配置思源S3同步3.1. 初始化数据仓库密钥3.2. 思源S3同步界面3.3. 配置七牛云KODO3.4. 如何将同步配置导…

以GD32F103C8T6为例的核心板原理图PCB绘制学习笔记简单总结

目录 GD32F103C8T6核心板 设计流程 基础知识 部分原理图解析 排针连接 (H1 - PZ254V-12-8P): 晶振 封装 基础知识 C0603封装 C0805 F1210封装 保险丝 L0603 贴片电感 LED-0603 R0603 HDR-TH_8P-P2.54-V-M-R2-C4-S2.54 排针 按键(SW-SMD-T6X…

Python(PyTorch)物理变化可微分神经算法

🎯要点 🎯使用受控物理变换序列实现可训练分层物理计算 | 🎯多模机械振荡、非线性电子振荡器和光学二次谐波生成神经算法验证 | 🎯训练输入数据,物理系统变换产生输出和可微分数字模型估计损失的梯度 | 🎯…

Code Practice Journal | Day52_Graph03

KamaCoder 101. 孤岛的总面积 题目:101. 孤岛的总面积 (kamacoder.com) 题解:代码随想录 (programmercarl.com) solution namespace ACMModeExample {class Program{static void Main(string[] args){// 读取矩阵的行数和列数string[] dimensions Cons…

[Matsim]Matsim学习笔记-population.xml的创建

学习需求 在利用matsim实现交通模拟时,需要把模拟的乘客出行数据替换成自己的,如何进行替换呢? 带着这个问题,调研学习matsim,实现population.xml的生成 调研笔记 幸运的发现matsim中实现了很多的writer工具类&#xf…

unity 使用Sqlite报错

Fallback handler could not load library C:/Users/Administrator/Desktop/xxx /_Data/MonoBleedingEdge/sqlite3&#xff0c; 出现DllNotFoundException: sqlite3 assembly:<unknown assembly> type:<unknown type> member:(null) 解决方法 &#xff1a;下载一个…

Nacos微服务注册管理中心与服务通信

参照springboot-alibaba-ribbon项目学习 E:\Codes\Idea_java_works\apesource\springboot\微服务\springboot_alibaba_ribbon Nacos 微服务注册中心-discover Nacos 是⼀个更易于构建云原⽣应⽤的动态服务发现、配置管理和服务管理平台。简单来说 Nacos 就是 注册中⼼ 配置…

Java入门:06.Java中的方法--进阶02.03

2 可变参数 方法调用时&#xff0c; 传递的实参数量&#xff0c;是由被调用方法的参数列表数列决定的。 一般来讲&#xff0c;传递的实参数量必须与形参变量数量相同&#xff0c;但是也有一种特殊的参数&#xff0c;允许调用时传递的实参数量是可变&#xff0c;这种参数就称为…

CSS3多行多栏布局

当前布局由6个等宽行组成&#xff0c;其中第四行有三栏&#xff0c;第五行有四栏。 重点第四行设置&#xff1a; 代码&#xff1a; <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title><style>img {hei…

做项目过程中问题小汇总 | vue3 elementplus js

el-card去除阴影 <el-card style"box-shadow: none;"> </el-card>el-button按钮加图标 <el-button type"primary" size"default" icon"Plus"click"addRole">添加职位</el-button>el-table表头的文…

linux和docker部署基本的命令掌握

git用到的指令 上传代码 git add . git commit -m zhushi git push 拉取代码 git clone 代码仓地址 git pulldocker用到的指令 # 查看docker下的容器进程,停止和删除 docker ps -a docker stop name(id) docker rm name(id) # docker下面的镜像和删除 docker images docker r…

AI 时代的编程革命:如何在挑战中抓住机遇?

AI 发展对软件开发的挑战与机遇&#xff1a;程序员应对策略 随着人工智能&#xff08;AI&#xff09;技术的快速进步&#xff0c;软件开发领域正经历深刻的变革。AI 不仅改变了编程的方式&#xff0c;也对程序员的职业发展产生了重要影响。在这个背景下&#xff0c;我们既看到…

WPF—数据模版绑定数据集合(listbox和panel)

WPF—数据模版绑定数据集合(listbox和panel) WPF中&#xff0c;可以使用ListBox或者Panel&#xff08;比如StackPanel或Canvas&#xff09;来展示数据集合&#xff0c;并使用数据模板DataTemplate来定义数据的呈现方式。以下是一些简单的例子&#xff0c;展示如何将数据集合绑…

C#高级进阶---关于插件开发(初版)

一、关于插件 插件开发是一种使应用程序功能可扩展的技术。通过插件&#xff0c;应用程序可以动态地加载和使用外部功能模块&#xff0c;而无需重新编译整个程序。 1. 插件架构设计 插件系统通常包含以下几个核心部分&#xff1a; 主程序&#xff08;Host Application&#x…

HTML5休闲小游戏《砖块破坏者》源码,引流、刷广告利器

HTML5休闲小游戏《砖块破坏者》源码&#xff0c;直接把源码上传到服务器就能使用了&#xff01; 下载链接&#xff1a;https://www.huzhan.com/code/goods468802.html

Linux:Bash中的命令介绍(简单命令、管道以及命令列表)

相关阅读 Linuxhttps://blog.csdn.net/weixin_45791458/category_12234591.html?spm1001.2014.3001.5482 在Bash中&#xff0c;命令执行的方式可以分为简单命令、管道和命令列表组成。这些结构提供了强大的工具&#xff0c;允许用户组合命令并精确控制其执行方式。以下是对这…

自己制作VOC转yolo的软件

不多说&#xff0c;直接贴源码&#xff0c;很简单&#xff0c;直接复制下来运行就行 from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QLabel, QLineEdit, QPushButton,QListWidget, QProgressBar, QHBoxLayout, QMainWindow, QFileDialog, QMenu,QDesktopWidget, QSplas…