Spaces:
Running
Running
File size: 6,463 Bytes
b12f6c3 a959e9a b12f6c3 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 | """
实际模型训练 - 训练 100 步(使用真实数据)
"""
import sys
import torch
import torch.optim as optim
from pathlib import Path
import json
sys.path.insert(0, '.')
from models.fusion_mini import FusionMini, FusionMiniConfig
def train_real():
"""实际训练(100 步)"""
print("[TRAIN] 开始实际模型训练(100 步)...")
print()
# 1. 创建小配置(实际使用)
print("[1] 创建模型配置...")
config = FusionMiniConfig(
vocab_size=100, # 小词表(匹配 tokenizer)
hidden_size=128, # 小隐层
num_hidden_layers=2, # 2 层
num_attention_heads=2, # 2 个注意力头
intermediate_size=256,
max_position_embeddings=64,
)
print(f" 词汇表大小: {config.vocab_size}")
print(f" 隐藏层大小: {config.hidden_size}")
print(f" 层数: {config.num_hidden_layers}")
print()
# 2. 创建模型
print("[2] 创建模型...")
model = FusionMini(config)
model.train() # 训练模式
param_count = sum(p.numel() for p in model.parameters()) / 1e3
print(f" 参数量: {param_count:.1f}K")
print(" 模型创建成功")
print()
# 3. 创建优化器
print("[3] 创建优化器...")
optimizer = optim.AdamW(
model.parameters(),
lr=5e-4,
weight_decay=0.01,
)
print(" 优化器创建成功")
print()
# 4. 加载训练数据
print("[4] 加载训练数据...")
data_path = Path("data/training_data.txt")
if not data_path.exists():
print(f" [ERROR] 训练数据不存在: {data_path}")
return False
with open(data_path, "r", encoding="utf-8") as f:
sentences = [line.strip() for line in f if line.strip()]
print(f" 句子数量: {len(sentences)}")
print(" 训练数据加载成功")
print()
# 5. 准备训练数据(简单编码)
print("[5] 准备训练数据...")
# 简单字符级编码
chars = sorted(list(set("".join(sentences))))
char_to_idx = {ch: i+3 for i, ch in enumerate(chars)} # +3 for [PAD], [UNK], [CLS]
char_to_idx["[PAD]"] = 0
char_to_idx["[UNK]"] = 1
char_to_idx["[CLS]"] = 2
# 编码句子
encoded_sentences = []
for sent in sentences:
encoded = [char_to_idx.get(ch, 1) for ch in sent] # 1 = [UNK]
encoded_sentences.append(encoded)
print(f" 词汇表大小: {len(char_to_idx)}")
print(f" 编码句子数量: {len(encoded_sentences)}")
print(" 训练数据准备成功")
print()
# 6. 训练 100 步
print("[6] 训练 100 步...")
losses = []
batch_size = 4
seq_len = 32
for step in range(100):
# 随机选择句子
indices = torch.randint(0, len(encoded_sentences), (batch_size,))
# 创建批次
batch_input = []
batch_labels = []
for idx in indices:
encoded = encoded_sentences[idx]
# 截断或填充到 seq_len
if len(encoded) > seq_len:
encoded = encoded[:seq_len]
else:
encoded = encoded + [0] * (seq_len - len(encoded))
# M4-M5 FIX: Do NOT pre-shift labels here.
# The model's forward() already applies the shift internally:
# shift_logits = logits[..., :-1, :]
# shift_labels = labels[..., 1:]
# Pre-shifting here would cause a double-shift bug.
batch_input.append(encoded) # Full sequence as input
batch_labels.append(encoded) # Full sequence as labels (model handles shift)
input_ids = torch.tensor(batch_input)
labels = torch.tensor(batch_labels)
# 清零梯度
optimizer.zero_grad()
# 前向传播
outputs = model(
input_ids=input_ids,
labels=labels,
return_dict=True,
)
loss = outputs["loss"]
losses.append(loss.item())
# 反向传播
loss.backward()
# 更新参数
optimizer.step()
# 每 10 步打印一次
if (step + 1) % 10 == 0:
avg_loss = sum(losses[-10:]) / min(10, len(losses))
print(f" Step {step+1:3d}: Loss = {loss.item():.4f} (Avg: {avg_loss:.4f})")
print(" 训练完成")
print()
# 7. 验证损失下降
print("[7] 验证损失下降...")
initial_loss = losses[0]
final_loss = losses[-1]
is_decreasing = final_loss < initial_loss
print(f" 初始 Loss: {initial_loss:.4f}")
print(f" 最终 Loss: {final_loss:.4f}")
print(f" Loss 变化: {final_loss - initial_loss:+.4f}")
print()
if is_decreasing:
print(" [PASS] Loss 持续下降")
print(" 训练有效!")
else:
print(" [WARN] Loss 未下降")
print(" 可能的问题:学习率太大 / 数据太少 / 模型太小")
print()
# 8. 保存模型
print("[8] 保存模型...")
output_dir = Path("output/real_model")
output_dir.mkdir(parents=True, exist_ok=True)
# 保存模型权重
torch.save(model.state_dict(), output_dir / "model.pt")
# 保存配置
config_dict = {
"vocab_size": config.vocab_size,
"hidden_size": config.hidden_size,
"num_hidden_layers": config.num_hidden_layers,
"num_attention_heads": config.num_attention_heads,
"intermediate_size": config.intermediate_size,
"max_position_embeddings": config.max_position_embeddings,
}
with open(output_dir / "config.json", "w") as f:
json.dump(config_dict, f, indent=2)
print(f" 模型保存路径: {output_dir}")
print(" 模型保存成功")
print()
print("[TRAIN] 实际模型训练完成")
return is_decreasing
if __name__ == "__main__":
print("=" * 60)
print("Fusion-LLM 实际模型训练(100 步)")
print("=" * 60)
print()
try:
success = train_real()
if success:
print()
print("[PASS] 训练测试通过")
except Exception as e:
print()
print(f"[FAIL] 训练测试出错: {e}")
import traceback
traceback.print_exc()
sys.exit(1)
sys.exit(0)
|