""" 实际模型训练 - 训练 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)