在DeepSeek-7B上使用Unsloth进行GRPO微调

在DeepSeek-7B上使用Unsloth进行GRPO微调

DeepSeek 在自然语言处理领域掀起了一场风暴。凭借其惊人的规模和性能,这一尖端模型在问题解答和文本摘要等任务中表现出色。它处理细微差别理解的能力使其成为各行各业的游戏规则改变者。微调增强了它的能力,使其适应细分市场的需求,并能快速提供精确的结果。通过在专业数据集上对 DeepSeek-7B 进行改进,微调技术将 DeepSeek-7B 从通才转变为领域专家。

本文探讨了GRPO(通用强化预训练优化)如何通过强化学习改进微调,以及 Unsloth 如何优化内存管理,从而加快 DeepSeek-7B 等大型模型的微调过程。这些方法共同实现了更快、更具成本效益的微调,推动了新一代人工智能应用的发展。

学习目标

通过本文的学习,您应该能够

  • 了解微调 DeepSeek-7B 的基本原理,以提高执行特殊任务的性能。
  • 发现 GRPO 相对于 PPO 的优势,提高微调训练的效率。
  • 使用 Unsloth 和 LoRA 对大型模型进行快速、内存效率高的微调。
  • 利用 Unsloth、vLLM 和 Hugging Face 设置 DeepSeek-7B 微调,并优化 GPU 性能。
  • 为强化学习中的结构化输出实现正确性和 XML 等奖励功能。
  • 使用 LoRA 加载、保存和重新加载微调模型,以实现高效内存和高性能推理。
  • 排除 GPU 内存和配置问题,实现无缝微调。
  • 探索如何扩展到更大的数据集、新的奖励函数和多模式模型的 GRPO。

了解DeepSeek模型和GRPO算法

什么是DeepSeek-R1-Distill-Qwen-7B?

DeepSeek-R1-Distill-Qwen-7B是建立在Qwen架构之上的最先进的大型语言模型。它采用稳健、可扩展的设计,利用数十亿个参数来处理复杂的 NLP 任务,如文本生成、问题解答和摘要。DeepSeek-7B 变体是其大型同类产品的精简版,这意味着它保留了大部分性能,同时在计算和内存使用方面更加高效。这使它非常适合部署在推理速度和准确性都很重要的环境中。它的架构采用了具有自我关注机制的转换器层,使其在处理文本中的长距离依赖关系时非常有效。

R1_Training

主要功能和架构概述

DeepSeek-7B 的核心是多层变压器架构,该架构具有高度并行性,可在大规模数据集上进行高效训练。每一层都由一系列多头自注意模块和前馈网络组成。注意力机制有助于模型在处理过程中专注于输入序列的相关部分,使其能够高效地完成需要上下文理解的任务。

Archi

Source: DeepSeek V3

DeepSeek-7B 通过位置编码、注意力层和前馈层处理标记嵌入,在保持高质量结果的同时,还能高效地扩展到大型数据集。其深入的上下文感知理解能力增强了微调后的跨域泛化能力。LoRA 等方法通过应用低秩更新提高了训练效率,即使在计算资源有限的情况下也能进行微调。

GRPO简介及其如何改进微调

GRPO(通用强化预训练优化)是一种先进的技术,旨在提高大型语言模型的微调效率。它将强化学习原理与预训练相结合,利用奖励信号而不是直接监督来完善模型的行为。GRPO 采用基于策略的优化方法反复优化模型参数。

在典型的微调方案中,模型是在有监督的数据集上训练的,它直接从地面实况标签中学习。与此相反,GRPO 引入了强化学习(RL)范式,对模型进行训练,以最大化指导其行为的奖励信号。这一过程能让模型更灵活地适应特定任务的细微差别,从而提高准确性和泛化能力。

GRPO 中策略优化的关键公式可表示为:

ObjFunc

其中:

Expl_ObjFunc

这种基于策略的方法可确保模型不断适应训练过程中提供的反馈,重点改进与特定任务目标相对应的奖励信号。

GRPO的奖励信号

在 GRPO 中,奖励功能可根据具体任务要求进行定义,引导模型关注所需的行为。奖励可以是多种因素的函数,如准确性、格式或逻辑一致性。例如,正确性奖励函数R_correct 可以定义为:

RewFunc

这种反馈机制允许 GRPO 逐步完善模型,强调对特定任务而言最重要的领域。

GRPO与PPO(近端策略优化)有何不同?

GRPO 引入了基于策略的强化学习来优化预训练过程,而 PPO(近端策略优化)则是强化学习中另一种广泛使用的算法,尤其是在微调大型模型时。PPO 以其稳定性和处理高维动作空间的能力而著称,因此在训练大规模模型时很受欢迎。不过,PPO 通常需要大量数据,而且对学习率等超参数很敏感。

GRPO 和 PPO 的主要区别在于策略优化的性质。在 PPO 中,策略更新使用的是削足适履目标,以防止与当前策略产生较大偏差,从而导致不稳定的训练。PPO 目标函数如下:

PPO

其中:

Expl_PPO

PPO 中的这种 “剪切 ”机制有助于避免可能导致不稳定的大规模策略更新,但也会减慢学习进程,尤其是对于 DeepSeek-7B 这样的大型模型。

剪切目标通过惩罚策略中的大偏差,确保模型不会进行大规模、不稳定的更新。不过,它也会在稳定性和学习速度之间做出权衡,尤其是对于较大的模型,更新次数和学习速度都必须仔细调整。

相比之下,GRPO 采用了一种更具适应性的动态奖励结构,使其能够直接根据任务的具体指标实现性能最大化,而无需依赖 “信任区域 ”方法。GRPO 的优化程序不需要剪切,其基于奖励的学习机制为微调提供了更直接、更高效的途径。因此,GRPO 通常只需要较少的更新即可达到最佳性能。

参数θ的梯度更新规则

在 GRPO 中,更新模型参数的梯度是通过模型的奖励反向传播来计算的。如果根据模型输出计算出时间步长 t 的奖励R_t,那么参数 θ 的梯度更新规则就是:

GRPO

与根据优势函数调整梯度的 PPO 削波法相比,这种梯度下降法更直接、更高效。PPO 算法与 GRPO 算法的主要区别总结如下:

特征 GRPO PPO
目标 随时间累积奖励最大化。 最小化剪切目标,实现稳定更新。
奖励信号 特定任务的自适应奖励。 基于优势的剪切奖励。
训练稳定性  更灵活、更直接。 通过剪切机制确保稳定性。
优化机制 直接奖励最大化。 剪切策略更新。
使用案例 任务自适应微调与奖励。 关注稳定性的一般 RL 任务。

Unsloth:提高微调效率

对 DeepSeek-7B 等大型语言模型进行微调的计算成本很高,需要大量内存和处理能力。Unsloth 是一个优化框架,旨在加快训练速度,同时大幅降低内存消耗。在使用LoRA(Low-Rank Adaptation)和 GRPO 时,它的作用尤为明显,因为它能确保高效利用 GPU 资源,并在消费级硬件上实现微调。

Unsloth如何优化模型训练?

Unsloth 引入了多项优化措施,以提高模型微调效率:

  • 内存高效加载: Unsloth 支持 4 位和 8 位量化,在保持性能的同时减少了模型的内存占用。
  • 快速训练和推理: 通过利用闪存注意力和分页优化器,Unsloth 可显著加快训练和推理速度。
  • 梯度检查点 它支持梯度检查点,只存储激活的子集并在需要时重新计算,从而减少了所需的 GPU 内存。
  • 与 LoRA 无缝集成:Unsloth 本机支持 LoRA,允许用户只训练模型参数子集,而不是整个网络。

使用 Unsloth 的模型加载过程非常简单,可实现高效执行。下文将详细介绍这一过程。

使用 Unsloth 的优势

  • 减少 GPU 内存使用量达 50%,允许在中级 GPU 上进行训练。
  • 通过集成优化的注意力机制,实现更快的训练。
  • 支持 vLLM(超大语言模型)以加速推理。
  • 与 GRPO 无缝协作,确保基于强化学习的微调具有资源效率。

通过将 Unsloth 纳入微调管道,研究人员和工程师可以最大限度地提高 DeepSeek-7B 的性能,而不会遇到常见的计算限制。

利用GRPO对DeepSeek-7B进行微调

前面几节介绍了 DeepSeek-7B 的架构和 GRPO 算法,在此基础上,现在是时候深入研究微调模型所需的实际步骤了。本节将引导您完成从设置环境到配置 GRPO 训练器的必要步骤,其中包括代码片段和对每一部分过程的详细解释。

如第 2 节所述,DeepSeek-7B 模型是处理大规模 NLP 任务的强大工具,如果与 GRPO(通用强化预训练优化)搭配使用,它将变得更加高效。通过应用 GRPO 方法,我们可以利用强化学习框架在特定任务上对 DeepSeek-7B 进行微调。这不仅能让模型产生更好的结果,还能比传统方法更有效地适应新数据。

现在,让我们探索使用 GRPO 和 Unsloth 微调 DeepSeek-7B 的详细步骤,并利用 LoRA 在训练过程中高效使用内存。

步骤 1:设置环境

要开始微调 DeepSeek-7B,首先需要设置环境。这包括安装 Unsloth、vllm 和其他必要的软件包。下面是安装这些软件包的命令:

!pip install unsloth vllm datasets
!pip install git+https://github.com/huggingface/trl.git

说明:

  • Unsloth:用于高效语言模型微调和内存优化的库。
  • vllm:实现大型模型的快速推理。
  • Dataset:用于处理各种 NLP 数据集(包括来自 Hugging Face 的数据集)的库。

安装完成后,我们就可以加载模型并开始微调了。

步骤 2:使用Unsloth加载模型

现在,我们将使用 Unsloth 加载 DeepSeek-7B 模型。该模型将使用 LoRA(Low-Rank Adaptation)加载,以实现高效微调。下面是这一步的代码片段:

from unsloth import FastLanguageModel
model, tokenizer = FastLanguageModel.from_pretrained(
model_name="unsloth/DeepSeek-R1-Distill-Qwen-7B",
max_seq_length=512,
load_in_4bit=True,  # Uses 4-bit quantization for memory efficiency
fast_inference=True,  # Enables fast inference for quicker processing
max_lora_rank=32,  # LoRA rank for fine-tuning efficiency
gpu_memory_utilization=0.6  # Controls memory usage
)

说明:

  • model_name:我们指定要加载的模型,本例中为 DeepSeek-R1-Distill-Qwen-7B。
  • max_seq_length:定义输入标记的最大序列长度。
  • load_in_4bit:使用 4 位量化,大大减少内存使用量。
  • fast_inference:这可以让 vLLM 加快推理时间。
  • max_lora_rank:LoRA 适应的秩,用于控制低秩矩阵的大小。
  • gpu_memory_utilization:调整模型使用 GPU 内存的大小,以避免内存不足错误。

预期结果:模型将以优化配置加载到内存中,以便使用 LoRA 进行微调。

步骤 3:应用LoRA进行高效微调

LoRA 用于优化 DeepSeek-7B 等大型模型的内存。通过应用 LoRA,我们只更新低秩矩阵,而不是整个模型,这样就能高效地微调内存。下面是代码片段:

model = FastLanguageModel.get_peft_model(
model,
r=32,  # Rank of LoRA layers, which controls memory and efficiency
target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", 
"up_proj", "down_proj"],  # Modules to apply LoRA to
lora_alpha=32,  # Scaling factor for LoRA
use_gradient_checkpointing="unsloth",  # Enables gradient checkpointing 
for long context fine-tuning
random_state=3407  # Seed for reproducibility
)

说明:

  • r:LoRA 矩阵的秩。秩越高,训练越智能,但速度越慢。
  • target_modules:应用 LoRA 的模型层(例如,q_proj 用于查询投影)。
  • lora_alpha:用于控制 LoRA 层重要性的缩放因子。
  • use_gradient_checkpointing:仅在需要时存储中间梯度,从而减少内存消耗。
  • random_state:确保微调过程的可重复性。

预期结果:现在模型的内存使用已经优化,可以在大型数据集上高效地进行微调。

OP1-thumbnail_webp-600x300-1

步骤 4:准备训练数据集

微调 DeepSeek-7B 需要一个特定格式的数据集。在这里,我们将把数据集从 JSON 文件格式加载并转换为拥抱脸数据集对象。代码如下:

import json
from datasets import Dataset
def load_and_transform_json(json_path):
with open(json_path, "r") as f:
data = json.load(f)
transformed_data = [{"question": entry["question"], "answer": entry["response"], "prompt": [{"content": SYSTEM_PROMPT, "role": "system"}, {"content": entry["question"], "role": "user"}]} for entry in data]
return transformed_data
json_file_path = "/content/your_dataset.json"  # Path to your JSON file
dataset = load_and_transform_json(json_file_path)

说明:

  • load_and_transform_json: 加载 JSON 文件并将其转换为培训所需的格式。
  • 数据包括每个条目的问题 答案 ,以及系统生成的提示

预期结果:数据集现在格式正确,可用于训练。下面是数据集的一个示例。

dataset_VNhbi7V-thumbnail_webp-600x300-1

步骤 5:为结构化输出设计奖励函数

在强化学习中,奖励函数会引导模型获得理想的输出结果。在这里,我们定义奖励函数来评估模型的响应。例如,正确性奖励函数(correctness_reward_func)检查提取的答案是否与预期答案一致。

def correctness_reward_func(prompts, completions, answer, **kwargs) -> list[float]:
responses = [completion[0]['content'] for completion in completions]
q = prompts[0][-1]['content']
extracted_responses = [extract_xml_answer(r) for r in responses]
return [2.0 if r == a else 0.0 for r, a in zip(extracted_responses, answer)]
def int_reward_func(completions, **kwargs) -> list[float]:
responses = [completion[0]['content'] for completion in completions]
extracted_responses = [extract_xml_answer(r) for r in responses]
return [0.5 if r.isdigit() else 0.0 for r in extracted_responses]
def strict_format_reward_func(completions, **kwargs) -> list[float]:
pattern = r"^<reasoning>\n.*?\n</reasoning>\n<answer>\n.*?\n</answer>\n$"
responses = [completion[0]["content"] for completion in completions]
matches = [re.match(pattern, r) for r in responses]
return [0.5 if match else 0.0 for match in matches]
def soft_format_reward_func(completions, **kwargs) -> list[float]:
pattern = r"<reasoning>.*?</reasoning>\s*<answer>.*?</answer>"
responses = [completion[0]["content"] for completion in completions]
matches = [re.match(pattern, r) for r in responses]
return [0.5 if match else 0.0 for match in matches]
def xmlcount_reward_func(completions, **kwargs) -> list[float]:
contents = [completion[0]["content"] for completion in completions]
return [count_xml(c) for c in contents]

说明:

  • correctness_reward_func:将提取的答案与预期答案进行比较。如果匹配,则奖励 2.0,否则奖励 0.0。
  • int_reward_func:奖励生成数字回答的模型。
  • strict_format_reward_func:确保模型的输出严格遵循 XML 格式,奖励格式良好的输出。
  • soft_format_reward_func:检查模型的输出是否松散地遵循所需的格式。
  • xmlcount_reward_func:评估输出在多大程度上遵循了 XML 结构,并对结构不良的响应进行惩罚。

预期结果:这些奖励函数将引导模型生成不仅正确,而且结构合理并符合所需格式的响应。

步骤 6:配置GRPO训练器

现在,我们将配置 GRPOTrainer 以使用训练数据集和奖励函数。GRPOConfig 对象用于指定学习率和批量大小等训练参数。

from trl import GRPOConfig, GRPOTrainer
training_args = GRPOConfig(
learning_rate=5e-6,
per_device_train_batch_size=1,
num_generations=6,
max_prompt_length=256,
max_completion_length=200,
max_steps=1,
)
trainer = GRPOTrainer(
model=model,
processing_class=tokenizer,
reward_funcs=[correctness_reward_func],
args=training_args,
train_dataset=dataset,
)
trainer.train()

说明:

  • GRPOConfig:配置各种训练参数,如学习率、批量大小和生成的代数。
  • GRPOTrainer:该类负责实际的训练过程。它接收模型、标记符、奖励函数和训练参数。

GRPOConfig参数说明:

  • learning_rate(学习率): 模型优化的学习率。较低的值,如 5e-6,可以在多次迭代中实现稳定的训练。
  • per_device_train_batch_size(每设备训练批次大小): 每个训练步骤的批量大小。这里设置为 1,即每个 GPU 一次处理一个示例。
  • num_generations(代数): 模型在每个微调步骤中产生的代数。
  • max_prompt_length(最大提示长度): 输入提示符的最大标记长度。
  • max_completion_length(最大完成长度): 模型输出的最大标记长度。
  • max_steps(最大训练步数): 要执行的训练步数。

预期结果:将使用 GRPO 算法使用定义的奖励函数对模型进行训练,对模型进行微调,使其在给定的数据集上表现更好。

Op2_0nxsY1J

保存和重载微调模型

一旦使用 GRPO 和 LoRA 对 DeepSeek-7B 模型进行了微调,就必须将模型保存到磁盘或云存储中,以备将来使用。在本节中,我们将介绍如何保存微调后的模型,并再次加载它进行推理。这将确保您能持续保持进展,避免从头开始重新训练。

保存LoRA微调模型

使用 LoRA 和 GRPO 微调模型后,需要将其保存到存储位置。这是确保以后可以重新加载模型而无需重新训练的关键步骤。以下是将微调后的模型(包括 LoRA 特定权重)保存到磁盘的方法:

# Define the path to save the fine-tuned model
model_save_path = "/content/deepseek_lora_finetuned"
# Save the model and tokenizer
model.save_pretrained(model_save_path)
tokenizer.save_pretrained(model_save_path)

说明:

  • model.save_pretrained:这将同时保存模型权重和 LoRA 特定层(如低秩适配矩阵)。
  • tokenizer.save_pretrained:保存标记化器,其中包括特殊标记和词汇等标记化逻辑。
  • model_save_path:存储模型的目录。可以是本地路径,也可以是云目录(如 Google Drive、S3)。

预期结果:模型和标记符将被保存到指定路径,以备将来使用。您以后可以使用保存的模型重新加载精确微调版本进行推理,而无需重新训练。

加载模型用于未来推理

保存微调模型后,您可以轻松地将其载入内存,用于推理或进一步微调。下面是加载已保存模型和标记符的代码,以及 LoRA 特有的配置:

from unsloth import FastLanguageModel
# Define the path where the model is saved
model_save_path = "/content/deepseek_lora_finetuned"
# Reload the model and tokenizer
model, tokenizer = FastLanguageModel.from_pretrained(
model_save_path,
max_seq_length=512,
load_in_4bit=True,  # Ensure it's still using efficient memory settings
fast_inference=True,  # Enable fast inference
max_lora_rank=32,  # LoRA rank must match what was used during fine-tuning
gpu_memory_utilization=0.6
)

说明:

  • FastLanguageModel.from_pretrained:此函数从指定路径加载已保存的模型权重和标记符。
  • max_lora_rank:推理过程中使用的 LoRA rank 必须与微调过程中使用的相匹配,以确保应用正确的适配。
  • load_in_4bit 和 gpu_memory_utilization:确保模型在加载推理时继续保持内存效率。

预期结果:从保存的目录中加载模型及其 LoRA 配置,以便高效执行推理。这意味着模型将利用微调参数,您可以直接开始生成响应或运行任务,而无需重新应用微调过程。

下面是用于微调本博客的数据集的输出示例。它与工艺流程表有关。看看模型是如何推理并生成对查询的响应的。使用 GRPO 模型进行微调包含了推理功能,这在下面的答案中有所体现。

QuestionOutput-thumbnail_webp-600x300-1

高级选项:保存到云存储

如果想将模型保存到云存储(如 Google Drive 或 Amazon S3),可以修改 model_save_path 指向相应的云目录。下面是一个使用 gdown 将模型保存到 Google Drive 的示例:

!pip install gdown
import gdown
# Upload the model to Google Drive
gdown.upload(model_save_path, output="path_to_google_drive_folder")

对于亚马逊 S3,您可以使用 boto3 库上传模型:

!pip install boto3
import boto3
s3 = boto3.client('s3')
# Upload model to S3
s3.upload_file("/content/deepseek_lora_finetuned", "your-bucket-name", 
"model_directory/deepseek_lora_finetuned")

说明:

  • gdown.upload:此函数将模型从本地环境上传到 Google Drive。
  • boto3:亚马逊用于与 S3 等 AWS 服务交互的 Python SDK。它允许你将模型直接上传到 S3 存储桶。

预期结果:您可以从云端保存和访问模型,从而便于在其他环境中共享和部署。

常见问题和故障排除

在微调 DeepSeek-7B 等大型模型时,可能会出现几个常见陷阱,尤其是与 GPU 内存、训练配置和奖励函数调整相关的陷阱。意识到这些问题并了解如何排除故障,可以在微调过程中节省大量时间。

1. GPU内存超载

对大型模型进行微调往往会导致 GPU 内存超载,尤其是在使用 LoRA 等高级配置或进行大批量训练时。要缓解这一问题

  • 减少批量大小,或调整 GRPOConfig 中的 per_device_train_batch_size 参数,以适应 GPU 的内存。
  • 通过设置use_gradient_checkpointing = “unsloth” 使用梯度检查点,存储中间激活以减少内存使用。
  • 如果遇到内存问题,请降低 LoRA 级别,因为级别越低所需内存越少。

2. 模型加载不当

有时,不正确的模型加载配置会导致问题,特别是在以 4 位精度加载大型模型或使用 LoRA 时。请务必

  • 确认 LoRA 等级和其他特定于模型的配置(如 max_lora_rank gpu_memory_utilization)已根据 GPU 的能力正确设置。
  • 在处理大型模型时,确保启用 vLLM 进行快速推理,以避免不必要的延迟。

3. 奖励函数不匹配

奖励函数的微调需要仔细考虑。不正确或过于严格的奖励函数配置可能会阻碍学习,使模型的性能低于最佳状态。排除故障:

  1. 检查奖励函数(如 correctness_reward_func strict_format_reward_func )的执行情况,确保它们与所需输出一致。
  2. 如果模型产生不稳定或不理想的响应,微调奖励阈值和评分机制。

4. 数据问题

数据质量和格式化是成功训练的关键。如果您使用的是自定义数据集,请将其转换为 Hugging Face 数据集格式,并确保对任何基于 JSON 的输入进行适当的解析和预处理。始终检查数据集是否存在任何差异或缺失字段,尤其是在复杂的奖励函数(如 correctness_reward_func,它依赖于精确的答案匹配)中。

5. 训练配置冲突

训练配置中的冲突,如学习率、优化器设置或梯度累积步骤不匹配,会导致性能不理想或收敛速度减慢。请务必确保根据硬件和训练目标的具体要求,对 GRPO 配置中的参数进行微调。此外,低学习率和高梯度累积步骤有助于稳定大型模型的训练。

通过解决这些常见问题并监控内存使用情况、数据格式和奖励函数的有效性,您可以简化微调过程,确保模型训练更加顺利。

小结

在本指南中,我们探讨了在 DeepSeek-7B(通用强化预训练优化)和 LoRA(低秩自适应)上进行 GRPO 微调的过程,结合这些技术的优势来优化大型模型训练。我们首先讨论了 DeepSeek-7B 和 GRPO 的架构,概述了 Unsloth 在内存管理和高效模型训练中的作用。我们还演示了从设置环境、用 LoRA 加载模型到应用基于强化学习的奖励函数进行微调所涉及的实际步骤。

有效的微调结合了 GRPO 和 LoRA:GRPO 通过基于策略的更新加强学习,而 LoRA 则实现了高效的内存训练。我们演示了定义奖励函数、使用 GRPOTrainer 进行优化,以及通过保存和重新加载确保模型的可用性。主要挑战包括扩展到更大的数据集,以及改进奖励函数以提高适应性。将 GRPO 扩展到多模式模型可进一步提高人工智能能力。

  • DeepSeek-7B 和 GRPO 为利用基于强化学习的优化技术微调大规模模型提供了强大的基础。
  • LoRA 优化了内存使用情况,并通过应用低秩适配实现了对大型模型的高效微调。
  • GRPO 与 PPO 等传统方法不同,它提供基于策略的更新,从而提高了训练效率。
  • 定义结构良好的奖励函数在强化学习微调中至关重要,它能引导模型实现高质量的输出。
  • 保存和重新加载微调模型的过程确保了模型的可重用性和长期性能。
  • 未来的改进重点是扩展到更大的数据集,尝试新的奖励函数,以及将 GRPO 应用于多模式模型(文本、图像、音频)。

评论留言