[CLIP-VIT-L + Qwen] 多模态大模型源码阅读 - trainer篇

[CLIP-VIT-L + Qwen] 多模态大模型源码阅读 - trainer篇

  • 前情提要
  • 源码阅读
    • 导包
    • 逐行解读
    • compute_loss方法(重构)
      • 整体含义
      • 逐行解读
    • save_model函数(重构)
      • 整体含义
      • 逐行解读
    • create_optimizer函数(重构)
      • 整体含义
      • 逐行解读
    • create_optimizer_and_scheduler函数(重构)
      • 整体含义
      • 逐行解读

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

前情提要

有关多模态大模型架构中的语言模型部分(MQwen.py)的代码请看(多模态大模型源码阅读 - 1、 多模态大模型源码阅读 - 2, 多模态大模型源码阅读 - 3,多模态大模型源码阅读 - 4),多模态大模型架构中的视觉模型(visual/CLIP-VIT.py)部分请看多模态大模型源码阅读 - 5
本节主要讲的是项目中的多模态Trainer部分,即项目文件trainer.py,该文件中的代码重构了部分transfomers.trainer的成员方法,以适配多模态场景下的模型训练,包括自定义的损失计算,参数保存,优化器配置,支持分布式训练(多卡场景)。

源码阅读

导包

import torch
from transformers import Trainer
from transformers.trainer import (is_sagemaker_mp_enabled,get_parameter_names,has_length,ALL_LAYERNORM_LAYERS,logger,
)
import os
from peft import get_peft_model_state_dict

逐行解读

import torch
from transformers import Trainer

torch不必赘述,深度学习的核心出装,构建和训练神经网络的必备库,调包调参侠(我)的福音。
Trainer类主要用于NLP和多模态任务,简化模型训练过程,在后续的代码中作为父类使用。

from transformers.trainer import (is_sagemaker_mp_enabled,get_parameter_names,has_length,ALL_LAYERNORM_LAYERS,logger,
)

is_sagemaker_mp_enabled检验是否在Amazon SageMaker的模型并行环境中运行。模型并行性允许将模型的不同组件分布到多个GPU设备上,用以加速大规模模型的训练。如果是单卡童鞋就不必在意这个设置~
get_parameter_names用以获取模型中的参数名,在设置优化器参数时,可以区分需要权重衰减的参数和不需要的参数。
has_length检测对象是否有长度信息,用于确定训练过程的迭代次数。在项目代码中没有用到。
ALL_LAYERNORM_LAYERS:包含所有LAYERNORM类型的层,用于在优化器配置中排除这些层的权重衰减。
logger:日志记录,输出训练过程中信息和调试信息。

import os
from peft import get_peft_model_state_dict

os:经常使用的库,主要用来创建文件、文件夹,开关文件。
peft(Parameter-Efficient Fine-Tuning),用于高效微调模型,在微调过程中会冻结预训练模型的大部分参数,仅保留少量的可训练参数,以在尽可能少的资源占用和时间下微调模型适配下游任务, 大名鼎鼎的LoRA、Prefix Tuning、Prompt Tuning 等都在这个库中。get_peft_model_state_dict用于获取微调后的adapter状态字典。例如使用LoRA对模型微调后,可以使用这一方法获取微调后的LoRA adapter状态字典。

compute_loss方法(重构)

class MultiModalTrainer(Trainer):def compute_loss(self, model, inputs, return_outputs=False):return model(image=inputs["images"],input_ids=inputs["input_ids"],labels=inputs["labels"],).loss

整体含义

为多模态场景自定义的损失计算重构方法,以适配多模态形式的输入,如image

逐行解读

class MultiModalTrainer(Trainer):def compute_loss(self, model, inputs, return_outputs=False):

自定义MultiModelTrainer类,继承自transfomers.Trainer,拥有其成员变量和方法。
model:可以同时处理图片和文本类型输入
inputs:包含图片输入,文本索引输入和有监督训练需要的标签数据。
return_outputs:指示是否返回模型输出,考虑到这个项目是科研级代码,所以这个参数没啥用(QWQ)。

        return model(image=inputs["images"],input_ids=inputs["input_ids"],labels=inputs["labels"],).loss

将inputs字典中的对应键下的值传递给model,获取其返回值中的损失值,用于后续的模型优化。

save_model函数(重构)

    def save_model(self, output_dir=None, _internal_call=False):from transformers.trainer import TRAINING_ARGS_NAME# Ensure output_dir is not Noneif output_dir is None:output_dir = self.args.output_dir# Create the output directory if it doesn't existos.makedirs(output_dir, exist_ok=True)# Save training argumentstorch.save(self.args, os.path.join(output_dir, TRAINING_ARGS_NAME))# Access the original modelmodel = self.model.module if hasattr(self.model, 'module') else self.model# Save LLM parameterssaved_params_LLM = get_peft_model_state_dict(model.LLM)torch.save(saved_params_LLM, os.path.join(output_dir, "adapter_model.bin"))# Save other parameterssaved_params_other = model.feature_proj.state_dict()torch.save(saved_params_other, os.path.join(output_dir, "other_params.bin"))# Save configurationconfig = model.LLM.peft_configselected_adapters = list(config.keys())config[selected_adapters[0]].save_pretrained(output_dir, auto_mapping_dict=None)

整体含义

保存训练过程中的模型及其相关配置到指定的目录,重构后适配了多模态模型模型和配置

逐行解读

    def save_model(self, output_dir=None, _internal_call=False):from transformers.trainer import TRAINING_ARGS_NAME

output_dir指定模型和相关配置的保存目录,_internal_call并没有用上,可能与某些内部逻辑有关。
TRAINING_ARGS_NAME用于保存训练模型参数名,是一个常量。

        # Ensure output_dir is not Noneif output_dir is None:output_dir = self.args.output_dir# Create the output directory if it doesn't existos.makedirs(output_dir, exist_ok=True)

如果没有指定模型配置的保存目录,就采用配置参数中的输出路径。同时使用os.makedirs方法在指定输出路径下穿件文件夹,exist_ok保证即使文件夹已经存在也不会报错。

        # Save training argumentstorch.save(self.args, os.path.join(output_dir, TRAINING_ARGS_NAME))# Access the original modelmodel = self.model.module if hasattr(self.model, 'module') else self.model

保存训练参数到指定文件夹的指定文件中,文件名为TRAINING_ARGS_NAME常量。如果模型被封装在DataParallel 或 DistributedDataParallel 中,通过self.model.module访问模型,否则直接使用self.model。

        # Save LLM parameterssaved_params_LLM = get_peft_model_state_dict(model.LLM)torch.save(saved_params_LLM, os.path.join(output_dir, "adapter_model.bin"))

我们传入的model参数实际上是一个以Qwen为语言模型,SIGLIP/CLIP-VIT为视觉模型的多模态模型参数,所有这里的model.LLM大概率是语言模型,saved_params_llm获取语言模型微调后的adapter状态字典,并将其存储到输出路径下的adapter_model.bin文件中。

        # Save other parameterssaved_params_other = model.feature_proj.state_dict()torch.save(saved_params_other, os.path.join(output_dir, "other_params.bin"))

这段代码用于将多模态模型的中间投影层参数存储到other_params.bin文件中,这里的中间投影层可以参考llava的相关论文,用于将视觉模型的输出映射到语言模型的向量空间,大概如下图所示。
在这里插入图片描述
projectionW就是中间投影层,也是整个多模态项目的核心出装。

        # Save configurationconfig = model.LLM.peft_configselected_adapters = list(config.keys())config[selected_adapters[0]].save_pretrained(output_dir, auto_mapping_dict=None)

peft_config方法获取peftmodel微调需要的参数配置。例如使用LoRA进行微调,config会包含所有必要的参数。config参数包含了adapter的类型、参数和设置。selected_keys获取参数字典中的所有键并将其转换为列表。save_pretrained将选择的适配器参数中的第一个存储到指定目录下,设置自动映射为None。

create_optimizer函数(重构)

    def create_optimizer(self):if is_sagemaker_mp_enabled():return super().create_optimizer()opt_model = self.modelif self.optimizer is None:decay_parameters = get_parameter_names(opt_model, ALL_LAYERNORM_LAYERS)decay_parameters = [name for name in decay_parameters if "bias" not in name]if self.args.feature_proj_lr is not None:projector_parameters = [name for name, _ in opt_model.named_parameters() if "feature_proj" in name]optimizer_grouped_parameters = [{"params": [p for n, p in opt_model.named_parameters() if (n in decay_parameters and n not in projector_parameters and p.requires_grad)],"weight_decay": self.args.weight_decay,},{"params": [p for n, p in opt_model.named_parameters() if (n not in decay_parameters and n not in projector_parameters and p.requires_grad)],"weight_decay": 0.0,},{"params": [p for n, p in opt_model.named_parameters() if (n in decay_parameters and n in projector_parameters and p.requires_grad)],"weight_decay": self.args.weight_decay,"lr": self.args.feature_proj_lr,},{"params": [p for n, p in opt_model.named_parameters() if (n not in decay_parameters and n in projector_parameters and p.requires_grad)],"weight_decay": 0.0,"lr": self.args.feature_proj_lr,},]else:optimizer_grouped_parameters = [{"params": [p for n, p in opt_model.named_parameters() if (n in decay_parameters and p.requires_grad)],"weight_decay": self.args.weight_decay,},{"params": [p for n, p in opt_model.named_parameters() if (n not in decay_parameters and p.requires_grad)],"weight_decay": 0.0,},]optimizer_cls, optimizer_kwargs = Trainer.get_optimizer_cls_and_kwargs(self.args)self.optimizer = optimizer_cls(optimizer_grouped_parameters, **optimizer_kwargs)return self.optimizer

整体含义

创建模型优化器,并且对模型的不同部分采取不同的训练策略,例如权重衰减等,并返回一个自定义的优化器。

逐行解读

   def create_optimizer(self):if is_sagemaker_mp_enabled():return super().create_optimizer()opt_model = self.model

如果启用了模型并行,则调用父类的创建优化器方法,否则将成员变量self.model赋值给opt_model,后续将根据opt模型的参数属性创建自定义的优化器。

        if self.optimizer is None:decay_parameters = get_parameter_names(opt_model, ALL_LAYERNORM_LAYERS)decay_parameters = [name for name in decay_parameters if "bias" not in name]

如果成员变量optimizer为None,代表我们尚未创建一个优化器,进入代码内部。用get_parameter_names获取opt_model中所有LAYERNORM类型层的参数名,并去除掉名字中带有‘bias’(偏置)的参数,这是因为我们不对偏置项进行权重衰减。其余的参数在后续都将应用权重衰减。

            if self.args.feature_proj_lr is not None:projector_parameters = [name for name, _ in opt_model.named_parameters() if "feature_proj" in name]

如果设置了投影层的学习率,我们获取opt_model中所有名字里带有‘feature_proj’的参数,这些参数都是投影层参数,代表我们的模型是多模态模型,具有投影层。

                optimizer_grouped_parameters = [{"params": [p for n, p in opt_model.named_parameters() if (n in decay_parameters and n not in projector_parameters and p.requires_grad)],"weight_decay": self.args.weight_decay,},{"params": [p for n, p in opt_model.named_parameters() if (n not in decay_parameters and n not in projector_parameters and p.requires_grad)],"weight_decay": 0.0,},{"params": [p for n, p in opt_model.named_parameters() if (n in decay_parameters and n in projector_parameters and p.requires_grad)],"weight_decay": self.args.weight_decay,"lr": self.args.feature_proj_lr,},{"params": [p for n, p in opt_model.named_parameters() if (n not in decay_parameters and n in projector_parameters and p.requires_grad)],"weight_decay": 0.0,"lr": self.args.feature_proj_lr,},]

初始化优化器参数组,列表中总共有四个字典,我们逐一来看(看着唬人,其实简单QAQ)。
第一个字典:‘params’键对应于不是投影层参数的所有权重衰减参数;‘weight_dacay’键对应配置参数中的权重衰减值,代表对这些参数应用权重衰减。
第二个字典:‘params’键对应于不是投影层参数的所有权重衰减参数;‘weight_dacay’键的值为0,代表不应用权重衰减。
第三个字典:‘params’键对应于投影层参数的所有权重衰减参数;‘weight_dacay’键对应配置参数中的权重衰减值,“lr”(学习率)为配置参数中的学习率值,代表应用权重衰减,并且初始化学习率。
第四个字典:‘params’键对应于投影层参数的所有权重衰减参数;‘weight_dacay’键的值为0,“lr”(学习率)为配置参数中的学习率值,代表不应用权重衰减,并且初始化学习率。
总而言之,对于非投影层的权重衰减参数,一组应用权重衰减,一组不应用权重衰减。这里的权重衰减参数是如何选出的参考上一段代码。对于投影层的权重衰减参数,一组应用权重衰减,一组不应用权重衰减,并且都有初始的学习率。

            else:optimizer_grouped_parameters = [{"params": [p for n, p in opt_model.named_parameters() if (n in decay_parameters and p.requires_grad)],"weight_decay": self.args.weight_decay,},{"params": [p for n, p in opt_model.named_parameters() if (n not in decay_parameters and p.requires_grad)],"weight_decay": 0.0,},]

与上一段代码相反,这里是不应用投影层的情况。参考上一段代码中的非投影层权重衰减参数的配置即可,同样是一组运用权重衰减,一组不应用权重衰减。

            optimizer_cls, optimizer_kwargs = Trainer.get_optimizer_cls_and_kwargs(self.args)self.optimizer = optimizer_cls(optimizer_grouped_parameters, **optimizer_kwargs)return self.optimizer

使用父类的get_optimizer_cls_and_kwargs方法获取优化器类和优化器参数,传递入配置参数。用之前代码定义的优化器参数组和优化器参数初始化优化器实例,并返回。

create_optimizer_and_scheduler函数(重构)

    def create_optimizer_and_scheduler(self, num_training_steps: int):super().create_optimizer_and_scheduler(num_training_steps)if self.args.local_rank != -1:self.model = torch.nn.parallel.DistributedDataParallel(self.model,device_ids=[self.args.local_rank],output_device=self.args.local_rank,find_unused_parameters=True)

整体含义

这段代码主要用于分布式训练,让模型能够在多个GPU上并行计算。

逐行解读

    def create_optimizer_and_scheduler(self, num_training_steps: int):super().create_optimizer_and_scheduler(num_training_steps)

根据传入的训练迭代次数调用父类的create_optimizer_and_scheduler函数。子类在父类的功能上进行拓展。

        if self.args.local_rank != -1:self.model = torch.nn.parallel.DistributedDataParallel(self.model,device_ids=[self.args.local_rank],output_device=self.args.local_rank,find_unused_parameters=True)

如果local_rank为-1,代表不处于分布式训练环境中,反之local_rank指定了GPU的索引,调用torch.nn.parallel.DistributedDataParallel方法,创建DDP模型。DDP可以让模型进行分布式数据并行。其中self.model为模型实例,device_ids指定模型训练用的GPU编码,output_device指定模型输出的GPU编码,find_unused_parameters检查模型在前向传播后是否有未使用的参数。
至此,项目的Trainer.py源码讲解完毕。

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

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

相关文章

CI/CD

目录 1.什么是CI/CD? 2.Gitlab仓库部署 3.部署Jenkins 3.1 使用jenkins拉取代码 3.2 对代码进行编译、打包 4.部署tomcat服务器 1.什么是CI/CD? 通俗来说就是启动一个服务,能够监听代码变化,然后自动执行打包,发布等流程: CICD 是持…

Jmeter版本下载国内外镜像源

官网最新版本 https://archive.apache.org/dist/jmeter/binaries/历史版本 https://archive.apache.org/dist/jmeter/binaries/ 国内镜像源1.阿里云 https://mirrors.aliyun.com/apache/jmeter/binaries/2.腾讯云 https://mirrors.cloud.tencent.com/apache/jmeter/

dubbo:dubbo+nacos整合springcloud gateway实现网关(三)

文章目录 0. 引言1. 集成gateway网关1.1 实操步骤1.2 dubbo提供者注册到nacos出现两个实例的问题 2. 源码3. 总结 0. 引言 上次我们讲到使用zookeeper作为注册中心搭建dubbo微服务框架,但是我们还缺少一个服务总入口,也就是我们的网关服务。所以今天我们…

Linux设置内网时间同步

背景:公司有三台服务器检测到同步外网的时间,现需要将其修改为同步公司内网自己搭建的ntp服务器 1、登录服务器检查 同步外网无疑 2、修改配置文件,同步内网ntp服务器时间 配置文件源内容如下: 修改后如下: [rootl…

特斯拉电动卡车事故引发安全调查,汽车制造商电动车战略调整

特斯拉电动卡车事故引关注 周三,美国国家运输安全委员会宣布启动对特斯拉电动半挂卡车在加州高速上发生的事故及随后起火事件的全面调查。此次调查将联合加州公路巡警局共同进行,旨在查明事故原因,确保道路安全。事故发生在周一深夜&#xff…

excel实现图片转文字功能/excel 实现导出图片功能/excel导出图片不失真(解决excel导出图片模糊的问题)

excel实现图片转文字功能 excel实现图片转文字功能:方法1:使用QQ的在线文档进行图片转文字方法2:使用WPS的excel文档进行图片转文字pdf图片转表格 使用excel 导出图片的方法(使用Excel内置的“复制为图片”功能)1. 复制…

在 uboot 中实现 UDP 协议

一、明确背景 uboot中有许多通信协议,像TFTP、NFS等,这些协议底层都是基于UDP协议来实现的,由于有一个板子在 uboot 段进行固件下载更新的需求,本来想基于TCP协议来实现自定义通信协议(TCP有自带的拥塞控制和重传机制&…

2024生成式AI商业落地白皮书_火山引擎

更多详细内容请下载资源 2024生成式AI商业落地白皮书-火山引擎

基于Python的mediapipe和opencv的人体骨骼、人体姿态关键点的实时跟踪项目

随着计算机视觉技术的发展,人体姿态估计在虚拟现实、运动分析、人机交互等领域得到了广泛应用。传统的姿态估计方法通常依赖于深度学习模型,需要大量的计算资源。而 Google 开发的 MediaPipe 框架则提供了高效且易于使用的解决方案,它可以在各…

Leetcode 237.19.83.82 删除链表重复结点 C++实现

Leetcode 237. 删除链表中的节点 问题:有一个单链表的head,我们想删除它其中的一个节点node。给你一个需要删除的节点 node 。你将 无法访问 第一个节点head。链表的所有值都是唯一的,并且保证给定的节点 node不是链表中的最后一个节点。删除…

重磅!RISC-V+OpenHarmony平板电脑发布

仟江水商业电讯(8月18日 北京 委托发布)RISC-V作为历史上全球发展速度最快、创新最为活跃的开放指令架构,正在不断拓展高性能计算领域的边界。OpenHarmony是由开放原子开源基金会孵化并运营的开源项目,已成为发展速度最快的智能终…

【机器学习】逻辑回归原理(极大似然估计,逻辑函数Sigmod函数模型详解!!!)

目录 🍔 逻辑回归应用场景 🍔 极大似然估计 2.1 为什么要有极大似然估计? 2.2 极大似然估计步骤 2.3 极大似然估计的例子 🍔 Sigmod函数模型 3.1 逻辑斯特函数的由来 3.2 Sigmod函数绘图 3.3 进一步探究-加入线性回归 3…

OW-VISCap——开放世界视频实例分割方法研究

概述 论文地址:https://arxiv.org/pdf/2404.03657 本文提出了一种名为 OW-VISCap(开放世界视频实例分割和字幕)的方法。其三大贡献是 开放世界对象查询:除了已知对象查询外,还引入了开放世界对象查询,以发…

专题---自底向上的计算机网络(计算机网络相关概述)

目录 计算机网络相关概述 物理层 数据链路层 网络层 运输层 应用层 网络安全 1.计算机网络相关概述(具体细节http://t.csdnimg.cn/NITAW) 什么是计算机网络? 计算机网络是将一个分散的,具有独立功能的计算机系统&#x…

jmeter中添加ip欺骗

1、首先在本机电脑中通过配置文件创建添加ip的配置文件,先创建一个txt格式的,直接修改文件名以及后缀为ips.bat 2、编辑该ips.bat文件,在文件中输入如下内容,用于快速给本机添加ip地址,(2,1&…

四川正信法律:男朋友借钱拉黑我了怎么办

在情感的海洋中,我们或许都曾遇到过打着爱情旗号的骗局。当男朋友借钱后选择拉黑,这不仅是物质上的损失,更是心灵上的重创。面对这样的困境,我们该如何应对? 面对现实,接受这一切的发生。痛苦与愤怒虽为正常反应&…

【MySQL】JDBC的基础使用

系列文章目录 第一章 数据库基础 第二章 数据库基本操作 第三章数据库约束 第四章表的设计 第五章查询进阶 第六章索引和事务 文章目录 系列文章目录前言一、JDBC基本概念二、JDBC的准备工作三、JDBC-Demo小结 四、JDBC进阶写法总结 前言 在前面对MySQL已经有了基本的认知&am…

状态dp或滑动窗口

前言&#xff1a;这个题目可以用状态dp来做&#xff0c;其实还有一个思路&#xff0c;类似滑动窗口&#xff0c;如果有遇到第二个0&#xff0c;左指针加一 class Solution { public:int longestSubarray(vector<int>& nums) {int n nums.size();vector<vector<…

2024年【起重信号司索工(建筑特殊工种)】考试题及起重信号司索工(建筑特殊工种)免费试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 起重信号司索工(建筑特殊工种)考试题是安全生产模拟考试一点通总题库中生成的一套起重信号司索工(建筑特殊工种)免费试题&#xff0c;安全生产模拟考试一点通上起重信号司索工(建筑特殊工种)作业手机同步练习。2024年…

洛谷 P4516 [JSOI2018] 潜入行动

题目来源于&#xff1a;洛谷 题目本质&#xff1a;背包&#xff0c;树形dp 解题思路&#xff1a; 假设当前合并两个背包f[u][a][p1][q1] 和f[v][b][p2][q2] &#xff0c;其中 v 是 u 的儿子。考虑合并后的f[u][ab][p3][q3],q3 是合并后点 u 是否被监听&#xff0c;有两种情况…