【深度学习实验】—— 利用 RNN 模型进行心脏病预测


  • 🍨 本文记录基于 TensorFlow 与 PyTorch 完成心脏病二分类任务的实践过程
  • 🍖 数据集:Data/heart.csv(303 条样本,13 个输入特征)

文章目录

  • [1. 简介](#1. 简介)
  • [2. 环境](#2. 环境)
  • [3. 代码实现](#3. 代码实现)
    • [3.1 前期准备](#3.1 前期准备)
      • [3.1.1 导入库与设置随机种子](#3.1.1 导入库与设置随机种子)
      • [3.1.2 数据加载与预处理](#3.1.2 数据加载与预处理)
    • [3.2 模型建立与训练](#3.2 模型建立与训练)
      • [3.2.1 TensorFlow 全连接网络](#3.2.1 TensorFlow 全连接网络)
      • [3.2.2 PyTorch RNN 分类器](#3.2.2 PyTorch RNN 分类器)
  • [4. 模型评估](#4. 模型评估)
    • [4.1 TensorFlow 训练过程与最终评估](#4.1 TensorFlow 训练过程与最终评估)
    • [4.2 PyTorch 训练过程与预测结果](#4.2 PyTorch 训练过程与预测结果)
    • [4.3 TensorFlow 与 PyTorch 结果对比](#4.3 TensorFlow 与 PyTorch 结果对比)
  • [5. 总结](#5. 总结)

1. 简介

项目 内容
任务 心脏病二分类:0 表示不会患心脏病,1 表示可能患心脏病
数据集 303 条样本、13 个特征、1 个标签列;正类 165 条,负类 138 条
TensorFlow 模型 全连接神经网络:Dense(24) → Dropout(0.2) → Dense(1)
PyTorch 模型 RNN 分类器:RNN(13, 200) → Linear(200, 50) → Linear(50, 2)
TensorFlow 测试结果 测试准确率 96.77%(30/31)
PyTorch 测试结果 最高测试准确率 90.3% ;第 50 轮最终准确率 87.1%(27/31)

本实验分别使用 TensorFlow 与 PyTorch 完成同一份心脏病数据的二分类训练,并记录数据处理、模型构建、训练过程和评估结果。两套实现的训练策略不同,因此文末会单独说明比较时需要注意的条件。

2. 环境

  • 语言环境:Python 3.12.13
  • 编译器:Jupyter Notebook
  • TensorFlow:TensorFlow 2.21.0,运行设备为 CPU(未检测到 GPU)
  • PyTorch:运行设备为 CPU
  • 数据处理:pandas、NumPy、scikit-learn
  • 可视化:Matplotlib、seaborn

3. 代码实现

3.1 前期准备

3.1.1 导入库与设置随机种子

TensorFlow 实现固定随机种子,保证数据划分和模型初始化尽量可复现;同时检测可用 GPU,若存在则启用按需分配显存。

python 复制代码
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout

import pandas as pd
import numpy as np
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt

tf.keras.utils.set_random_seed(42)

gpus = tf.config.list_physical_devices("GPU")
if gpus:
    gpu0 = gpus[0]
    tf.config.experimental.set_memory_growth(gpu0, True)
    tf.config.set_visible_devices([gpu0], "GPU")

PyTorch 实现使用 torch.cuda.is_available() 自动选择运行设备。本次运行结果为 device(type='cpu')

python 复制代码
import numpy as np
import pandas as pd
import torch
from torch import nn
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from torch.utils.data import TensorDataset, DataLoader

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

3.1.2 数据加载与预处理

数据包含年龄、性别、胸痛类型、静息血压、胆固醇、最大心率等 13 个输入特征,target 为二分类标签。原始数据没有缺失值。

TensorFlow 实现先按类别分层划分数据,再只以训练集拟合标准化器,避免测试集信息进入训练过程。最终训练集为 272 条、测试集为 31 条。

python 复制代码
df = pd.read_csv("./Data/heart.csv")
X = df.iloc[:, :-1]
y = df.iloc[:, -1]

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.1, random_state=1, stratify=y
)

sc = StandardScaler()
X_train = sc.fit_transform(X_train)
X_test = sc.transform(X_test)

PyTorch 实现将标准化后的特征转为 float32 张量、标签转为 int64 张量,并扩展为 (样本数, 1, 13),因此 RNN 接收到的是一个时间步、13 个特征的输入。该实现得到的训练集形状为 torch.Size([272, 1, 13]),标签形状为 torch.Size([272])

python 复制代码
X = df.iloc[:, :-1]
y = df.iloc[:, -1]

sc = StandardScaler()
X = sc.fit_transform(X)

X = torch.tensor(np.array(X), dtype=torch.float32)
y = torch.tensor(np.array(y), dtype=torch.int64)

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.1, random_state=1
)
X_train = X_train.unsqueeze(1)
X_test = X_test.unsqueeze(1)

train_dl = DataLoader(TensorDataset(X_train, y_train), batch_size=64, shuffle=False)
test_dl = DataLoader(TensorDataset(X_test, y_test), batch_size=64, shuffle=False)

注意:PyTorch 的原始运行过程在划分前对完整特征矩阵做了标准化,且未使用分层划分;这一点会影响其与 TensorFlow 结果的严格可比性,详见第 5 节。

3.2 模型建立与训练

3.2.1 TensorFlow 全连接网络

心脏病数据属于结构化表格数据,TensorFlow 使用轻量级全连接网络建模,而不是将独立特征当作时间序列。网络只有 361 个可训练参数:隐藏层使用 ReLU 激活与 L2 正则化,Dropout(0.2) 随机失活部分隐藏单元,以缓解小样本场景下的过拟合;输出层使用 Sigmoid 给出患病概率。

组件 配置 作用
输入层 13 个标准化特征 接收每位受试者的表格特征
隐藏层 Dense(24, relu) + L2(5e-3) 学习非线性特征组合,并限制权重过大
正则化层 Dropout(0.2) 降低特征共适应,缓解过拟合
输出层 Dense(1, sigmoid) 输出二分类概率
python 复制代码
model = Sequential([
    tf.keras.layers.Input(shape=(X_train.shape[1],)),
    Dense(24, activation='relu',
          kernel_regularizer=tf.keras.regularizers.l2(5e-3)),
    Dropout(0.2),
    Dense(1, activation='sigmoid'),
])
model.summary()

训练使用 Adam 优化器和二元交叉熵损失。训练轮数上限为 400,但通过 EarlyStopping 监控验证损失:若连续 10 轮改善不足 1e-3,则停止训练并恢复验证损失最低时的权重。

python 复制代码
opt = tf.keras.optimizers.Adam(learning_rate=2e-3)
model.compile(loss='binary_crossentropy', optimizer=opt, metrics=['accuracy'])

early_stopping = tf.keras.callbacks.EarlyStopping(
    monitor='val_loss', patience=10, min_delta=1e-3,
    restore_best_weights=True
)

history = model.fit(
    X_train, y_train, validation_split=0.2, epochs=400,
    batch_size=32, callbacks=[early_stopping], verbose=1
)

3.2.2 PyTorch RNN 分类器

PyTorch 模型以单步 RNN 编码 13 个特征,随后使用两个全连接层完成二分类。RNN 的隐藏状态维度为 200,输出层为两个 logits,故训练时搭配 CrossEntropyLoss,无需额外添加 Softmax 层。

python 复制代码
class ModelRNN(nn.Module):
    def __init__(self):
        super().__init__()
        self.rnn0 = nn.RNN(input_size=13, hidden_size=200,
                           num_layers=1, batch_first=True)
        self.fc0 = nn.Linear(200, 50)
        self.fc1 = nn.Linear(50, 2)

    def forward(self, x):
        out, _ = self.rnn0(x)
        out = out[:, -1, :]
        out = self.fc0(out)
        return self.fc1(out)

model = ModelRNN().to(device)

训练阶段对每个 mini-batch 完成前向传播、损失计算、反向传播和参数更新;测试阶段使用 torch.no_grad(),仅计算准确率与损失。

python 复制代码
loss_fn = nn.CrossEntropyLoss()
opt = torch.optim.Adam(model.parameters(), lr=1e-4)
epochs = 50

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)

4. 模型评估

4.1 TensorFlow 训练过程与最终评估

EarlyStopping 在第 26 轮停止训练,并恢复第 17 轮附近的最佳验证权重。训练准确率与验证准确率在最佳轮次分别约为 87.1% 和 85.5%,验证损失最低约为 0.445;训练曲线没有出现持续扩大的训练/验证差距。

最终在从未参与训练或验证的 31 条测试数据上评估:

python 复制代码
test_loss, test_accuracy = model.evaluate(X_test, y_test, verbose=0)
print(f"Test accuracy: {test_accuracy:.2%}")
bash 复制代码
Test accuracy: 96.77%

4.2 PyTorch 训练过程与预测结果

PyTorch 共训练 50 轮。测试准确率在第 12 至 15 轮达到最高的 90.3%,随后维持在 87.1%;与此同时,测试损失在后期继续下降,说明准确率这一离散指标在仅 31 条测试样本上会产生波动。

轮次 训练准确率 训练损失 测试准确率 测试损失
1 46.0% 0.692 64.5% 0.670
12 80.1% 0.575 90.3% 0.505
15 82.4% 0.542 90.3% 0.460
50 84.9% 0.358 87.1% 0.284

第 50 轮输出的混淆矩阵为 [[13, 3], [1, 14]]:16 个真实负类中有 13 个识别正确,15 个真实正类中有 14 个识别正确。最终准确率为 (13 + 14) / 31 = 87.1%

对单条测试样本的预测结果为类别 0,其含义为"不会患心脏病"。

4.3 TensorFlow 与 PyTorch 结果对比

维度 TensorFlow PyTorch
模型 正则化全连接网络 单步 RNN + 两层全连接网络
训练集 / 测试集 272 / 31,分层划分 272 / 31,随机划分
标准化 仅由训练集拟合 划分前由完整特征矩阵拟合
过拟合控制 L2、Dropout、EarlyStopping 未使用 EarlyStopping 或最佳权重保存
最佳测试准确率 96.77% 90.3%
最终轮测试准确率 96.77%(恢复最佳权重) 87.1%(第 50 轮)

5. 总结

  1. TensorFlow 的正则化全连接网络表现最好 :在 31 条独立测试样本上得到 96.77% 准确率。该模型参数量小,L2 正则化、Dropout 和 EarlyStopping 有助于控制小数据集上的过拟合。

  2. PyTorch RNN 已具备有效的分类能力 :训练中最高测试准确率达到 90.3%,最终混淆矩阵显示其对正类的识别较好(14/15),但第 50 轮未保留最高准确率对应的模型权重,因此最终准确率回落到 87.1%。

  3. 两组结果不宜直接作严格的横向优劣判断:两者的划分方式和标准化时机不同。若要公平比较,应统一为"先分层划分、只在训练集拟合标准化器、固定随机种子",并让两种实现使用相同的训练/验证/测试索引。

  4. 测试集规模较小:测试集仅有 31 条样本,单个样本就会带来约 3.23 个百分点的准确率变化。后续建议使用交叉验证,并报告准确率的均值与标准差。

  5. 后续优化方向:对于 PyTorch,可在验证集指标最佳时保存模型、加入 EarlyStopping 与权重衰减;对于两种实现,都可以尝试特征选择、学习率调度和交叉验证,以获得更稳定的泛化评估。

相关推荐
2601_956319881 小时前
2026年用示例、拆解和练习提升量化理解效率
人工智能·python
王志来137944730082 小时前
从分散到集成:工控服务器机箱采购如何实现“一站式”破局
运维·服务器·人工智能·python
三十岁老牛再出发2 小时前
08.18每日总结
c++·python·numpy·pandas
️学习的小王2 小时前
Anaconda国内镜像配置与conda常用命令
ide·经验分享·python·conda
Zane19942 小时前
daemon 线程说没就没?一文讲透 threading 的适用场景与线程安全
后端·python
up up day2 小时前
Python enchant 模块使用教程
python
zoujiahui_20182 小时前
Python 包与环境管理工具 uv
开发语言·python·uv
程序猿阿森3 小时前
Python 闭包与装饰器:从入门到精通
开发语言·python·面试
老大白菜3 小时前
Qwen3.8-27B 本地推理 + DeepSeek Harness 配置
python·qwen·deepseek·harness