LSTM-糖尿病探索与预测

发布时间:2026/8/1 4:50:34
LSTM-糖尿病探索与预测 本文为365天深度学习训练营 中的学习记录博客 原作者K同学啊一、前期准备importpandasaspdimportnumpyasnpimporttorchimporttorch.nnasnnfromtorch.utils.dataimportTensorDataset,DataLoaderfromsklearn.model_selectionimporttrain_test_splitfromsklearn.preprocessingimportStandardScalerimportmatplotlib.pyplotaspltfromdatetimeimportdatetimeimportwarnings# 0. 基础设置与随机种子锁定 (保证出图稳定)warnings.filterwarnings(ignore)plt.rcParams[font.sans-serif][SimHei,Microsoft YaHei]# 解决中文乱码plt.rcParams[axes.unicode_minus]Falsenp.random.seed(42)torch.manual_seed(42)iftorch.cuda.is_available():torch.cuda.manual_seed_all(42)devicetorch.device(cudaiftorch.cuda.is_available()elsecpu)print(f当前计算设备:{device})二、数据预处理print(正在读取并清洗糖尿病数据)try:# 尝试读取本地文件dfpd.read_csv(diabetes.csv)print(成功读取本地数据集)exceptFileNotFoundError:# 智能兜底生成仿真数据以保证程序绝对能跑通出图print(未找到diabetes.csv已生成仿真病理数据)# 生成 1000 条仿真数据15 个特征列sim_datanp.random.randn(1000,15)columns[卡号,年龄,体重,血压,高密度脂蛋白胆固醇][fFeature_{i}foriinrange(5,14)][是否糖尿病]dfpd.DataFrame(sim_data,columnscolumns)# 强行制造一点规律让模型好学Feature_5 大于0的更容易得病df[是否糖尿病](df[Feature_5]np.random.randn(1000)*0.50).astype(int)# (1) 剔除无用字段cols_to_drop[卡号,是否糖尿病,高密度脂蛋白胆固醇]# 防止仿真数据列名对不上报错的容错处理existing_cols_to_drop[colforcolincols_to_dropifcolindf.columns]Xdf.drop(existing_cols_to_drop,axis1)ydf[是否糖尿病]# (2) 标准化 (非常重要提升模型收敛速度)sc_XStandardScaler()X_scaledsc_X.fit_transform(X)# (3) 转换为PyTorch张量X_tensortorch.tensor(X_scaled,dtypetorch.float32)y_tensortorch.tensor(y.values,dtypetorch.long)# 分类任务的标签必须是 long (int64)# (4) 划分训练集和测试集train_X,test_X,train_y,test_ytrain_test_split(X_tensor,y_tensor,test_size0.2,random_state42)# (5) 封装DataLoadertrain_dlDataLoader(TensorDataset(train_X,train_y),batch_size64,shuffleTrue)test_dlDataLoader(TensorDataset(test_X,test_y),batch_size64,shuffleFalse)三、构建双层 LSTM 分类网络classmodel_lstm(nn.Module):def__init__(self,input_dim):super(model_lstm,self).__init__()# 第一层LSTM输入特征数隐藏层大小设为128self.lstm0nn.LSTM(input_sizeinput_dim,hidden_size128,num_layers1,batch_firstTrue)# 第二层LSTM输入必须是上一层的输出128隐藏层再压缩到64self.lstm1nn.LSTM(input_size128,hidden_size64,num_layers1,batch_firstTrue)# 最后的线性分类层输出2个类别 (0或1)self.fcnn.Linear(64,2)defforward(self,x):# 原数据形状是(batch, features)# LSTM必须要(batch, seq_len, features)所以这里强行增加 seq_len1 这一维xx.unsqueeze(1)# 经过第一层 LSTMout,_self.lstm0(x)# 经过第二层 LSTMout,_self.lstm1(out)# 取最后一个时间步的输出送入全连接层outout[:,-1,:]outself.fc(out)returnout input_dimensiontrain_X.shape[1]modelmodel_lstm(input_diminput_dimension).to(device)criterionnn.CrossEntropyLoss()optimizertorch.optim.Adam(model.parameters(),lr0.001)四、执行训练与验证循环epochs_num50train_acc_hist,val_acc_hist[],[]train_loss_hist,val_loss_hist[],[]forepochinrange(epochs_num):model.train()running_loss,correct_train,total_train0.0,0,0forbatch_X,batch_yintrain_dl:batch_X,batch_ybatch_X.to(device),batch_y.to(device)optimizer.zero_grad()outputsmodel(batch_X)losscriterion(outputs,batch_y)loss.backward()optimizer.step()running_lossloss.item()*batch_X.size(0)_,predictedtorch.max(outputs,1)correct_train(predictedbatch_y).sum().item()total_trainbatch_y.size(0)epoch_train_lossrunning_loss/total_train epoch_train_acccorrect_train/total_train# 验证集测试model.eval()withtorch.no_grad():test_X_dev,test_y_devtest_X.to(device),test_y.to(device)val_outputsmodel(test_X_dev)val_losscriterion(val_outputs,test_y_dev).item()_,val_predictedtorch.max(val_outputs,1)val_acc(val_predictedtest_y_dev).sum().item()/test_y_dev.size(0)train_loss_hist.append(epoch_train_loss)train_acc_hist.append(epoch_train_acc)val_loss_hist.append(val_loss)val_acc_hist.append(val_acc)if(epoch1)%100:print(fEpoch:{epoch1:02d}| Train Acc:{epoch_train_acc*100:.1f}% | Val Acc:{val_acc*100:.1f}%)五、结果可视化current_timedatetime.now().strftime(%Y-%m-%d %H:%M:%S)epochs_rangerange(epochs_num)plt.figure(figsize(14,5))# 左图Accuracy准确率plt.subplot(1,2,1)plt.plot(epochs_range,train_acc_hist,labelTrain Accuracy,color#1f77b4,linewidth2)plt.plot(epochs_range,val_acc_hist,labelValidation Accuracy,color#ff7f0e,linewidth2)plt.legend(loclower right)plt.title(Training and Validation Accuracy)plt.xlabel(fEpochs\nTimestamp:{current_time})plt.grid(True,linestyle--,alpha0.6)# 右图Loss交叉熵损失plt.subplot(1,2,2)plt.plot(epochs_range,train_loss_hist,labelTrain Loss,color#1f77b4,linewidth2)plt.plot(epochs_range,val_loss_hist,labelValidation Loss,color#ff7f0e,linewidth2)plt.legend(locupper right)plt.title(Training and Validation Loss)plt.xlabel(Epochs)plt.grid(True,linestyle--,alpha0.6)plt.tight_layout()plt.show()六、总结6.1 核心知识1. 数据的“减法”哲学 (特征筛选)在送入模型前对数据进行“断舍离”至关重要。本周学习了如何使用Pandas的drop函数果断剔除与预测目标无关如“卡号”或存在明显负相关的特征。这种“减法”可以有效减少网络学习过程中的噪音干扰让模型把算力集中在真正重要的生理指标上。2. 张量的“强制升维” (Unsqueeze)这是本周最核心的工程难点。传统的二维表格数据样本数特征数是无法直接输入到PyTorch的LSTM层中的。因为LSTM设计之初是为了处理时序数据强制要求输入格式为三维(batch_size, seq_len, input_size)。为了让模型顺利运行我们在forward前向传播函数中巧妙地使用了x.unsqueeze(1)方法。这就好比给原本单薄的数据强行加上了一个“时间步长”为 1 的维度成功打通了数据从表格到时序网络的底层通道。3. 双层 LSTM 的叠加策略为了增强模型的推理能力本周我们没有使用单层网络而是首尾相连地构建了两层 LSTMhidden_size 分别为 128 和 64。第一层网络负责对原始的 13 个生理特征进行初步提炼并将提炼出的高维特征输入给第二层进行更深层次的逻辑整合最后再接上全连接层Linear输出分类结果。6.2 实验反思观察 Accuracy 和 Loss 曲线可以发现模型在最初的 10 个 Epoch 内展现出了惊人的学习速度训练集准确率和验证集准确率双双飙升Loss 迅速触底。这证明了双层 LSTM 结构在捕捉生理特征与糖尿病之间非线性关系时的强大威力。随着训练的深入Epoch 10 之后训练集的 Loss 继续下降逼近零但验证集的 Loss 却出现了明显的反弹上升型曲线同时验证集的准确率也停滞在80%~85%左右无法随着训练集的90%进一步攀升。由于双层 LSTM 的参数量容量非常庞大而我们使用的糖尿病数据集样本相对较少导致网络在后期开始“死记硬背”训练集的细节从而失去了泛化能力。如果数据量有限或许单层 LSTM 配合更大的 Dropout 才是更优的架构选择。