import pickle
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import os
from sklearn.metrics import confusion_matrix
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
import torch
import torch.nn as nn
from torch.utils.data import TensorDataset, DataLoader
from tqdm import tqdm
# 设置随机种子
torch.manual_seed(42)
np.random.seed(42)
def load_data(file_path):
"""安全加载并预处理数据"""
with open(file_path, 'rb') as f:
data = pickle.load(f, encoding='latin1')
# 调试信息
print(f"成功加载数据,总样本数: {len(data)}")
sample_shape = next(iter(data.values())).shape
print(f"单个样本形状: {sample_shape} (格式: [时间步长, 通道数, 特征数])")
# 统一处理所有样本为相同长度(取最小长度)
min_length = min(arr.shape[0] for arr in data.values())
print(f"统一长度: {min_length} (取所有样本的最小长度)")
# 提取I路信号并统一长度
X = np.array([arr[:min_length, 0, 0] for arr in data.values()], dtype=np.float32)
X = X.reshape(-1, min_length, 1) # 形状: (样本数, 时间步长, 特征数)
# 提取调制类型标签
modulations = [key[0] for key in data.keys()]
label_encoder = LabelEncoder()
y = label_encoder.fit_transform(modulations)
print(f"调制类型及对应标签:\n{pd.Series(label_encoder.classes_).to_frame('标签')}")
# 分割数据集
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42)
# 归一化
mean, std = X_train.mean(), X_train.std()
X_train = (X_train - mean) / (std + 1e-8)
X_test = (X_test - mean) / (std + 1e-8)
return X_train, y_train, X_test, y_test, label_encoder
class SignalTransformer(nn.Module):
"""稳健的Transformer模型"""
def __init__(self, input_dim=1, num_classes=11, d_model=128, nhead=8, num_layers=3, dropout=0.1):
super().__init__()
self.input_proj = nn.Linear(input_dim, d_model)
self.pos_encoder = nn.Parameter(torch.zeros(1, 1000, d_model))
encoder_layer = nn.TransformerEncoderLayer(
d_model=d_model, nhead=nhead, dim_feedforward=512, dropout=dropout, batch_first=True)
self.transformer = nn.TransformerEncoder(encoder_layer, num_layers=num_layers)
self.classifier = nn.Sequential(
nn.LayerNorm(d_model),
nn.Dropout(dropout),
nn.Linear(d_model, num_classes)
)
nn.init.xavier_uniform_(self.pos_encoder)
def forward(self, x):
x = self.input_proj(x)
seq_len = x.size(1)
x = x + self.pos_encoder[:, :seq_len, :]
x = self.transformer(x).mean(dim=1)
return self.classifier(x)
def train_model():
# 设备配置
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"\n训练设备: {device}")
# 加载数据
data_path = r"D:\Anaconda\envs\DL\Lib\site-packages\caffe2\python\data\train\2016.04C.multisnr.pkl"
try:
X_train, y_train, X_test, y_test, label_encoder = load_data(data_path)
except Exception as e:
print(f"数据加载失败: {str(e)}")
return
# 转换为PyTorch张量
train_data = TensorDataset(
torch.from_numpy(X_train).float(),
torch.from_numpy(y_train).long()
)
test_data = TensorDataset(
torch.from_numpy(X_test).float(),
torch.from_numpy(y_test).long()
)
# 数据加载器 (batch size改回32)
train_loader = DataLoader(train_data, batch_size=32, shuffle=True)
test_loader = DataLoader(test_data, batch_size=32)
# 模型初始化
model = SignalTransformer(num_classes=len(label_encoder.classes_)).to(device)
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.AdamW(model.parameters(), lr=3e-4, weight_decay=1e-4)
# 训练记录
history = {
'epoch': [], 'train_loss': [], 'val_loss': [],
'val_acc': [], 'best_epoch': 0, 'best_acc': 0
}
# 训练循环(保持5轮不变)
for epoch in range(5):
model.train()
train_loss = 0
# 使用with语句管理进度条
with tqdm(train_loader, desc=f'Epoch {epoch+1}/5') as progress_bar:
for inputs, labels in progress_bar:
inputs, labels = inputs.to(device), labels.to(device)
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
train_loss += loss.item()
progress_bar.set_postfix({'loss': loss.item()})
# 验证
model.eval()
val_loss, correct = 0, 0
all_preds, all_labels = [], []
# 验证阶段也添加进度条
with tqdm(test_loader, desc='验证中') as val_bar:
with torch.no_grad():
for inputs, labels in val_bar:
inputs, labels = inputs.to(device), labels.to(device)
outputs = model(inputs)
val_loss += criterion(outputs, labels).item()
preds = outputs.argmax(dim=1)
correct += (preds == labels).sum().item()
all_preds.extend(preds.cpu().numpy())
all_labels.extend(labels.cpu().numpy())
val_bar.set_postfix({'val_loss': val_loss/len(val_bar)})
# 记录结果
val_acc = correct / len(test_data)
history['epoch'].append(epoch+1)
history['train_loss'].append(train_loss/len(train_loader))
history['val_loss'].append(val_loss/len(test_loader))
history['val_acc'].append(val_acc)
if val_acc > history['best_acc']:
history['best_acc'] = val_acc
history['best_epoch'] = epoch+1
torch.save(model.state_dict(), 'best_model.pth')
# 使用tqdm.write确保输出不干扰进度条
tqdm.write(f"Epoch {epoch+1}: 训练损失={history['train_loss'][-1]:.4f}, "
f"验证损失={history['val_loss'][-1]:.4f}, "
f"准确率={val_acc:.4f}")
# 保存结果
save_results(history, all_labels, all_preds, label_encoder.classes_)
print(f"\n最佳模型在Epoch {history['best_epoch']}, 准确率={history['best_acc']:.4f}")
def save_results(history, true_labels, pred_labels, classes):
"""保存所有结果并显示可视化"""
os.makedirs('results', exist_ok=True)
# 创建两个独立图形
fig1, ax1 = plt.subplots(figsize=(12, 10))
fig2, (ax2, ax3) = plt.subplots(1, 2, figsize=(12, 5))
# 1. 混淆矩阵
cm = confusion_matrix(true_labels, pred_labels)
sns.heatmap(cm, annot=True, fmt='d', xticklabels=classes, yticklabels=classes, ax=ax1)
ax1.set_title("Confusion Matrix")
fig1.savefig("results/confusion_matrix.png", bbox_inches='tight')
# 2. 训练曲线
ax2.plot(history['epoch'], history['train_loss'], label='Train Loss')
ax2.plot(history['epoch'], history['val_loss'], label='Validation Loss')
ax2.set_xlabel('Epoch')
ax2.set_ylabel('Loss')
ax2.legend()
ax3.plot(history['epoch'], history['val_acc'], label='Validation Accuracy', color='green')
ax3.set_xlabel('Epoch')
ax3.set_ylabel('Accuracy')
ax3.legend()
fig2.savefig("results/training_metrics.png", bbox_inches='tight')
# 同时显示两个窗口
plt.show(block=False) # 不阻塞代码执行
plt.pause(0.1) # 短暂暂停确保窗口创建
# 3. 训练日志表格
df = pd.DataFrame({
'Epoch': history['epoch'],
'Training Loss': history['train_loss'],
'Validation Loss': history['val_loss'],
'Accuracy': history['val_acc']
})
df.to_csv("results/training_log.csv", index=False)
print("\n训练日志摘要:")
print(df.round(4))
if __name__ == "__main__":
train_model()
以上代码输出的时候,混淆矩阵弹窗和损失精度曲线图弹窗一闪即逝,我需要这两个弹窗能正常显示,请你修改后发我完整代码
最新发布