7B模型GRPO逆袭14B!小模型也懂统筹,日程规划AI惊艳

2026-01-20AI工具

7B模型GRPO逆袭14B!小模型也懂统筹,日程规划AI惊艳

在人工智能浪潮席卷全球的当下,大型语言模型(LLM)的发展可谓一日千里。进入2025年,特别是继DeepSeek等模型的成功之后,越来越多的人开始尝试利用GRPO(Generalized Reinforcement learning with Policy Optimization)技术,来训练自己的推理模型。

作为一名热衷实践的中国自媒体作者,我自然也按捺不住心中的好奇,投身其中。亲手让一个语言模型仅通过提示词和奖励机制来学习,而不是像监督微调那样需要大量的完成样本,这本身就是一件引人入胜的尝试。市面上大多数的GRPO训练案例,往往集中在像GSM8K数学推理或“倒计时游戏”这类相对标准化的任务上。我则希望能尝试一些更具原创性的应用,真正地“摸爬滚打”一番。

于是,一个想法在我脑海中浮现:我们能否训练出一个模型,使其能够根据一系列事件及其优先级,自动生成一份优化后的日程表呢?初步的实验结果显示,像ChatGPT这类通用型大模型通常能够解决这类问题,但对于参数量在140亿以下的小型语言模型(SLM)而言,这依然是个不小的挑战。这无疑为我提供了一个绝佳的挑战机会!

我很快意识到,选择一个原创问题意味着我必须从零开始,独立思考问题定义、数据生成、基础模型选择、奖励函数设计等一系列环节,并进行多轮训练,才能期待模型能够有所收获。这个过程中充满了学习与探索,而这正是我希望通过这篇文章,与大家分享的宝贵经验。我将把我所有的代码都整理到了Qwen Scheduler GRPO的代码库中,欢迎大家查阅。这篇文章侧重于我的实际操作体验,如果您对GRPO的理论知识有所了解(例如DeepSeekMath的论文或外媒Hugging Face平台的推理课程),那将有助于更好地理解本文。

定义问题:让AI学会“统筹规划”

首先,我们来明确一下希望语言模型解决的核心问题。我们将给模型提供一份活动列表,其中包含每个活动的开始和结束时间,并明确哪些是高优先级事件。模型的目标是生成一份日程表,使得所选活动的总加权持续时间最大化。在这个设定中,高优先级事件的权重为2,普通事件的权重为1。

您可能会发现,这个问题的定义清晰合理,但又带有一些“人为设定”的色彩。或许您会更倾向于最大化利润,或者认为一份过于紧凑的日程表并非最佳选择。这完全没有问题!那只是一个不同的问题,而您同样可以通过GRPO来训练一个解决该问题的模型。可以说,一旦您能够通过简洁的提示词清晰地向模型解释任务,并想清楚如何奖励优质的输出,甚至能识别出模型在训练过程中那些令人鼓舞的行为,那么,您就已经站在了尝试GRPO的有利位置。关于这一点,我稍后还会详细展开。现在,我们先来看一个问题示例。

输入示例:一份待规划的事件清单

以下是精简后的提示词示例(完整提示词可在文章后续部分找到):

Task: create an optimized schedule based on the given events.
Rules: ...
You must use this format:
<think>...</think>
<schedule>
<event>
<name>...</name>
<start>...</start>
<end>...</end>
</event>
...
</schedule>
---
Events:
- Event A (01:27 - 01:42)
- Event B (01:15 - 02:30)
- Event C (15:43 - 17:43)
Priorities:
- Event B

输出示例:AI生成的智能日程

模型会根据上述输入,生成一份包含思考过程和最终日程的输出。

<think>A detailed reasoning</think>
<schedule>
<event>
<name>Event B</name>
<start>01:15</start>
<end>02:30</end>
</event>
<event>
<name>Event C</name>
<start>15:43</start>
<end>17:43</end>
</event>
</schedule>

经过一番深入研究,我发现这类问题实际上属于“区间调度问题”范畴。我所定义的具体问题是加权区间调度问题的一个变体,这类问题可以通过动态规划算法高效求解。这一点非常重要,因为它使得我们能够轻松计算出任何给定输入的最佳可能得分,这正是我们模型在训练过程中需要努力达成的目标。

数据集构建:AI学习的“教科书”

问题明确之后,下一步就是构建用于模型训练和评估的数据集。数据集中的每一行核心内容,仅仅是包含事件列表和优先级的一段提示词,供模型进行调度。与监督微调不同,我们不需要提供模型应该遵循的参考完成结果。这也是为什么在数学等可验证领域,为GRPO构建数据集通常更为简便的原因之一。我们还在每一行数据中包含了最优分数(即可能的最大加权持续时间)。模型在训练时不会直接看到这个分数,但我们用它来计算奖励,从而引导模型朝着正确的方向学习。

有了这些要求,编写数据集生成脚本相对容易。我们使用了不同类别的事件名称,例如“🎶音乐节”、“🎓大学课程”、“🧑‍💻科技大会”等等,力求场景多样化。每个示例都包含随机数量的事件(4到8个),时间跨度也各不相同。我们特意确保某些事件存在时间上的重叠。同时,我们还会随机将一些事件标记为优先级。最终,我们生成了500个训练样本和100个测试样本。您可以在我的GitHub代码库中找到数据集生成脚本,而生成好的数据集也已经上传到了外媒Hugging Face平台供大家下载。

模型训练:从“零基础”到“小专家”

完整的训练笔记本也已放在我的代码库中。如今,有许多训练库都支持GRPO算法。外媒Hugging Face平台的TRL库就是一个不错的选择,它甚至支持使用vLLM来加速训练过程中的样本生成。而我这次选择使用了Unsloth,这是一个专门为TRL打补丁的库,旨在显著降低GPU内存占用。我使用的是一块NVIDIA A6000 GPU(48GB显存),但通过一些小的调整和足够的耐心,您完全可以在拥有16GB显存的免费Colab或Kaggle实例上复现我的实验。Unsloth对于GPU资源有限的开发者来说,是进行实验的好帮手,不过它也存在一些小bug,有时可能会让人感到沮丧,这一点我们稍后会提到。

选择并加载训练模型

我最终决定训练Qwen2.5-Coder-7B-Instruct模型,这是来自中国阿里团队Qwen系列中一款针对代码任务优化的语言模型。这个选择并非空穴来风,而是基于两点主要考量:

第一,我最初也尝试过更小的模型(0.5B和1.5B),但在观察它们对日程调度提示的完成情况时,我发现这些超小模型生成的推理结果往往令人费解。这让我有了第一个重要的领悟:如果基础模型的预训练效果或模型规模使其能力与预期相去甚远,那么GRPO也无法创造奇迹。

第二,我选择了一个代码专用模型而不是通用模型,部分原因在于这在处理数学任务时很常见,另一部分则是因为我注意到这款模型在遵循指定格式(如<think><schedule>等)方面已经表现得相当出色。

现在,我们可以使用Unsloth加载模型了。为了节省GPU资源,我们将采用QLoRA技术进行训练。

from unsloth import FastLanguageModel
max_seq_length = 2048
lora_rank = 32
model, tokenizer = FastLanguageModel.from_pretrained(
model_name = "Qwen/Qwen2.5-Coder-7B-Instruct",
max_seq_length = max_seq_length,
load_in_4bit = True,
fast_inference = True,
max_lora_rank = lora_rank,
gpu_memory_utilization = 0.85, # Reduce if out of memory
)
model = FastLanguageModel.get_peft_model(
model, r = lora_rank,
target_modules = [
"q_proj",
"k_proj",
"v_proj",
"o_proj",
"gate_proj",
"up_proj",
"down_proj",
], # Remove QKVO if out of memory
lora_alpha = lora_rank,
use_gradient_checkpointing = "unsloth", # Enable long context finetuning
random_state = 3407,)

如果您的显存不足48GB,可以调整gpu_memory_utilizationlora_ranktarget_modules等参数;后两个参数会影响模型的学习能力。

数据集预处理:为AI“量身定制”任务说明

我们数据集中的每个示例都包含事件和优先级。我们需要对数据集进行预处理,以添加任务的整体描述和说明,这些内容将以系统消息和用户消息的形式呈现。

import datasets
SYSTEM_PROMPT = """You are a precise event scheduler.
1. First, reason through the problem inside <think> and </think> tags. Here you can create drafts, compare alternatives, and check for mistakes.
2. When confident, output the final schedule inside <schedule> and </schedule> tags. Your schedule must strictly follow the rules provided by the user."""
USER_PROMPT ="""Task: create an optimized schedule based on the given events.
Rules:
- The schedule MUST be in strict chronological order. Do NOT place priority events earlier unless their actual start time is earlier.
- Event start and end times are ABSOLUTE. NEVER change, shorten, adjust, or split them.
- Priority events (weight = 2) carry more weight than normal events (weight = 1), but they MUST still respect chronological order.
- Maximize the sum of weighted event durations.
- No overlaps allowed. In conflicts, include the event with the higher weighted time.
- Some events may be excluded if needed to meet these rules.
You must use this format:
<think>...</think>
<schedule>
<event>
<name>...</name>
<start>...</start>
<end>...</end>
</event>
...
</schedule>
--- """
ds = datasets.load_dataset("anakin87/events-scheduling", split="train")
ds = ds.map(
lambda x: {
"prompt": [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": USER_PROMPT + x["prompt"]},
]
}
)

奖励函数:引导AI“明辨是非”的关键

GRPO作为一种强化学习算法,其训练过程中会为每个提示词从模型中生成多个样本(在本实验中为8个)。模型参数的更新方向,正是为了生成更高奖励的响应。因此,虽然我们不需要在数据集中提供完整的参考结果,但奖励函数的设计却至关重要。

在像学习GSM8K这类相对简单的任务中,常见的做法是定义多个奖励函数并将其求和:一个函数检查输出格式是否正确,另一个则检查最终答案是否与已知解决方案匹配。威廉·布朗(William Brown)关于学习GSM8K的示例就非常精彩,大家可以参考。而对于TRL/Unsloth自定义奖励函数的指导,也可以查阅官方文档。在我们的实验中,为输出格式设计奖励函数相对容易:

import re
overall_pattern = (r"<think>.+<\/think>.*<schedule>.*(<event>.*<name>.+<\/name>.*<start>\d{2}:\d{2}<\/start>.*"
r"<end>\d{2}:\d{2}<\/end>.*<\/event>)+.*<\/schedule>")
overall_regex = re.compile(overall_pattern, re.DOTALL)
def format_reward(prompts, completions, **kwargs):
responses = [completion[0]['content'] for completion in completions]
return [0.0 if not overall_regex.match(response) else 10.0 for response in responses]

然而,要奖励日程表本身的质量就复杂多了。一个有效的日程表通常具备以下特点:事件来源于原始提示词(没有凭空捏造的事件或更改时间)、事件按时间顺序排列、没有重叠事件、事件数量至少为两个。我们还希望鼓励模型生成最大化加权总持续时间的日程表。

我尝试了不同数量的奖励函数,最终才确定了下方展示的方案,期间也积累了一些观察心得。我曾尝试用一个单一的奖励函数来评估分数:如果日程表有效(满足上述所有标准),则返回schedule_score/optimal_score,否则返回0。结果发现这是一个糟糕的主意,因为它过于不连续,大多数时候都返回0,无法提供足够的信号来指导模型的学习。

另一方面,我也曾尝试使用多个奖励函数,每个奖励函数对应一个要求:现有事件、时间顺序、无重叠等。在这种情况下,我发现即使模型有所学习,但训练效果并不理想,无法生成优质的日程表。奖励函数过多有时也会适得其反。

在“奖励过多”的阶段,我遭遇了一个经典的强化学习问题:奖励作弊(reward hacking)。我最初忘记排除少于两个事件的日程表。突然之间,我的奖励分数看起来非常棒!模型在格式、时间顺序、无重叠等方面都获得了高分……然而,当我查看实际输出时,我发现模型找到了一个漏洞:它只是生成只有一个事件的日程表(通常是高优先级事件)。这完美地满足了我的大部分独立奖励函数。这并非模型变得聪明,而是我的奖励设置存在可利用的缺陷。这是一次有趣的经验。

最终,我采用了两个奖励函数:一个鼓励生成按时间顺序排列的日程表,另一个则最大化分数。正如您在下方看到的,我试图将其他要求也纳入这两个奖励函数中,从而给予它们权重。

def sorted_events_reward(completions, **kwargs):
scores = []
responses = [completion[0]['content'] for completion in completions]
for response in responses:
scheduled_events = get_events(response) # not a valid schedule: should be discarded
if len(scheduled_events) < 2:
scores.append(0.0)
continue
scheduled_events_minutes = [(ev[0], time_to_minutes(ev[1]), time_to_minutes(ev[2])) for ev in scheduled_events]
if all(scheduled_events_minutes[i][1] < scheduled_events_minutes[i+1][1] for i in range(len(scheduled_events_minutes)-1)):
scores.append(20.0)
else:
scores.append(0)
return scores
def score_reward(prompts, completions, events, priority_events, optimal_score, **kwargs):
scores = []
responses = [completion[0]['content'] for completion in completions]
for content, valid_events, priorities, opt_score in zip(responses, events, priority_events, optimal_score):
scheduled_events = get_events(content) # Get valid scheduled events
existing_events = {ev for ev in scheduled_events if [ev[0], ev[1], ev[2]] in valid_events}
# penalize choosing nonexistent events or less than 2 events (not a valid schedule)
if len(existing_events)<len(scheduled_events) or len(existing_events) < 2:
scores.append(0.0)
continue
# Convert to minutes
existing_events_minutes = [(ev[0], time_to_minutes(ev[1]), time_to_minutes(ev[2])) for ev in existing_events]
# remove overlapping events and remove both events - to penalize overlaps
overlapping_events = set()
for j in range(len(existing_events_minutes)):
for k in range(j + 1, len(existing_events_minutes)):
if (existing_events_minutes[j][1] <= existing_events_minutes[k][2] and existing_events_minutes[j][2] >= existing_events_minutes[k][1]):
overlapping_events.add(existing_events_minutes[j])
overlapping_events.add(existing_events_minutes[k])
existing_events_minutes = [ev for ev in existing_events_minutes if ev not in overlapping_events]
# Calculate score
score = sum(2 * (ev[2] - ev[1]) if ev[0] in priorities else ev[2] - ev[1] for ev in existing_events_minutes)
scores.append((score/opt_score) * 70)
return scores

简而言之,在我的实验中,我采用了以下奖励函数:

  1. 格式奖励(0-10分)
  2. 事件排序奖励(0-20分)
  3. 分数奖励(0-70分)

累积奖励的总分范围在0到100之间。我在这一步进行了大量迭代,虽然它仍不完美,但已经足以让模型开始学习。

配置训练并启动!

from trl import GRPOConfig, GRPOTrainer
tokenized_prompts = [tokenizer.apply_chat_template(prompt, tokenize=True, add_generation_prompt=True) for prompt in ds['prompt']]
exact_max_prompt_length = max([len(tokenized_prompt) for tokenized_prompt in tokenized_prompts])
max_prompt_length = 448 # manually adjusted
new_model_id="anakin87/qwen-scheduler-7b-grpo"
training_args = GRPOConfig(
learning_rate = 8e-6,
adam_beta1 = 0.9,
adam_beta2 = 0.99,
weight_decay = 0.1,
warmup_ratio = 0.01,
lr_scheduler_type = "cosine",
optim = "paged_adamw_8bit",
logging_steps = 1,
per_device_train_batch_size = 8,
gradient_accumulation_steps = 1,
num_generations = 8, # Decrease if out of memory
max_prompt_length = max_prompt_length,
max_completion_length = max_seq_length - max_prompt_length,
max_grad_norm = 0.1,
output_dir = "outputs",
overwrite_output_dir = True,
push_to_hub = True,
hub_model_id=new_model_id,
hub_strategy="every_save",
save_strategy="steps",
save_steps=50,
save_total_limit=1,
num_train_epochs=3,
)
trainer = GRPOTrainer(
model = model,
processing_class = tokenizer,
reward_funcs=[
format_reward,
sorted_events_reward,
score_reward,
],
args = training_args,
train_dataset = ds,
)
trainer.train()

max_prompt_length是提示词的最大长度,过长的提示词会被截断。我们可以轻松计算这个值,然后手动进行调整。num_generations是GRPO的一个关键参数,它指示为每个提示词生成的样本数量。算法的学习基础是比较不同样本的优劣,并引导模型生成奖励更高的样本。更多的样本能为模型提供更多学习信息,理论上可能带来更好的结果,但也会消耗更多的时间和内存。关于其他GRPOConfig参数的详细信息,请查阅TRL的API参考文档。

成果展示:训练效果一览

训练曲线
plots

从奖励曲线来看,模型在训练过程中确实有所提升。从一开始,样本在格式对齐方面就表现良好。而随着时间的推移,模型在遵守时间顺序和生成优质日程表方面的得分也逐渐提高,这表明模型确实学会了奖励函数所定义的行为。其他记录的指标,如reward_std(奖励组内的平均标准差)和kl(模型与参考模型之间的平均KL散度)也显示出可接受的值。您可以在这份Weight and Biases报告中查看所有训练曲线和指标。

样本检查:AI的“思考过程”

在训练过程中,我们会随机记录一部分生成的样本。您可以在此处浏览完整的日志。检查这些完成结果非常有意思,也能提供很多启发。正是在这个过程中,我意识到那些小型模型根本无法理解任务:随着时间的推移,它们的推理能力并没有实质性地提高。以下是一个在不同训练阶段,针对相同输入提示词的模型输出示例:

Events:
- Analyzing product performance (04:33 - 06:03)
- One-on-one with manager (08:41 - 08:56)
- Client call with Acme Corp (09:13 - 10:58)
- Meeting with Lisa at café (10:14 - 11:14)
- Updating website content (12:21 - 13:36)
- Preparing PTO handover (16:07 - 17:37)
- Marketing standup on Zoom (17:58 - 19:43)
- Brainstorming with Luke and Patrick (19:11 - 19:56)
Priorities:
- Analyzing product performance
- Meeting with Lisa at café
Step 473/1500
<think> At first glance, the events overlap in the time slot from 10:14 to 11:14. The priority event "Meeting with Lisa at café" is higher in weight compared to "Client call with Acme Corp", so we must include it. However, this will exclude the client call if we want to prioritize "Meeting with Lisa at café". To resolve this, we need to decide whether to exclude the client call or the brainstorming session (19:11 - 19:56) which is a normal event. Since "Meeting with Lisa at café" is a priority event and it overlaps with the client call, we should include "Meeting with Lisa at café" and exclude the client call. This will give us more time for "Updating website content" (12:21 - 13:36) and "Preparing PTO handover" (16:07 - 17:37), which are also normal events. Here is the adjusted schedule: [...] This schedule maximizes the sum of weighted event durations while respecting all the rules provided. </think>
<schedule>
<event>
<name>Analyzing product performance</name>
<start>04:33</start>
<end>06:03</end>
</event>
<event>
<name>One-on-one with manager</name>
<start>08:41</start>
<end>08:56</end>
</event>
<event>
<name>Client call with Acme Corp</name>
<start>09:13</start>
<end>10:14</end>
</event>
<event>
<name>Meeting with Lisa at café</name>
<start>10:14</start>
<end>11:14</end>
</event>
<event>
<name>Updating website content</name>
<start>12:21</start>
<end>13:36</end>
</event>
<event>
<name>Preparing PTO handover</name>
<start>16:07</start>
<end>17:37</end>
</event>
<event>
<name>Marketing standup on Zoom</name>
<start>17:58</start>
<end>19:43</end>
</event>
<event>
<name>Brainstorming with Luke and Patrick</name>
<start>19:11</start>
<end>19:56</end>
</event>
</schedule>

在这个阶段,模型已经展现出对给定约束条件进行反思的良好能力,但它却错误地更改了一些事件的时间,以期最大化持续时间。这表明模型还需要进一步学习如何严格遵守规则。

Step 973/1500
<think> To create an optimized schedule, I need to prioritize events based on provided weights and ensure no overlaps. The priority events are "Analyzing product performance" and "Meeting with Lisa at café". Let\'s evaluate the schedule step-by-step.
1. **Analyzing product performance (04:33 - 06:03)** - This is a priority event (weight = 2).
2. **One-on-one with manager (08:41 - 08:56)** - This is a normal event (weight = 1).
3. **Client call with Acme Corp (09:13 - 10:58)** - This is a normal event (weight = 1). It conflicts with "Meeting with Lisa at café", which is also a priority event. Since "Meeting with Lisa at café" has higher priority, we exclude this event.
4. **Meeting with Lisa at café (10:14 - 11:14)** - This is a priority event (weight = 2). However, it conflicts with "Client call with Acme Corp" as discussed. We need to decide whether to exclude this event or find another slot.
5. **Updating website content (12:21 - 13:36)** - This is a normal event (weight = 1).
6. **Preparing PTO handover (16:07 - 17:37)** - This is a normal event (weight = 1).
7. **Marketing standup on Zoom (17:58 - 19:43)** - This is a normal event (weight = 1). It conflicts with "Preparing PTO handover" as they overlap. We need to decide whether to exclude one of them.
8. **Brainstorming with Luke and Patrick (19:11 - 19:56)** - This is a normal event (weight = 1). It conflicts with "Marketing standup on Zoom" as they overlap. We need to decide whether to exclude one of them.
To maximize the sum of weighted event durations, we should exclude conflicts with the highest priority events first.
- Exclude "Client call with Acme Corp" since it conflicts with "Meeting with Lisa at café" (higher priority).
- Exclude "Marketing standup on Zoom" since it conflicts with "Preparing PTO handover" and does not interfere with any higher priority events.
Thus, the optimized schedule is as follows: </think>
<schedule>
<event>
<name>Analyzing product performance</name>
<start>04:33</start>
<end>06:03</end>
</event>
<event>
<name>One-on-one with manager</name>
<start>08:41</start>
<end>08:56</end>
</event>
<event>
<name>Updating website content</name>
<start>12:21</start>
<end>13:36</end>
</event>
<event>
<name>Preparing PTO handover</name>
<start>16:07</start>
<end>17:37</end>
</event>
<event>
<name>Brainstorming with Luke and Patrick</name>
<start>19:11</start>
<end>19:56</end>
</event>
</schedule>

这一次,模型的表现有了显著提升!它明确考虑了权重,并生成了一个有效的日程表。这表明模型在学习如何权衡优先级和避免冲突方面取得了进步。

Step 1470/1500
<think> The goal is to create a schedule that maximizes the sum of weighted event durations while respecting the chronological order, priorities, and non-overlapping constraints. Let\'s analyze the events and prioritize them:
1. **Analyzing product performance**: Start time: 04:33, End time: 06:03, Priority: 2
2. **One-on-one with manager**: Start time: 08:41, End time: 08:56, Priority: 1
3. **Client call with Acme Corp**: Start time: 09:13, End time: 10:58, Priority: 1
4. **Meeting with Lisa at café**: Start time: 10:14, End time: 11:14, Priority: 2
5. **Updating website content**: Start time: 12:21, End time: 13:36, Priority: 1
6. **Preparing PTO handover**: 16:07 - 17:37 (Priority 1)
7. **Marketing standup on Zoom**: Start time: 17:58, End time: 19:43, Priority: 1
8. **Brainstorming with Luke and Patrick**: Start time: 19:11, End time: 19:56, Priority: 1
Given the priorities, we must ensure that "Analyzing product performance" and "Meeting with Lisa at café" are included. Let\'s try to fit the rest while avoiding overlaps:
1. **Analyzing product performance**: 04:33 - 06:03 (Priority 2)
2. **One-on-one with manager**: 08:41 - 08:56 (Priority 1)
3. **Client call with Acme Corp**: 09:13 - 10:58 (Priority 1)
4. **Meeting with Lisa at café**: 10:14 - 11:14 (Priority 2)
5. **Updating website content**: 12:21 - 13:36 (Priority 1)
6. **Preparing PTO handover**: 16:07 - 17:37 (Priority 1)
The next event, "Marketing standup on Zoom", starts at 17:58, but the availability is only until 19:43. We can fit "Brainstorming with Luke and Patrick" from 19:11 to 19:56 before the standup.
1. **Analyzing product performance**: 04:33 - 06:03 (Priority 2)
2. **One-on-one with manager**: 08:41 - 08:56 (Priority 1)
3. **Client call with Acme Corp**: 09:13 - 10:58 (Priority 1)
4. **Meeting with Lisa at café**: 10:14 - 11:14 (Priority 2)
5. **Updating website content**: 12:21 - 13:36 (Priority 1)
6. **Preparing PTO handover**: 16:07 - 17:37 (Priority 1)
7. **Brainstorming with Luke and Patrick**: 19:11 - 19:56 (Priority 1)
Final schedule:
- Analyzing product performance: 04:33 - 06:03
- One-on-one with manager: 08:41 - 08:56
- Client call with Acme Corp: 09:13 - 10:58
- Meeting with Lisa at café: 10:14 - 11:14
- Updating website content: 12:21 - 13:36
- Preparing PTO handover: 16:07 - 17:37
- Brainstorming with Luke and Patrick: 19:11 - 19:56
This schedule respects the chronological order, includes the high-priority events, and avoids any overlaps. </think>
<schedule>
<event>
<name>Analyzing product performance</name>
<start>04:33</start>
<end>06:03</end>
</event>
<event>
<name>One-on-one with manager</name>
<start>08:41</start>
<end>08:56</end>
</event>
<event>
<name>Client call with Acme Corp</name>
<start>09:13</start>
<end>10:58</end>
</event>
<event>
<name>Meeting with Lisa at café</name>
<start>10:14</start>
<end>11:14</end>
</event>
<event>
<name>Updating website content</name>
<start>12:21</start>
<end>13:36</end>
</event>
<event>
<name>Preparing PTO handover</name>
<start>16:07</start>
<end>17:37</end>
</event>
<event>
<name>Brainstorming with Luke and Patrick</name>
<start>19:11</start>
<end>19:56</end>
</event>
</schedule>

有趣的是……在这个样本中,尽管模型经过了漫长的推理过程,它最终生成的日程表中却包含了两个重叠的事件。虽然观察样本很有价值,但我们仍然需要系统化的评估。

至于“顿悟时刻”(aha moment)的出现,在DeepSeek-R1论文中,作者展示了GRPO训练过程中模型在“以拟人化语气重新思考”方面出现的“顿悟时刻”。这曾是他们的模型和GRPO的一大亮点,也促使实践者们在训练中寻找/复现这种行为。然而,近期的一些研究(1;2)对此提出了一些质疑。他们发现,类似“顿悟时刻”的现象,在任何GRPO训练开始之前,就已经存在于基础模型中了。鉴于这些有说服力的原因,我并未花时间去寻找所谓的“顿悟时刻”。但我仍然认为,区分基础模型中固有的行为和通过训练完全习得的新行为,这个研究方向本身非常有趣。

模型评估:检阅AI的“毕业答卷”

为了进行评估,我们使用了我们模型从未接触过的事件调度数据集的测试集。评估过程分为两步:

  1. 推理阶段: 为每个测试提示词生成一份日程表。
  2. 评分阶段: 根据我们设定的规则(格式正确、事件>=2个、仅包含现有事件、按时间顺序排列、无重叠)检查每个生成的日程表。如果违反了任何规则,该日程表在该提示词下的得分为0。如果有效,其得分为(加权持续时间/最优加权持续时间)* 100。

您可以在我的GitHub代码库中找到这些脚本。

Unsloth小插曲:一个待解决的“小麻烦”

在推理过程中,我遇到了一个问题。使用Unsloth训练的模型无法可靠地转换为标准的Hugging Face Transformers模型;尝试转换可能会得到一个完全不同的模型。这是一个已知的问题。如果我需要在生产环境中使用它,比如用vLLM进行服务,这会是一个重大的障碍,也会让人感到相当沮丧。

不过,对于这次实验,我只是使用Unsloth进行了评估推理。由于Unsloth是对TRL的补丁,因此很容易调整代码并使用更稳定的TRL。请注意,使用TRL可能需要更多的GPU显存。我希望比较qwen-scheduler-7b-grpo(我们训练的模型)、Qwen2.5-Coder-7B-Instruct(原始模型)以及Qwen2.5-Coder-14B-Instruct(同系列更大规模的模型)的性能。由于样本显示模型在第2个epoch和第3个epoch之间性能可能有所下降,我也评估了第2个epoch结束时的检查点。
Evaluation histograms

模型 格式错误 <2事件 重叠 未排序 不存在的事件 有效日程表 平均得分
Qwen2.5-Coder-7B-Instruct 3 1 31 29 27 9 8.05
Qwen2.5-Coder-14B-Instruct 1 1 38 6 6 48 45.85
qwen-scheduler-7b-grpo 0 0 36 0 1 62 58.54
qwen-scheduler-7b-grpo(第二代) 0 0 30 2 1 67 62.25

初步观察:GRPO的成效与挑战

新媒网跨境了解到,从评估结果来看,GRPO确实发挥了作用!它在激发模型所需行为方面相当有效。我们微调后的模型甚至在某些任务上,超越了其两倍规模的原始模型。模型几乎完美地学会了格式要求、时间顺序排列以及只使用现有事件。然而,模型在处理事件重叠问题上仍然存在挑战,这在测试集中表现得尤为明显。

未来展望:如何让AI更“精明”?

目前,我在这个项目上的时间和计算资源已经用尽,但以下是我接下来会尝试改进模型的方向。主要的问题无疑是事件重叠。我们看到模型尚未有效地学会如何避免它们。此外,第三个epoch中略微下降的性能可能表明模型正在学习一种次优策略。

关于奖励函数的改进思路:

  1. 更强的重叠惩罚:score_reward函数中,我们目前只是从分数计算中移除了重叠事件。我们可以考虑引入更严厉的惩罚机制,例如对重叠事件进行负分扣除。
  2. 专门的重叠奖励: 另一种方法是添加一个专门检查事件重叠情况的奖励函数。我们也要记住,奖励函数默认是累加的,因此,根据我们设计惩罚方式的不同,这两种方法在数学上可能是等效的。

总结与心得:AI训练之路的“灯塔”

在这篇文章中,我们共同探索了如何利用GRPO对语言模型进行二次训练,使其能够完成一项可验证的新任务:根据事件列表和优先级创建日程表。我们涵盖了问题定义、数据集生成、基础模型选择、奖励函数设计、使用Unsloth进行训练以及最终的评估环节。亲自动手实践,让我对GRPO以及将强化学习应用于大型语言模型有了更深刻的理解。

  1. GRPO在可验证任务中的独特魅力
    • 它简化了典型的强化学习设置(例如PPO中使用的):没有价值模型;奖励模型通常被确定性奖励函数取代(可验证奖励的强化学习)。
    • 由于您的数据集只需要提示词(不需要完成结果),因此数据收集比监督微调更容易、成本更低。
  2. “激发”而非“教授”
    使用GRPO和类似算法,更多的是从训练好的模型中“激发”出期望的行为,而非完全“教授”全新的知识。如果您需要模型掌握根本性的新技能,那么指令微调(以及知识蒸馏)可能更为有效(《强化学习真的能激发LLM超越基础模型的推理能力吗?》这篇论文值得深思)。如果您对这些话题感兴趣,建议关注Nathan Lambert和外媒Interconnects博客。
  3. 基础模型至关重要
    如果基础模型在采样过程中从未在任务上表现出任何有希望的行为,那么GRPO可能对您的问题无效。您可能首先需要一个更大或更好的基础模型。
  4. “顿悟时刻”或被高估
    在DeepSeek-R1论文中,作者展示了GRPO训练过程中模型在“以拟人化语气重新思考”方面出现的“顿悟时刻”。新媒网跨境认为,虽然这曾被视为一项突破,但最近的研究表明,类似的行为可能已经存在于基础模型中。
  5. 奖励函数设计是核心
    您的奖励函数应该能够准确捕捉您的目标,提供可学习的信号(对模型的鼓励),并且具备鲁棒性。如果奖励函数不够鲁棒,您可能会经历奖励作弊:模型找到捷径来最大化奖励,却并未真正解决您预期的问题。
  6. Unsloth:又爱又恨的工具
    如果您没有太多GPU资源,或者正处于实验阶段,Unsloth可能会非常有用。这个团队在GPU效率方面取得了令人印象深刻的成就。然而,目前该库存在一些难以解决的bug,这使得它不适合用于严肃的生产环境。如果您拥有足够的显存,TRL会是更稳定的选择。

新媒网跨境获悉,如果这篇文章对您有所启发,欢迎关注我的外媒Hugging Face主页和领英账号。如果发现任何错误或不准确之处,也请随时指正。

参考资源

  • Qwen Scheduler GRPO GitHub代码库:您可以在这里找到我用于本次实验的所有代码。
  • 外媒Hugging Face合集:数据集和模型。
  1. GRPO论文及资源
    (1) DeepSeekMath: Pushing the Limits of Mathematical Reasoning in Open Language Models(DeepSeekMath:将开放语言模型的数学推理能力推向极限)
    (2) 外媒Hugging Face推理课程
    (3) DeepSeek-R1: Incentivizing Reasoning Capability in LLMs via Reinforcement Learning(DeepSeek-R1:通过强化学习激励LLM的推理能力)
    (4) There May Not be Aha Moment in R1-Zero-like Training — A Pilot Study(R1-Zero式训练中可能没有顿悟时刻——一项初步研究)
    (5) Understanding R1-Zero-Like Training: A Critical Perspective(理解R1-Zero式训练:一个批判性视角)
    (6) Does Reinforcement Learning Really Incentivize Reasoning Capacity in LLMs Beyond the Base Model?(强化学习真的能激发LLM超越基础模型的推理能力吗?)
    (7) Nathan Lambert的Interconnects博客
  2. 实用资源
    (1) GRPO TRL文档
    (2) 使用TRL学习GSM8K的GRPO——作者William Brown
    (3) GRPO Unsloth文档

新媒网(公号: 新媒网跨境发布),是一个专业的跨境电商、游戏、支付、贸易和广告社区平台,为百万跨境人传递最新的海外淘金精准资讯情报。

本文来源:新媒网 https://nmedialink.com/posts/7b-model-grpo-beats-14b-ai-scheduler.html

评论(0)
暂无评论,快来抢沙发~
2025年下半年,中国自媒体作者成功实践GRPO技术,利用强化学习训练Qwen2.5-Coder-7B-Instruct小型语言模型,实现日程表智能优化。该项目通过提示词与奖励机制,让模型根据事件优先级自动生成最大化加权持续时间的日程。实验揭示了GRPO在激发LLM特定行为方面的潜力,并成功超越了原始模型性能。尽管在处理事件重叠上仍有挑战,但该实践为LLM的创新应用提供了宝贵经验,所有代码已开源,展示了在计算资源有限下,GRPO训练小型模型解决复杂‘区间调度问题’的可行性。
发布于 2026-01-20
查看人数 176
人民币汇率走势
CNY
亚马逊热销榜
共 0 SKU 上次更新 NaN:NaN:NaN
类目: 切换分类
暂无数据
暂无数据
关注我们
NMedia
新媒网跨境发布
本站原创内容版权归作者及NMedia共同所有,未经许可,禁止以任何形式转载。