基于 Qwen2 大模型微调技术详细教程(LoRA 参数高效微调和 SwanLab 可视化监控)

我之前曾把大模型比作成一位无所不能无所不知且不知疲惫的“大师”。我们在日常工作、学习中等一些通用知识方面的问题,通常情况下,我们均可通过 Prompt 提示词就能从“大师”那里得到期望的结果。

但是,在某些垂直场景的特定任务(包括:个性化服务、内部私有数据等)中,这位“大师”可能就不一定能胜任了:

  1. 数据隐私安全: 保密项目、创业团体和企业内部数据是需要保证绝对安全的,“大师”的知识来自预训练的公开数据,在推理时就缺乏这方面知识。
  2. Prompt 长度和截取: 使用清晰详细的 Prompt 提示词,确实能帮助“大师”理解我们需求,从而更好的输出结果。但是大模型对输入序列的长度有限制,超长会被截断,同时超长的 Prompt 提示意味着推理成本更高、推理效率更低,可能达不到预期的效果。
  3. 个性化需求: 预训练的大模型,其对问题的理解和输出方式基本固定,无法满足个性化的需求。

这个时候,我们可以通过标记好的结构化数据,让“大师”进一步学习(即:微调),通过调整“大师”的知识(即:调整大模型参数),达到处理特定任务的能力。

根据我们需要调整的大模型的参数量,微调技术大致可以分为 2 种:

  1. 全量微调FFT(Full Fine-Tuning),它使用特定领域的数据集对模型的所有参数进行调整,微调的参数量跟预训练时一样多,训练成本和资源会很高,同时可能因数据集等原因出现过拟合问题,导致发生灾难性遗忘(Catastrophic Forgetting),即我们可能会让大模型在某个领域的能力变的更好,但也可能会让原来其它表现好领域的能力变差。
  2. 参数高效微调PEFT(Parameter-Efficient Fine-Tuning),它仅更新模型中的小部分参数,保持大部分预训练权重不变,在保持模型性能的同时减少所需的计算资源和存储空间,可在有效避免过拟合问题的同时,还有助于保留模型在广泛任务上的通用知识(即:泛化能力)。

LoRA(Low-Rank Adaptation)是一种高效的大模型PEFT微调技术,它是通过在预训练模型的关键层(如全连接层和自注意力层)之间添加低秩矩阵来完成微调。这些低秩矩阵的引入使得模型能够适应新的任务,而无需改变原有的大量参数。由于低秩矩阵的参数数量远小于原有层的参数数量,这就大大减少了需要训练的参数总数。

LoRA的优势在于,即使在资源有限的情况下,也可以有效地对大型预训练模型进行微调,使其适应各种下游任务,如文本分类、命名实体识别等。此外,由于 LoRA 的微调通常只需要较少的数据,这也使得它成为小数据集场景下的一个有力工具。

我将通过本教程,基于Qwen2-0.5B开源的预训练大模型,和大家一起进行一次大模型文本分类能力的微调。在 AI 蓬勃发展的今天,期望能通过本教程,与大家一起在我们的 AI 知识库里新增储备微调知识,逐步做到肚里有货,从容不迫

完成一次完整的大模型微调,大致需要以下几个步骤:

  1. 环境准备: 主要是 Python 依赖库安装
  2. 数据集准备: 针对特定任务,准备相关的数据,数据内容包含Prompt 提示词输出即可
  3. 准备大模型: 我们可以通过 HF、ModelScope 等下载预训练大模型权重
  4. 大模型微调: 包括加载大模型、数据集格式化处理、LoRA 参数准备等。最后,微调过程我们通过swanlab可视化界面查看

环境准备和安装依赖包

首先,我们需要通过Miniconda安装 Python 依赖库:

# 切换环境
conda activate PY3.12.2

# 安装依赖库
pip install transformers datasets peft accelerate modelscope swanlab

以上 6 个库的主要用途简单介绍:

  1. transformers HuggingFace 出品的深度学习框架,是 NLP(自然语言处理)领域最流行的训练与推理框架。在本教程中主要用于加载模型、训练以及推理。
  2. datasets HuggingFace 出品的数据集工具,在本教程中主要用于加载数据集。
  3. peft HuggingFace 出品的微调工具,是一个流行的实现 LoRA 和其他微调技术的库。本教程中主要用于微调训练,与微调后模型推理。
  4. accelerate HuggingFace 出品的帮助简化分布式训练和混合精度训练的库。本教程中主要用于支持混合精度训练。
  5. modelscope ModelScope 库使开发人员能够通过丰富的 API 设计执行推理、训练和评估,从而促进跨不同 AI 领域的最先进模型的统一体验。代码中将主要用于在国内环境中下载 Qwen 大模型。
  6. swanlab 西安电子科技大学出品,深度学习实验管理与训练的可视化工具,可记录整个实验的超参数、指标、训练环境、Python 版本等,并通过可视化图表展示,帮助我们分析训练的结果。本教程中主要用于记录指标和可视化界面。

数据集准备和处理

ModelScope 上有很多公开免费的数据集供我们使用:datasets

本教程我们使用的是一个开放性问题进行分类的数据集:zh_cls_fudan-news

git lfs install
git clone https://www.modelscope.cn/datasets/swift/zh_cls_fudan-news.git

下载完成之后,我们会看到 2 个后缀为.jsonl的文件:

zh_cls_fudan-news
├── README.md
├── dataset_infos.json
├── test.jsonl
└── train.jsonl

.jsonl文件一般存储的是多行文本,每一行文本是一个 JSON 格式内容,即是多行 JSON 格式内容组合的文件。

train.jsonl是训练的输入文件,而test.jsonl则是训练的验证文件。他们每行 JSON 格式内容都包含textcategoryoutput共 3 个属性,分代表模型输入、可选的分类列表和最终模型输出的分类。

数据集样例

我们对大模型微调的目标,就是希望微调后的大模型能够根据textcategory组成的提示词,输出正确的output分类。

预训练大模型准备

本教程中,我使用的是Qwen2-0.5B模型,我们把大模型下载到本地(目录:Qwen2-0.5B):

git lfs install
git clone https://www.modelscope.cn/qwen/Qwen2-0.5B.git

如果 Git 克隆失败中断,可以继续克隆下载:

cd Qwen2-0.5B
git lfs pull

Qwen2-0.5B模型权重文件

大模型微调

大模型微调包括:包括加载大模型、数据集格式化处理、LoRA 参数准备等。最后,微调过程我们通过SwanLab可视化界面监控整个微调过程。

微调可视化配置

我们使用SwanLab来监控整个训练过程,并评估最终的模型效果。如果是第一次使用 SwanLab,则需要注册 SwanLab 账号:https://swanlab.cn,注册成功之后,在用户设置页面复制API Key,在训练开始时需要用到。

SwanLab API Key获取

为了便于我们查看我们微调的数据,我们还需要创建一个项目(项目名称:Qwen2-FineTuning):

SwanLob创建项目

大模型加载、设置和微调

由于微调涉及到好几步*强烈建议大家使用Jupyter Lab**工具进行代码调试和验证,它可以把整个代码分成多个区块,单个区块可以多次执行。

# Qwen2-0.5B-train.py
import json
import pandas as pd
import torch
from datasets import Dataset
from modelscope import AutoTokenizer
from swanlab.integration.huggingface import SwanLabCallback
from peft import LoraConfig, TaskType, get_peft_model
from transformers import AutoModelForCausalLM, TrainingArguments, Trainer, DataCollatorForSeq2Seq
import os
import swanlab

# 权重根目录
BASE_DIR = 'D:\ModelSpace\Qwen2'

# 设备名称
device = 'cuda' if torch.cuda.is_available() else 'cpu'
# device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

# 数据集处理函数,包括:训练数据集和测试数据集
def dataset_jsonl_transfer(origin_path, new_path):
    """
    将原始数据集转换为大模型微调所需数据格式的新数据集
    """
    messages = []

    # 读取原JSONL文件
    with open(origin_path, "r", encoding="utf-8") as file:
        for line in file:
            # 解析每一行原始数据(每一行均是一个JSON格式)
            data = json.loads(line)
            text = data["text"]
            catagory = data["category"]
            output = data["output"]
            message = {
                "input": f"文本:{text},分类选项列表:{catagory}",
                "output": output,
            }
            messages.append(message)

    # 保存处理后的JSONL文件,每行也是一个JSON格式
    with open(new_path, "w", encoding="utf-8") as file:
        for message in messages:
            file.write(json.dumps(message, ensure_ascii=False) + "\n")

# 在使用数据集训练大模型之前,对每行数据进行预处理
def process_func(example):
    """
    将数据集进行预处理
    """
    MAX_LENGTH = 384
    input_ids, attention_mask, labels = [], [], []
    instruction = tokenizer(f"<|im_start|>system\n你是一个文本分类领域的专家,你会接收到一段文本和几个潜在的分类选项列表,请输出文本内容的正确分类<|im_end|>\n<|im_start|>user\n{example['input']}<|im_end|>\n<|im_start|>assistant\n", add_special_tokens=False)  # add_special_tokens 不在开头加 special_tokens
    response = tokenizer(f"{example['output']}", 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
    }

# 加载预训练模型和分词器
model_dir = os.path.join(BASE_DIR, 'Qwen2-0.5B')
tokenizer = AutoTokenizer.from_pretrained(model_dir, use_fast=False, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(model_dir, device_map=device, torch_dtype=torch.bfloat16)
model.enable_input_require_grads() # 开启梯度检查点时,要执行该方法

# 加载、处理数据集和测试集
train_dataset_path = os.path.join(BASE_DIR, 'zh_cls_fudan-news', 'train.jsonl')
test_dataset_path = os.path.join(BASE_DIR, 'zh_cls_fudan-news', 'test.jsonl')

train_jsonl_new_path = os.path.join(BASE_DIR, 'train.jsonl')
test_jsonl_new_path = os.path.join(BASE_DIR, 'test.jsonl')

if not os.path.exists(train_jsonl_new_path):
    dataset_jsonl_transfer(train_dataset_path, train_jsonl_new_path)
if not os.path.exists(test_jsonl_new_path):
    dataset_jsonl_transfer(test_dataset_path, test_jsonl_new_path)

# 得到微调数据集
train_df = pd.read_json(train_jsonl_new_path, lines=True)
train_ds = Dataset.from_pandas(train_df)
train_dataset = train_ds.map(process_func, remove_columns=train_ds.column_names)

# 创建LoRA配置
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 比例
)

# 将LoRA应用于模型
model = get_peft_model(model, config)

# 创建微调参数
args = TrainingArguments(
    output_dir=os.path.join(BASE_DIR, 'output', 'Qwen2-0.5B'),
    per_device_train_batch_size=4,
    gradient_accumulation_steps=4,
    logging_steps=10,
    num_train_epochs=2,
    save_steps=100,
    learning_rate=1e-4,
    save_on_each_node=True,
    gradient_checkpointing=True,
    report_to="none",
)

# SwanLab微调过程回调数据
swanlab_callback = SwanLabCallback(project="Qwen2-FineTuning", experiment_name="Qwen2-0.5B")

trainer = Trainer(
    model=model,
    args=args,
    train_dataset=train_dataset,
    data_collator=DataCollatorForSeq2Seq(tokenizer=tokenizer, padding=True),
    callbacks=[swanlab_callback],
)

# 开始微调
trainer.train()

# 模型结果结果评估
def predict(messages, model, tokenizer):
    text = tokenizer.apply_chat_template(
        messages,
        tokenize=False,
        add_generation_prompt=True
    )
    model_inputs = tokenizer([text], return_tensors="pt").to(device)

    generated_ids = model.generate(
        model_inputs.input_ids,
        max_new_tokens=512
    )
    generated_ids = [
        output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids)
    ]

    return tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]

# 模型评估:获取测试集的前10条测试数据
test_df = pd.read_json(test_jsonl_new_path, lines=True)[:10]

test_text_list = []
for index, row in test_df.iterrows():
    instruction = row['你是一个文本分类领域的专家,你会接收到一段文本和几个潜在的分类选项列表,请输出文本内容的正确分类']
    input_value = row['input']

    messages = [
        {"role": "system", "content": f"{instruction}"},
        {"role": "user", "content": f"{input_value}"}
    ]

    response = predict(messages, model, tokenizer)
    messages.append({"role": "assistant", "content": f"{response}"})

    result_text = f"{messages[0]}\n\n{messages[1]}\n\n{messages[2]}"
    test_text_list.append(swanlab.Text(result_text, caption=response))

swanlab.log({"Prediction": test_text_list})
swanlab.finish()

以上就是大模型微调的全部代码,微调的总体流程如下:

  1. 首先,我们通过PyTorch库检查 CUDA 是否可用,优先使用 CUDA 设备,否则退回 CPU 设备
  2. 然后,定义了 2 个函数:分别是数据集预处理函数、单行数据预处理函数(主要用于把原始数据集映射成大模型微调的数据内容)
  3. 接着准备开始微调了,首先加载预训练模型和分词器,trust_remote_code=True代表从本地磁盘加载模型权重
  4. 然后使用定义好的函数,处理原始数据集,并处理为微调数据集
  5. 接着创建LoRA配置,并把LoRA配置应用于预训练模型
  6. 接下来创建微调参数(output_dir="./output/Qwen2-0.5B"代表微调之后的权重文件目录),并设置SwanLab回调函数
  7. 最后,启动微调:trainer.train()
  8. 微调完成之后,我们通过 10 条测试数据,对模型进行了评估验证

从原始数据集映射成大模型数据集进度、速度和耗时(共 4000 条数据):

微调数据集处理

我们启动模型微调后,SwanLab 需要我们输入API Key,输入即可。

输入SwanLab API Key

查看微调进展

我们可以在Jupyter Lab中直接开启看板,非常方便的查看微调情况。

SwanLab查看微调进展

微调完成,可以看到在测试样例评估上,微调后Qwen2大模型能够给出准确的文本分类:

微调结果验证

至此,我们已经完成了Qwen2-0.5B大模型的微调工作,接下来就可以使用微调后模型完成特定任务了(文本分类)!

如何学习大模型

现在社会上大模型越来越普及了,已经有很多人都想往这里面扎,但是却找不到适合的方法去学习。

作为一名资深码农,初入大模型时也吃了很多亏,踩了无数坑。现在我想把我的经验和知识分享给你们,帮助你们学习AI大模型,能够解决你们学习中的困难。

我已将重要的AI大模型资料包括市面上AI大模型各大白皮书、AGI大模型系统学习路线、AI大模型视频教程、实战学习,等录播视频免费分享出来,需要的小伙伴可以扫取。

一、AGI大模型系统学习路线

很多人学习大模型的时候没有方向,东学一点西学一点,像只无头苍蝇乱撞,我下面分享的这个学习路线希望能够帮助到你们学习AI大模型。

在这里插入图片描述

二、AI大模型视频教程

在这里插入图片描述

三、AI大模型各大学习书籍

在这里插入图片描述

四、AI大模型各大场景实战案例

在这里插入图片描述

五、结束语

学习AI大模型是当前科技发展的趋势,它不仅能够为我们提供更多的机会和挑战,还能够让我们更好地理解和应用人工智能技术。通过学习AI大模型,我们可以深入了解深度学习、神经网络等核心概念,并将其应用于自然语言处理、计算机视觉、语音识别等领域。同时,掌握AI大模型还能够为我们的职业发展增添竞争力,成为未来技术领域的领导者。

再者,学习AI大模型也能为我们自己创造更多的价值,提供更多的岗位以及副业创收,让自己的生活更上一层楼。

因此,学习AI大模型是一项有前景且值得投入的时间和精力的重要选择。

  • 7
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值