GRPO:比PPO更简单的RLHF算法

DeepSeek的创新:不需要Critic,用组内对比代替


📚 目录

  1. GRPO是什么:PPO的简化版
  2. PPO的问题:为什么需要改进
  3. GRPO的核心创新:组内对比
  4. 详细机制:从公式到代码
  5. 对比PPO:优势与权衡
  6. 代码实现

📌 前置概念:从PPO到GRPO

GRPO在RLHF中的位置

markdown 复制代码
大模型对齐(Alignment)
└─ RLHF方法
    └─ 阶段3: RL微调
        ├─ PPO(2017年,OpenAI)← 主流方法
        │   组件:Actor + Critic + RM + Reference
        │   问题:需要训练Critic,计算开销大
        │
        └─ GRPO(2024年,DeepSeek)← 本文重点
            组件:Actor + RM + Reference(不需要Critic!)
            创新:用组内对比代替Critic

一句话总结

GRPO = PPO - Critic + 组内对比

erlang 复制代码
PPO:
  需要4个组件(Actor、Critic、RM、Reference)
  Critic用来计算Advantage(优势函数)

GRPO:
  只需要3个组件(Actor、RM、Reference)
  用组内对比计算Advantage,不需要Critic

结果:
  ✓ 简单:少训练一个模型(Critic)
  ✓ 快速:减少50%的前向传播
  ✓ 有效:效果和PPO差不多

🤔 Part 1: PPO的问题 ------ 为什么需要改进

1.1 回顾PPO的流程

ini 复制代码
PPO训练一次的流程:

1. Actor生成回答
   "什么是黑洞?" → "黑洞是引力极强的天体..."

2. RM打分
   reward = 8.5分

3. Critic预测
   value = 8.0分

4. 计算Advantage
   advantage = reward - value = 8.5 - 8.0 = 0.5

5. 用Advantage更新Actor
   增加这个回答的概率(因为advantage>0)

1.2 Critic的问题

问题1:需要额外训练一个大模型

diff 复制代码
Critic是什么?
- 和Actor同样大小的模型(比如7B参数)
- Base模型 + Value Head

开销:
- 显存:需要加载两个7B模型(Actor + Critic)
- 计算:每次前向都要跑两个模型
- 训练:Critic也要训练(MSE loss)

例子:
Actor (7B) + Critic (7B) = 14B参数
→ 需要至少28GB显存(FP16)

问题2:Critic预测不准

ini 复制代码
Critic的任务:
预测"这个回答能得多少分"

问题:
训练初期Critic很不准
→ value预测偏差大
→ advantage = reward - value 不可靠
→ Actor得到错误的训练信号

例子:
实际reward = 8.5分
Critic预测 = 6.0分(预测太低)
advantage = 8.5 - 6.0 = 2.5(被夸大了)
→ Actor过度增加这个回答的概率

问题3:两个模型要同步训练

diff 复制代码
PPO需要同时训练:
- Actor:根据advantage更新
- Critic:根据预测误差更新

问题:
- 训练不稳定(两个模型互相影响)
- 超参数多(两个学习率、两个优化器)
- 调试困难(不知道是哪个模型的问题)

1.3 核心问题

Critic的本质作用:提供一个"baseline"

diff 复制代码
Advantage = reward - baseline

作用:
- 降低方差(variance reduction)
- 让训练更稳定

但代价:
- 需要训练一个大模型
- 预测不准反而有害
- 增加计算开销

思考:
能不能用更简单的方式提供baseline?
→ GRPO的答案:用组内对比!

💡 Part 2: GRPO的核心创新 ------ 组内对比

2.1 核心思想

不用Critic,而是让多个回答互相对比

makefile 复制代码
PPO的方式(绝对评分):
────────────────────────────
问题:"什么是黑洞?"
回答A:"黑洞是引力极强的天体..."

RM打分:8.5分
Critic预测:8.0分
Advantage = 8.5 - 8.0 = 0.5

问题:需要Critic


GRPO的方式(相对对比):
────────────────────────────
问题:"什么是黑洞?"

让Actor生成4个不同的回答:
回答A:"黑洞是引力极强的天体..." → RM打分:8.5
回答B:"黑洞是一种天体" → RM打分:7.0
回答C:"不知道" → RM打分:3.0
回答D:"黑洞是时空的弯曲..." → RM打分:8.0

计算平均分(baseline):
avg_reward = (8.5 + 7.0 + 3.0 + 8.0) / 4 = 6.625

计算Advantage(和组内平均比):
Advantage_A = 8.5 - 6.625 = +1.875(好于平均)
Advantage_B = 7.0 - 6.625 = +0.375(略好)
Advantage_C = 3.0 - 6.625 = -3.625(很差)
Advantage_D = 8.0 - 6.625 = +1.375(好)

更新Actor:
- 增加A和D的概率(advantage>0)
- 降低C的概率(advantage<0)

关键:baseline来自组内平均,不需要Critic!

2.2 类比:考试成绩的评价

PPO的方式(需要老师预测):

arduino 复制代码
小明考了85分

老师预测:"这道题平均能考80分"(Critic)
小明表现 = 85 - 80 = +5分(好于预期)

问题:
- 需要一个"老师"(Critic模型)
- 老师的预测可能不准

GRPO的方式(用同学对比):

ini 复制代码
小明考了85分
让小明再考3次(同一道题):
第1次:85分
第2次:70分
第3次:60分
第4次:80分

班级平均(小明自己的4次)= (85+70+60+80)/4 = 73.75分
小明第1次表现 = 85 - 73.75 = +11.25(好于自己的平均)

好处:
- 不需要"老师"
- 用自己和自己比,更客观

2.3 为什么组内对比有效?

理论基础:Self-Baseline

markdown 复制代码
关键洞察:
Actor当前的平均表现 = 很好的baseline

原因:
1. 来自Actor自己的分布
   - 不是外部预测(Critic)
   - 是Actor的真实能力

2. 自动归一化
   - 好的回答 → advantage > 0
   - 差的回答 → advantage < 0
   - 平均advantage = 0(数学保证)

3. 降低方差
   - 和Critic效果一样
   - 但不需要训练额外模型

数学表达:

css 复制代码
PPO:
  Advantage = R(s,a) - V(s)
  其中V(s)由Critic预测

GRPO:
  Advantage = R(s,a) - mean(R(s, a₁), R(s, a₂), ..., R(s, aₙ))
  其中a₁, a₂, ..., aₙ是Actor对同一个s生成的多个回答

区别:
  PPO的baseline = Critic预测的value
  GRPO的baseline = 组内平均reward

🔧 Part 3: GRPO详细机制

3.1 组(Group)的概念

vbnet 复制代码
什么是"组"?
────────────────────────────────
对同一个prompt,生成多个不同的回答

例子:
Prompt: "什么是黑洞?"
Group size = 4(生成4个回答)

Group成员:
1. "黑洞是引力极强的天体..."
2. "黑洞是一种天体"
3. "不知道"
4. "黑洞是时空的弯曲..."

这4个回答组成一个"组"

Group Size的选择:

diff 复制代码
Group Size = 1:
- 退化成没有baseline
- 方差大,训练不稳定

Group Size = 4(常用):
- 平衡计算开销和效果
- DeepSeek论文中用的

Group Size = 8:
- baseline更准确
- 但计算开销翻倍

Group Size = ∞(理论上):
- 相当于Critic(期望值)
- 但不现实

3.2 完整训练流程

python 复制代码
# 伪代码展示GRPO的一次迭代

# ========== Step 1: 准备Prompts ==========
prompts = ["什么是黑洞?", "如何学习Python?", ...]  # 256个
group_size = 4  # 每个prompt生成4个回答

# ========== Step 2: Actor生成(关键!)==========
all_responses = []
all_log_probs = []

for prompt in prompts:
    group_responses = []
    group_log_probs = []

    # 对同一个prompt,生成多个回答
    for _ in range(group_size):
        response = actor.generate(prompt, do_sample=True)  # 采样生成
        log_prob = actor.get_log_prob(prompt, response)

        group_responses.append(response)
        group_log_probs.append(log_prob)

    all_responses.append(group_responses)
    all_log_probs.append(group_log_probs)

# 结果:
# prompt: "什么是黑洞?"
# responses: [回答1, 回答2, 回答3, 回答4]

# ========== Step 3: RM打分 ==========
all_rewards = []

for prompt, group_responses in zip(prompts, all_responses):
    group_rewards = []

    for response in group_responses:
        reward = reward_model(prompt, response)
        group_rewards.append(reward)

    all_rewards.append(group_rewards)

# 结果:
# group_rewards = [8.5, 7.0, 3.0, 8.0]

# ========== Step 4: Reference计算KL ==========
# (和PPO一样,略)

# ========== Step 5: 计算组内Advantage(核心!)==========
all_advantages = []

for group_rewards in all_rewards:
    # 组内平均作为baseline
    baseline = sum(group_rewards) / len(group_rewards)

    # 每个回答的advantage
    group_advantages = []
    for reward in group_rewards:
        advantage = reward - baseline
        group_advantages.append(advantage)

    all_advantages.append(group_advantages)

# 结果:
# baseline = 6.625
# advantages = [+1.875, +0.375, -3.625, +1.375]
# 注意:sum(advantages) = 0(自动归一化)

# ========== Step 6: 训练Actor(和PPO类似)==========
for epoch in range(ppo_epochs):
    for prompt, group_responses, old_log_probs, advantages in data:
        for response, old_lp, adv in zip(group_responses, old_log_probs, advantages):
            # PPO更新(和之前一样)
            new_lp = actor(prompt, response)
            ratio = torch.exp(new_lp - old_lp)
            ratio_clipped = torch.clamp(ratio, 1-ε, 1+ε)

            loss = -torch.min(ratio * adv, ratio_clipped * adv).mean()
            loss.backward()
            optimizer.step()

# 关键:不需要训练Critic!

3.3 可视化对比

PPO的数据流:

ini 复制代码
Prompt: "什么是黑洞?"
    ↓
Actor生成1个回答
    ↓
RM打分:8.5
    ↓
Critic预测:8.0  ← 需要Critic模型
    ↓
Advantage = 8.5 - 8.0 = 0.5
    ↓
更新Actor

GRPO的数据流:

ini 复制代码
Prompt: "什么是黑洞?"
    ↓
Actor生成4个回答
    ↓
RM打分:[8.5, 7.0, 3.0, 8.0]
    ↓
组内平均:6.625  ← 不需要Critic!
    ↓
Advantages = [+1.875, +0.375, -3.625, +1.375]
    ↓
更新Actor(用4个样本)

⚖️ Part 4: GRPO vs PPO

4.1 组件对比

组件 PPO GRPO 说明
Actor 都需要
Critic GRPO不需要
RM 都需要
Reference 都需要
总数 4个 3个 GRPO少25%

4.2 计算开销对比

ini 复制代码
假设:7B参数模型,FP16精度

PPO(单个样本):
────────────────────────────
1. Actor生成:7B × 2 bytes = 14GB
2. RM打分:7B × 2 bytes = 14GB
3. Reference:7B × 2 bytes = 14GB
4. Critic预测:7B × 2 bytes = 14GB

峰值显存:~28GB(Actor + Critic同时加载)
前向次数:4次


GRPO(单个样本,group_size=4):
────────────────────────────
1. Actor生成4次:7B × 2 bytes = 14GB
2. RM打分4次:7B × 2 bytes = 14GB
3. Reference 4次:7B × 2 bytes = 14GB
4. 不需要Critic!

峰值显存:~14GB(只需Actor)
前向次数:12次(Actor生成4次 + RM 4次 + Ref 4次)


对比:
────────────────────────────
显存:GRPO节省50%(不需要同时加载Critic)
计算:GRPO多200%(但可以并行)
训练复杂度:GRPO更简单(只训练Actor)

4.3 优势对比

GRPO的优势:

diff 复制代码
✅ 简单
- 少一个模型(不需要Critic)
- 少一个损失函数(不需要Value loss)
- 少一个优化器

✅ 节省显存
- 不需要同时加载Actor和Critic
- 单机可以训练更大的模型

✅ 训练稳定
- 不需要平衡Actor和Critic的学习率
- 不会因为Critic预测不准导致问题

✅ 自动归一化
- Advantage自动满足:sum(adv) = 0
- 不需要额外的归一化步骤

PPO的优势:

diff 复制代码
✅ 样本效率更高
- 一个prompt生成1个回答
- GRPO需要生成多个回答

✅ 理论更成熟
- 已有大量研究和实践经验
- GRPO相对较新(2024年)

✅ Critic能学到长期价值
- Critic预测累积奖励
- 理论上能提供更好的baseline

4.4 效果对比

根据DeepSeek的论文:

markdown 复制代码
实验设置:
- 模型:7B参数
- 数据:相同的prompts
- 评价:人类偏好评测

结果:
────────────────────────────
                PPO    GRPO
胜率            48%    52%
训练时间        10h    8h
峰值显存        28GB   14GB
实现复杂度      高     中

结论:
GRPO效果略好,速度更快,显存更少

4.5 何时用GRPO?

scss 复制代码
推荐用GRPO:
✓ 显存有限(单机训练大模型)
✓ 想要简单实现(不想调Critic)
✓ Critic训练不稳定
✓ 资源有限(不能同时加载两个大模型)

继续用PPO:
✓ 已有成熟的PPO pipeline
✓ 样本效率很重要(数据有限)
✓ 需要非常精确的baseline
✓ 有充足的计算资源

💻 Part 5: 代码实现

5.1 核心代码

python 复制代码
import torch
import torch.nn as nn
import torch.nn.functional as F

# ========== GRPO训练函数 ==========

def grpo_train_step(
    actor,
    reward_model,
    reference_model,
    prompts,
    group_size=4,
    epsilon=0.2
):
    """
    GRPO的一次训练步骤

    Args:
        actor: Actor模型
        reward_model: 奖励模型(固定)
        reference_model: 参考模型(固定)
        prompts: 输入的prompts列表
        group_size: 每个prompt生成几个回答
        epsilon: PPO的裁剪阈值
    """

    # ━━━━━ Step 1: 生成多个回答(关键!)━━━━━
    all_responses = []
    all_old_log_probs = []

    for prompt in prompts:
        group_responses = []
        group_log_probs = []

        # 对同一个prompt生成多个回答
        for _ in range(group_size):
            with torch.no_grad():
                # 采样生成(do_sample=True)
                response = actor.generate(
                    prompt,
                    do_sample=True,
                    temperature=1.0
                )
                log_prob = actor.get_log_prob(prompt, response)

            group_responses.append(response)
            group_log_probs.append(log_prob)

        all_responses.append(group_responses)
        all_old_log_probs.append(group_log_probs)

    # ━━━━━ Step 2: RM打分 ━━━━━
    all_rewards = []

    with torch.no_grad():
        for prompt, group_responses in zip(prompts, all_responses):
            group_rewards = []

            for response in group_responses:
                reward = reward_model(prompt, response)
                group_rewards.append(reward)

            all_rewards.append(group_rewards)

    # ━━━━━ Step 3: Reference计算KL ━━━━━
    all_kl_penalties = []
    beta = 0.1  # KL系数

    with torch.no_grad():
        for prompt, group_responses, group_log_probs in zip(
            prompts, all_responses, all_old_log_probs
        ):
            group_kl = []

            for response, log_prob in zip(group_responses, group_log_probs):
                ref_log_prob = reference_model(prompt, response)
                kl = (log_prob - ref_log_prob).sum()
                kl_penalty = beta * kl
                group_kl.append(kl_penalty)

            all_kl_penalties.append(group_kl)

    # ━━━━━ Step 4: 计算总奖励 ━━━━━
    all_total_rewards = []

    for group_rewards, group_kl in zip(all_rewards, all_kl_penalties):
        group_total_rewards = []

        for reward, kl in zip(group_rewards, group_kl):
            total_reward = reward - kl
            group_total_rewards.append(total_reward)

        all_total_rewards.append(group_total_rewards)

    # ━━━━━ Step 5: 计算组内Advantage(核心创新!)━━━━━
    all_advantages = []

    for group_rewards in all_total_rewards:
        # 组内平均作为baseline
        baseline = sum(group_rewards) / len(group_rewards)

        # 计算每个回答的advantage
        group_advantages = []
        for reward in group_rewards:
            advantage = reward - baseline
            group_advantages.append(advantage)

        all_advantages.append(group_advantages)

    # 验证:组内advantage的和应该接近0
    for advantages in all_advantages:
        assert abs(sum(advantages)) < 1e-6, "Advantage should sum to 0"

    # ━━━━━ Step 6: PPO更新Actor ━━━━━
    actor.train()
    total_loss = 0

    for prompt, group_responses, old_log_probs, advantages in zip(
        prompts, all_responses, all_old_log_probs, all_advantages
    ):
        for response, old_lp, adv in zip(group_responses, old_log_probs, advantages):
            # 新策略的log概率
            new_lp = actor.get_log_prob(prompt, response)

            # PPO clip
            ratio = torch.exp(new_lp - old_lp)
            ratio_clipped = torch.clamp(ratio, 1 - epsilon, 1 + epsilon)

            # 损失
            surr1 = ratio * adv
            surr2 = ratio_clipped * adv
            loss = -torch.min(surr1, surr2).mean()

            total_loss += loss

    # 反向传播
    optimizer.zero_grad()
    total_loss.backward()
    optimizer.step()

    return total_loss.item()


# ========== 完整训练循环 ==========

def train_grpo(
    actor,
    reward_model,
    reference_model,
    prompts_dataset,
    num_iterations=1000,
    group_size=4
):
    """
    完整的GRPO训练循环
    """
    optimizer = torch.optim.Adam(actor.parameters(), lr=1e-5)

    for iteration in range(num_iterations):
        # 采样prompts
        prompts = sample_prompts(prompts_dataset, batch_size=64)

        # GRPO训练步骤
        loss = grpo_train_step(
            actor,
            reward_model,
            reference_model,
            prompts,
            group_size=group_size
        )

        if iteration % 10 == 0:
            print(f"Iteration {iteration}: Loss = {loss:.4f}")

        # 保存checkpoint
        if iteration % 100 == 0:
            torch.save(actor.state_dict(), f'actor_iter{iteration}.pt')

    return actor

5.2 关键点解释

python 复制代码
# 1. 生成多个回答(最重要的区别)
for _ in range(group_size):
    response = actor.generate(prompt, do_sample=True)  # 采样生成
    # 不能用greedy,否则每次生成一样的

# 2. 组内平均作为baseline
baseline = sum(group_rewards) / len(group_rewards)
advantages = [r - baseline for r in group_rewards]
# 不需要Critic!

# 3. 验证Advantage归一化
assert sum(advantages) ≈ 0  # 数学保证

# 4. 更新和PPO一样
ratio = exp(new_log_prob - old_log_prob)
ratio_clipped = clip(ratio, 1-ε, 1+ε)
loss = -min(ratio * adv, ratio_clipped * adv)

5.3 GRPO vs PPO代码对比

python 复制代码
# PPO训练步骤
def ppo_train_step(actor, critic, reward_model, ref, prompts):
    # 1. 生成1个回答
    response = actor.generate(prompt)

    # 2. RM打分
    reward = reward_model(prompt, response)

    # 3. Critic预测(需要Critic!)
    value = critic(prompt, response)

    # 4. 计算Advantage
    advantage = reward - value

    # 5. 更新Actor
    # ... PPO更新 ...

    # 6. 更新Critic(额外的训练步骤)
    critic_loss = (value - reward) ** 2
    critic_loss.backward()


# GRPO训练步骤
def grpo_train_step(actor, reward_model, ref, prompts, group_size):
    # 1. 生成多个回答
    responses = [actor.generate(prompt) for _ in range(group_size)]

    # 2. RM打分
    rewards = [reward_model(prompt, r) for r in responses]

    # 3. 组内平均(不需要Critic!)
    baseline = sum(rewards) / len(rewards)

    # 4. 计算Advantage
    advantages = [r - baseline for r in rewards]

    # 5. 更新Actor
    # ... PPO更新(用多个样本)...

    # 不需要更新Critic!


关键区别:
1. GRPO生成多个回答
2. GRPO不需要Critic
3. GRPO用组内平均作为baseline
4. GRPO不需要训练Critic

🎓 Part 6: 总结

6.1 核心要点

GRPO的创新:

scss 复制代码
问题:
PPO需要Critic预测baseline
→ 需要额外训练一个大模型
→ 预测可能不准
→ 增加计算开销

GRPO的解决方案:
用组内平均作为baseline
→ 不需要Critic
→ 自动归一化
→ 节省显存和训练时间

公式:
PPO:  Advantage = R(s,a) - V_critic(s)
GRPO: Advantage = R(s,a) - mean(R(s, a₁), ..., R(s, aₙ))

一句话总结:

GRPO = PPO - Critic + 组内对比

6.2 优缺点对比

维度 PPO GRPO 胜者
模型数量 4个 3个 GRPO
显存占用 高(需要Critic) GRPO
训练复杂度 复杂(两个模型) 简单 GRPO
样本效率 高(1个回答) 低(多个回答) PPO
理论成熟度 成熟(2017) 较新(2024) PPO
实际效果 略好 GRPO

6.3 选择建议

用GRPO如果:

复制代码
✓ 显存有限(比如单张A100训练7B模型)
✓ 想要简单实现
✓ PPO的Critic训练不稳定
✓ 初次尝试RLHF

用PPO如果:

复制代码
✓ 有成熟的PPO代码库
✓ 样本效率很重要(prompts有限)
✓ 需要非常精确的value估计
✓ 有充足的计算资源

6.4 发展趋势

makefile 复制代码
2017: PPO(OpenAI)
├─ 核心:Clip机制
└─ 问题:需要Critic

2024: GRPO(DeepSeek)
├─ 核心:组内对比
└─ 改进:去掉Critic

未来:
├─ 更简单的方法?
├─ 不需要RM?(DPO、IPO)
└─ 完全离线?(Offline RL)

6.5 快速记忆

记住GRPO的三个关键词:

  1. Group(组)

    • 对同一个prompt生成多个回答
  2. Relative(相对)

    • 用组内对比,不用绝对评分
  3. No Critic(不需要Critic)

    • 用组内平均作为baseline

记住GRPO的核心公式:

python 复制代码
# 组内平均
baseline = mean(rewards)

# Advantage
advantages = [r - baseline for r in rewards]

# 自动归一化
assert sum(advantages) == 0

🤔 Part 7: 常见问题

Q1: Group Size选多大?

diff 复制代码
Group Size = 1:
- 退化成没有baseline
- 方差大 ❌

Group Size = 2:
- baseline不够稳定
- 效果一般

Group Size = 4(推荐):
- DeepSeek论文用的
- 平衡效果和开销 ✅

Group Size = 8:
- baseline更稳定
- 但计算开销翻倍
- 如果资源充足可以用

Group Size太大:
- 计算开销线性增长
- 收益递减

Q2: GRPO比PPO快吗?

diff 复制代码
显存:
GRPO更少(不需要Critic)
单张A100可以训练更大的模型

计算量:
GRPO更多(生成多个回答)
但可以并行(batch inference)

实际训练时间:
取决于瓶颈是显存还是计算
- 显存瓶颈:GRPO更快(可以更大batch)
- 计算瓶颈:差不多(并行抵消开销)

总体:GRPO通常更快(因为显存是瓶颈)

Q3: GRPO的baseline准确吗?

diff 复制代码
理论分析:

PPO的Critic:
- 优点:学习到的期望值
- 缺点:预测可能不准,需要训练

GRPO的组内平均:
- 优点:真实的采样平均
- 缺点:只有N个样本(N=group_size)

当N足够大时:
GRPO的baseline ≈ PPO的Critic预测值

实践中:
N=4就足够好(DeepSeek实验证明)

Q4: 能不能每个prompt生成不同数量的回答?

diff 复制代码
可以,但不推荐

固定Group Size(推荐):
- 实现简单
- 批处理效率高
- 每个prompt的baseline质量一致

动态Group Size:
- 实现复杂
- 难以批处理
- 不同prompt的advantage不可比
- 收益不大

Q5: GRPO和DPO有什么区别?

diff 复制代码
DPO(Direct Preference Optimization):
- 完全不用RL
- 直接优化偏好对比
- 不需要RM
- 更简单,但效果可能略差

GRPO(Group Relative Policy Optimization):
- 还是RL(PPO框架)
- 需要RM
- 去掉Critic
- 效果和PPO接近

选择:
- 想最简单 → DPO
- 想效果好 → GRPO或PPO
- 有大量计算资源 → PPO
相关推荐
shelter2 小时前
并发操作session对象导致登录闪退问题
后端
兆子龙2 小时前
TypeScript高级类型编程:从入门到精通
前端·后端
IT_陈寒2 小时前
Python开发者的效率革命:这5个技巧让你的代码提速50%!
前端·人工智能·后端
MekoLi292 小时前
Spring AI 与 LangChain4j 从入门到精通:Java 后端开发者的 AI 实战手册
后端·面试
树獭叔叔2 小时前
从RLHF到PPO:让AI学会说人话
后端·aigc·openai
沸点小助手2 小时前
「AI 能力提升场」沸点获奖名单公示|本周互动话题上新🎊
aigc·openai·vibecoding
Meepo_haha2 小时前
创建Spring Initializr项目
java·后端·spring
Memory_荒年2 小时前
SpringBoot事务源码深度游:从注解到数据库的“奇幻漂流”
java·后端·spring
编码忘我2 小时前
为什么要用SpringBoot
java·后端