本文详细介绍LoRA(低秩适应)技术,一种高效的大语言模型微调方法。通过冻结预训练权重,仅训练低秩矩阵来减少参数量,显著降低显存需求(如GPT3 175B显存从1.2TB降至350GB)。文章提供完整代码实现,包括使用peft库进行模型配置、数据准备、训练和部署的全流程,帮助开发者以较低资源成本实现大模型定制化微调。

前排提示,文末有大模型AGI-CSDN独家资料包哦!

1、论文地址和代码仓

《LORA: LOW-RANK ADAPTATION OF LARGE LANGUAGE MODELS》
《大语言模型的低秩适应》

https://arxiv.org/pdf/2106.09685
https://github.com/microsoft/LoRA

2、核心原理

冻结左边的预训练权重矩阵W,只训练A和B两个低秩矩阵,然后,通过W、A和B两边的计算结果叠加得到最后的计算结果。不过,工程实践中是将W、A和B三个合并成新的权总矩阵,再进行计算。

重新训练,只训练A和B

图中,x为输入,h为输出结果,W为预训练的权重矩阵,A和B为重新训练的低秩权重矩阵,dxd表示预训练模型的维度,d x r表示低秩举证的维度,其中r<<d,A=N(0,σ^2) 表示A的训练初始值是均值为0,方差为σ^2的正态分布举证,学术名称高斯初始化;B=0 值全为0的矩阵,学术名称零初始化。

LoRA数学公式表示如下:

理论上的数学公式

实际训练中数学公式表示如下:

实际训练中数学公式

其中,r为LoRA秩,r越大信息越丰富,但计算量越大,α为超参

合并模型参数的数学公式表示如下:

合并模型参数的数学公式

加号前面的W0表示新知识,后面的△W表示旧知识

LoRA的训练过程如下:

LoRA的训练

有几个关键点
1)为什么LoRA使重新训练效率更高
因为全量微调重新训练需要计算得到的参数个数为d x d,而LoRA计算得到的参数个数为2 x r x d,举个例子,r=10,d=1000,则全量微调矩阵为1000 x 1000,参数为1000000个,LoRA矩阵为20000,则LoRA需要训练地参数个数远小于全量训练举证,所以效率更高。
2)为什么LoRA可以达到微调的目的(数学依据)
因为全量微调中的矩阵d x d存在冗余的信息,可以通过低阶的矩阵来表示
举个例子

A =[[1,2,3],[2,4,6],[3,6,9]]

A矩阵为3 x 3的矩阵,实际上, [2, 4, 6](第二行) = 2 x [1, 2, 3] (第一行), [3, 6, 9] = 3 x [1, 2, 3](第一行) ,也就是说只需要有第一行的信息,就可以表示矩阵A。数学上给了个定义就叫做秩,矩阵的秩定义是非零子式的线性无关行(列)向量的最大个数,秩表示的是矩阵的信息量,A 矩阵的最大线性无关的行行数为1。
简而言之,就是3 x 3的矩阵,可以使用1 x 3 的矩阵来表示。这就是LoRA微调的数学依据。
3)实验数据证明
根据LoRA论文实验的结果,LoRA微调使得GPT3 175B的训练,显存消耗从1.2TB降至350GB。

LoRA和其余微调方法对比

3、代码实现

3.1 通过peft实现LoRA微调

源码地址:

github开源的地址

https://github.com/xujinhelaw/chat-bot-ananas/tree/master/llm-server/llm-finetune

gittee开源的地址:

https://gitee.com/xuhelaw/chat-bot-ananas/tree/master/llm-server/llm-finetune
项目结构如下 :

chat-bot-ananas/ (根项目)└── llm-server/ (大模型服务端模块)│   └── llm-server/ (大模型服务端模块)│      ├── alpaca_data.json(大模型微调训练的数据集)│      ├── environment.yml(大模型微调需要的依赖包)│      ├── load_lora_model.py (启动大模型并叠加微调参数的代码逻辑)│      ├── lora_finetune.py (大模型微调的代码逻辑)│      └── README.md (大模型微调模块的README)│   ├── api.py(大模型启动和开发接口代码)│   ├── chatmachine.py(大模型访问客户端代码)│   ├── download.py(大模型下载代码)│   ├── environment.yml(大模型部署和访问客户端需要的依赖包)└── pom.xml(后端依赖管理pom文件)└── pom.xml (根 POM,管理子模块)└──settings.xml(maven仓配置文件)

通过peft实现LoRA的微调,代码如下所示:

# lora_finetune.pyimport os#os.environ["WANDB_PROJECT"] = "lora-finetune-demo"  # Optional: 使用 wandb 记录训练from transformers import(    AutoModelForCausalLM,    AutoTokenizer,    TrainingArguments,    Trainer,    DataCollatorForLanguageModeling)from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_trainingfrom datasets import load_dataset, Datasetimport jsonimport torchimport warningsimport datetime#transformers >= 4.37.0 废弃了旧的梯度检查点设置方式_set_gradient_checkpointing() 方法(Qwen 就是这么做的)warnings.filterwarnings("ignore", message="You are using an old version of the checkpointing format")# -------------------------------# 1. 模型与 tokenizer 加载# -------------------------------model_path ="../qwen/Qwen-7B-Chat"# 可替换为你想微调的模型#从 Hugging Face 的模型仓库中加载与指定预训练模型(model_path)对应的分词器(Tokenizer)tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)#将分词器(tokenizer)的填充标记(pad token) 设置为与结束标记(eos token) 相同# 🔥 关键:打印原始状态print(f"Original eos_token: {tokenizer.eos_token}, eos_token_id: {tokenizer.eos_token_id}")print(f"Original pad_token: {tokenizer.pad_token}, pad_token_id: {tokenizer.pad_token_id}")# ✅ 使用 add_special_tokens 真正设置 pad_tokentokenizer.pad_token ='<|endoftext|>'tokenizer.pad_token_id =151643# ✅ 再次验证print(f"✅ Final pad_token: {tokenizer.pad_token}")print(f"✅ Final pad_token_id: {tokenizer.pad_token_id}")print(f"✅ Final vocab size: {len(tokenizer)}")tokenizer.padding_side ="right"# 是否使用 4-bit 量化 (QLoRA)use_4bit =Trueif use_4bit:from transformers import BitsAndBytesConfig    bnb_config = BitsAndBytesConfig(        load_in_4bit=True,        bnb_4bit_quant_type="nf4",        bnb_4bit_compute_dtype=torch.bfloat16,        bnb_4bit_use_double_quant=True,)    model = AutoModelForCausalLM.from_pretrained(        model_path,        quantization_config=bnb_config,        device_map="auto",# 自动分配到 GPU        trust_remote_code=True)# 为量化模型准备:添加梯度检查点和激活检查    model = prepare_model_for_kbit_training(model)else:    model = AutoModelForCausalLM.from_pretrained(        model_path,        device_map="auto",        torch_dtype=torch.bfloat16,        trust_remote_code=True)# 👇 打印所有包含 'proj' 的 nn.Linear 层名称print("🔍 Finding projection layers in Qwen2-7B:")target_candidates =[]for name, module in model.named_modules():if'proj'in name andisinstance(module, torch.nn.Linear):print(f"  {name}")        target_candidates.append(name)# 可选:提取最后一级名称(如 q_proj, v_proj 等)# 例如:从 'model.layers.0.self_attn.q_proj' 提取 'q_proj'base_names =list(set([name.split('.')[-1]for name in target_candidates]))print(f"\n🎯 Candidate target_modules: {base_names}")# -------------------------------# 2. 加载与预处理数据集# -------------------------------# 使用 Alpaca 风格的指令数据集(示例用 'tatsu-lab/alpaca'),格式如下#{#    "instruction": "解释为什么天空是蓝色的",#    "input": "",  # 无额外输入时为空#    "output": "天空呈现蓝色是因为瑞利散射现象..."#}## 读取本地的 JSON 数据data_path ="alpaca_data.json"# 替换为你自己的数据路径withopen(data_path,"r", encoding="utf-8")as f:    train_datas  = json.load(f)# 将alpaca格式的数据转为qwen的chattemplate格式defconvert_format(data_list):    converted_datas =[]for item in data_list:# 构建 user 的 content        user_content = item["instruction"]if item["input"].strip():# 检查 input 是否非空(去除空格后)            user_content =f"{item['instruction']}\n\n{item['input']}"# 或者根据语义调整顺序,比如 input 是主要文本时:f"{item['input']}\n\n{item['instruction']}"        messages =[{"role":"system","content":"你是一个智能助手"},{"role":"user","content": user_content},{"role":"assistant","content": item["output"]}]        converted_datas.append({"messages": messages})return converted_datas# 调用转换函数converted_datas = convert_format(train_datas)defcreate_and_prepare_dataset(data_list):"""    将原始数据列表转换为 Hugging Face Dataset 格式,并应用聊天模板。    """defapply_chat_template(example):        messages = example["messages"]# 使用分词器的 apply_chat_template 方法将消息列表转换为模型输入格式try:            prompt = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=False)except Exception as e:print(f"Error applying chat template: {e}")            prompt =""# 或者可以跳过这个样本print(f"打印 LoRA 训练数据。 text: {prompt}")return{"text": prompt }# 创建 Dataset 对象    raw_dataset = Dataset.from_list(data_list)# 应用模板函数到整个数据集    processed_dataset = raw_dataset.map(apply_chat_template)return processed_dataset# 应用聊天模板dataset = create_and_prepare_dataset(converted_datas)print(f"🚀  打印 LoRA 训练数据。dataset:{dataset}")# Tokenize 函数deftokenize_function(examples):return tokenizer(        examples["text"],        padding=False,        max_length=512,        truncation=True,        return_tensors=None,# 返回 Python list,由 Trainer 处理)# 3. 处理数据集tokenized_dataset = dataset.map(    tokenize_function,    batched=True,    remove_columns=[col for col in["messages","text"]if col in dataset.column_names],    num_proc=4)print(f"🚀  打印 处理后的数据集 tokenized_dataset :{tokenized_dataset}")# 数据整理器(自动处理 padding)data_collator = DataCollatorForLanguageModeling(tokenizer, mlm=False)# -------------------------------# 3. 配置 LoRA# -------------------------------lora_config = LoraConfig(    r=8,# LoRA 秩    lora_alpha=16,# 超参# c_attn 是 Qwen 中 QKV 投影的统一层,还有["c_attn", "c_proj", "w1", "w2"]    target_modules=["c_attn","c_proj","w1","w2"],#在低秩更新模块中引入 10% 的随机丢弃概率,#避免模型过度依赖 LoRA 新增参数拟合训练数据中的噪声,提高对未见过数据的适配能力    lora_dropout=0.1,#指定不对模型的偏置参数(bias)进行微调或修改    bias="none",    task_type="CAUSAL_LM"# 因果语言建模)#将原始预训练模型与 LoRA(或 QLoRA)配置结合,生成一个支持参数高效微调的 PEFT 模型print(f"\n🎯  将原始预训练模型与 LoRA(或 QLoRA)配置结合!这个过程比较耗时,请耐心等待!")start_time = datetime.datetime.now()model = get_peft_model(model, lora_config)end_time = datetime.datetime.now()cos_time =(end_time - start_time).secondsprint(f"原始预训练模型与 LoRA(或 QLoRA)配置结合完成。耗时:{cos_time} 秒。")model.print_trainable_parameters()# 查看可训练参数量(通常 <1%)# -------------------------------# 4. 配置训练参数# -------------------------------training_args = TrainingArguments(    output_dir="./lora-alpaca-qwen2",# 模型训练结果( checkpoint、日志等 )的保存路径    num_train_epochs=200,# 训练的总轮数,即完整遍历训练集的次数    per_device_train_batch_size=4,# 每个设备(如单张GPU)上的训练批次大小    gradient_accumulation_steps=4,# 梯度累积步数,每累积4个批次后再更新一次参数(变相增大总batch size)    learning_rate=2e-4,# 学习率,LoRA微调常用2e-4 ~ 5e-4    logging_steps=10,# 每训练10步记录一次日志(如损失值)    save_steps=100,# 每训练500步保存一次模型 checkpoint    save_total_limit=2,# 最多保留2个最新的模型 checkpoint,避免占用过多存储空间    fp16=False,# 不使用FP16混合精度训练    bf16=torch.cuda.is_bf16_supported(),# 若GPU支持BF16精度则启用(比FP16更稳定,显存占用相似)    optim="paged_adamw_8bit",# 使用8位量化的PagedAdamW优化器(配合bitsandbytes库,减少显存占用)    lr_scheduler_type="cosine",# 学习率调度器类型,采用余弦退火策略(训练后期自动降低学习率)    warmup_ratio=0.03,# 学习率预热比例,前3%的训练步数逐渐将学习率从0提升到设定值(稳定训练初期)#report_to="wandb",  # 训练日志报告到Weights & Biases平台(需提前安装wandb并登录)    disable_tqdm=False,# 不禁用tqdm进度条(显示训练进度)    gradient_checkpointing=True,# 启用梯度检查点(牺牲少量计算速度,大幅减少显存占用))# -------------------------------# 5. 创建 Trainer 并开始训练# -------------------------------trainer = Trainer(    model=model,    args=training_args,    train_dataset=tokenized_dataset,# <<< 这里传入了数据集!    data_collator=data_collator,    tokenizer=tokenizer,)print("🚀 开始 LoRA 微调...")trainer.train()# -------------------------------# 6. 保存 LoRA 适配器# -------------------------------model.save_pretrained("lora-alpaca-qwen2-finetuned")tokenizer.save_pretrained("lora-alpaca-qwen2-finetuned")print("✅ LoRA 微调完成,适配器已保存到 'lora-alpaca-qwen2-finetuned'")

3.2 执行lora微调的脚本

python lora_finetune.py

整个训练过程比较耗时。

原始预训练模型与 LoRA配置结合

LoRA微调完成

3.3 启动大模型并叠加lora微调的参数

# 返回llm-server的目录,直接执行api.py,
# 因为在api.py做了判断处理,如果生成微调的参数,则直接叠加
python api.py

大模型并叠加lora微调启动成功

3.4 通过python实现的客户端访问大模型

重新开一个终端,启动客户端

# 因为是新开的终端,记得切到虚拟环境# 返回llm-server的目录,并执行
source activate qwen
python chatmachine.py

4、LoRA微调效果

训练数据集的回答

实际大模型的回答

读者福利:倘若大家对大模型感兴趣,那么这套大模型学习资料一定对你有用。

针对0基础小白:

如果你是零基础小白,快速入门大模型是可行的。
大模型学习流程较短,学习内容全面,需要理论与实践结合
学习计划和方向能根据资料进行归纳总结

包括:大模型学习线路汇总、学习阶段,大模型实战案例,大模型学习视频,人工智能、机器学习、大模型书籍PDF。带你从零基础系统性的学好大模型!

😝有需要的小伙伴,可以保存图片到wx扫描二v码免费领取【保证100%免费】🆓

请添加图片描述

👉AI大模型学习路线汇总👈

大模型学习路线图,整体分为7个大的阶段:(全套教程文末领取哈)

第一阶段: 从大模型系统设计入手,讲解大模型的主要方法;

第二阶段: 在通过大模型提示词工程从Prompts角度入手更好发挥模型的作用;

第三阶段: 大模型平台应用开发借助阿里云PAI平台构建电商领域虚拟试衣系统;

第四阶段: 大模型知识库应用开发以LangChain框架为例,构建物流行业咨询智能问答系统;

第五阶段: 大模型微调开发借助以大健康、新零售、新媒体领域构建适合当前领域大模型;

第六阶段: 以SD多模态大模型为主,搭建了文生图小程序案例;

第七阶段: 以大模型平台应用与开发为主,通过星火大模型,文心大模型等成熟大模型构建大模型行业应用。

👉大模型实战案例👈

光学理论是没用的,要学会跟着一起做,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。

在这里插入图片描述

👉大模型视频和PDF合集👈

这里我们能提供零基础学习书籍和视频。作为最快捷也是最有效的方式之一,跟着老师的思路,由浅入深,从理论到实操,其实大模型并不难

在这里插入图片描述

👉学会后的收获:👈

• 基于大模型全栈工程实现(前端、后端、产品经理、设计、数据分析等),通过这门课可获得不同能力;

• 能够利用大模型解决相关实际项目需求: 大数据时代,越来越多的企业和机构需要处理海量数据,利用大模型技术可以更好地处理这些数据,提高数据分析和决策的准确性。因此,掌握大模型应用开发技能,可以让程序员更好地应对实际项目需求;

• 基于大模型和企业数据AI应用开发,实现大模型理论、掌握GPU算力、硬件、LangChain开发框架和项目实战技能, 学会Fine-tuning垂直训练大模型(数据准备、数据蒸馏、大模型部署)一站式掌握;

• 能够完成时下热门大模型垂直领域模型训练能力,提高程序员的编码能力: 大模型应用开发需要掌握机器学习算法、深度学习框架等技术,这些技术的掌握可以提高程序员的编码能力和分析能力,让程序员更加熟练地编写高质量的代码。

👉获取方式:

😝有需要的小伙伴,可以保存图片到wx扫描二v码免费领取【保证100%免费】🆓

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐