Download src/trainer/trainer_worker.py from tahamajs/AsyncTensorRLHF: direct link, hf CLI and curl.
- Browser
- Download file 4.06 kB
-
https://huggingface.co/tahamajs/AsyncTensorRLHF/resolve/main/src/trainer/trainer_worker.py
- Command line
-
hf download hf://tahamajs/AsyncTensorRLHF/src/trainer/trainer_worker.py
-
curl -L -o trainer_worker.py https://huggingface.co/tahamajs/AsyncTensorRLHF/resolve/main/src/trainer/trainer_worker.py
4.06 kB
| # Trainer worker implementation (Ray actor or standalone worker) | |
| from typing import Any, List, Optional | |
| import torch | |
| try: | |
| import ray | |
| ray_remote = ray.remote(num_gpus=1) | |
| except ImportError: | |
| ray = None | |
| def ray_remote(cls): | |
| return cls | |
| from ..buffer.replay_buffer import BoundedReplayBuffer, Experience | |
| from .ppo_loss import compute_ppo_loss, compute_m2po_loss | |
| from .grpo_loss import compute_grpo_loss | |
| class TrainerWorker: | |
| """Ray actor or standalone worker that samples experiences and runs PPO/M2PO/GRPO updates. | |
| Arguments | |
| --------- | |
| model_path: str or torch.nn.Module, optional | |
| Path to model or model module. | |
| buffer: Any | |
| Shared replay buffer or actor handle. | |
| use_grpo: bool, default False | |
| Whether to train with GRPO loss. | |
| use_m2po: bool, default False | |
| Whether to train with M2PO loss. | |
| device: str, optional | |
| Device to use ('cuda' or 'cpu'). | |
| """ | |
| def __init__( | |
| self, | |
| model_path: Optional[Any] = None, | |
| buffer: Optional[Any] = None, | |
| use_grpo: bool = False, | |
| use_m2po: bool = False, | |
| device: Optional[str] = None, | |
| lr: float = 1e-4, | |
| ): | |
| self.buffer = buffer | |
| self.use_grpo = use_grpo | |
| self.use_m2po = use_m2po | |
| self.device = torch.device( | |
| device if device is not None else ("cuda" if torch.cuda.is_available() else "cpu") | |
| ) | |
| if isinstance(model_path, torch.nn.Module): | |
| self.model = model_path.to(self.device) | |
| else: | |
| self.model = torch.nn.Sequential( | |
| torch.nn.Linear(128, 128), | |
| torch.nn.ReLU(), | |
| torch.nn.Linear(128, 128), | |
| ).to(self.device) | |
| self.optimizer = torch.optim.AdamW(self.model.parameters(), lr=lr) | |
| self.version = 0 | |
| def step(self, batch_size: int = 64) -> Optional[float]: | |
| """Perform a single training step.""" | |
| if self.buffer is None: | |
| return None | |
| # Sample from buffer (actor handle or local instance) | |
| if hasattr(self.buffer, "sample"): | |
| if hasattr(self.buffer.sample, "remote") and ray is not None: | |
| exps: List[Experience] = ray.get(self.buffer.sample.remote(batch_size)) | |
| else: | |
| exps: List[Experience] = self.buffer.sample(batch_size) | |
| else: | |
| return None | |
| if not exps: | |
| return None | |
| # Pad or stack tensors | |
| max_prompt = max(e.prompt_ids.shape[-1] for e in exps) | |
| max_gen = max(e.generated_ids.shape[-1] for e in exps) | |
| padded_old_logprobs = [] | |
| padded_advantages = [] | |
| for e in exps: | |
| lp = e.log_probs.to(self.device) | |
| if lp.shape[-1] < max_gen: | |
| lp = torch.cat([lp, torch.zeros(max_gen - lp.shape[-1], device=self.device)]) | |
| padded_old_logprobs.append(lp) | |
| r = float(e.reward) | |
| padded_advantages.append(torch.full((max_gen,), r, device=self.device)) | |
| old_log_probs = torch.stack(padded_old_logprobs) | |
| advantages = torch.stack(padded_advantages) | |
| # Compute simulated policy log probs with small perturbation for optimization | |
| dummy_in = torch.randn(len(exps), 128, device=self.device) | |
| rep = self.model(dummy_in) | |
| policy_log_probs = old_log_probs + 0.01 * rep.mean(dim=-1, keepdim=True) | |
| if self.use_grpo: | |
| loss = compute_grpo_loss( | |
| policy_log_probs.unsqueeze(0), | |
| old_log_probs.unsqueeze(0), | |
| advantages.unsqueeze(0), | |
| ) | |
| elif self.use_m2po: | |
| loss = compute_m2po_loss(policy_log_probs, old_log_probs, advantages) | |
| else: | |
| loss = compute_ppo_loss(policy_log_probs, old_log_probs, advantages) | |
| self.optimizer.zero_grad() | |
| loss.backward() | |
| self.optimizer.step() | |
| self.version += 1 | |
| return float(loss.item()) | |
| def get_version(self) -> int: | |
| return self.version | |