File size: 3,538 Bytes
dd9e164
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
# models/train_bc.py
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
import os
import sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from config import NUM_EPOCHS, LEARNING_RATE, MODEL_DIR, DEVICE
from models.king_ai import KingAI
from data.dataset import get_dataloaders


def train():
    """训练行为克隆模型"""
    # 检测设备
    if torch.backends.mps.is_available():
        device = torch.device("mps")
        print("✅ 使用 MPS (Apple Silicon GPU) 加速")
    elif torch.cuda.is_available():
        device = torch.device("cuda")
        print("✅ 使用 CUDA (NVIDIA GPU) 加速")
    else:
        device = torch.device("cpu")
        print("⚠️ 使用 CPU 训练")
    
    # 加载数据
    print("\n加载数据...")
    train_loader, val_loader = get_dataloaders(
        frames_dir="data/frames/game_01",
        annotation_file="data/annotations/annotations.json"
    )
    
    # 创建模型
    model = KingAI().to(device)
    criterion = nn.CrossEntropyLoss()
    optimizer = optim.Adam(model.parameters(), lr=LEARNING_RATE)
    scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=20, gamma=0.5)
    
    print(f"\n开始训练 {NUM_EPOCHS} 轮...")
    print("=" * 50)
    
    best_acc = 0.0
    
    for epoch in range(NUM_EPOCHS):
        # 训练阶段
        model.train()
        train_loss = 0.0
        train_correct = 0
        train_total = 0
        
        for images, actions in train_loader:
            images, actions = images.to(device), actions.to(device)
            
            optimizer.zero_grad()
            outputs = model(images)
            loss = criterion(outputs, actions)
            loss.backward()
            optimizer.step()
            
            train_loss += loss.item()
            _, predicted = torch.max(outputs, 1)
            train_total += actions.size(0)
            train_correct += (predicted == actions).sum().item()
        
        train_acc = 100 * train_correct / train_total
        
        # 验证阶段
        model.eval()
        val_loss = 0.0
        val_correct = 0
        val_total = 0
        
        with torch.no_grad():
            for images, actions in val_loader:
                images, actions = images.to(device), actions.to(device)
                outputs = model(images)
                loss = criterion(outputs, actions)
                
                val_loss += loss.item()
                _, predicted = torch.max(outputs, 1)
                val_total += actions.size(0)
                val_correct += (predicted == actions).sum().item()
        
        val_acc = 100 * val_correct / val_total
        
        scheduler.step()
        
        print(f"Epoch [{epoch+1:3d}/{NUM_EPOCHS}] "
              f"Train Loss: {train_loss/len(train_loader):.4f} "
              f"Train Acc: {train_acc:.2f}% | "
              f"Val Loss: {val_loss/len(val_loader):.4f} "
              f"Val Acc: {val_acc:.2f}%")
        
        # 保存最佳模型
        if val_acc > best_acc:
            best_acc = val_acc
            torch.save(model.state_dict(), os.path.join(MODEL_DIR, "best_model.pth"))
            print(f"  ✅ 保存最佳模型 (准确率: {val_acc:.2f}%)")
    
    # 保存最终模型
    torch.save(model.state_dict(), os.path.join(MODEL_DIR, "final_model.pth"))
    print(f"\n🎉 训练完成!最佳验证准确率: {best_acc:.2f}%")
    print(f"模型保存在: {MODEL_DIR}")


if __name__ == "__main__":
    train()