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

模型微调方法LoRA

简介:个人学习分享,如有错误,欢迎批评指正。

在近年来,随着大型语言模型(如GPT-3、GPT-4等)的迅速发展,如何高效、经济地微调这些模型以适应特定任务成为了一个重要的研究方向。Low-Rank Adaptation(低秩适配,简称LoRA)是一种创新的方法,旨在通过低秩矩阵分解技术高效地微调大型语言模型。

一、背景与动机

1. 大型语言模型的发展

大型语言模型凭借其在自然语言处理任务中的卓越表现,广泛应用于文本生成、翻译、问答系统等领域。然而,这些模型通常拥有数十亿甚至上千亿的参数,导致以下问题:

  • 高昂的计算成本:训练和微调这些模型需要大量的计算资源,导致计算成本高。
  • 存储需求巨大:每个微调任务需要存储一份完整的模型参数,导致存储成本高昂。
  • 微调效率低:针对不同任务分别微调模型,缺乏共享机制,降低了效率。

2. 微调方法的发展

为了解决上述问题,研究者提出了多种微调方法,包括:

  • 全参数微调(Full Fine-Tuning):调整模型的所有参数,适应性强但成本高。
  • 冻结部分参数:只微调部分参数,降低计算和存储成本,但可能限制模型的适应能力。
  • 参数高效微调(Parameter-Efficient Fine-Tuning, PEFT):通过引入少量可训练参数,实现高效微调。LoRA 即为其中一种典型方法。

二、LoRA的具体实现步骤

在这里插入图片描述

1. 确定适配层

在大型语言模型(如 Transformer 架构)中,LoRA 通常应用于关键的权重矩阵,如自注意力机制中的查询(Query)、键(Key)、值(Value)矩阵以及前馈网络中的权重矩阵。这些权重矩阵通常是大型的线性变换矩阵,适合进行低秩分解

2. 插入低秩适配模块

对于每个需要适配的权重矩阵 W W W,LoRA 通过插入两个新的可训练矩阵 A A A B B B 来表示权重的更新:

W ′ = W + Δ W = W + A ⋅ B W' = W + \Delta W = W + A \cdot B W=W+ΔW=W+AB

其中,原始权重 W W W 保持冻结,不参与训练。只有 A A A B B B 参与训练,从而实现参数高效的微调。

3. 参数初始化

  • 矩阵 A A A 的初始化:通常采用随机初始化,使用小的随机值(如从标准正态分布中采样并乘以一个缩放因子),以确保初始的 Δ W \Delta W ΔW 对模型输出的影响较小。
  • 矩阵 B B B 的初始化:通常初始化为零矩阵,这样初始的 Δ W = A ⋅ B = 0 \Delta W = A \cdot B = 0 ΔW=AB=0确保初始模型行为与原始模型一致

4. 训练过程

在微调过程中,只有矩阵 A A A B B B 是可训练的参数。训练目标仍然是最小化任务特定的损失函数,但优化器只更新 A A A B B B,保持 W W W 不变。

训练步骤如下:

  1. 前向传播:计算 W ′ = W + A ⋅ B W' = W + A \cdot B W=W+AB,并使用 W ′ W' W 进行前向传播。
  2. 损失计算:根据任务目标计算损失。
  3. 反向传播:仅计算 A A A B B B 的梯度。
  4. 参数更新:使用优化器更新 A A A B B B

5. 推理阶段

在推理阶段,使用微调后的权重 W ′ = W + A ⋅ B W' = W + A \cdot B W=W+AB 进行计算。由于 W W W 是冻结的,只需要存储 A A A B B B 即可,不需要额外的计算开销。

三、LoRA在Transformer中的集成

Transformer 架构是目前大型语言模型的主流架构,LoRA 主要针对 Transformer 中的线性层进行适配。以下以 Transformer 的自注意力机制为例,说明 LoRA 的集成方式。

1. 自注意力机制中的权重矩阵

在自注意力机制中,有三个主要的权重矩阵:查询矩阵 W Q W_Q WQ、键矩阵 W K W_K WK 和值矩阵 W V W_V WV。这些矩阵用于计算注意力得分和加权输出。

2. 应用 LoRA

对于每个权重矩阵 W W W(即 W Q , W K , W V W_Q, W_K, W_V WQ,WK,WV),LoRA 引入两个低秩矩阵 A A A B B B,并将其更新添加到原始权重上:

W q ′ = W q + A q ⋅ B q W'_q = W_q + A_q \cdot B_q Wq=Wq+AqBq

W k ′ = W k + A k ⋅ B k W'_k = W_k + A_k \cdot B_k Wk=Wk+AkBk

W v ′ = W v + A v ⋅ B v W'_v = W_v + A_v \cdot B_v Wv=Wv+AvBv

其中, A q , B q , A k , B k , A v , B v A_q, B_q, A_k, B_k, A_v, B_v Aq,Bq,Ak,Bk,Av,Bv 分别对应于查询、键和值的适配矩阵。

3. 前馈网络中的适配

同样地,Transformer 的前馈网络中的权重矩阵(如 W 1 W_1 W1 W 2 W_2 W2)也可以应用 LoRA 进行适配:

W 1 ′ = W 1 + A W 1 ⋅ B W 1 W'_1 = W_1 + A_{W1} \cdot B_{W1} W1=W1+AW1BW1

W 2 ′ = W 2 + A W 2 ⋅ B W 2 W'_2 = W_2 + A_{W2} \cdot B_{W2} W2=W2+AW2BW2

通过这种方式,整个 Transformer 层的关键权重矩阵都得到了高效的适配。

四、LoRA的数学基础详解

1. 矩阵的秩与低秩分解

1.1 矩阵的秩(Rank of a Matrix)

定义:一个矩阵的秩其行或列中线性无关向量的最大数量。换句话说,秩衡量了矩阵的“信息量”或“复杂性”。

  • 形式定义:对于一个 m × n m \times n m×n 的矩阵 W W W,其秩 rank ( W ) \text{rank}(W) rank(W) W W W所有行或列的最大线性无关集的大小
  • 性质
    • 0 ≤ rank ( W ) ≤ min ⁡ ( m , n ) 0 \leq \text{rank}(W) \leq \min(m, n) 0rank(W)min(m,n)
    • 一个矩阵的秩等于其非零奇异值的数量(由奇异值分解决定)。

1.2 奇异值分解(Singular Value Decomposition, SVD)

定义:奇异值分解是将任意 m × n m \times n m×n 矩阵 W W W 分解为三个矩阵的乘积:

W = U Σ V T W = U \Sigma V^T W=UΣVT

其中:

  • U U U 是一个 m × m m \times m m×m 的正交矩阵。
  • Σ \Sigma Σ 是一个 m × n m \times n m×n 的对角矩阵,对角线上是 W W W 的奇异值(非负实数),按降序排列。
  • V V V 是一个 n × n n \times n n×n 的正交矩阵。

应用:SVD 可以用于低秩近似,通过保留最大的 r r r 个奇异值及其对应的奇异向量,得到一个近似矩阵:

W ≈ U r Σ r V r T W \approx U_r \Sigma_r V_r^T WUrΣrVrT

其中 U r U_r Ur V r V_r Vr 分别是 U U U V V V 的前 r r r 列, Σ r \Sigma_r Σr 是保留前 r r r 个奇异值的对角矩阵。

1.3 低秩矩阵

定义:一个矩阵 W W W 的秩远小于其维度时,称其为低秩矩阵。即对于 W ∈ R m × n W \in \mathbb{R}^{m \times n} WRm×n,如果 rank ( W ) = r \text{rank}(W) = r rank(W)=r r ≪ min ⁡ ( m , n ) r \ll \min(m, n) rmin(m,n),则 W W W 是低秩矩阵。

性质

  • 低秩矩阵具有较少的自由参数,便于存储和计算。
  • 在许多实际应用中,数据矩阵往往是低秩或近似低秩的,这使得低秩分解成为有效的降维和特征提取工具。

2. LoRA的低秩假设

LoRA 基于一个关键假设,即在微调大型语言模型的过程中,模型权重的更新 Δ W \Delta W ΔW 可以通过两个低秩矩阵的乘积来近似表示

Δ W = A ⋅ B \Delta W = A \cdot B ΔW=AB

其中:

  • A ∈ R d × r A \in \mathbb{R}^{d \times r} ARd×r
  • B ∈ R r × k B \in \mathbb{R}^{r \times k} BRr×k
  • r r r 是一个远小于 d d d k k k 的秩(即 r ≪ d , k r \ll d, k rd,k),称为“瓶颈维度”。

2.1 参数量的减少

传统的全参数微调需要调整和存储整个权重矩阵 W W W,其参数量为 d × k d \times k d×k。而 LoRA 通过低秩分解,将更新表示为 A ⋅ B A \cdot B AB,其参数量为:

参数量 = d × r + r × k \text{参数量} = d \times r + r \times k 参数量=d×r+r×k

r ≪ d , k r \ll d, k rd,k 时,参数量大幅减少。例如,对于 W ∈ R 4096 × 4096 W \in \mathbb{R}^{4096 \times 4096} WR4096×4096 r = 4 r = 4 r=4

参数量 = 4096 × 4 + 4 × 4096 = 32768 \text{参数量} = 4096 \times 4 + 4 \times 4096 = 32768 参数量=4096×4+4×4096=32768

相比全量参数( 409 6 2 = 16 , 777 , 216 4096^2 = 16,777,216 40962=16,777,216),参数量减少了约 99.8%。

2.2 低秩假设的合理性

  • 参数共享:低秩分解通过共享参数 A A A B B B,捕捉权重更新中的全局结构和模式,避免了全参数微调中的冗余
  • 泛化能力:低秩矩阵具有更高的泛化能力,能够在有限的参数量下捕捉到任务相关的重要信息,减少过拟合的风险
  • 经验支持:在实践中,许多微调任务表明,权重更新确实具有低秩特性,尤其是在大规模模型和丰富数据的情况下。

3. 参数化更新的数学表示

3.1 权重更新的表示

在微调过程中,假设原始权重矩阵为 W W W,微调后的权重矩阵为 W ′ W' W,则:

W ′ = W + Δ W = W + A ⋅ B W' = W + \Delta W = W + A \cdot B W=W+ΔW=W+AB

这里:

  • W W W 保持冻结,不参与训练。
  • A A A B B B 是可训练的低秩矩阵,用于表示权重的适配更新。

3.2 多层适配

在 Transformer 架构中,通常有多个线性层(如自注意力机制中的查询、键、值矩阵,以及前馈网络的线性层)。对于每个需要适配的权重矩阵 W i W_i Wi,引入对应的 A i A_i Ai B i B_i Bi

W i ′ = W i + A i ⋅ B i ∀ i ∈ { 适配层集合 } W'_i = W_i + A_i \cdot B_i \quad \forall i \in \{\text{适配层集合}\} Wi=Wi+AiBii{适配层集合}

3.3 缩放因子(Scaling Factor)

为了控制适配更新的幅度,引入一个缩放因子 α \alpha α

W ′ = W + α r A ⋅ B W' = W + \frac{\alpha}{r} A \cdot B W=W+rαAB

通常, α \alpha α 被设置为与 r r r 成正比,以保持数值稳定性和梯度的适当尺度

3.4 初始化策略

  • 矩阵 A A A:通常采用随机小值初始化,如正态分布 N ( 0 , σ 2 ) \mathcal{N}(0, \sigma^2) N(0,σ2),其中 σ \sigma σ 是一个小的缩放因子(例如 0.01),确保初始的 Δ W \Delta W ΔW 对模型的影响较小。
  • 矩阵 B B B:通常初始化为零矩阵 Δ W = 0 \Delta W = 0 ΔW=0,模型行为与原始模型一致。

4. 理论支持与优势

4.1 表达能力

虽然低秩矩阵的参数量较少,但通过组合 A A A B B B,LoRA 能够捕捉到复杂的权重更新模式。理论上,任何 W W W 的近似可以通过足够大的 r r r 实现,但 LoRA 通过选择适当的 r r r,在参数效率和表达能力之间取得平衡。

4.2 减少过拟合

低秩限制了一组特定的权重更新,使模型更倾向于学习全局一致的适配模式,减少了微调过程中的过拟合风险,尤其是在数据量有限的情况下。

4.3 计算与存储效率

由于 A A A B B B 的低秩特性,微调过程中涉及的矩阵乘法和存储需求显著减少,适合资源受限的环境,如边缘设备和大规模分布式训练。

4.4 参数共享与模块化

LoRA 的低秩适配模块可以被多个任务共享或模块化插入,促进了参数的重用和模型的灵活扩展,适合多任务学习和模型集成。

5. 数学推导与示例

5.1 权重更新的最小化问题

微调的目标是通过调整权重 W ′ W' W 最小化任务特定的损失函数 L ( W ′ ) \mathcal{L}(W') L(W)。在 LoRA 框架下,问题转化为:

min ⁡ A , B L ( W + A ⋅ B ) \min_{A, B} \mathcal{L}(W + A \cdot B) A,BminL(W+AB)

其中, A ∈ R d × r A \in \mathbb{R}^{d \times r} ARd×r B ∈ R r × k B \in \mathbb{R}^{r \times k} BRr×k

5.2 梯度计算

在反向传播过程中,只需计算关于 A A A B B B 的梯度,而不涉及 W W W

∂ L ∂ A = ∂ L ∂ W ′ ⋅ B ⊤ \frac{\partial \mathcal{L}}{\partial A} = \frac{\partial \mathcal{L}}{\partial W'} \cdot B^\top AL=WLB

∂ L ∂ B = A ⊤ ⋅ ∂ L ∂ W ′ \frac{\partial \mathcal{L}}{\partial B} = A^\top \cdot \frac{\partial \mathcal{L}}{\partial W'} BL=AWL

5.3 优化算法

使用标准的优化算法(如 Adam)仅更新 A A A B B B,其参数更新规则为:

A ← A − η ∂ L ∂ A A \leftarrow A - \eta \frac{\partial \mathcal{L}}{\partial A} AAηAL

B ← B − η ∂ L ∂ B B \leftarrow B - \eta \frac{\partial \mathcal{L}}{\partial B} BBηBL

其中 η \eta η 是学习率。

5.4 示例计算

假设:

  • W ∈ R 4 × 4 W \in \mathbb{R}^{4 \times 4} WR4×4
  • r = 2 r = 2 r=2
  • 初始化 A A A B B B 如下:

A = [ a 11 a 12 a 21 a 22 a 31 a 32 a 41 a 42 ] , B = [ b 11 b 12 b 13 b 14 b 21 b 22 b 23 b 24 ] A = \begin{bmatrix} a_{11} & a_{12} \\ a_{21} & a_{22} \\ a_{31} & a_{32} \\ a_{41} & a_{42} \end{bmatrix}, \quad B = \begin{bmatrix} b_{11} & b_{12} & b_{13} & b_{14} \\ b_{21} & b_{22} & b_{23} & b_{24} \end{bmatrix} A= a11a21a31a41a12a22a32a42 ,B=[b11b21b12b22b13b23b14b24]

Δ W = A ⋅ B \Delta W = A \cdot B ΔW=AB 为:

Δ W = [ a 11 b 11 + a 12 b 21 a 11 b 12 + a 12 b 22 a 11 b 13 + a 12 b 23 a 11 b 14 + a 12 b 24 a 21 b 11 + a 22 b 21 a 21 b 12 + a 22 b 22 a 21 b 13 + a 22 b 23 a 21 b 14 + a 22 b 24 a 31 b 11 + a 32 b 21 a 31 b 12 + a 32 b 22 a 31 b 13 + a 32 b 23 a 31 b 14 + a 32 b 24 a 41 b 11 + a 42 b 21 a 41 b 12 + a 42 b 22 a 41 b 13 + a 42 b 23 a 41 b 14 + a 42 b 24 ] \Delta W = \begin{bmatrix} a_{11}b_{11} + a_{12}b_{21} & a_{11}b_{12} + a_{12}b_{22} & a_{11}b_{13} + a_{12}b_{23} & a_{11}b_{14} + a_{12}b_{24} \\ a_{21}b_{11} + a_{22}b_{21} & a_{21}b_{12} + a_{22}b_{22} & a_{21}b_{13} + a_{22}b_{23} & a_{21}b_{14} + a_{22}b_{24} \\ a_{31}b_{11} + a_{32}b_{21} & a_{31}b_{12} + a_{32}b_{22} & a_{31}b_{13} + a_{32}b_{23} & a_{31}b_{14} + a_{32}b_{24} \\ a_{41}b_{11} + a_{42}b_{21} & a_{41}b_{12} + a_{42}b_{22} & a_{41}b_{13} + a_{42}b_{23} & a_{41}b_{14} + a_{42}b_{24} \end{bmatrix} ΔW= a11b11+a12b21a21b11+a22b21a31b11+a32b21a41b11+a42b21a11b12+a12b22a21b12+a22b22a31b12+a32b22a41b12+a42b22a11b13+a12b23a21b13+a22b23a31b13+a32b23a41b13+a42b23a11b14+a12b24a21b14+a22b24a31b14+a32b24a41b14+a42b24

通过优化 A A A B B B,即可实现 Δ W \Delta W ΔW 的最优表示,从而微调模型权重 W ′ W' W

5.5 数值稳定性与缩放因子

引入缩放因子 α r \frac{\alpha}{r} rα 可以缓解数值不稳定性,确保微调过程中梯度和更新幅度适中:

W ′ = W + α r A ⋅ B W' = W + \frac{\alpha}{r} A \cdot B W=W+rαAB

其中, α \alpha α 通常设为与 r r r 成正比的常数(如 α = r \alpha = r α=r),以保持 Δ W \Delta W ΔW 的整体规模。

6. 理论分析与性能保障

6.1 表达力与近似误差

低秩近似 Δ W = A ⋅ B \Delta W = A \cdot B ΔW=AB 的表达力取决于 r r r 的选择。根据矩阵近似理论,给定矩阵 W W W,存在最佳的低秩近似(通过截断SVD),使得近似误差在Frobenius范数下最小:

min ⁡ A ∈ R d × r , B ∈ R r × k ∥ W − A ⋅ B ∥ F \min_{A \in \mathbb{R}^{d \times r}, B \in \mathbb{R}^{r \times k}} \|W - A \cdot B\|_F ARd×r,BRr×kminWABF

虽然 LoRA 的优化目标略有不同(因为它在微调任务中直接优化 A A A B B B 以最小化任务损失),但低秩近似仍然能够有效捕捉权重更新中的主要模式。

6.2 参数效率与泛化

低秩限制通过减少自由参数,有助于提升模型的泛化能力。过多的可训练参数容易导致过拟合,尤其是在数据稀缺或任务复杂度高的情况下。LoRA 通过低秩适配,在参数效率和模型表现之间实现了平衡。

6.3 结合梯度信息

在训练过程中,LoRA 的低秩矩阵 A A A B B B 可以高效地捕捉梯度信息中的主成分,提升优化效率。特别是在大规模模型中,梯度的低秩结构可以被更好地利用,从而加速收敛。

五、LoRA进行微调的实例及Python代码

在本示例中,我们将展示如何使用LoRA(Low-Rank Adaptation)对大型预训练语言模型进行复杂微调。我们将使用Hugging Face的transformers库和peft(Parameter-Efficient Fine-Tuning)库来实现这一过程。具体来说,我们将:

  1. 选择模型和数据集:使用预训练的GPT-2模型,并在复杂的多任务数据集(如MultiWOZ)上进行微调。
  2. 集成LoRA:在GPT-2的多个关键层中插入LoRA适配模块,包括自注意力层和前馈网络。
  3. 优化训练:采用混合精度训练、梯度累积、学习率调度等高级技术,以提高训练效率和模型性能。
  4. 评估与验证:在验证集上评估微调后的模型性能,确保其在各个任务上的表现。

前提条件
确保已安装以下Python库:

pip install transformers datasets peft torch

1. 选择模型和数据集

我们选择GPT-2作为基础模型,并使用MultiWOZ数据集进行微调。MultiWOZ是一个复杂的多任务对话数据集,涵盖多个领域(如餐厅预订、酒店预订、出租车服务等),适合展示LoRA在多任务学习中的应用。

2. Python代码实现

下面是一个详细的Python脚本,展示如何使用LoRA对GPT-2进行复杂微调。

import torch
from transformers import GPT2Tokenizer, GPT2LMHeadModel, Trainer, TrainingArguments, DataCollatorForLanguageModeling
from datasets import load_dataset
from peft import get_peft_config, get_peft_model, LoraConfig, TaskType

# 检查GPU是否可用
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"Using device: {device}")

# 1. 加载预训练模型和分词器
model_name = "gpt2-large"  # 选择较大的GPT-2模型以展示复杂性
tokenizer = GPT2Tokenizer.from_pretrained(model_name)
model = GPT2LMHeadModel.from_pretrained(model_name)
model.to(device)

# 2. 准备数据集
# 这里以MultiWOZ为例,需先下载并处理成文本格式
# 为简化示例,我们使用HuggingFace的wikitext数据集
dataset = load_dataset("wikitext", "wikitext-103-raw-v1")

# 合并训练和验证集中的文本
def tokenize_function(examples):
    return tokenizer(examples["text"], return_special_tokens_mask=True)

tokenized_datasets = dataset.map(tokenize_function, batched=True, num_proc=4, remove_columns=["text"])

# 设置块大小
block_size = 1024

def group_texts(examples):
    # Concatenate all texts
    concatenated = {k: sum(examples[k], []) for k in examples.keys()}
    total_length = len(concatenated[list(examples.keys())[0]])
    # Drop the small remainder
    total_length = (total_length // block_size) * block_size
    # Split by chunks of block_size
    result = {k: [t[i:i + block_size] for i in range(0, total_length, block_size)] for k, t in concatenated.items()}
    return result

lm_datasets = tokenized_datasets.map(group_texts, batched=True, num_proc=4)

# 3. 配置LoRA
# 定义LoRA配置
peft_config = LoraConfig(
    task_type=TaskType.CAUSAL_LM,  # 因为我们使用的是GPT-2
    inference_mode=False,
    r=8,  # 低秩的秩,调整为适合的值
    lora_alpha=32,
    lora_dropout=0.1,
    target_modules=["c_attn", "c_proj", "fc1", "fc2"]  # 选择要应用LoRA的模块
)

# 将LoRA配置应用到模型
model = get_peft_model(model, peft_config)
model.print_trainable_parameters()  # 打印可训练参数,验证LoRA是否正确集成

# 4. 设置训练参数
training_args = TrainingArguments(
    output_dir="./lora_gpt2_wikitext",
    overwrite_output_dir=True,
    num_train_epochs=3,
    per_device_train_batch_size=2,
    per_device_eval_batch_size=2,
    gradient_accumulation_steps=16,  # 相当于总批量大小为32
    evaluation_strategy="epoch",
    save_strategy="epoch",
    learning_rate=3e-4,
    weight_decay=0.01,
    fp16=True,  # 使用混合精度
    logging_steps=100,
    save_total_limit=2,
    report_to="none",  # 不使用任何报告工具
)

# 5. 数据整理
data_collator = DataCollatorForLanguageModeling(
    tokenizer=tokenizer,
    mlm=False,  # 对于GPT-2是因果语言模型,不是MLM
)

# 6. 定义Trainer
trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=lm_datasets["train"],
    eval_dataset=lm_datasets["validation"],
    data_collator=data_collator,
)

# 7. 开始训练
trainer.train()

# 8. 保存模型和LoRA适配模块
model.save_pretrained("./lora_gpt2_wikitext")
tokenizer.save_pretrained("./lora_gpt2_wikitext")

# 可选:评估模型
eval_results = trainer.evaluate()
print(f"Perplexity: {torch.exp(torch.tensor(eval_results['eval_loss']))}")

# 9. 推理示例
from transformers import pipeline

# 加载微调后的模型
model = GPT2LMHeadModel.from_pretrained("./lora_gpt2_wikitext")
model = get_peft_model(model, peft_config)
model.to(device)

# 创建生成文本的pipeline
text_generator = pipeline("text-generation", model=model, tokenizer=tokenizer, device=0 if torch.cuda.is_available() else -1)

# 生成文本
prompt = "In a distant future, artificial intelligence"
generated_text = text_generator(prompt, max_length=100, num_return_sequences=1)
print(generated_text[0]['generated_text'])

3. 代码详解

  1. 加载预训练模型和分词器
model_name = "gpt2-large"  # 选择较大的GPT-2模型以展示复杂性
tokenizer = GPT2Tokenizer.from_pretrained(model_name)
model = GPT2LMHeadModel.from_pretrained(model_name)
model.to(device)

我们选择了gpt2-large作为基础模型,并加载了相应的分词器和模型权重。将模型移动到可用的设备(GPU或CPU)以加速训练。

  1. 准备数据集
dataset = load_dataset("wikitext", "wikitext-103-raw-v1")

为了简化示例,我们使用了Hugging Face的wikitext-103-raw-v1数据集。你可以根据需要替换为MultiWOZ或其他复杂数据集。

  1. 数据预处理
def tokenize_function(examples):
    return tokenizer(examples["text"], return_special_tokens_mask=True)

tokenized_datasets = dataset.map(tokenize_function, batched=True, num_proc=4, remove_columns=["text"])

block_size = 1024

def group_texts(examples):
    concatenated = {k: sum(examples[k], []) for k in examples.keys()}
    total_length = len(concatenated[list(examples.keys())[0]])
    total_length = (total_length // block_size) * block_size
    result = {k: [t[i:i + block_size] for i in range(0, total_length, block_size)] for k, t in concatenated.items()}
    return result

lm_datasets = tokenized_datasets.map(group_texts, batched=True, num_proc=4)

将文本数据分词,并将其分割成固定长度的块(block_size=1024),适合GPT-2的输入要求。

  1. 配置LoRA
peft_config = LoraConfig(
    task_type=TaskType.CAUSAL_LM,  # 因为我们使用的是GPT-2
    inference_mode=False,
    r=8,  # 低秩的秩,调整为适合的值
    lora_alpha=32,
    lora_dropout=0.1,
    target_modules=["c_attn", "c_proj", "fc1", "fc2"]  # 选择要应用LoRA的模块
)

  • task_type指定了任务类型,这里为因果语言建模。
  • r是LoRA的秩,决定了低秩矩阵的大小。
  • lora_alpha和lora_dropout是LoRA的超参数。
  • target_modules指定了要插入LoRA适配器的模块名称。对于GPT-2,这些通常是自注意力层中的c_attn和c_proj,以及前馈网络中的fc1和fc2。
model = get_peft_model(model, peft_config)
model.print_trainable_parameters()

使用peft库将LoRA配置应用到模型,并打印出可训练的参数,验证LoRA是否正确集成。

  1. 设置训练参数
training_args = TrainingArguments(
    output_dir="./lora_gpt2_wikitext",
    overwrite_output_dir=True,
    num_train_epochs=3,
    per_device_train_batch_size=2,
    per_device_eval_batch_size=2,
    gradient_accumulation_steps=16,  # 相当于总批量大小为32
    evaluation_strategy="epoch",
    save_strategy="epoch",
    learning_rate=3e-4,
    weight_decay=0.01,
    fp16=True,  # 使用混合精度
    logging_steps=100,
    save_total_limit=2,
    report_to="none",  # 不使用任何报告工具
)

  • 混合精度训练:fp16=True启用混合精度训练,减少显存占用,加速训练。
  • 梯度累积:gradient_accumulation_steps=16将有效批量大小扩大为2 * 16 = 32,适用于显存有限的情况。
  • 学习率和权重衰减:选择合适的学习率和权重衰减参数以优化训练。
  • 保存策略:每个训练周期结束时保存模型。
  1. 定义Trainer
trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=lm_datasets["train"],
    eval_dataset=lm_datasets["validation"],
    data_collator=data_collator,
)

使用Hugging Face的Trainer类来管理训练过程,自动处理训练循环、评估和保存模型。

  1. 开始训练
trainer.train()

启动训练过程,模型将根据配置的参数进行微调。

  1. 保存模型和LoRA适配模块
model.save_pretrained("./lora_gpt2_wikitext")
tokenizer.save_pretrained("./lora_gpt2_wikitext")

将微调后的模型和分词器保存到指定目录。

  1. 推理示例
from transformers import pipeline

# 加载微调后的模型
model = GPT2LMHeadModel.from_pretrained("./lora_gpt2_wikitext")
model = get_peft_model(model, peft_config)
model.to(device)

# 创建生成文本的pipeline
text_generator = pipeline("text-generation", model=model, tokenizer=tokenizer, device=0 if torch.cuda.is_available() else -1)

# 生成文本
prompt = "In a distant future, artificial intelligence"
generated_text = text_generator(prompt, max_length=100, num_return_sequences=1)
print(generated_text[0]['generated_text'])

加载微调后的模型,并使用Hugging Face的pipeline生成文本,验证微调效果。

总结
LoRA通过低秩矩阵分解技术,将大型模型的权重更新表示为两个低秩矩阵的乘积,显著减少了需要训练和存储的参数量。这一方法基于矩阵的秩与低秩近似的数学原理,通过巧妙的参数化更新,实现了高效的模型适配。其数学基础不仅保证了参数效率和计算成本的降低,还在理论上支持了其在实际任务中的有效性和泛化能力。随着进一步的研究和优化,LoRA有望在更多复杂任务和更大规模模型中发挥关键作用。


结~~~


http://www.kler.cn/a/350181.html

相关文章:

  • DC系列靶机-DC5
  • k8s微服务
  • Vue预渲染:深入探索prerender-spa-plugin与vue-meta-info的联合应用
  • 关于Linux下C++程序内存dump的分析和工具
  • Java项目:160 基于springboot物流管理系统(PPT+论文+说明文档)
  • C++面向对象--------继承篇
  • [Linux#65][TCP] 详解 延迟应答 | 捎带应答 | 流量控制 | 拥塞控制
  • Chromium HTML attribute与c++接口对应关系分析
  • Tomcat 配置:方便运行 Java Web 项目
  • java.io.StreamCorruptedException: invalid stream header的原因及解决方法
  • 地级市-国内旅游收入、国内旅游人数数据(2000-2023年)
  • easyocr 本地部署模型 识别图像 ocr - python 实现
  • windows下安装、配置neo4j并服务化启动
  • Ngin入门套餐
  • Rocky linux SSD安装
  • dlib库实现人脸检测
  • 使用C#获取系统关键信息:CPU、内存、硬盘、用户与网络状态
  • STM32 输入捕获模式详解:PWM 输入捕获与 PWI 模式(续篇)
  • 【C++】—通俗易懂的理解C++中的模板
  • css中 global 和 deep(两个样式穿透) 区别