Spaces:
Running
Running
| """ | |
| 训练优化:混合精度训练(AMP)+ 梯度累积 | |
| """ | |
| import sys | |
| import torch | |
| import torch.nn as nn | |
| from pathlib import Path | |
| from typing import Optional | |
| sys.path.insert(0, '.') | |
| class AMPTrainer: | |
| """ | |
| 混合精度训练器(Automatic Mixed Precision) | |
| 特性: | |
| - 自动混合精度(FP16/FP32) | |
| - 梯度缩放(GradScaler) | |
| - 支持 CPU 和 GPU | |
| """ | |
| def __init__( | |
| self, | |
| model: nn.Module, | |
| optimizer: torch.optim.Optimizer, | |
| use_amp: bool = True, | |
| grad_accum_steps: int = 1, | |
| max_grad_norm: float = 1.0, | |
| ): | |
| self.model = model | |
| self.optimizer = optimizer | |
| self.use_amp = use_amp and torch.cuda.is_available() | |
| self.grad_accum_steps = grad_accum_steps | |
| self.max_grad_norm = max_grad_norm | |
| # 梯度缩放器(仅 CUDA) | |
| self.scaler = torch.amp.GradScaler('cuda') if self.use_amp else None | |
| # 训练状态 | |
| self.step_count = 0 | |
| self.accum_count = 0 | |
| def train_step(self, loss: torch.Tensor): | |
| """ | |
| 单步训练(含梯度累积) | |
| Args: | |
| loss: 损失值(已除以 grad_accum_steps) | |
| """ | |
| # 缩放损失(梯度累积) | |
| scaled_loss = loss / self.grad_accum_steps | |
| if self.use_amp: | |
| # 混合精度反向传播 | |
| self.scaler.scale(scaled_loss).backward() | |
| else: | |
| scaled_loss.backward() | |
| self.accum_count += 1 | |
| # 达到累积步数时更新参数 | |
| if self.accum_count >= self.grad_accum_steps: | |
| if self.use_amp: | |
| # 梯度裁剪(先 unscale) | |
| self.scaler.unscale_(self.optimizer) | |
| torch.nn.utils.clip_grad_norm_(self.model.parameters(), self.max_grad_norm) | |
| # 更新参数 | |
| self.scaler.step(self.optimizer) | |
| self.scaler.update() | |
| else: | |
| # 梯度裁剪 | |
| torch.nn.utils.clip_grad_norm_(self.model.parameters(), self.max_grad_norm) | |
| # 更新参数 | |
| self.optimizer.step() | |
| self.optimizer.zero_grad() | |
| self.step_count += 1 | |
| self.accum_count = 0 | |
| def state_dict(self): | |
| """返回训练状态""" | |
| return { | |
| 'step_count': self.step_count, | |
| 'accum_count': self.accum_count, | |
| 'use_amp': self.use_amp, | |
| 'grad_accum_steps': self.grad_accum_steps, | |
| } | |
| class GradientAccumulator: | |
| """ | |
| 梯度累积器(独立于训练器的轻量级版本) | |
| 用于在显存不足时模拟更大的 batch size | |
| """ | |
| def __init__(self, accum_steps: int = 4): | |
| self.accum_steps = max(accum_steps, 1) | |
| self.current_step = 0 | |
| def should_update(self) -> bool: | |
| """是否应该更新参数""" | |
| self.current_step += 1 | |
| if self.current_step >= self.accum_steps: | |
| self.current_step = 0 | |
| return True | |
| return False | |
| def scale_loss(self, loss: torch.Tensor) -> torch.Tensor: | |
| """缩放损失以抵消累积效应""" | |
| return loss / self.accum_steps | |
| def create_training_config( | |
| batch_size: int = 4, | |
| grad_accum_steps: int = 4, | |
| learning_rate: float = 5e-5, | |
| warmup_steps: int = 100, | |
| max_steps: int = 1000, | |
| use_amp: bool = True, | |
| max_grad_norm: float = 1.0, | |
| weight_decay: float = 0.01, | |
| ): | |
| """ | |
| 创建训练配置 | |
| Args: | |
| batch_size: 实际 batch size | |
| grad_accum_steps: 梯度累积步数(有效 batch = batch_size * grad_accum_steps) | |
| learning_rate: 学习率 | |
| warmup_steps: 预热步数 | |
| max_steps: 最大训练步数 | |
| use_amp: 是否使用混合精度 | |
| max_grad_norm: 最大梯度范数 | |
| weight_decay: 权重衰减 | |
| Returns: | |
| dict: 训练配置 | |
| """ | |
| return { | |
| 'batch_size': batch_size, | |
| 'grad_accum_steps': grad_accum_steps, | |
| 'effective_batch_size': batch_size * grad_accum_steps, | |
| 'learning_rate': learning_rate, | |
| 'warmup_steps': warmup_steps, | |
| 'max_steps': max_steps, | |
| 'use_amp': use_amp, | |
| 'max_grad_norm': max_grad_norm, | |
| 'weight_decay': weight_decay, | |
| } | |
| if __name__ == "__main__": | |
| print("=" * 60) | |
| print("Fusion-LLM 训练优化测试(混合精度 + 梯度累积)") | |
| print("=" * 60) | |
| print() | |
| # 创建模型 | |
| print("[1] 创建模型...") | |
| from models.fusion_mini import FusionMini, FusionMiniConfig | |
| config = FusionMiniConfig(vocab_size=100, hidden_size=32, num_hidden_layers=1) | |
| model = FusionMini(config) | |
| optimizer = torch.optim.AdamW(model.parameters(), lr=5e-5) | |
| print(" 模型已创建") | |
| print() | |
| # 测试 AMPTrainer | |
| print("[2] 测试 AMPTrainer...") | |
| trainer = AMPTrainer( | |
| model=model, | |
| optimizer=optimizer, | |
| use_amp=False, # CPU 上不用 AMP | |
| grad_accum_steps=4, | |
| max_grad_norm=1.0, | |
| ) | |
| # 模拟训练 | |
| for i in range(12): | |
| input_ids = torch.randint(0, 100, (2, 64)) | |
| attention_mask = torch.ones(2, 64) | |
| labels = torch.randint(0, 100, (2, 64)) | |
| outputs = model(input_ids=input_ids, attention_mask=attention_mask, labels=labels) | |
| loss = outputs["loss"] if isinstance(outputs, dict) else outputs[0] | |
| trainer.train_step(loss) | |
| state = trainer.state_dict() | |
| print(f" 训练步数: {state['step_count']}") | |
| print(f" 累积步数: {state['accum_count']}") | |
| print(f" 使用 AMP: {state['use_amp']}") | |
| print(f" 梯度累积: {state['grad_accum_steps']} 步") | |
| assert state['step_count'] == 3, f"Expected 3 steps, got {state['step_count']}" | |
| print(" AMPTrainer 测试通过") | |
| print() | |
| # 测试 GradientAccumulator | |
| print("[3] 测试 GradientAccumulator...") | |
| accum = GradientAccumulator(accum_steps=4) | |
| updates = [] | |
| for i in range(16): | |
| if accum.should_update(): | |
| updates.append(i + 1) | |
| print(f" 更新次数: {len(updates)}") | |
| print(f" 更新步数: {updates}") | |
| assert len(updates) == 4, f"Expected 4 updates, got {len(updates)}" | |
| print(" GradientAccumulator 测试通过") | |
| print() | |
| # 测试训练配置 | |
| print("[4] 测试训练配置...") | |
| train_config = create_training_config( | |
| batch_size=4, | |
| grad_accum_steps=8, | |
| learning_rate=1e-4, | |
| use_amp=True, | |
| ) | |
| print(f" 有效 batch size: {train_config['effective_batch_size']}") | |
| assert train_config['effective_batch_size'] == 32 | |
| print(" 训练配置测试通过") | |
| print() | |
| print("[PASS] 训练优化测试全部通过") | |
| sys.exit(0) | |