zhan1206 commited on
Commit
3b8065d
·
1 Parent(s): e8c0992

Feat: Add AMP trainer and gradient accumulation for training optimization

Browse files
Files changed (1) hide show
  1. train/training_optimizations.py +229 -0
train/training_optimizations.py ADDED
@@ -0,0 +1,229 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ 训练优化:混合精度训练(AMP)+ 梯度累积
3
+ """
4
+ import sys
5
+ import torch
6
+ import torch.nn as nn
7
+ from pathlib import Path
8
+ from typing import Optional
9
+
10
+ sys.path.insert(0, '.')
11
+
12
+
13
+ class AMPTrainer:
14
+ """
15
+ 混合精度训练器(Automatic Mixed Precision)
16
+
17
+ 特性:
18
+ - 自动混合精度(FP16/FP32)
19
+ - 梯度缩放(GradScaler)
20
+ - 支持 CPU 和 GPU
21
+ """
22
+
23
+ def __init__(
24
+ self,
25
+ model: nn.Module,
26
+ optimizer: torch.optim.Optimizer,
27
+ use_amp: bool = True,
28
+ grad_accum_steps: int = 1,
29
+ max_grad_norm: float = 1.0,
30
+ ):
31
+ self.model = model
32
+ self.optimizer = optimizer
33
+ self.use_amp = use_amp and torch.cuda.is_available()
34
+ self.grad_accum_steps = grad_accum_steps
35
+ self.max_grad_norm = max_grad_norm
36
+
37
+ # 梯度缩放器(仅 CUDA)
38
+ self.scaler = torch.amp.GradScaler('cuda') if self.use_amp else None
39
+
40
+ # 训练状态
41
+ self.step_count = 0
42
+ self.accum_count = 0
43
+
44
+ def train_step(self, loss: torch.Tensor):
45
+ """
46
+ 单步训练(含梯度累积)
47
+
48
+ Args:
49
+ loss: 损失值(已除以 grad_accum_steps)
50
+ """
51
+ # 缩放损失(梯度累积)
52
+ scaled_loss = loss / self.grad_accum_steps
53
+
54
+ if self.use_amp:
55
+ # 混合精度反向传播
56
+ self.scaler.scale(scaled_loss).backward()
57
+ else:
58
+ scaled_loss.backward()
59
+
60
+ self.accum_count += 1
61
+
62
+ # 达到累积步数时更新参数
63
+ if self.accum_count >= self.grad_accum_steps:
64
+ if self.use_amp:
65
+ # 梯度裁剪(先 unscale)
66
+ self.scaler.unscale_(self.optimizer)
67
+ torch.nn.utils.clip_grad_norm_(self.model.parameters(), self.max_grad_norm)
68
+
69
+ # 更新参数
70
+ self.scaler.step(self.optimizer)
71
+ self.scaler.update()
72
+ else:
73
+ # 梯度裁剪
74
+ torch.nn.utils.clip_grad_norm_(self.model.parameters(), self.max_grad_norm)
75
+
76
+ # 更新参数
77
+ self.optimizer.step()
78
+
79
+ self.optimizer.zero_grad()
80
+ self.step_count += 1
81
+ self.accum_count = 0
82
+
83
+ def state_dict(self):
84
+ """返回训练状态"""
85
+ return {
86
+ 'step_count': self.step_count,
87
+ 'accum_count': self.accum_count,
88
+ 'use_amp': self.use_amp,
89
+ 'grad_accum_steps': self.grad_accum_steps,
90
+ }
91
+
92
+
93
+ class GradientAccumulator:
94
+ """
95
+ 梯度累积器(独立于训练器的轻量级版本)
96
+
97
+ 用于在显存不足时模拟更大的 batch size
98
+ """
99
+
100
+ def __init__(self, accum_steps: int = 4):
101
+ self.accum_steps = max(accum_steps, 1)
102
+ self.current_step = 0
103
+
104
+ def should_update(self) -> bool:
105
+ """是否应该更新参数"""
106
+ self.current_step += 1
107
+ if self.current_step >= self.accum_steps:
108
+ self.current_step = 0
109
+ return True
110
+ return False
111
+
112
+ def scale_loss(self, loss: torch.Tensor) -> torch.Tensor:
113
+ """缩放损失以抵消累积效应"""
114
+ return loss / self.accum_steps
115
+
116
+
117
+ def create_training_config(
118
+ batch_size: int = 4,
119
+ grad_accum_steps: int = 4,
120
+ learning_rate: float = 5e-5,
121
+ warmup_steps: int = 100,
122
+ max_steps: int = 1000,
123
+ use_amp: bool = True,
124
+ max_grad_norm: float = 1.0,
125
+ weight_decay: float = 0.01,
126
+ ):
127
+ """
128
+ 创建训练配置
129
+
130
+ Args:
131
+ batch_size: 实际 batch size
132
+ grad_accum_steps: 梯度累积步数(有效 batch = batch_size * grad_accum_steps)
133
+ learning_rate: 学习率
134
+ warmup_steps: 预热步数
135
+ max_steps: 最大训练步数
136
+ use_amp: 是否使用混合精度
137
+ max_grad_norm: 最大梯度范数
138
+ weight_decay: 权重衰减
139
+
140
+ Returns:
141
+ dict: 训练配置
142
+ """
143
+ return {
144
+ 'batch_size': batch_size,
145
+ 'grad_accum_steps': grad_accum_steps,
146
+ 'effective_batch_size': batch_size * grad_accum_steps,
147
+ 'learning_rate': learning_rate,
148
+ 'warmup_steps': warmup_steps,
149
+ 'max_steps': max_steps,
150
+ 'use_amp': use_amp,
151
+ 'max_grad_norm': max_grad_norm,
152
+ 'weight_decay': weight_decay,
153
+ }
154
+
155
+
156
+ if __name__ == "__main__":
157
+ print("=" * 60)
158
+ print("Fusion-LLM 训练优化测试(混合精度 + 梯度累积)")
159
+ print("=" * 60)
160
+ print()
161
+
162
+ # 创建模型
163
+ print("[1] 创建模型...")
164
+ from models.fusion_mini import FusionMini, FusionMiniConfig
165
+ config = FusionMiniConfig(vocab_size=100, hidden_size=32, num_hidden_layers=1)
166
+ model = FusionMini(config)
167
+ optimizer = torch.optim.AdamW(model.parameters(), lr=5e-5)
168
+ print(" 模型已创建")
169
+ print()
170
+
171
+ # 测试 AMPTrainer
172
+ print("[2] 测试 AMPTrainer...")
173
+ trainer = AMPTrainer(
174
+ model=model,
175
+ optimizer=optimizer,
176
+ use_amp=False, # CPU 上不用 AMP
177
+ grad_accum_steps=4,
178
+ max_grad_norm=1.0,
179
+ )
180
+
181
+ # 模拟训练
182
+ for i in range(12):
183
+ input_ids = torch.randint(0, 100, (2, 64))
184
+ attention_mask = torch.ones(2, 64)
185
+ labels = torch.randint(0, 100, (2, 64))
186
+
187
+ outputs = model(input_ids=input_ids, attention_mask=attention_mask, labels=labels)
188
+ loss = outputs["loss"] if isinstance(outputs, dict) else outputs[0]
189
+
190
+ trainer.train_step(loss)
191
+
192
+ state = trainer.state_dict()
193
+ print(f" 训练步数: {state['step_count']}")
194
+ print(f" 累积步数: {state['accum_count']}")
195
+ print(f" 使用 AMP: {state['use_amp']}")
196
+ print(f" 梯度累积: {state['grad_accum_steps']} 步")
197
+ assert state['step_count'] == 3, f"Expected 3 steps, got {state['step_count']}"
198
+ print(" AMPTrainer 测试通过")
199
+ print()
200
+
201
+ # 测试 GradientAccumulator
202
+ print("[3] 测试 GradientAccumulator...")
203
+ accum = GradientAccumulator(accum_steps=4)
204
+ updates = []
205
+ for i in range(16):
206
+ if accum.should_update():
207
+ updates.append(i + 1)
208
+
209
+ print(f" 更新次数: {len(updates)}")
210
+ print(f" 更新步数: {updates}")
211
+ assert len(updates) == 4, f"Expected 4 updates, got {len(updates)}"
212
+ print(" GradientAccumulator 测试通过")
213
+ print()
214
+
215
+ # 测试训练配置
216
+ print("[4] 测试训练配置...")
217
+ train_config = create_training_config(
218
+ batch_size=4,
219
+ grad_accum_steps=8,
220
+ learning_rate=1e-4,
221
+ use_amp=True,
222
+ )
223
+ print(f" 有效 batch size: {train_config['effective_batch_size']}")
224
+ assert train_config['effective_batch_size'] == 32
225
+ print(" 训练配置测试通过")
226
+ print()
227
+
228
+ print("[PASS] 训练优化测试全部通过")
229
+ sys.exit(0)