ceselder Claude Fable 5 commited on
Commit
73adae4
·
1 Parent(s): af8715e

rl: true per-token entropy bonus (maximize r + beta*H, explicit diversity knob, no KL)

Browse files
Files changed (2) hide show
  1. scripts/rl.py +15 -3
  2. src/mxf/config.py +1 -0
scripts/rl.py CHANGED
@@ -174,7 +174,7 @@ def update(actor, opt, submodule, ids, attn, p_len, marker, old_lp, adv, dirs_re
174
  gen_mask = attn[:, p_len:].bool()
175
  total_tok = max(int(gen_mask.sum()), 1)
176
  lo, hi = 1 - a.clip_eps, 1 + a.clip_eps
177
- loss_sum, clipped_tok = 0.0, 0
178
  opt.zero_grad(set_to_none=True)
179
  for s in range(0, n, a.micro_batch):
180
  e = min(s + a.micro_batch, n)
@@ -183,12 +183,20 @@ def update(actor, opt, submodule, ids, attn, p_len, marker, old_lp, adv, dirs_re
183
  STEER_COEFF, device, torch.bfloat16)
184
  with hooked(submodule, hook):
185
  logits = actor(input_ids=b_ids, attention_mask=b_attn).logits[:, p_len - 1 : -1]
186
- new_lp = torch.log_softmax(logits.float(), -1).gather(-1, b_ids[:, p_len:, None]).squeeze(-1)
187
  del logits
 
188
  m = gen_mask[s:e].to(device)
189
  ratio = torch.exp(new_lp - old_lp[s:e].to(device)).clamp(max=a.tis_cap) # TIS, upper only
190
  A = adv[s:e, None].to(device)
191
  loss = (-torch.minimum(ratio * A, ratio.clamp(lo, hi) * A) * m).sum() / total_tok
 
 
 
 
 
 
 
192
  loss.backward() # micro-losses share the global normalizer → grads sum correctly
193
  loss_sum += loss.item()
194
  clipped_tok += int((((ratio < lo) | (ratio > hi)) & m).sum())
@@ -199,7 +207,8 @@ def update(actor, opt, submodule, ids, attn, p_len, marker, old_lp, adv, dirs_re
199
  else: # stepping Adam on nan/inf grads corrupts moments AND weights
200
  opt.zero_grad(set_to_none=True)
201
  print(f"[update] non-finite grad norm ({gn}) — skipping step", flush=True)
202
- return {"loss": loss_sum, "grad_norm": gn, "clipfrac": clipped_tok / total_tok}
 
203
 
204
 
205
  def main():
@@ -226,6 +235,8 @@ def main():
226
  ap.add_argument("--len-penalty-start", type=int, default=cfg.len_penalty_start)
227
  ap.add_argument("--len-penalty-per-tok", type=float, default=cfg.len_penalty_per_tok)
228
  ap.add_argument("--no-gates", action="store_true", help="disable fluency/distinct/len shaping")
 
 
229
  ap.add_argument("--tp", type=int, default=int(os.environ.get("WORLD_SIZE", "1")))
230
  ap.add_argument("--vllm-gpu-mem", type=float, default=0.35)
231
  ap.add_argument("--vllm-max-len", type=int, default=1024)
@@ -337,6 +348,7 @@ def main():
337
  log = {"reward/mean": raw_r.mean().item(), "reward/std": raw_r.std().item(),
338
  "reward/max": raw_r.max().item(), "reward/shaped_mean": r.mean().item(),
339
  "reward/gate_frac": gate_frac, "ratio/clipfrac": stats["clipfrac"],
 
340
  "loss": stats["loss"], "grad_norm": stats["grad_norm"],
341
  "rollout/mean_logp": torch.cat(old_lps).mean().item(),
342
  "rollout/len_mean": n_gen / (B * G), "tokens_per_sec": n_gen / secs,
 
174
  gen_mask = attn[:, p_len:].bool()
175
  total_tok = max(int(gen_mask.sum()), 1)
176
  lo, hi = 1 - a.clip_eps, 1 + a.clip_eps
177
+ loss_sum, clipped_tok, ent_sum = 0.0, 0, 0.0
178
  opt.zero_grad(set_to_none=True)
179
  for s in range(0, n, a.micro_batch):
180
  e = min(s + a.micro_batch, n)
 
183
  STEER_COEFF, device, torch.bfloat16)
184
  with hooked(submodule, hook):
185
  logits = actor(input_ids=b_ids, attention_mask=b_attn).logits[:, p_len - 1 : -1]
186
+ logp_full = torch.log_softmax(logits.float(), -1)
187
  del logits
188
+ new_lp = logp_full.gather(-1, b_ids[:, p_len:, None]).squeeze(-1)
189
  m = gen_mask[s:e].to(device)
190
  ratio = torch.exp(new_lp - old_lp[s:e].to(device)).clamp(max=a.tis_cap) # TIS, upper only
191
  A = adv[s:e, None].to(device)
192
  loss = (-torch.minimum(ratio * A, ratio.clamp(lo, hi) * A) * m).sum() / total_tok
193
+ if a.entropy_coef > 0:
194
+ # true per-token entropy (unbiased, logits are already here) — maximize r + β·H(π):
195
+ # keeps the policy stochastic for Bo-N without KL's behavior-anchoring side effect
196
+ ent = -(logp_full.exp() * logp_full).sum(-1)
197
+ ent_sum += float((ent.detach() * m).sum())
198
+ loss = loss - a.entropy_coef * (ent * m).sum() / total_tok
199
+ del logp_full
200
  loss.backward() # micro-losses share the global normalizer → grads sum correctly
201
  loss_sum += loss.item()
202
  clipped_tok += int((((ratio < lo) | (ratio > hi)) & m).sum())
 
207
  else: # stepping Adam on nan/inf grads corrupts moments AND weights
208
  opt.zero_grad(set_to_none=True)
209
  print(f"[update] non-finite grad norm ({gn}) — skipping step", flush=True)
210
+ return {"loss": loss_sum, "grad_norm": gn, "clipfrac": clipped_tok / total_tok,
211
+ "entropy": ent_sum / total_tok}
212
 
213
 
214
  def main():
 
235
  ap.add_argument("--len-penalty-start", type=int, default=cfg.len_penalty_start)
236
  ap.add_argument("--len-penalty-per-tok", type=float, default=cfg.len_penalty_per_tok)
237
  ap.add_argument("--no-gates", action="store_true", help="disable fluency/distinct/len shaping")
238
+ ap.add_argument("--entropy-coef", type=float, default=cfg.entropy_coef,
239
+ help="β for maximize r + β·H(π): direct diversity pressure (Bo-N depends on it)")
240
  ap.add_argument("--tp", type=int, default=int(os.environ.get("WORLD_SIZE", "1")))
241
  ap.add_argument("--vllm-gpu-mem", type=float, default=0.35)
242
  ap.add_argument("--vllm-max-len", type=int, default=1024)
 
348
  log = {"reward/mean": raw_r.mean().item(), "reward/std": raw_r.std().item(),
349
  "reward/max": raw_r.max().item(), "reward/shaped_mean": r.mean().item(),
350
  "reward/gate_frac": gate_frac, "ratio/clipfrac": stats["clipfrac"],
351
+ "policy/entropy": stats["entropy"],
352
  "loss": stats["loss"], "grad_norm": stats["grad_norm"],
353
  "rollout/mean_logp": torch.cat(old_lps).mean().item(),
354
  "rollout/len_mean": n_gen / (B * G), "tokens_per_sec": n_gen / secs,
src/mxf/config.py CHANGED
@@ -68,6 +68,7 @@ class RLConfig:
68
  lr: float = 1e-6
69
  clip_eps: float = 0.2
70
  tis_cap: float = 2.0 # TIS upper ratio cap — absorbs residual vLLM/HF kernel mismatch
 
71
  max_new_tokens: int = 96
72
  min_new_tokens: int = 16
73
  temperature: float = 1.0
 
68
  lr: float = 1e-6
69
  clip_eps: float = 0.2
70
  tis_cap: float = 2.0 # TIS upper ratio cap — absorbs residual vLLM/HF kernel mismatch
71
+ entropy_coef: float = 0.0 # β in maximize r + β·H(π); explicit diversity knob (no KL)
72
  max_new_tokens: int = 96
73
  min_new_tokens: int = 16
74
  temperature: float = 1.0