当前位置: 首页 > news >正文

大模型微调技术总结及使用GPU对VisualGLM-6B进行高效微调

1. 概述

在深度学习中,微调(Fine-tuning)是一种重要的技术,用于改进预训练模型的性能。在预训练模型的基础上,针对特定任务(如文本分类、机器翻译、情感分析等),使用相对较小的有监督数据集对模型进行进一步训练的过程。预训练模型学习到了语言的一般规律,如语法、语义等知识,通过微调,模型可以利用预训练过程中学习到的知识,并结合特定任务的数据,在该任务上达到更好的准确率、召回率等性能指标。

在微调过程中,通常会冻结预训练模型的一部分参数,只训练模型的顶层或特定层,以便模型能够针对新任务进行调整。所需的Fine-tuning量取决于预训练语料库和任务特定语料库之间的相似性。如果两者相似,可能只需要少量的Fine tuning。如果两者不相似,则可能需要更多的Fine tuning。
在这里插入图片描述
为什么需要微调?

  • 微调的价值:可以帮助我们更好地利用预训练模型的知识,加速和优化新任务的训练过程,同时减少对新数据的需求和降低训练成本。
  • 减少对新数据的需求:从头开始训练一个大型神经网络通常需要大量的数据和计算资源,而在实际应用中,我们可能只有有限的数据集。通过微调预训练模型,我们可以利用预训练模型已经学到的知识,减少对新数据的需求,从而在小数据集上获得更好的性能。
  • 降低训练成本:由于我们只需要调整预训练模型的部分参数,而不是从头开始训练整个模型,因此可以大大减少训练时间和所需的计算资源。这使得微调成为一种高效且经济的解决方案,尤其适用于资源有限的环境。

2. 微调的分类

  • 全微调(Full Fine-tuning):这是一种比较直接的方法,使用特定任务的数据集对整个预训练模型的所有参数进行微调,适用于任务和预训练模型之间存在较大差异的情况,或任务需要模型具有高度灵活性和自适应能力的情况。这种方法需要较大的计算资源和时间,但可以获得更好的性能。也可能会出现 “灾难性遗忘” 的问题,即模型在微调过程中过度拟合特定任务的数据,而忘记了预训练过程中学习到的一些通用知识。

  • 参数高效微调(Parameter-Efficient Fine-Tuning, PEFT):为了解决全微调的问题,参数高效微调方法应运而生。通过微调少量参数来达到接近微调全量参数的效果,使得在GPU资源不足的情况下也可以微调大模型。PEFT技术包括LoRA、QLoRA、适配器调整(Adapter Tuning)、前缀调整(Prefix Tuning)、提示调整(Prompt Tuning)及P-Tuning v2等多种方法。
    在这里插入图片描述

3. 常见的微调技术

3.1 LoRA

LoRA(Low-Rank Adaptation)由微软在2021年提出,通过在模型的关键层次中引入小型、低秩的矩阵来实现模型行为的微调,而无需对整个模型结构进行大幅度修改。简单来说,Lora的本质就是对所有权重矩阵套了一层“壳”,这些壳会对原来的预训练权重矩阵进行加减使其更加适合下游任务,即实现微调。他的假设前提是预训练模型具有低的"内在维度",因此认为在模型适配下游任务的过程中,权重更新也应该具有低的“内在秩”。通常只需要全模型微调的 10%-20% 的计算资源。因此在资源受限的环境下,LoRA 可以轻松实现大规模语言模型的微调。LoRA 通常与 Transformer 架构结合使用,特别是在自注意力模块和前馈神经网络中。在实际应用中,LoRA 可以与其他参数高效微调方法(如适配器调整、前缀调整等)结合使用,以进一步提高微调效果。

具体来说,权重矩阵会被分解成两个小矩阵的乘积。假设原始权重矩阵为 W,尺寸为 d×d。LoRA 会将其更新为 W ′ =W+ΔW,其中 ΔW=A×B,A 和 B 是两个低秩矩阵。矩阵 A 用于降维(从大维度到小维度),矩阵 B 用于升维(从小维度回到大维度),B 和 A 的尺寸分别为 d×r 和 r×d,而 r≪d。这种方法在训练时只更新 A 和 B,而原始权重矩阵 W 保持不变,从而大大减少了训练参数量,提高了计算效率。矩阵 A 会使用高斯初始化,矩阵 B 则初始化为零矩阵。这样在训练初期,新增的通路对模型的影响为零,训练开始时不会对原有模型的行为产生影响。
在这里插入图片描述

3.2 QLoRA

2023 年 5 月 23 日 QLoRA 的推出,它代表具有低秩适配器的量化 LLM,这是一种能够以最小的内存使用量高效微调大型语言模型的方法。QLoRA 不会从头开始重新训练整个模型,而是向模型添加少量新参数,这些参数专门针对新任务进行训练,同时将原始预训练语言模型参数保持在 4 位量化状态。

QLoRA 背后的关键理念是通过减少内存使用量来提高 LLM 的效率,同时保持可靠的性能。它通过几个步骤实现这一目标:引入 4 位量化、一种称为 4 位 NormalFloat (NF4) 的新数据类型、双量化和分页优化器。

(1)4 位量化
传统的深度学习模型通常使用 32 位浮点数(float32)或 16 位浮点数(float16)进行计算。这些数据类型占用较多内存,尤其是在处理大型语言模型时,内存需求会非常高。而 QLoRA 引入了 4 位量化,即将模型的权重和激活值从 32 位或 16 位浮点数压缩到 4 位。这种量化可以显著减少内存占用,从而提高模型的效率。

(2)4位NormalFloat (NF4) 数据类型
在量化过程中,选择合适的数据类型至关重要。不同的数据类型在精度和内存占用之间需要权衡。NF4 是一种针对正态分布权重优化的 4 位数据类型。与其他4位数据类型(如INT4)相比,NF4 在实验中表现出更好的性能,能够在量化后保持较高的精度。

(3)双量化
即使使用了 4 位量化,量化过程中仍然会引入一些量化常数,这些常数本身也需要占用内存。因此,QLoRA 通过进一步量化这些量化常数,进一步减少内存占用。

(4)分页优化器
在训练大型模型时,梯度检查点(gradient checkpointing)是一种常用的技巧,用于减少内存占用。然而,这种方法在某些情况下会导致内存峰值问题。QLoRA 通过将模型参数划分为多个小组,并分别处理每个小组,从而避免了梯度检查点期间的内存峰值问题。此外,它还使用NVIDIA统一内存(Unified Memory)来管理内存页面,进一步优化内存使用。

在这里插入图片描述

3.3 Adapter Tuning

Adapter Tuning 方法由 Houlsby N 等人在2019年提出,它涉及将称为适配器的小型神经网络模块整合到Transformer模型中,这些适配器模块作为每个Transformer层内的附加组件。针对每一个 Transformer 层,增加了两个 Adapter 结构(分别是多头注意力的投影之后和第二个 feed-forward 层之后),在训练时,固定住原来预训练模型的参数不变,只对新增的 Adapter 结构和 Layer Norm 层进行微调,从而保证了训练的高效性。虽然通过增加模型层数来引入额外的灵活性,但这也导致了额外的推理延迟。

什么是适配器模块?

我们从三点来看一下适配器模块在 transformer 架构中的应用:

  • 适配器模块(右)首先将原始 d 维特征投影到较小的 m 维向量,应用非线性,然后将其投影回 d 维。
  • 可以看出,该模块具有跳跃连接功能——有了它,当投影层的参数初始化为接近零时,最终导致模块的接近恒等初始化。这对于稳定的微调是必需的,并且很直观,因为有了它,我们基本上不会干扰预训练的学习。
  • 在变压器块(左)中,适配器直接应用于每个层(注意力和前馈)的输出。

如何确定m的值?

  • 适配器模块中的大小 m 决定了可优化参数的数量,因此存在参数与性能的权衡。
  • 原始论文通过实验调查了不同适配器尺寸 m 的性能保持相当稳定,因此对于给定模型,所有下游任务都可以使用固定尺寸。
    在这里插入图片描述

3.4 Prefix Tuning

前缀微调(Prefix tuning)是一种用于语言模型的技术,其中在每个Transformer层中添加了一组可训练的连续向量序列,这些向量被称为前缀。这些前缀是特定于任务的,可以被视为虚拟的标记嵌入。在训练过程中,仅更新这些前缀参数,而模型的其余部分保持不变。

前缀向量的优化是通过一种重参数化技巧实现的。不是直接优化前缀,而是学习一个MLP(多层感知器)函数,它将一个较小的矩阵映射到前缀的参数矩阵。这种重参数化技巧有助于实现稳定的训练。优化完成后,映射函数会被丢弃,只保留推导出的前缀向量,以增强特定任务的性能。这种方法专注于仅训练前缀参数,使其成为一种参数高效的模型优化方法。
在这里插入图片描述

3.5 Prompt Tuning

提示微调(Prompt tuning)是一种与前缀微调不同的技术(也就是我们常说的P-Tuning),它侧重于在语言模型的输入层引入可训练的提示向量。该方法基于离散提示方法(人工设计,如文本模板),并通过在输入文本中包含软提示标记(以自由形式或前缀形式)来扩展输入文本。在实现过程中,特定任务的提示嵌入与输入文本嵌入相结合,并输入到语言模型中。简单来说,就是通过设计或学习一个固定的提示,将其与输入文本结合,引导模型生成期望的输出。

该方法在少样本或零样本学习中表现较好,但对提示的设计依赖较强。
在这里插入图片描述

3.6 P-Tuning v2

P-Tuning v2 是 Prompt Tuning 的改进版本,通过引入连续提示嵌入和多层提示机制,提升提示的表达能力和模型性能。

  • 连续提示嵌入:将离散提示替换为可学习的连续向量,增强了提示的灵活性。
  • 多层提示:在模型的不同层引入提示嵌入,捕捉多层次的语义信息。
  • 提示编码器:使用双向 LSTM 或 Transformer 编码提示嵌入,进一步提升提示的语义表达能力。
  • 通用性:适用于多种预训练模型(如BERT、GPT等),且在少样本学习中表现更稳定。
    在这里插入图片描述

4. 微调步骤

笔者重点介绍怎么使用参数高效微调方法微调视觉语言大模型。

VisualGLM-6B 是一个结合视觉和语言的多模态大模型,支持图像和文本的联合理解与生成。对其进行参数高效微调(Parameter-Efficient Fine-Tuning, PEFT)可以显著降低计算和存储成本。以下是针对使用 GPU 对VisualGLM-6B 的高效微调步骤:

(1)选择高效微调方法

笔者推荐使用 LoRA 或 Adapter,因为它们在多模态任务中表现良好。

(2)环境部署

  • 安装 PyTorch 和 Hugging Face Transformers。
  • 安装 PEFT 库(如 peft)和 VisualGLM-6B 相关依赖。笔者已经在之前的博客中介绍过 VisualGLM-6B 的环境部署与推理。
  • 注意:GPU 需要至少 24GB 显存(如 NVIDIA A100、RTX 3090 等)。
conda create -n visualglm python=3.8 -y
conda activate visualglm
conda install pytorch==2.0.0 torchvision==0.15.0 torchaudio==2.0.0 pytorch-cuda=11.7 -c pytorch -c nvidia
pip install transformers peft datasets accelerate

(2)加载预训练模型

使用 Hugging Face 加载 VisualGLM-6B 的预训练模型和分词器。将模型移动到 GPU。同样笔者已经在之前的博客中介绍过如何加载本地模型。

from transformers import AutoModelForCausalLM, AutoTokenizer
import torch# 加载模型和分词器
model_name = "THUDM/visualglm-6b"
model = AutoModelForCausalLM.from_pretrained(model_name, trust_remote_code=True)
tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True)# 将模型移动到 GPU
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)

(3) 应用高效微调方法

以 LoRA 为例,使用 peft 库实现:

  • 配置 LoRA
from peft import get_peft_model, LoraConfig, TaskType# 配置 LoRA
lora_config = LoraConfig(task_type=TaskType.CAUSAL_LM,  # 任务类型r=8,  # 低秩矩阵的秩lora_alpha=32,  # 缩放因子lora_dropout=0.1,  # Dropout 概率target_modules=["query_key_value"],  # 目标模块(VisualGLM-6B 中的注意力层)
)# 应用 LoRA
model = get_peft_model(model, lora_config)
model.to(device)  # 将 LoRA 模型移动到 GPU

(4)准备数据

加载多模态数据集(如图像-文本对),并对图像和文本进行预处理。

from datasets import load_dataset
from torchvision import transforms
from PIL import Image# 加载本地数据集
dataset = load_dataset("json", data_files="dataset/annotations.json")# 图像预处理
image_transform = transforms.Compose([transforms.Resize((224, 224)),transforms.ToTensor(),
])# 文本预处理
def preprocess_function(examples):examples["image"] = [image_transform(Image.open(img).convert("RGB")) for img in examples["image_path"]]examples["text"] = tokenizer(examples["text"], padding="max_length", truncation=True, return_tensors="pt")return examplestokenized_dataset = dataset.map(preprocess_function, batched=True)

(5)设置训练参数

from transformers import TrainingArgumentstraining_args = TrainingArguments(output_dir="./results",per_device_train_batch_size=4,  # 根据 GPU 显存调整per_device_eval_batch_size=4,num_train_epochs=100,learning_rate=1e-4,evaluation_strategy="epoch",save_strategy="epoch",logging_dir="./logs",fp16=True,  # 启用混合精度训练以节省显存gradient_accumulation_steps=2,  # 梯度累积
)

(6)训练模型

使用 Trainer 进行训练。

from transformers import Trainertrainer = Trainer(model=model,args=training_args,train_dataset=tokenized_dataset["train"],eval_dataset=tokenized_dataset["test"],
)# 开始训练
trainer.train()

(7)评估模型

在测试集上评估模型性能。计算任务相关指标(如生成文本的 BLEU 分数或图像-文本匹配准确率)。

results = trainer.evaluate()
print(results)

(8)保存模型

保存微调后的模型和配置。

model.save_pretrained("./fine-tuned-visualglm-6b")
tokenizer.save_pretrained("./fine-tuned-visualglm-6b")

(9)部署与推理

from transformers import pipeline
from PIL import Image# 加载微调后的模型
model = AutoModelForCausalLM.from_pretrained("./fine-tuned-visualglm-6b", trust_remote_code=True)
tokenizer = AutoTokenizer.from_pretrained("./fine-tuned-visualglm-6b", trust_remote_code=True)device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)# 推理示例
image = Image.open("image.jpg").convert("RGB")
text = "Describe the image:"
inputs = tokenizer(text, return_tensors="pt").to(device)
image_input = image_transform(image).unsqueeze(0).to(device)outputs = model.generate(inputs["input_ids"], image_input=image_input)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))

5. 自定义数据集

数据集结构:

  • 图像文件:存储图像的文件夹。
  • 标注文件:存储图像-文本对的元数据(如 JSON、CSV 文件)。

示例

dataset/
├── images/
│   ├── 1.jpg
│   ├── 2.jpg
│   └── 3.jpg
└── annotations.json

标注文件(如 annotations.json)通常包含以下信息:

  • 图像路径:指向图像文件的路径。
  • 文本描述:与图像相关的文本描述。

JSON 格式示例:

[{"image_path": "images/1.jpg","text": "A cat sitting on a wooden table."},{"image_path": "images/2.jpg","text": "A group of people playing soccer in a field."},{"image_path": "images/3.jpg","text": "A beautiful sunset over the mountains."}
]

CSV 格式示例:

image_path,text
images/1.jpg,"A cat sitting on a wooden table."
images/2.jpg,"A group of people playing soccer in a field."
images/3.jpg,"A beautiful sunset over the mountains."
http://www.lryc.cn/news/530937.html

相关文章:

  • WPF进阶 | WPF 样式与模板:打造个性化用户界面的利器
  • Java 大视界 -- Java 大数据在自动驾驶中的数据处理与决策支持(68)
  • 自动化构建-make/Makefile 【Linux基础开发工具】
  • python学opencv|读取图像(五十二)使用cv.matchTemplate()函数实现最佳图像匹配
  • 通信方式、点对点通信、集合通信
  • TCP编程
  • OpenAI 实战进阶教程 - 第七节: 与数据库集成 - 生成 SQL 查询与优化
  • Apache Iceberg数据湖技术在海量实时数据处理、实时特征工程和模型训练的应用技术方案和具体实施步骤及代码
  • QT交叉编译环境搭建(Cmake和qmake)
  • Turing Complete-成对的麻烦
  • 寒假刷题Day20
  • deepseek 本地化部署和小模型微调
  • 【Java异步编程】基于任务类型创建不同的线程池
  • makailio-alias_db模块详解
  • 文字显示省略号
  • [LeetCode] 字符串完整版 — 双指针法 | KMP
  • 从零开始部署Dify:后端与前端服务完整指南
  • springboot中路径默认配置与重定向/转发所存在的域对象
  • 二叉树——429,515,116
  • Leetcode 3444. Minimum Increments for Target Multiples in an Array
  • 分享半导体Fab 缺陷查看系统,平替klarity defect系统
  • Java基础——分层解耦——IOC和DI入门
  • DeepSeek-R1 本地部署教程(超简版)
  • Vue3学习笔记-模板语法和属性绑定-2
  • csapp笔记3.6节——控制(1)
  • PYH与MAC的桥梁MII/MIIM
  • 国内flutter环境部署(记录篇)
  • 选择排序_75. 颜色分类
  • C++ Primer 标准库vector
  • C# 数组和列表的基本知识及 LINQ 查询