Skip to content

Commit 37fb707

Browse files
committed
fix bugs and add description for KL-divergence variants
1 parent 471e351 commit 37fb707

4 files changed

Lines changed: 79 additions & 34 deletions

File tree

ding/policy/ppo.py

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,14 @@ class PPOPolicy(Policy):
7777
# (bool) Whether ignore done (usually for max step termination env).
7878
ignore_done=False,
7979
# (str) The type of KL divergence loss, ['k1', 'k2', 'k3']
80+
# http://joschu.net/blog/kl-approx.html
8081
kl_type='k1',
8182
# (float) The weight of KL divergence loss.
8283
kl_beta=0.0,
84+
# (str or None) The path of pretrained model checkpoint.
85+
# If provided, KL regularizer will be calculated between current policy and pretrained policy.
86+
# Default to None, which means KL is not calculated.
87+
pretrained_model_path=None,
8388
),
8489
# collect_mode config
8590
collect=dict(
@@ -190,6 +195,15 @@ def _init_learn(self) -> None:
190195

191196
self._learn_model = model_wrap(self._model, wrapper_name='base')
192197

198+
# load pretrained model
199+
if self._cfg.learn.pretrained_model_path is not None:
200+
self._pretrained_model = copy.deepcopy(self._model)
201+
state_dict = torch.load(self._cfg.learn.pretrained_model_path, map_location='cpu')
202+
self._pretrained_model.load_state_dict(state_dict)
203+
self._pretrained_model.eval()
204+
else:
205+
self._pretrained_model = None
206+
193207
# Algorithm config
194208
self._value_weight = self._cfg.learn.value_weight
195209
self._entropy_weight = self._cfg.learn.entropy_weight
@@ -291,17 +305,23 @@ def _forward_learn(self, data: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
291305
# Normalize advantage in a train_batch
292306
adv = (adv - adv.mean()) / (adv.std() + 1e-8)
293307

308+
if self._pretrained_model is not None:
309+
with torch.no_grad():
310+
logit_pretrained = self._pretrained_model.forward(batch['obs'], mode='compute_actor')['logit']
311+
else:
312+
logit_pretrained = None
313+
294314
# Calculate ppo error
295315
if self._action_space == 'continuous':
296316
ppo_batch = ppo_data(
297317
output['logit'], batch['logit'], batch['action'], output['value'], batch['value'], adv,
298-
batch['return'], batch['weight']
318+
batch['return'], batch['weight'], logit_pretrained
299319
)
300320
ppo_loss, ppo_info = ppo_error_continuous(ppo_batch, self._clip_ratio, kl_type=self._kl_type)
301321
elif self._action_space == 'discrete':
302322
ppo_batch = ppo_data(
303323
output['logit'], batch['logit'], batch['action'], output['value'], batch['value'], adv,
304-
batch['return'], batch['weight']
324+
batch['return'], batch['weight'], logit_pretrained
305325
)
306326
ppo_loss, ppo_info = ppo_error(ppo_batch, self._clip_ratio, kl_type=self._kl_type)
307327
elif self._action_space == 'hybrid':
@@ -332,7 +352,6 @@ def _forward_learn(self, data: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
332352
)
333353
wv, we = self._value_weight, self._entropy_weight
334354
kl_div = ppo_info.kl_div
335-
# 正确的、符合规范的修改
336355
total_loss = (
337356
ppo_loss.policy_loss + wv * ppo_loss.value_loss - we * ppo_loss.entropy_loss +
338357
self._kl_beta * kl_div

ding/rl_utils/ppo.py

Lines changed: 54 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,16 @@
66
from ding.hpc_rl import hpc_wrapper
77

88
ppo_data = namedtuple(
9-
'ppo_data', ['logit_new', 'logit_old', 'action', 'value_new', 'value_old', 'adv', 'return_', 'weight']
9+
'ppo_data',
10+
['logit_new', 'logit_old', 'action', 'value_new', 'value_old', 'adv', 'return_', 'weight', 'logit_pretrained']
1011
)
1112
ppo_data_continuous = namedtuple(
1213
'ppo_data_continuous',
1314
['mu_sigma_new', 'mu_sigma_old', 'action', 'value_new', 'value_old', 'adv', 'return_', 'weight']
1415
)
15-
ppo_policy_data = namedtuple('ppo_policy_data', ['logit_new', 'logit_old', 'action', 'adv', 'weight'])
16+
ppo_policy_data = namedtuple(
17+
'ppo_policy_data', ['logit_new', 'logit_old', 'action', 'adv', 'weight', 'logit_pretrained']
18+
)
1619
ppo_policy_data_continuous = namedtuple(
1720
'ppo_policy_data_continuous', ['mu_sigma_new', 'mu_sigma_old', 'action', 'adv', 'weight']
1821
)
@@ -22,6 +25,32 @@
2225
ppo_info = namedtuple('ppo_info', ['approx_kl', 'clipfrac', 'kl_div'])
2326

2427

28+
def calculate_kl_div(logr: torch.Tensor, kl_type: str) -> torch.Tensor:
29+
"""
30+
Overview:
31+
Calculate different Monte-Carlo estimators for KL-divergence KL(q, p) = E_q[log(q/p)],
32+
where q is the current policy and p is the pretrained policy.
33+
The implementation is based on John Schulman's blog post "Approximating KL Divergence".
34+
Reference: http://joschu.net/blog/kl-approx.html
35+
Arguments:
36+
- logr (:obj:`torch.Tensor`): The log-ratio of probabilities, which should be log(q/p) = logp_new - logp_pretrained.
37+
- kl_type (:obj:`str`): The type of KL divergence estimator to use.
38+
- 'k1': The standard, unbiased but high-variance estimator: `E_q[log(q/p)]`.
39+
- 'k2': A biased, low-variance estimator from a second-order approximation: `E_q[1/2 * (log(p/q))^2]`.
40+
- 'k3': An unbiased, low-variance estimator: `E_q[(p/q - 1) - log(p/q)]`.
41+
Returns:
42+
- kl_div (:obj:`torch.Tensor`): The calculated KL divergence estimate.
43+
"""
44+
if kl_type == 'k1':
45+
return logr.mean()
46+
elif kl_type == 'k2':
47+
return (logr ** 2 / 2).mean()
48+
elif kl_type == 'k3':
49+
return (torch.exp(-logr) - 1 + logr).mean()
50+
else:
51+
raise ValueError(f"Unknown kl_type: {kl_type}")
52+
53+
2554
def shape_fn_ppo(args, kwargs):
2655
r"""
2756
Overview:
@@ -97,8 +126,8 @@ def ppo_error(
97126
assert dual_clip is None or dual_clip > 1.0, "dual_clip value must be greater than 1.0, but get value: {}".format(
98127
dual_clip
99128
)
100-
logit_new, logit_old, action, value_new, value_old, adv, return_, weight = data
101-
policy_data = ppo_policy_data(logit_new, logit_old, action, adv, weight)
129+
logit_new, logit_old, action, value_new, value_old, adv, return_, weight, logit_pretrained = data
130+
policy_data = ppo_policy_data(logit_new, logit_old, action, adv, weight, logit_pretrained)
102131
policy_output, policy_info = ppo_policy_error(policy_data, clip_ratio, dual_clip, kl_type=kl_type)
103132
value_data = ppo_value_data(value_new, value_old, return_, weight)
104133
value_loss = ppo_value_error(value_data, clip_ratio, use_value_clip)
@@ -152,7 +181,7 @@ def ppo_policy_error(
152181
.. note::
153182
For the action mask often used in LLM/VLM, users can set the `weight` to the action mask.
154183
"""
155-
logit_new, logit_old, action, adv, weight = data
184+
logit_new, logit_old, action, adv, weight, logit_pretrained = data
156185
if weight is None:
157186
weight = torch.ones_like(adv)
158187
dist_new = torch.distributions.categorical.Categorical(logits=logit_new)
@@ -185,15 +214,13 @@ def ppo_policy_error(
185214
clipped = ratio.gt(1 + clip_ratio) | ratio.lt(1 - clip_ratio)
186215
clipfrac = torch.as_tensor(clipped).float().mean().item()
187216

188-
logr = logp_old - logp_new
189-
if kl_type == 'k1':
190-
kl_div = logr.mean()
191-
elif kl_type == 'k2':
192-
kl_div = (logr ** 2 / 2).mean()
193-
elif kl_type == 'k3':
194-
kl_div = (torch.exp(-logr) - 1 + logr).mean()
217+
if logit_pretrained is not None:
218+
dist_pretrained = torch.distributions.categorical.Categorical(logits=logit_pretrained)
219+
logp_pretrained = dist_pretrained.log_prob(action)
220+
logr = logp_new - logp_pretrained
221+
kl_div = calculate_kl_div(logr, kl_type)
195222
else:
196-
raise ValueError(f"Unknown kl_type: {kl_type}")
223+
kl_div = 0
197224

198225
return ppo_policy_loss(policy_loss, entropy_loss), ppo_info(approx_kl, clipfrac, kl_div)
199226

@@ -298,7 +325,7 @@ def ppo_error_continuous(
298325
assert dual_clip is None or dual_clip > 1.0, "dual_clip value must be greater than 1.0, but get value: {}".format(
299326
dual_clip
300327
)
301-
mu_sigma_new, mu_sigma_old, action, value_new, value_old, adv, return_, weight = data
328+
mu_sigma_new, mu_sigma_old, action, value_new, value_old, adv, return_, weight, logit_pretrained = data
302329
if weight is None:
303330
weight = torch.ones_like(adv)
304331

@@ -331,15 +358,13 @@ def ppo_error_continuous(
331358
else:
332359
value_loss = 0.5 * ((return_ - value_new).pow(2) * weight).mean()
333360

334-
logr = logp_old - logp_new
335-
if kl_type == 'k1':
336-
kl_div = logr.mean()
337-
elif kl_type == 'k2':
338-
kl_div = (logr ** 2 / 2).mean()
339-
elif kl_type == 'k3':
340-
kl_div = (torch.exp(-logr) - 1 + logr).mean()
361+
if logit_pretrained is not None:
362+
dist_pretrained = Independent(Normal(logit_pretrained['mu'], logit_pretrained['sigma']), 1)
363+
logp_pretrained = dist_pretrained.log_prob(action)
364+
logr = logp_new - logp_pretrained
365+
kl_div = calculate_kl_div(logr, kl_type)
341366
else:
342-
raise ValueError(f"Unknown kl_type: {kl_type}")
367+
kl_div = 0
343368

344369
return ppo_loss(policy_loss, value_loss, entropy_loss), ppo_info(approx_kl, clipfrac, kl_div)
345370

@@ -384,7 +409,7 @@ def ppo_policy_error_continuous(
384409
assert dual_clip is None or dual_clip > 1.0, "dual_clip value must be greater than 1.0, but get value: {}".format(
385410
dual_clip
386411
)
387-
mu_sigma_new, mu_sigma_old, action, adv, weight = data
412+
mu_sigma_new, mu_sigma_old, action, adv, weight, logit_pretrained = data
388413
if weight is None:
389414
weight = torch.ones_like(adv)
390415

@@ -409,14 +434,12 @@ def ppo_policy_error_continuous(
409434
clipped = ratio.gt(1 + clip_ratio) | ratio.lt(1 - clip_ratio)
410435
clipfrac = torch.as_tensor(clipped).float().mean().item()
411436

412-
logr = logp_old - logp_new
413-
if kl_type == 'k1':
414-
kl_div = logr.mean()
415-
elif kl_type == 'k2':
416-
kl_div = (logr ** 2 / 2).mean()
417-
elif kl_type == 'k3':
418-
kl_div = (torch.exp(-logr) - 1 + logr).mean()
437+
if logit_pretrained is not None:
438+
dist_pretrained = Independent(Normal(logit_pretrained['mu'], logit_pretrained['sigma']), 1)
439+
logp_pretrained = dist_pretrained.log_prob(action)
440+
logr = logp_new - logp_pretrained
441+
kl_div = calculate_kl_div(logr, kl_type)
419442
else:
420-
raise ValueError(f"Unknown kl_type: {kl_type}")
443+
kl_div = 0
421444

422445
return ppo_policy_loss(policy_loss, entropy_loss), ppo_info(approx_kl, clipfrac, kl_div)

dizoo/atari/config/serial/pong/pong_ppo_config.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from easydict import EasyDict
22

33
pong_ppo_config = dict(
4+
exp_name='pong_ppo_seed0',
45
env=dict(
56
collector_env_num=8,
67
evaluator_env_num=8,
@@ -41,6 +42,7 @@
4142
grad_clip_value=0.5,
4243
kl_beta=0.01,
4344
kl_type='k1',
45+
pretrained_model_path='The path of your pretrained model',
4446
),
4547
collect=dict(
4648
n_sample=3200,

dizoo/atari/config/serial/spaceinvaders/spaceinvaders_onppo_config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
grad_clip_value=0.5,
4747
kl_beta=0.05,
4848
kl_type='k1',
49+
pretrained_model_path='The path of your pretrained model',
4950
),
5051
collect=dict(
5152
n_sample=1024,

0 commit comments

Comments
 (0)