使用cnn对桌面已有的名字为sleep的文件中的Excel传感器数据进行睡眠分期实验,使用pytorch并写出完整代码并标明注释
接下来,得考虑数据预处理。可能需要读取Excel文件,检查是否有缺失值,进行标准化或归一化。然后,将数据转换成适合CNN输入的格式,比如时间序列分段,可能用滑动窗口方法生成样本。
然后是数据集划分,分为训练集、验证集和测试集。需要确保数据分布均衡,特别是不同睡眠阶段的样本数量可能不均衡,可能需要采用分层抽样。
模型方面,使用CNN处理时间序列数据,可能需要一维卷积层。结构可能包括多个卷积层、池化层,最后接全连接层进行分类。激活函数选ReLU,输出层用Softmax。损失函数用交叉熵,优化器用Adam。
训练过程中要监控准确率和损失,可能需要早停法防止过拟合。还要考虑批处理大小、学习率等超参数调整。
代码部分需要包括数据加载、预处理、数据集创建、模型定义、训练循环和评估。注释要清晰,说明每一步的作用。可能还需要数据可视化,比如损失曲线和混淆矩阵。
需要注意的是,实际应用中可能需要调整网络结构,比如卷积核大小、层数,或者加入BatchNorm层和Dropout来提升性能。另外,数据增强方法也可能有用,比如添加噪声或时间扭曲,以增加模型的泛化能力。
可能遇到的挑战包括数据量不足、类别不平衡、特征提取是否充分。这时候可以考虑数据增强、调整类别权重,或者尝试其他模型如LSTM结合CNN。此外,确保数据预处理步骤正确,比如标准化方法是否适合传感器数据。
最后,测试模型时要看混淆矩阵,分析各个睡眠阶段的分类效果,找出模型可能存在的弱点,进一步优化。好的!我将按照步骤为您构建基于CNN的睡眠分期实验代码。以下是详细实现(代码包含详细注释):
"""
基于CNN的睡眠分期实验代码
环境要求:Python 3.8+ / PyTorch 1.12+ / pandas / scikit-learn
"""
# 〇、环境准备
import torch
import torch.nn as nn
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from torch.utils.data import Dataset, DataLoader
import matplotlib.pyplot as plt
import numpy as np
# 一、数据预处理
# 假设sleep.xlsx结构:包含多列传感器数据和最后一列'sleep_stage'(0-4分类标签)
def load_data(file_path):
"""
加载并预处理Excel数据
参数:file_path - Excel文件路径
返回:处理后的特征和标签
"""
# 读取数据
df = pd.read_excel(file_path)
# 处理缺失值(根据实际情况选择填充方式)
df = df.fillna(method='ffill') # 前向填充
# 分离特征和标签
features = df.iloc[:, :-1].values # 假设最后一列是标签
labels = df.iloc[:, -1].values
# 标准化特征
scaler = StandardScaler()
features = scaler.fit_transform(features)
return features, labels
# 二、数据集构建
class SleepDataset(Dataset):
"""自定义睡眠数据集类"""
def __init__(self, features, labels, window_size=30):
"""
参数:
features: 标准化后的特征数组
labels: 对应标签数组
window_size: 滑动窗口大小(时间步长)
"""
self.features = []
self.labels = []
# 使用滑动窗口生成序列样本
for i in range(len(features) - window_size):
self.features.append(features[i:i+window_size])
self.labels.append(labels[i+window_size]) # 预测当前窗口最后一个时间点的状态
def __len__(self):
return len(self.features)
def __getitem__(self, idx):
return (
torch.FloatTensor(self.features[idx]).permute(1, 0), # 转换为(channel, time_step)
torch.LongTensor([self.labels[idx]])
)
# 三、CNN模型定义
class SleepCNN(nn.Module):
"""睡眠分期CNN模型"""
def __init__(self, input_channels, num_classes):
super().__init__()
self.cnn = nn.Sequential(
# 输入形状:(batch_size, input_channels, time_steps)
nn.Conv1d(input_channels, 64, kernel_size=3, padding=1),
nn.ReLU(),
nn.MaxPool1d(2),
nn.Conv1d(64, 128, kernel_size=3, padding=1),
nn.ReLU(),
nn.MaxPool1d(2),
nn.Conv1d(128, 256, kernel_size=3, padding=1),
nn.ReLU(),
nn.AdaptiveAvgPool1d(1) # 全局平均池化
)
self.classifier = nn.Sequential(
nn.Linear(256, 128),
nn.ReLU(),
nn.Dropout(0.5),
nn.Linear(128, num_classes)
)
def forward(self, x):
x = self.cnn(x)
x = x.view(x.size(0), -1) # 展平
return self.classifier(x)
# 四、训练配置
# 参数设置
BATCH_SIZE = 64
EPOCHS = 100
LEARNING_RATE = 0.001
WINDOW_SIZE = 30 # 30个时间点为一个样本
# 加载数据
features, labels = load_data("sleep.xlsx")
# 划分数据集
X_train, X_temp, y_train, y_temp = train_test_split(features, labels, test_size=0.3, stratify=labels)
X_val, X_test, y_val, y_test = train_test_split(X_temp, y_temp, test_size=0.5, stratify=y_temp)
# 创建数据集
train_dataset = SleepDataset(X_train, y_train, WINDOW_SIZE)
val_dataset = SleepDataset(X_val, y_val, WINDOW_SIZE)
test_dataset = SleepDataset(X_test, y_test, WINDOW_SIZE)
# 创建数据加载器
train_loader = DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size=BATCH_SIZE)
test_loader = DataLoader(test_dataset, batch_size=BATCH_SIZE)
# 初始化模型
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = SleepCNN(input_channels=X_train.shape[1], num_classes=len(np.unique(labels))).to(device)
# 定义损失函数和优化器
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=LEARNING_RATE)
# 五、训练循环
best_val_acc = 0
train_losses = []
val_accuracies = []
for epoch in range(EPOCHS):
# 训练阶段
model.train()
running_loss = 0.0
for inputs, labels in train_loader:
inputs = inputs.to(device)
labels = labels.to(device).squeeze()
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
running_loss += loss.item()
# 验证阶段
model.eval()
correct = 0
total = 0
with torch.no_grad():
for inputs, labels in val_loader:
inputs = inputs.to(device)
labels = labels.to(device).squeeze()
outputs = model(inputs)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
# 记录指标
epoch_loss = running_loss / len(train_loader)
val_acc = correct / total
train_losses.append(epoch_loss)
val_accuracies.append(val_acc)
# 保存最佳模型
if val_acc > best_val_acc:
best_val_acc = val_acc
torch.save(model.state_dict(), "best_model.pth")
print(f"Epoch {epoch+1}/{EPOCHS} | Loss: {epoch_loss:.4f} | Val Acc: {val_acc:.4f}")
# 六、结果可视化
# 绘制训练曲线
plt.figure(figsize=(12,5))
plt.subplot(1,2,1)
plt.plot(train_losses, label='Training Loss')
plt.title("Training Loss Curve")
plt.xlabel("Epoch")
plt.ylabel("Loss")
plt.subplot(1,2,2)
plt.plot(val_accuracies, label='Validation Accuracy')
plt.title("Validation Accuracy Curve")
plt.xlabel("Epoch")
plt.ylabel("Accuracy")
plt.show()
# 七、测试评估
model.load_state_dict(torch.load("best_model.pth"))
model.eval()
test_correct = 0
test_total = 0
all_preds = []
all_labels = []
with torch.no_grad():
for inputs, labels in test_loader:
inputs = inputs.to(device)
labels = labels.to(device).squeeze()
outputs = model(inputs)
_, predicted = torch.max(outputs.data, 1)
test_total += labels.size(0)
test_correct += (predicted == labels).sum().item()
all_preds.extend(predicted.cpu().numpy())
all_labels.extend(labels.cpu().numpy())
print(f"\nTest Accuracy: {test_correct/test_total:.4f}")
# 绘制混淆矩阵
from sklearn.metrics import confusion_matrix
import seaborn as sns
cm = confusion_matrix(all_labels, all_preds)
plt.figure(figsize=(8,6))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues')
plt.title("Confusion Matrix")
plt.xlabel("Predicted")
plt.ylabel("True")
plt.show()
关键改进点说明:
- 数据预处理:加入滑动窗口处理,更适合时序数据分析
- 模型结构:使用深层CNN+全局池化,避免全连接层过拟合
- 评估机制:加入早停法(通过保存最佳模型实现)和混淆矩阵可视化
- 设备兼容:自动检测GPU加速
使用注意事项:
- 需要根据实际数据调整:
- 修改
load_data()
中的特征列索引 - 调整
window_size
参数匹配采样频率 - 修改
num_classes
参数匹配实际分期数
- 修改
- 建议预处理步骤:
- 检查数据平衡性,必要时进行过采样/欠采样
- 添加更复杂的数据增强(如添加噪声、时间扭曲)
- 性能提升方向:
- 加入BatchNorm层
- 尝试LSTM-CNN混合架构
- 调整超参数(学习率、窗口大小等)
实际应用时,请确保:
- Excel数据格式正确
- 已安装所有依赖库
- 有足够的GPU显存(如需处理大数据集)
- 根据实际需求调整分类阈值或损失函数权重