1+ import pytest
2+ import numpy as np
3+ import torch
4+ from ding .rl_utils .grpo import grpo_policy_data , grpo_policy_error # 导入GRPO相关函数
5+
6+
7+ @pytest .fixture
8+ def batch_size ():
9+ return 4
10+
11+
12+ @pytest .fixture
13+ def seq_length ():
14+ return 8
15+
16+
17+ @pytest .fixture
18+ def dictionary_num ():
19+ return 1000
20+
21+
22+
23+ @pytest .mark .unittest
24+ def test_grpo_policy_loss_with_mask (batch_size : int = 4 , seq_length : int = 8 , vocab_size : int = 1000 ):
25+ """测试GRPO策略损失的计算"""
26+ # 1. 创建测试数据
27+ logit_new = torch .randn (batch_size , seq_length , vocab_size ).requires_grad_ (True ) # 当前策略的logits
28+ logit_old = logit_new + torch .randn_like (logit_new ) * 0.1 # 旧策略的logits(稍微偏离当前策略)
29+ logit_ref = logit_new + torch .randn_like (logit_new ) * 0.2 # 参考策略的logits
30+ action = torch .randint (0 , vocab_size , (batch_size , seq_length )) # 随机采样的token
31+ adv = torch .randn (batch_size ) # 每个序列的优势值
32+ weight = torch .ones (batch_size , seq_length ) # 掩码
33+ weight [:, - 2 :] = 0 # 设置最后两个时间步为padding
34+
35+ # 2. 创建grpo_policy_data实例
36+ data = grpo_policy_data (
37+ logit_new = logit_new , # 当前策略的输出
38+ logit_old = logit_old , # 旧策略的输出
39+ logit_ref = logit_ref , # 参考策略的输出
40+ action = action , # 实际采样的token
41+ adv = adv , # 优势值
42+ weight = weight # 掩码
43+ )
44+
45+ # 3. 计算GRPO损失
46+ loss , info = grpo_policy_error (
47+ data = data ,
48+ clip_ratio = 0.2 , # PPO截断比率
49+ beta = 0.1 # KL散度权重
50+ )
51+
52+ # 4. 验证输出
53+ assert isinstance (loss .policy_loss , torch .Tensor )
54+ assert loss .policy_loss .shape == torch .Size ([]) # 确保是标量
55+ assert not torch .isnan (loss .policy_loss )
56+ assert not torch .isinf (loss .policy_loss )
57+
58+ # 5. 测试梯度
59+ assert logit_new .grad is None
60+ loss .policy_loss .backward ()
61+ assert isinstance (logit_new .grad , torch .Tensor )
62+
63+ # 6. 验证指标
64+ assert 'mean_kl' in info ._asdict ()
65+ assert 'mean_ratio' in info ._asdict ()
66+ assert 'mean_clipped' in info ._asdict ()
67+ assert all ([np .isscalar (v ) for v in info ._asdict ().values ()])
68+
69+
70+ @pytest .mark .unittest
71+ def test_grpo_policy_loss_without_mask (batch_size : int = 4 , seq_length : int = 8 , vocab_size : int = 1000 ):
72+ """测试GRPO策略损失的计算"""
73+ # 1. 创建测试数据
74+ logit_new = torch .randn (batch_size , seq_length , vocab_size ).requires_grad_ (True ) # 当前策略的logits
75+ logit_old = logit_new + torch .randn_like (logit_new ) * 0.1 # 旧策略的logits(稍微偏离当前策略)
76+ logit_ref = logit_new + torch .randn_like (logit_new ) * 0.2 # 参考策略的logits
77+ action = torch .randint (0 , vocab_size , (batch_size , seq_length )) # 随机采样的token
78+ adv = torch .randn (batch_size ) # 每个序列的优势值
79+
80+
81+ # 2. 创建grpo_policy_data实例
82+ data = grpo_policy_data (
83+ logit_new = logit_new , # 当前策略的输出
84+ logit_old = logit_old , # 旧策略的输出
85+ logit_ref = logit_ref , # 参考策略的输出
86+ action = action , # 实际采样的token
87+ adv = adv , # 优势值
88+ weight = None # 掩码
89+ )
90+
91+ # 3. 计算GRPO损失
92+ loss , info = grpo_policy_error (
93+ data = data ,
94+ clip_ratio = 0.2 , # PPO截断比率
95+ beta = 0.1 # KL散度权重
96+ )
97+
98+ # 4. 验证输出
99+ assert isinstance (loss .policy_loss , torch .Tensor )
100+ assert loss .policy_loss .shape == torch .Size ([]) # 确保是标量
101+ assert not torch .isnan (loss .policy_loss )
102+ assert not torch .isinf (loss .policy_loss )
103+
104+ # 5. 测试梯度
105+ assert logit_new .grad is None
106+ loss .policy_loss .backward ()
107+ assert isinstance (logit_new .grad , torch .Tensor )
108+
109+ # 6. 验证指标
110+ assert 'mean_kl' in info ._asdict ()
111+ assert 'mean_ratio' in info ._asdict ()
112+ assert 'mean_clipped' in info ._asdict ()
113+ assert all ([np .isscalar (v ) for v in info ._asdict ().values ()])
0 commit comments