如何使用LoRA通过微调增强大模型

一直以来更加把精力放在AI的应用上,但是随着时间的推移发现模型微调也是AI应用一个无法迈过的坎,学习的路还很长,写文章也是督促自己继续下去的动力

环境安装

首先我们要做的就是微调模型的环境安装,这里我列出一下我使用的版本

CUDA版本

pytorch版本

依赖包

ini 复制代码
pip install modelscope==1.16.1
pip install transformers==4.43.1
pip install accelerate==0.32.1
pip install peft==0.11.1
pip install datasets==2.20.0

如果发现下载缓慢可以更换pip的仓库源

arduino 复制代码
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple

数据准备

这里我们可以从魔塔中寻找自己感兴趣的训数据集

李火旺的数据集一下就进入我是视野,这里有意思的数据集好像也不多

寻找数据集时需要注意找我们的训练方式可以直接使用的数据集,我这边使用的训练方式是SFT,火子哥这个数据集是DPO方式训练的,我不能直接使用,但是找到了另一个甄嬛的数据集,也是挺有意思的

数据集的数据结构

这里只介绍两种数据集的结构,下面这种我们决定使用的数据集就是甄嬛传的数据集

SFT(监督式微调,Supervised Fine-Tuning)

  1. instruction 提示语/用户输入/上下文
  2. input 可选补充信息,如果为空表示没有附加输入
  3. output 模型应该生成的答案(监督信号)

DPO 训练是一种"偏好对比学习"

  1. prompt 用户输入,给模型看的提示语
  2. chosen 一个好的(偏好更高的)模型回答
  3. rejected 一个差的、被拒绝的回答

这是chatgpt给出的区别解释

项目 SFT(有监督微调) DPO(直接偏好优化)
🔧 训练目标 学会输出"标准答案" 学会偏好"更好"的回答
📊 数据需求 单条 input + output 多条 input + (chosen, rejected) 对比数据
📦 数据难度 易收集(只需一个好答案) 难收集(要构造两个回答并打分优劣)
🧠 模型训练方式 监督学习 近似偏好学习(强化学习的简化版)
💬 学到的能力 更加规范、模板化 更符合人类偏好,更自然、人性化
🚧 训练稳定性 高,收敛快 依赖对比样本质量,调参更敏感
⚠️ 错误传播风险 输出错误直接作为学习目标,会放大数据缺陷 错误样本影响小,因训练的是倾向
✅ 模型偏好优化 强,适用于对齐阶段(如RLHF)
🤖 生成多样性 低,偏模板化 高,鼓励多样表达

训练代码

引入必要依赖

javascript 复制代码
from datasets import Dataset
import pandas as pd
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM, DataCollatorForSeq2Seq, TrainingArguments, Trainer, GenerationConfig
from peft import LoraConfig, TaskType, get_peft_model
  1. datasets.Dataset 用于构造训练数据集
  2. pandas 用于读取和处理 json 数据
  3. transformers 加载预训练模型、分词器、训练参数等
  4. peft 进行 LoRA 低秩适配训练

数据处理函数

ini 复制代码
def process_func(example):
    MAX_LENGTH = 384    # Llama分词器会将一个中文字切分为多个token,因此需要放开一些最大长度,保证数据的完整性
    input_ids, attention_mask, labels = [], [], []
    instruction = tokenizer(f"<|begin_of_text|><|start_header_id|>system<|end_header_id|>\n\nCutting Knowledge Date: December 2023\nToday Date: 26 Jul 2024\n\n现在你要扮演皇帝身边的女人--甄嬛<|eot_id|><|start_header_id|>user<|end_header_id|>\n\n{example['instruction'] + example['input']}<|eot_id|><|start_header_id|>assistant<|end_header_id|>\n\n", add_special_tokens=False)  # add_special_tokens 不在开头加 special_tokens
    response = tokenizer(f"{example['output']}<|eot_id|>", add_special_tokens=False)
    input_ids = instruction["input_ids"] + response["input_ids"] + [tokenizer.pad_token_id]
    attention_mask = instruction["attention_mask"] + response["attention_mask"] + [1]  # 因为eos token咱们也是要关注的所以 补充为1
    labels = [-100] * len(instruction["input_ids"]) + response["input_ids"] + [tokenizer.pad_token_id]
    if len(input_ids) > MAX_LENGTH:  # 做一个截断
        input_ids = input_ids[:MAX_LENGTH]
        attention_mask = attention_mask[:MAX_LENGTH]
        labels = labels[:MAX_LENGTH]
    return {
        "input_ids": input_ids,
        "attention_mask": attention_mask,
        "labels": labels
    }

训练代码

ini 复制代码
if __name__ == "__main__":
    model = AutoModelForCausalLM.from_pretrained('./LLM-Research/Meta-Llama-3___1-8B-Instruct', device_map="auto",torch_dtype=torch.bfloat16)
    model.enable_input_require_grads() # 开启梯度检查点时,要执行该方法
    tokenizer = AutoTokenizer.from_pretrained('./LLM-Research/Meta-Llama-3___1-8B-Instruct', use_fast=False, trust_remote_code=True)
    tokenizer.pad_token = tokenizer.eos_token

    # 将JSON文件转换为CSV文件
    df = pd.read_json('huanhuan.json')
    ds = Dataset.from_pandas(df)
    tokenized_id = ds.map(process_func, remove_columns=ds.column_names)

    config = LoraConfig(
        task_type=TaskType.CAUSAL_LM,
        target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"],
        inference_mode=False, # 训练模式
        r=8, # Lora 秩
        lora_alpha=32, # Lora alaph,具体作用参见 Lora 原理
        lora_dropout=0.1# Dropout 比例
    )
    model = get_peft_model(model, config)
    model.print_trainable_parameters() # 打印总训练参数

    args = TrainingArguments(
        output_dir="./output/llama3_1_instruct_lora",
        per_device_train_batch_size=4,
        gradient_accumulation_steps=4,
        logging_steps=10,
        num_train_epochs=3,
        save_steps=100,
        learning_rate=1e-4,
        save_on_each_node=True,
        gradient_checkpointing=True
    )
    trainer = Trainer(
        model=model,
        args=args,
        train_dataset=tokenized_id,
        data_collator=DataCollatorForSeq2Seq(tokenizer=tokenizer, padding=True),
    )
    trainer.train() # 开始训练
训练代码的补充解释
加载模型和分词器
ini 复制代码
model = AutoModelForCausalLM.from_pretrained(...)
tokenizer = AutoTokenizer.from_pretrained(...)
读取数据集并转换格式
ini 复制代码
df = pd.read_json('huanhuan.json')
ds = Dataset.from_pandas(df)
tokenized_id = ds.map(process_func, remove_columns=ds.column_names)
LORA配置并应用到模型
ini 复制代码
config = LoraConfig(...)
model = get_peft_model(model, config)
训练参数配置
ini 复制代码
args = TrainingArguments(...)
创建 Trainer 并开始训练
scss 复制代码
trainer = Trainer(...)
trainer.train()

模型选择

模型我们选择一个8B的小模型,也是在modelscope下载就可以

复制代码
Meta-Llama-3-8B-Instruct

可以使用下面的代码下载

java 复制代码
import torch
from modelscope import snapshot_download, AutoModel, AutoTokenizer
import os

model_dir = snapshot_download('LLM-Research/Meta-Llama-3-8B-Instruct', cache_dir='/root/autodl-tmp', revision='master')

下载时间有点长,可能要耐心等待一下

耗时2个小时左右,漫长的等待,都是钱啊

开始训练

我设置了进行3轮训练

整个流程耗时20分钟左右,最大的耗时还是模型下载

微调后对话

复制代码
就写到这里,后续有什么新的内容再分享吧,微调学习才刚起步还需要多多实践,还有数据集制作等很多内容要学习
相关推荐
我命由我123452 分钟前
Spring Cloud - Spring Cloud 声明式接口调用(Fiegn 声明式接口调用概述、Fiegn 使用)
java·后端·spring·spring cloud·微服务·架构·java-ee
canonical_entropy4 分钟前
领域驱动设计(DDD)中聚合根的最主要职责真的是维护一致性吗?
后端·架构·领域驱动设计
文火冰糖的硅基工坊6 分钟前
[人工智能-大模型-66]:模型层技术 - 两种编程范式:数学函数式编程与逻辑推理式编程,构建起截然不同的智能系统。
人工智能·神经网络·算法·1024程序员节
创思通信17 分钟前
树莓派的YOLO智能AI识别系统,识别ESP32还是STM32
人工智能·stm32·yolo
AntBlack24 分钟前
不当韭菜 : 好像真有点效果 ,想藏起来自己用了
前端·后端·python
funfan051733 分钟前
【开发AI】Windows安装和使用Milvus的保姆级教程
人工智能·windows·milvus
Fuly102435 分钟前
使用docker安装向量数据库milvus
人工智能
darkfive1 小时前
构建大模型安全自动化测试框架:从手工POC到AI对抗AI的递归Fuzz实践
人工智能·安全·ai·自动化
一点一木1 小时前
火山方舟 Responses API 实战指南:从概念到「公司尽调 Dossier 生成器」
前端·人工智能·api
橙子家1 小时前
Serilog 日志库的简介
后端