理解API代码:高效REST API集成的综合指南
LLM微调系列:LORA(5)
2024-12-06
Lora
LoRA,英文全称Low-Rank Adaptation of Large Language Models。
冻结预训练好的模型权重参数,然后在每个Transformer块里注入可训练的层,由于不需要对模型的权重参数重新计算梯度,所以,大大减少了需要训练的计算量。
从上图可以直观的看出,预训练模型旁增加了右侧的“旁支”,也就是先用一个Linear层A,将数据从 d维降到r,再用第二个Linear层B,将数据从r变回d维。最后再将左右两部分的结果相加融合,得到输出的hidden_state。
- 在训练阶段,预训练参数W固定,只更新A和B参数,A和B模拟W的变化量。
- 在推理阶段,用A、B参数与原预训练参数相加替换原有预训练模型的参数。推理过程没有额外的参数量和计算量
- 相当于是用LoRA去模拟Full-finetune的过程,几乎不会带来效果损失
AdaLORA
AdaLoRA是自适应预算分配以实现参数有效的微调,由于在不太重要的权重矩阵添加更多的参数会产生很少的收益,甚至会损害模型性能。因此为了降低不重要的权重的计算,AdaLoRA通过调整增量矩阵的秩,以控制参数参与计算的量。
- 关键的增量矩阵被分配了高秩,这样可以捕获更细粒度和特定任务的信息。
- 不太重要的增量矩阵被修剪为具有较低的秩,以防止过度拟合并节省计算资源。
调整矩阵秩的方法
lora是根据 让模型学习这两个矩阵,用近似SVD分解的结果,同时将 的秩统一成为
AdaLora只直接利用SVD分解的结果,根据 学习型 这三个权重。
- 首先,初始化 为0矩阵, 为高斯随机矩阵。这样初始化的目的和lora一样,能在训练开始确保 是0矩阵,避免引入噪声。
- 固定 ,更新。得到的 中对角线较小的值更新为0,相当于0对应的 进行mask,让下一次计算不参与计算,这里就相当于变秩。只将重置为0,而不是将整个删掉,是因为随着模型的学习和更新,后面这样的三元组可能会变的重要,显然用mask更合理。
- 中较小的值更新为0后固定住,再更新 ,此时不重要的信息就不参与计算了。
- 重复2、3步
下面的内容解释了这些参数的中文含义
QLora实战
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer, TrainingArguments, BitsAndBytesConfig
# modelpath="meta-llama/Llama-2-7b-hf"
modelpath="meta-llama/Meta-Llama-3-8B"
# Load 4-bit quantized model
model = AutoModelForCausalLM.from_pretrained(
modelpath,
device_map="auto",
quantization_config=BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_use_double_quant=True,
bnb_4bit_compute_dtype=torch.bfloat16,
),
torch_dtype=torch.bfloat16,
)
model.config.use_cache = False
tokenizer = AutoTokenizer.from_pretrained(modelpath)
tokenizer.pad_token = tokenizer.eos_token
配置lora
LoRAConfig()
用于配置,哪些位置添加lora层。在这里,将 LoRA 层添加到自注意力模块的所有线性投影(attn_matrices=[“q”, “k”, “v”])以及中间和输出线性层。
import adapters
from adapters import LoRAConfig
adapters.init(model)
config = LoRAConfig(
selfattn_lora=True,
intermediate_lora=True,
output_lora=True,
attn_matrices=["q", "k", "v"],
alpha=16, r=64, dropout=0.1
)
model.add_adapter("assistant_adapter", config=config)
model.train_adapter("assistant_adapter")
刚查需要更新的参数
print(model.adapter_summary()) # 观察需要微调的参数量
for param in model.parameters():
if param.ndim == 1:
# cast the small parameters (e.g. layernorm) to fp32 for stability
param.data = param.data.to(torch.float32)
# Enable gradient checkpointing to reduce required memory if needed
# model.gradient_checkpointing_enable()
# model.enable_input_require_grads()
class CastOutputToFloat(torch.nn.Sequential):
def forward(self, x): return super().forward(x).to(torch.float32)
model.lm_head = CastOutputToFloat(model.lm_head)
model
数据准备
from datasets import load_dataset
dataset = load_dataset("timdettmers/openassistant-guanaco")
def tokenize(element):
return tokenizer(
element["text"],
truncation=True,
max_length=512, # can set to longer values such as 2048
add_special_tokens=False,
)
dataset_tokenized = dataset.map(
tokenize,
batched=True,
num_proc=os.cpu_count(), # multithreaded
remove_columns=["text"] # don't need this anymore, we have tokens from here on
)
开始训练
from adapters import AdapterTrainer
from transformers import DataCollatorForLanguageModeling
args = TrainingArguments(
output_dir="output/llama_qlora",
per_device_train_batch_size=1,
per_device_eval_batch_size=1,
evaluation_strategy="steps",
logging_steps=10,
save_steps=500,
eval_steps=187,
save_total_limit=3,
gradient_accumulation_steps=16,
max_steps=1875,
lr_scheduler_type="constant",
optim="paged_adamw_32bit",
learning_rate=0.0002,
group_by_length=True,
bf16=True,
warmup_ratio=0.03,
max_grad_norm=0.3,
)
trainer = AdapterTrainer(
model=model,
tokenizer=tokenizer,
data_collator=DataCollatorForLanguageModeling(tokenizer, mlm=False),
train_dataset=dataset_tokenized["train"],
eval_dataset=dataset_tokenized["test"],
args=args,
)
trainer.train()
推理
from transformers import logging
logging.set_verbosity(logging.CRITICAL)
def prompt_model(model, text: str):
batch = tokenizer(f"### Human: {text}\n### Assistant:", return_tensors="pt")
batch = batch.to(model.device)
model.eval()
with torch.inference_mode(), torch.cuda.amp.autocast():
output_tokens = model.generate(**batch, max_new_tokens=50)
return tokenizer.decode(output_tokens[0], skip_special_tokens=True)
print(prompt_model(model, "Explain Calculus to a primary school student"))
权重融合
model.merge_adapter("assistant_adapter")
- LORA
https://arxiv.org/pdf/2106.09685
https://github.com/microsoft/LoRA
- AdaLORA
https://arxiv.org/pdf/2303.10512
https://github.com/QingruZhang/AdaLoRA
文章转自微信公众号@CourseAI
同话题下的热门内容