🍨 本文为🔗365天深度学习训练营中的学习记录博客
- 🍖 原作者:K同学啊
1.检查GPU
import numpy as np
import pandas as pd
import torch
from torch import nn
import torch.nn.functional as F
import seaborn as sns
#设置GPU训练,也可以使用CPU
device=torch.device("cuda" if torch.cuda.is_available() else "cpu")
device
2.查看数据
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import torch
import torch.nn as nn
from torch.utils.data import DataLoader, TensorDataset
plt.rcParams["font.sans-serif"] = ["Microsoft YaHei"] # 显示中文
plt.rcParams['axes.unicode_minus'] = False # 显示负号
data_df = pd.read_csv("data/alzheimers_disease_data.csv")
data_df.head()
# 标签中文化
data_df.rename(columns={ "Age": "年龄", "Gender": "性别", "Ethnicity": "种族", "EducationLevel": "教育水平", "BMI": "身体质量指数(BMI)", "Smoking": "吸烟状况", "AlcoholConsumption": "酒精摄入量", "PhysicalActivity": "体育活动时间", "DietQuality": "饮食质量评分", "SleepQuality": "睡眠质量评分", "FamilyHistoryAlzheimers": "家族阿尔茨海默病史", "CardiovascularDisease": "心血管疾病", "Diabetes": "糖尿病", "Depression": "抑郁症史", "HeadInjury": "头部受伤", "Hypertension": "高血压", "SystolicBP": "收缩压", "DiastolicBP": "舒张压", "CholesterolTotal": "胆固醇总量", "CholesterolLDL": "低密度脂蛋白胆固醇(LDL)", "CholesterolHDL": "高密度脂蛋白胆固醇(HDL)", "CholesterolTriglycerides": "甘油三酯", "MMSE": "简易精神状态检查(MMSE)得分", "FunctionalAssessment": "功能评估得分", "MemoryComplaints": "记忆抱怨", "BehavioralProblems": "行为问题", "ADL": "日常生活活动(ADL)得分", "Confusion": "混乱与定向障碍", "Disorientation": "迷失方向", "PersonalityChanges": "人格变化", "DifficultyCompletingTasks": "完成任务困难", "Forgetfulness": "健忘", "Diagnosis": "诊断状态", "DoctorInCharge": "主诊医生" },inplace=True)
data_df.columns
data_df.isnull().sum()
from sklearn.preprocessing import LabelEncoder
# 创建 LabelEncoder 实例
label_encoder = LabelEncoder()
# 对非数值型列进行标签编码
data_df['主诊医生'] = label_encoder.fit_transform(data_df['主诊医生'])
data_df.head()
# 计算是否患病, 人数
counts = data_df["诊断状态"].value_counts()
# 计算百分比
sizes = counts / counts.sum() * 100
# 绘制环形图
fig, ax = plt.subplots()
wedges, texts, autotexts = ax.pie(sizes, labels=sizes.index, autopct='%1.2ff%%', startangle=90, wedgeprops=dict(width=0.3))
plt.title("患病占比(1患病,0没有患病)")
plt.show()
plt.figure(figsize=(40, 35))
sns.heatmap(data_df.corr(), annot=True, fmt=".2f")
plt.show()
data_df['年龄'].min(), data_df['年龄'].max()
# 计算每一个年龄段患病人数
age_bins = range(60, 91)
grouped = data_df.groupby('年龄').agg({'诊断状态': ['sum', 'size']}) # 分组、聚合函数: sum求和,size总大小
grouped.columns = ['患病', '总人数']
grouped['不患病'] = grouped['总人数'] - grouped['患病'] # 计算不患病的人数
# 设置绘图风格
sns.set(style="whitegrid")
plt.figure(figsize=(12, 5))
# 获取x轴标签(即年龄)
x = grouped.index.astype(str) # 将年龄转换为字符串格式便于显示
# 画图
plt.bar(x, grouped["不患病"], 0.35, label="不患病", color='skyblue')
plt.bar(x, grouped["患病"], 0.35, label="患病", color='salmon')
# 设置标题
plt.title("患病年龄分布", fontproperties='Microsoft YaHei')
plt.xlabel("年龄", fontproperties='Microsoft YaHei')
plt.ylabel("人数", fontproperties='Microsoft YaHei')
# 如果需要对图例也应用相同的字体
plt.legend(prop={'family': 'Microsoft YaHei'})
# 展示
plt.tight_layout()
plt.show()
3.特征选择
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import classification_report
data = data_df.copy()
X = data_df.iloc[:, 1:-2]
y = data_df.iloc[:, -2]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 标准化
sc = StandardScaler()
X_train = sc.fit_transform(X_train)
X_test = sc.transform(X_test)
# 模型创建
tree = DecisionTreeClassifier()
tree.fit(X_train, y_train)
pred = tree.predict(X_test)
reporter = classification_report(y_test, pred)
print(reporter)
# Set a font that supports CJK characters (e.g., SimHei for Chinese on Windows)
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'Noto Sans CJK JP'] # Choose appropriate fonts
# Disable the "unicode minus" setting to avoid rendering issues
plt.rcParams['axes.unicode_minus'] = False
# 特征展示
feature_importances = tree.feature_importances_
features_rf = pd.DataFrame({'特征': X.columns, '重要度': feature_importances})
features_rf.sort_values(by='重要度', ascending=False, inplace=True)
plt.figure(figsize=(20, 10))
sns.barplot(x='重要度', y='特征', data=features_rf)
plt.xlabel('重要度')
plt.ylabel('特征')
plt.title('随机森林特征图')
plt.show()
from sklearn.feature_selection import RFE
# 使用 RFE 来选择特征
rfe_selector = RFE(estimator=tree, n_features_to_select=20) # 选择前20个特征
rfe_selector.fit(X, y)
X_new = rfe_selector.transform(X)
feature_names = np.array(X.columns)
selected_feature_names = feature_names[rfe_selector.support_]
print(selected_feature_names)
feature_selection = ['年龄', '种族','教育水平','身体质量指数(BMI)', '酒精摄入量', '体育活动时间', '饮食质量评分', '睡眠质量评分', '心血管疾病',
'收缩压', '舒张压', '胆固醇总量', '低密度脂蛋白胆固醇(LDL)', '高密度脂蛋白胆固醇(HDL)', '甘油三酯',
'简易精神状态检查(MMSE)得分', '功能评估得分', '记忆抱怨', '行为问题', '日常生活活动(ADL)得分']
X = data_df[feature_selection]
# 标准化
sc = StandardScaler()
X = sc.fit_transform(X)
X = torch.tensor(np.array(X), dtype=torch.float32)
y = torch.tensor(np.array(y), dtype=torch.long)
# 再次进行特征选择
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
X_train.shape, y_train.shape
test_X = X_test[0].reshape(1, -1) # X_test[0]即我们的输入数据
pred = model(test_X.to(device)).argmax(1).item()
print("模型预测结果为:",pred)
print("=="*20)
print("0:未患病")
print("1:已患病")
4.创建模型与编译训练
batch_size = 32
train_dl = DataLoader(
TensorDataset(X_train, y_train),
batch_size=batch_size,
shuffle=True
)
test_dl = DataLoader(
TensorDataset(X_test, y_test),
batch_size=batch_size,
shuffle=False
)
class Rnn_Model(nn.Module):
def __init__(self):
super().__init__()
# 调用rnn
self.rnn = nn.RNN(input_size=20, hidden_size=200, num_layers=1, batch_first=True)
self.fc1 = nn.Linear(200, 50)
self.fc2 = nn.Linear(50, 2)
def forward(self, x):
x, hidden1 = self.rnn(x)
x = self.fc1(x)
x = self.fc2(x)
return x
# 数据不大,cpu即可
device = "cpu"
model = Rnn_Model().to(device)
model
model(torch.randn(32, 20)).shape
5.编译及训练模型
def train(data, model, loss_fn, opt):
size = len(data.dataset)
batch_num = len(data)
train_loss, train_acc = 0.0, 0.0
for X, y in data:
X, y = X.to(device), y.to(device)
pred = model(X)
loss = loss_fn(pred, y)
# 反向传播
opt.zero_grad() # 梯度清零
loss.backward() # 求导
opt.step() # 设置梯度
train_loss += loss.item()
train_acc += (pred.argmax(1) == y).type(torch.float).sum().item()
train_loss /= batch_num
train_acc /= size
return train_acc, train_loss
def test(data, model, loss_fn):
size = len(data.dataset)
batch_num = len(data)
test_loss, test_acc = 0.0, 0.0
with torch.no_grad():
for X, y in data:
X, y = X.to(device), y.to(device)
pred = model(X)
loss = loss_fn(pred, y)
test_loss += loss.item()
test_acc += (pred.argmax(1) == y).type(torch.float).sum().item()
test_loss /= batch_num
test_acc /= size
return test_acc, test_loss
loss_fn = nn.CrossEntropyLoss() # 损失函数
learn_lr = 1e-4 # 超参数
optimizer = torch.optim.Adam(model.parameters(), lr=learn_lr) # 优化器
train_acc = []
train_loss = []
test_acc = []
test_loss = []
epoches = 50
for i in range(epoches):
model.train()
epoch_train_acc, epoch_train_loss = train(train_dl, model, loss_fn, optimizer)
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)
# 输出
template = ('Epoch:{:2d}, Train_acc:{:.1f}%, Train_loss:{:.3f}, Test_acc:{:.1f}%, Test_loss:{:.3f}')
print(template.format(i + 1, epoch_train_acc*100, epoch_train_loss, epoch_test_acc*100, epoch_test_loss))
print("Done")
6.结果可视化
import matplotlib.pyplot as plt
#隐藏警告
import warnings
warnings.filterwarnings("ignore") #忽略警告信息
from datetime import datetime
current_time = datetime.now() # 获取当前时间
epochs_range = range(epoches)
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 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= Loss')
plt.show()
from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay
pred = model(X_test.to(device)).argmax(1).cpu().numpy()
# 计算混淆矩阵
cm = confusion_matrix(y_test, pred)
# 计算
plt.figure(figsize=(6, 5))
sns.heatmap(cm, annot=True, fmt="d", cmap="Blues")
# 标题
plt.title("混淆矩阵")
plt.xlabel("Predicted Label")
plt.ylabel("True Label")
plt.tight_layout() # 自适应
plt.show()

总结:
1. 数据准备与预处理
- 导入库:主要使用了
pandas
、numpy
、matplotlib
、seaborn
、sklearn
和torch
进行数据处理和建模。 - 读取数据:从
data/alzheimers_disease_data.csv
加载数据集,并对列名进行了中文化重命名以便于理解。 - 缺失值检查:通过
isnull().sum()
检查是否存在缺失值。 - 标签编码:对“主诊医生”字段使用
LabelEncoder
进行编码,将其转换为数值型数据。 - 数据可视化:
- 绘制饼图展示患病比例(1为患病,0为未患病)。
- 使用热力图分析各特征之间的相关性。
- 绘制柱状图展示不同年龄段的患病情况。
2. 特征选择与处理
- 划分训练集和测试集:使用
train_test_split
将数据划分为训练集和测试集(8:2)。 - 标准化处理:使用
StandardScaler
对数据进行标准化,以提高模型性能。 - 特征重要性评估:
- 使用决策树(
DecisionTreeClassifier
)计算特征重要性,并绘制条形图展示最重要的特征。 - 使用递归特征消除(RFE)方法选择前20个关键特征。
- 使用决策树(
- 最终特征选择:选择了20个最具代表性的特征用于后续建模。
3. 构建RNN模型
-
定义RNN模型:
class Rnn_Model(nn.Module): def __init__(self): super().__init__() self.rnn = nn.RNN(input_size=20, hidden_size=200, num_layers=1, batch_first=True) self.fc1 = nn.Linear(200, 50) self.fc2 = nn.Linear(50, 2) def forward(self, x): x, hidden1 = self.rnn(x) x = self.fc1(x) x = self.fc2(x) return x
模型结构:
- 输入层:20维特征
- 隐藏层:200个隐藏单元
- 全连接层:输出2类(0:未患病,1:已患病)
-
数据加载器:使用
DataLoader
将数据封装成批次,便于训练时使用。
4. 训练与评估模型
- 损失函数:使用交叉熵损失函数
CrossEntropyLoss
。 - 优化器:使用
Adam
优化器,学习率为1e-4
。 - 训练过程:
- 定义了
train()
和test()
函数,分别用于训练和验证模型。 - 在每一轮训练后,记录训练和测试的准确率与损失。
- 总共训练50个epoch。
- 定义了
- 结果输出:
- 输出每个epoch的训练和测试准确率、损失值。
5. 可视化与评估
- 准确率和损失曲线:
- 使用
matplotlib
绘制了训练和测试的准确率和损失变化趋势。
- 使用
- 混淆矩阵:
- 使用
confusion_matrix
计算模型在测试集上的预测结果与真实标签的对比。 - 绘制热力图展示混淆矩阵,直观评估模型表现。
- 使用
6. 结果分析
- 模型预测示例:
展示了单个样本的预测结果。pred = model(test_X.to(device)).argmax(1).item() print("模型预测结果为:",pred) print("=="*20) print("0:未患病") print("1:已患病")