第R6周:LSTM实现糖尿病探索与预测
- 🍨 本文为🔗365天深度学习训练营 中的学习记录博客
- 🍖 原作者:K同学啊
学习目标:
学习使用LSTM对糖尿病进行探索预测
本文预测准确率,请尝试将其提升到70%以上
🏡 我的环境:
语言环境:Python3.8
编译器:Jupyter Lab
深度学习环境:torch1.12.1 torchvision0.20.1
1、数据预处理
1.1设置GPU
#设置GPU
import torch.nn as nn
import torch.nn.functional as F
import torchvision,torch device = torch.device("cuda" if torch.cuda.is_available() else "cpu")print(device)
1.2导入数据
#导入数据
import numpy as np
import pandas as pd
import seaborn as sns
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
plt.rcParams['savefig.dpi'] = 500 #图片像素
plt.rcParams['figure.dpi'] = 500 #分辨率plt.rcParams['font.sans-serif'] = ['SimHei'] #用来正常显示中文标签import warnings
warnings.filterwarnings('ignore')DataFrame = pd.read_excel('dia.xls')
print(DataFrame.head())print(DataFrame.shape)
# 查看是否有缺失值
print("数据缺失值------------------")
print(DataFrame.isnull().sum())
1.3数据检查
# 查看数据是否有重复值
print("数据重复值------------------")
print('数据的重复值为:'f'{DataFrame.duplicated().sum()}')
1.4数据分布分析
feature_map = {'年龄': '年龄','高密度脂蛋白胆固醇': '高密度脂蛋白胆固醇','低密度脂蛋白胆固醇': '低密度脂蛋白胆固醇','极低密度脂蛋白胆固醇': '极低密度脂蛋白胆固醇','甘油三酯': '甘油三酯','总胆固醇': '总胆固醇','脉搏': '脉搏','舒张压': '舒张压','高血压史': '高血压史','尿素氮': '尿素氮','尿酸': '尿酸','肌酐': '肌酐','体重检查结果': '体重检查结果'
}plt.figure(figsize=(15, 10))
for i, (col, col_name) in enumerate(feature_map.items(), 1):plt.subplot(3, 5, i)sns.boxplot(x=DataFrame['是否糖尿病'], y=DataFrame[col],hue=DataFrame['是否糖尿病'], # 按同一分组着色palette={0: 'orange', 1: 'blue'}, # 定义0/1组颜色dodge=False, # 避免箱体左右偏移legend=False # 隐藏重复图例)plt.title(f'{col_name}的箱线图', fontsize=14)plt.ylabel('数值', fontsize=12)plt.grid(axis='y', linestyle='--', alpha=0.7)# 强制显示分组标签(防止因hue隐藏x轴刻度)plt.xticks(ticks=[0, 1], labels=['0', '1']) plt.tight_layout()
plt.show()
2、构建模型
2.1数据集构建
import torch
# 构建数据集from sklearn.preprocessing import StandardScaler# '高密度脂蛋白胆固醇'字段与糖尿病负相关,故在X 中去掉该字段
X = DataFrame.drop(['卡号', '是否糖尿病', '高密度脂蛋白胆固醇'], axis=1)
y = DataFrame['是否糖尿病']# sc_X = StandardScaler()
# X = sc_X.fit_transformX = torch.tensor(np.array(X), dtype=torch.float32)
y = torch.tensor(np.array(y), dtype=torch.int64)train_X, test_X, train_y, test_y = train_test_split(X, y, test_size=0.2, random_state=1)train_X.shape, train_y.shape
2.2LSTM模型
from torch.utils.data import TensorDataset, DataLoadertrain_dl = DataLoader(TensorDataset(train_X, train_y), batch_size=64, shuffle=False)
test_dl = DataLoader(TensorDataset(test_X, test_y), batch_size=64, shuffle=False)# 定义模型
class model_lstm(nn.Module):def __init__(self):super(model_lstm, self).__init__()self.lstm0 = nn.LSTM(input_size=13, hidden_size=200,num_layers=1, batch_first=True)self.lstm1 = nn.LSTM(input_size=200, hidden_size=200,num_layers=1, batch_first=True)self.fc0 = nn.Linear(200, 2) # 输出 2 类def forward(self, x):# 如果 x 是 2D 的,转换为 3D 张量,假设 seq_len=1if x.dim() == 2:x = x.unsqueeze(1) # [batch_size, 1, input_size]# LSTM 处理数据out, (h_n, c_n) = self.lstm0(x) # 第一层 LSTM# 使用第二个 LSTM,并传递隐藏状态out, (h_n, c_n) = self.lstm1(out, (h_n, c_n)) # 第二层 LSTM# 获取最后一个时间步的输出out = out[:, -1, :] # 选择序列的最后一个时间步的输出out = self.fc0(out) # [batch_size, 2]return outmodel = model_lstm().to(device)
print(model)
2.3训练函数和测试函数
# 训练模型def train(dataloader, model, loss_fn, optimizer):size = len(dataloader.dataset) # 训练集的大小num_batches = len(dataloader) # 批次数目train_loss, train_acc = 0, 0 # 初始化训练损失和正确率model.train() # 设置模型为训练模式for X, y in dataloader: # 获取数据和标签# 如果 X 是 2D 的,调整为 3Dif X.dim() == 2:X = X.unsqueeze(1) # [batch_size, 1, input_size],即假设 seq_len=1X, y = X.to(device), y.to(device) # 将数据移动到设备# 计算预测误差pred = model(X) # 网络输出loss = loss_fn(pred, y) # 计算网络输出和真实值之间的差距# 反向传播optimizer.zero_grad() # 清除上一步的梯度loss.backward() # 反向传播optimizer.step() # 更新权重# 记录acc与losstrain_acc += (pred.argmax(1) == y).type(torch.float).sum().item()train_loss += loss.item()train_acc /= size # 平均准确率train_loss /= num_batches # 平均损失return train_acc, train_lossdef test(dataloader, model, loss_fn):size = len(dataloader.dataset) # 测试集的大小num_batches = len(dataloader) # 批次数目, (size/batch_size,向上取test_loss, test_acc = 0, 0# 当不进行训练时,停止梯度更新,节省计算内存消耗with torch.no_grad():for imgs, target in dataloader:imgs, target = imgs.to(device), target.to(device)# 计算losstarget_pred = model(imgs)loss = loss_fn(target_pred, target)test_loss += loss.item()test_acc += (target_pred.argmax(1) == target).type(torch.float).sum().item()test_acc /= sizetest_loss /= num_batchesreturn test_acc, test_loss
2.4正式训练
loss_fn = nn.CrossEntropyLoss() # 创建损失函数
learn_rate = 1e-4 # 学习率
opt = torch.optim.Adam(model.parameters(), lr=learn_rate)
epochs = 50
train_loss = []
train_acc = []
test_loss = []
test_acc = []
for epoch in range(epochs):model.train()epoch_train_acc, epoch_train_loss = train(train_dl, model, loss_fn, opt)model.eval()epoch_test_acc, epoch_test_loss = test(test_dl, model, loss_fn)train_acc.append(epoch_train_acc)train_loss.append(epoch_train_loss)test_acc.append(epoch_test_acc)test_loss.append(epoch_test_loss)# 获取当前的学习率lr = opt.state_dict()['param_groups'][0]['lr']template = ('Epoch:{:2d}, Train_acc:{:.1f}%, Train_loss:{:.3f}, Test_acc:{:.1f}%, Test_loss:{:.3f},Lr:{:.2E}')print(template.format(epoch + 1, epoch_train_acc * 100, epoch_train_loss, epoch_test_acc * 100, epoch_test_loss, lr))print("=" * 20, 'Done', "=" * 20)
3、模型评估
3.1Loss与ACC图
import matplotlib.pyplot as plt
#隐藏警告
import warnings
warnings.filterwarnings("ignore") #忽略警告信息
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
plt.rcParams['figure.dpi'] = 100 #分辨率from datetime import datetime
current_time = datetime.now()epochs_range = range(epochs)plt.figure(figsize=(12, 3))
plt.subplot(1, 2, 1)plt.plot(epochs_range, train_acc, label='Training Accuracy')
plt.plot(epochs_range, test_acc, label='Test Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')
plt.xlabel(current_time)plt.subplot(1, 2, 2)
plt.plot(epochs_range, train_loss, label='Training Loss')
plt.plot(epochs_range, test_loss, label='Test Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()
4、总结
- 关键观察点
(1) 训练准确率(Training Accuracy)
• 趋势:从初始值约 0.50 快速上升至 0.80 附近,最终趋于平稳。
• 分析:
• 模型在训练数据上学习效果显著,能够较好地拟合训练集。
• 最终稳定在较高水平(0.80),表明模型容量足够捕捉训练数据的特征。
(2) 验证准确率(Validation Accuracy)
• 趋势:从 0.50 逐步上升至约 0.65-0.70,但波动明显,后期未显著提升。
• 分析:
• 验证集性能低于训练集,可能存在 轻微过拟合(训练集表现远优于验证集)。
• 波动可能源于数据噪声或验证集分布差异。
(3) 训练损失(Training Loss)
• 趋势:从 0.70 持续下降至接近 0.40,收敛平稳。
• 分析:
• 损失函数优化有效,模型对训练数据的误差逐步减小。
(4) 验证损失(Validation Loss)
• 趋势:初期下降后稳定在 0.50 附近,未进一步降低。
• 分析:
• 验证损失未随训练损失同步下降,进一步支持 过拟合猜想。
• 可能原因:模型复杂度过高或训练数据量不足。
- 模型性能总结
指标 训练集 验证集 结论
准确率(Accuracy) 高(0.80)且稳定 中等(0.65-0.70)波动 泛化能力一般,需防过拟合
损失(Loss) 持续下降至低值(0.40) 稳定在中等值(0.50) 验证集优化未达最优
• 优势:模型能够有效学习训练数据的特征,训练过程收敛稳定。
• 问题:验证集性能未同步提升,泛化能力受限。
- 改进建议
(1) 减轻过拟合
• 数据增强:增加训练数据或引入合成样本(如 SMOTE)。
• 正则化:添加 Dropout 层或 L2 正则化(weight_decay)。
• 早停(Early Stopping):根据验证损失提前终止训练(避免后期过拟合)。
(2) 模型优化
• 调整超参数:降低学习率、减少 LSTM 隐藏层维度。
• 交叉验证:使用 K 折交叉验证更稳健评估性能。
• 集成方法:结合多个 LSTM 模型的预测结果(如 Bagging)。
(3) 其他检查
• 数据分布:验证训练集与验证集的数据分布是否一致。
• 特征工程:检查输入特征是否包含冗余或噪声特征。
- 结论
当前 LSTM 模型在糖尿病预测任务中表现出 中等泛化能力,但存在过拟合风险。
下一步重点:通过正则化、数据扩充和超参数调优提升验证集性能,确保模型在真实场景中的可靠性。