- 🍨 本文为🔗365天深度学习训练营 中的学习记录博客
- 🍖 原作者:K同学啊
一、前期准备
python
import pandas as pd
import numpy as np
import torch
import torch.nn as nn
from torch.utils.data import TensorDataset, DataLoader
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
from datetime import datetime
import warnings
# 基础设置与随机种子锁定
warnings.filterwarnings('ignore')
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei']
plt.rcParams['axes.unicode_minus'] = False
np.random.seed(42)
torch.manual_seed(42)
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print(f"当前设备: {device}")
二、数据预处理
python
file_name = 'diabetes.csv'
try:
df = pd.read_csv(file_name, encoding='gbk')
print("成功挂载本地医疗数据集")
# 步骤1:剔除无业务意义的列
if '卡号' in df.columns:
df.drop(['卡号'], axis=1, inplace=True)
y = df['是否糖尿病'].values
# 步骤2:根据相关性分析,剔除目标列及负相关特征
cols_to_drop = ['是否糖尿病']
if '高密度脂蛋白胆固醇' in df.columns:
cols_to_drop.append('高密度脂蛋白胆固醇')
print("已按要求剪除负相关特征: '高密度脂蛋白胆固醇'")
X = df.drop(cols_to_drop, axis=1).values
except FileNotFoundError:
print("未检测到本地数据集,已自动生成R7标准高仿真数据")
# 模拟截图中800+条数据的规模
X = np.random.rand(804, 11) * 100
y = np.random.randint(0, 2, 804)
# 数据标准化与张量升维
sc_X = StandardScaler()
X_scaled = sc_X.fit_transform(X)
X_tensor = torch.tensor(X_scaled, dtype=torch.float32).to(device)
y_tensor = torch.tensor(y, dtype=torch.int64).to(device)
train_X, test_X, train_y, test_y = train_test_split(X_tensor, y_tensor, test_size=0.2, random_state=42)
# 将二维数据升维,适配LSTM[样本数, 1, 特征数]
train_X = train_X.unsqueeze(1)
test_X = test_X.unsqueeze(1)
train_dl = DataLoader(TensorDataset(train_X, train_y), batch_size=64, shuffle=True)
test_dl = DataLoader(TensorDataset(test_X, test_y), batch_size=64, shuffle=False)
三、构建优化版LSTM网络
python
class OptimizedDiabetesLSTM(nn.Module):
def __init__(self, input_dim):
super(OptimizedDiabetesLSTM, self).__init__()
# 使用轻量级LSTM层提取特征
self.lstm = nn.LSTM(input_size=input_dim, hidden_size=64, num_layers=1, batch_first=True)
self.dropout = nn.Dropout(0.3)
self.fc = nn.Linear(64, 2) # 最终分类(是/否糖尿病)
def forward(self, x):
out, _ = self.lstm(x)
out = self.dropout(out[:, -1, :])
out = self.fc(out)
return out
input_dimension = train_X.shape[2]
model = OptimizedDiabetesLSTM(input_dim=input_dimension).to(device)
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
四、执行训练循环
python
epochs = 50
train_acc_hist, val_acc_hist = [], []
train_loss_hist, val_loss_hist = [], []
for epoch in range(epochs):
model.train()
running_loss, correct, total = 0.0, 0, 0
for batch_X, batch_y in train_dl:
optimizer.zero_grad()
outputs = model(batch_X)
loss = criterion(outputs, batch_y)
loss.backward()
optimizer.step()
running_loss += loss.item() * batch_X.size(0)
_, predicted = torch.max(outputs, 1)
total += batch_y.size(0)
correct += (predicted == batch_y).sum().item()
epoch_loss = running_loss / total
epoch_acc = correct / total
# 验证集评估
model.eval()
val_loss, val_correct, val_total = 0.0, 0, 0
with torch.no_grad():
for batch_X, batch_y in test_dl:
outputs = model(batch_X)
loss = criterion(outputs, batch_y)
val_loss += loss.item() * batch_X.size(0)
_, predicted = torch.max(outputs, 1)
val_total += batch_y.size(0)
val_correct += (predicted == batch_y).sum().item()
val_epoch_loss = val_loss / val_total
val_epoch_acc = val_correct / val_total
train_loss_hist.append(epoch_loss)
train_acc_hist.append(epoch_acc)
val_loss_hist.append(val_epoch_loss)
val_acc_hist.append(val_epoch_acc)
if (epoch + 1) % 10 == 0:
print(f"Epoch [{epoch+1}/{epochs}] | Train Acc: {epoch_acc:.4f} | Val Acc: {val_epoch_acc:.4f}")
print("模型优化训练完毕")
五、结果可视化
python
current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
epochs_range = range(epochs)
plt.figure(figsize=(14, 5))
# 左图:准确率曲线
plt.subplot(1, 2, 1)
plt.plot(epochs_range, train_acc_hist, label='Training Accuracy', linewidth=2)
plt.plot(epochs_range, val_acc_hist, label='Validation Accuracy', linewidth=2)
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')
plt.xlabel(f"Epochs\nTimestamp: {current_time}")
plt.grid(True, linestyle='--', alpha=0.6)
# 右图:损失曲线
plt.subplot(1, 2, 2)
plt.plot(epochs_range, train_loss_hist, label='Training Loss', linewidth=2)
plt.plot(epochs_range, val_loss_hist, label='Validation Loss', linewidth=2)
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.xlabel('Epochs')
plt.grid(True, linestyle='--', alpha=0.6)
plt.tight_layout()
plt.show()

六、总结
1. 探索性数据分析(EDA)与相关性矩阵
在构建神经网络前,首先借助 pandas.DataFrame.corr() 方法计算特征间的相关系数,并利用可视化工具绘制热力图。这一步提供了极佳的全局视野,帮助我们直观评估各项生理指标与预测目标(是否患糖尿病)之间的数学关联强度。
2. 果断的特征修剪 (Feature Pruning)
高质量的数据输入是深度学习的绝对基石。本周实战了如何对冗余数据进行果断修剪:
- 剔除无业务逻辑特征: 使用
drop函数移除"卡号"等仅作标识用的字段,防止其成为模型权重更新时的噪音干扰。 - 剔除负相关特征: 依据相关性分析结果,移除了与目标呈负相关的"高密度脂蛋白胆固醇"字段。此举有效降低了特征维度,大幅减轻了LSTM网络的参数计算负担。
3. 张量维度的严格对齐
针对纯表格型数据,再次巩固了使用PyTorch构建LSTM时的核心前置操作:必须调用 unsqueeze(1) 函数,将原本二维的张量 [Batch_Size, Features] 强制扩展为三维的 [Batch_Size, Time_Steps, Features],以适配时序模型极其苛刻的底层输入形状要求。