def METHOD_NAME(self, data_loader: DataLoader, num_steps: int, device: str = "cuda:0"):
    target_step = self.current_step + num_steps
    with tqdm(total=num_steps, dynamic_ncols=True) as pbar:
        # Configure progress bar
        pbar.set_description(f"Round {self.current_round}")
        for batch in self.get_batch(data_loader, num_steps):
            # Forward
            with torch.cuda.amp.autocast(enabled=self.use_half_precision):
                loss = self.training_step(self.model, batch)
            # Backward
            self.opt.zero_grad()
            self.scaler.scale(loss).backward()
            # Gradient clipping
            self.scaler.unscale_(self.opt)
            torch.nn.utils.clip_grad_norm_(self.model.parameters(), max_norm=1.0, norm_type=2.0)
            # Apply gradient
            self.scaler.step(self.opt)
            self.sch.step()
            self.scaler.update()
            # Update progress bar
            pbar.set_postfix(loss=f"{loss.item():.2f}")
            pbar.update(1)
            self.current_step += 1
            if self.current_step >= target_step:
                break
            if self.abort is not None and self.abort.triggered:
                break