| 143 | |
| 144 | |
| 145 | class BasicTrainer(object): |
| 146 | def __init__(self, policy: nn.Module, config: DictConfig, seed: int, run_dir: str, reference_model: Optional[nn.Module] = None, rank: int = 0, world_size: int = 1): |
| 147 | """A trainer for a language model, supporting either SFT or DPO training. |
| 148 | |
| 149 | If multiple GPUs are present, naively splits the model across them, effectively |
| 150 | offering N times available memory, but without any parallel computation. |
| 151 | """ |
| 152 | self.seed = seed |
| 153 | self.rank = rank |
| 154 | self.world_size = world_size |
| 155 | self.config = config |
| 156 | self.run_dir = run_dir |
| 157 | |
| 158 | tokenizer_name_or_path = config.model.tokenizer_name_or_path or config.model.name_or_path |
| 159 | rank0_print(f'Loading tokenizer {tokenizer_name_or_path}') |
| 160 | self.tokenizer = transformers.AutoTokenizer.from_pretrained(tokenizer_name_or_path, cache_dir=get_local_dir(config.local_dirs)) |
| 161 | if self.tokenizer.pad_token_id is None: |
| 162 | self.tokenizer.pad_token_id = self.tokenizer.eos_token_id |
| 163 | |
| 164 | data_iterator_kwargs = dict( |
| 165 | names=config.datasets, |
| 166 | tokenizer=self.tokenizer, |
| 167 | shuffle=True, |
| 168 | max_length=config.max_length, |
| 169 | max_prompt_length=config.max_prompt_length, |
| 170 | sft_mode=config.loss.name == 'sft', |
| 171 | ) |
| 172 | |
| 173 | self.policy = policy |
| 174 | self.reference_model = reference_model |
| 175 | |
| 176 | self.train_iterator = get_batch_iterator(**data_iterator_kwargs, split='train', n_epochs=config.n_epochs, n_examples=config.n_examples, batch_size=config.batch_size, silent=rank != 0, cache_dir=get_local_dir(config.local_dirs)) |
| 177 | rank0_print(f'Loaded train data iterator') |
| 178 | self.eval_iterator = get_batch_iterator(**data_iterator_kwargs, split='test', n_examples=config.n_eval_examples, batch_size=config.eval_batch_size, silent=rank != 0, cache_dir=get_local_dir(config.local_dirs)) |
| 179 | self.eval_batches = list(self.eval_iterator) |
| 180 | rank0_print(f'Loaded {len(self.eval_batches)} eval batches of size {config.eval_batch_size}') |
| 181 | |
| 182 | def get_batch_samples(self, batch: Dict[str, torch.LongTensor]) -> Tuple[str, str]: |
| 183 | """Generate samples from the policy (and reference model, if doing DPO training) for the given batch of inputs.""" |
| 184 | |
| 185 | # FSDP generation according to https://github.com/pytorch/pytorch/issues/100069 |
| 186 | ctx = lambda: (FSDP.summon_full_params(self.policy, writeback=False, recurse=False) if 'FSDP' in self.config.trainer else contextlib.nullcontext()) |
| 187 | with ctx(): |
| 188 | policy_output = self.policy.generate( |
| 189 | batch['prompt_input_ids'], attention_mask=batch['prompt_attention_mask'], max_length=self.config.max_length, do_sample=True, pad_token_id=self.tokenizer.pad_token_id) |
| 190 | |
| 191 | if self.config.loss.name in {'dpo', 'ipo'}: |
| 192 | ctx = lambda: (FSDP.summon_full_params(self.reference_model, writeback=False, recurse=False) if 'FSDP' in self.config.trainer else contextlib.nullcontext()) |
| 193 | with ctx(): |
| 194 | reference_output = self.reference_model.generate( |
| 195 | batch['prompt_input_ids'], attention_mask=batch['prompt_attention_mask'], max_length=self.config.max_length, do_sample=True, pad_token_id=self.tokenizer.pad_token_id) |
| 196 | |
| 197 | policy_output = pad_to_length(policy_output, self.config.max_length, self.tokenizer.pad_token_id) |
| 198 | policy_output = all_gather_if_needed(policy_output, self.rank, self.world_size) |
| 199 | policy_output_decoded = self.tokenizer.batch_decode(policy_output, skip_special_tokens=True) |
| 200 | |
| 201 | if self.config.loss.name in {'dpo', 'ipo'}: |
| 202 | reference_output = pad_to_length(reference_output, self.config.max_length, self.tokenizer.pad_token_id) |
nothing calls this directly
no outgoing calls
no test coverage detected