“兄弟包会的”--lora微调入门实战

1 lora介绍

Lora参数微调通常指的是在大型语言模型(LLM)中,使用LoRA(Low-Rank Adaptation)方法进行高效的参数调整。LoRA是一种参数高效微调技术,它通过低秩分解来模拟参数的改变量,从而大大减少了下游任务的可训练参数数量。

2 lora核心思想

LoRA的核心思想是利用矩阵的低秩特性,将原始模型的参数矩阵分解成两个较小的矩阵的乘积。在微调过程中,只训练这两个较小的矩阵的参数,而保持原始模型的大部分参数不变。这种方法显著降低了对显存的需求,同时保持了模型的性能。

个人观点:通俗理解,就是大模型参数量太多了,秩(秩通俗理解为矩阵所携带的信息量)太多了,但是你微调,数据量远远小于参数量,你用不到那么多秩去训练,所以用一点秩训练就可以了,从这一过程中可以看到,当数据量足够大的时候,是没必要做lora微调的。

3 lora数学计算流程

3.1 SVD奇异值分解矩阵

计算公式如下
A m × n = U m × m Σ m × n V n × n T A_{m×n} = U_{m×m} \Sigma_{m×n} V^{\mathrm{T}}_{n×n} Am×n=Um×mΣm×nVn×nT
其中:

U U U为m行m列,该矩阵的每一个列向量都 A A T AA^T AAT的特征向量。

Σ \Sigma Σ为m行n列,将 A T A A^TA ATA A A T AA^T AAT的特征值开根号,得到的就是该矩阵主对角线上的元素。

V V V为n行n列,该矩阵的每一个列向量都是 A T A A^TA ATA的特征向量。

因此,我们如果想取一个秩为1的矩阵,只需要取第一个矩阵的一列,第二个矩阵的一个元素,第三个矩阵的一行即可,最后得出的矩阵仍为m×n,大大减少了参数计算量。

import torch
import numpy as np
torch.manual_seed(0)

# ------------------------------------
# n:输入数据维度
# m:输出数据维度
# ------------------------------------
n = 10
m = 10

# ------------------------------------
# 随机初始化权重W
# 之所以这样初始化,是为了让W不要满秩,
# 这样才有低秩分解的意义
# ------------------------------------
nr = 10
mr = 2
W = torch.randn(nr,mr)@torch.randn(mr,nr)

# ------------------------------------
# 随机初始化输入数据x
# ------------------------------------
x = torch.randn(n)

# ------------------------------------
# 计算Wx
# ------------------------------------
y = W@x
print("原始权重W计算出的y值为:\n", y)

# ------------------------------------
# 计算W的秩
# ------------------------------------
r= np.linalg.matrix_rank(W)
print("W的秩为: ", r)

# ------------------------------------
# 对W做SVD分解
# ------------------------------------
U, S, V = torch.svd(W)

# ------------------------------------
# 根据SVD分解结果,
# 计算低秩矩阵A和B
# ------------------------------------
U_r = U[:, :r]
S_r = torch.diag(S[:r])
V_r = V[:,:r].t()

B = U_r@S_r # shape = (d, r)
A = V_r     # shape = (r, d)

# ------------------------------------
# 计算y_prime = BAx
# ------------------------------------
y_prime = B@A@x

print("SVD分解W后计算出的y值为:\n", y)

print("原始权重W的参数量为: ", W.shape[0]*W.shape[1])
print("低秩适配后权重B和A的参数量为: ", A.shape[0]*A.shape[1] + B.shape[0]*B.shape[1])

输出结果

输出结果

3.2 lora计算流程

如图

lora计算流程

有一个细节需注意,初始阶段,R×N层初始化为0,目的是加入lora后不会对训练好的模型产生扰动。在推理过程中,lora并不会影响到推理速度。

**注:**基础的lora微调只对全连接层能调整,最后为残差连接。

4 lora实战

原简单程序

import torch
from torch import nn
net = nn.Sequential(nn.Linear(10, 20), nn.ReLU(), nn.Linear(20, 2))
print(net)

结果

原程序

lora微调

import torch
from torch import nn
from peft import LoraConfig, get_peft_model
net = nn.Sequential(nn.Linear(10, 20), nn.ReLU(), nn.Linear(20, 2))
# print(net)
config = LoraConfig(target_modules=["0"])
net2 = get_peft_model(net, config)
print(net2)

结果

lora微调后

可以看到第0层结构发生了更改。

配置文件参考

from peft import LoraConfig, TaskType, get_peft_model

# 定义 LoRA 的配置参数
config = LoraConfig(
    task_type=TaskType.CAUSAL_LM,  # 指定任务类型为自回归语言建模任务(Causal Language Modeling),如 GPT 系列的模型
    target_modules=["q_proj", "k_proj", "v_proj", "o_proj", 
                    "gate_proj", "up_proj", "down_proj"],  # 这些是模型中要应用 LoRA 的目标模块
    inference_mode=False,  # 表示训练模式,设置为 True 时是推理模式;为 False 是训练模式
    r=8,  # LoRA 的秩 r,代表分解时矩阵的秩,越大越能保留原有的模型信息,但计算复杂度也会增加
    lora_alpha=32,  # LoRA 的 alpha 超参数,控制了低秩矩阵的缩放因子。一般设置为与 r 成比例的值
    lora_dropout=0.1,  # 在 LoRA 模块中应用 Dropout,防止过拟合,防止微调时模型过拟合到训练数据
)

# 应用 PEFT(LoRA)到预训练的模型上
model = get_peft_model(model, config)  # 使用 get_peft_model 函数,将定义好的 LoRA 配置应用到预训练模型中

参考文献

https://blog.csdn.net/forest_LL/article/details/135343642

https://zhuanlan.zhihu.com/p/651951920
B站讲解

### 如何使用 InternVL2_5-8B 模型进行 LoRA 微调 #### 准备环境与依赖安装 为了确保能够顺利运行LoRA微调实验,需先准备好相应的开发环境并安装必要的库。这通常涉及到Python虚拟环境的创建以及特定版本PyTorch和其他辅助工具包的安装。 ```bash conda create -n internvl_lora python=3.9 conda activate internvl_lora pip install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu117 pip install transformers datasets accelerate bitsandbytes peft lmdeploy xtuner ``` #### 下载预训练模型 获取InternVL2_5-8B的基础权重文件对于后续的操作至关重要。依据给定的信息,可以通过命令行复制指定路径下的模型至本地工作目录[^3]。 ```bash cp -8B /root/model/ ``` #### 配置LoRA参数设置 针对多模态大模型如InternVL实施低秩适应(LoRA),主要集中在调整网络内部某些层的学习率及其结构变化上。通过修改配置文件或直接在脚本中定义这些超参来实现个性化定制化需求。 ```python from peft import LoraConfig, get_peft_model config = { "target_modules": ["q_proj", "v_proj"], # 调整目标模块 "rank": 4, # 设置LORA矩阵维度大小 "alpha": 32 # 控制缩放因子 } peft_config = LoraConfig(**config) model = get_peft_model(model, peft_config) ``` #### 数据集准备与处理 数据的质量直接影响到最终效果的好坏,在此阶段要精心挑选适合的任务场景的数据源,并对其进行清洗、标注等一系列前期准备工作。考虑到冷笑话生成这一特殊应用场景,可能需要收集大量幽默短句作为训练样本[^2]。 ```python from datasets import load_dataset dataset_name = 'your_cold_joke_dataset' data_files = {'train': f'{dataset_name}/train.json', 'test': f'{dataset_name}/test.json'} raw_datasets = load_dataset('json', data_files=data_files) def preprocess_function(examples): return tokenizer([text for text in examples["jokes"]], truncation=True, padding="max_length") tokenized_datasets = raw_datasets.map(preprocess_function, batched=True) ``` #### 开始微调过程 当一切就绪之后就可以启动实际的微调流程了。这里会利用之前提到过的`xtuner`来进行高效优化求解器的选择和调度策略的设计等工作[^1]。 ```python import os from transformers import Trainer, TrainingArguments training_args = TrainingArguments( output_dir="./results", evaluation_strategy="epoch", learning_rate=2e-5, per_device_train_batch_size=8, per_device_eval_batch_size=8, num_train_epochs=3, weight_decay=0.01, logging_dir='./logs', logging_steps=10, ) trainer = Trainer( model=model, args=training_args, train_dataset=tokenized_datasets['train'], eval_dataset=tokenized_datasets['test'] ) trainer.train() ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值