1.前期准备工作
1.1.设置硬件设备
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
输出:
device(type='cuda')
1.2.导入数据
df=pd.read_csv("E:/DATABASE/RNN/R3/heart.csv")
df
输出:
2.构建数据集
2.1.标准化
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_splitx=df.iloc[:,:-1]
y=df.iloc[:,-1]# 将每一列特征标准化为正态分布,注意,标准化是针对每一列而言的
sc=StandardScaler()
x=sc.fit_transform(x)
x.shape,y.shape
输出:
((303, 13), (303,))
2.2.划分数据集
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.shape,y_train.shape
输出:
(torch.Size([272, 13]), torch.Size([272]))
2.3.构建数据加载器
from torch.utils.data import TensorDataset,DataLoadertrain_dl=DataLoader(TensorDataset(x_train,y_train),batch_size=64,shuffle=True)
test_dl=DataLoader(TensorDataset(x_test,y_test),batch_size=64,shuffle=False)
3.模型训练
3.1.构建模型
class model_rnn(nn.Module):def __init__(self):super(model_rnn,self).__init__()self.rnn0=nn.RNN(input_size=13,hidden_size=200,num_layers=1,batch_first=True)self.fc0=nn.Linear(200,64)self.fc1=nn.Linear(64,2)def forward(self,x):out,hidden1=self.rnn0(x)out=self.fc0(out)out=self.fc1(out)return outmodel=model_rnn().to(device)
model
输出:
model_rnn((rnn0): RNN(13, 200, batch_first=True)(fc0): Linear(in_features=200, out_features=64, bias=True)(fc1): Linear(in_features=64, out_features=2, bias=True)
)
查看模型的输出数据集格式:
model(torch.rand(30,13).to(device)).shape
输出:
torch.Size([30, 2])
3.2.定义训练函数
# 训练循环
def train(dataloader,model,loss_fn,optimizer):size=len(dataloader.dataset) # 训练集的大小num_batches=len(dataloader) # 批次数目,(size/batch_size,向上取整)train_loss,train_acc=0,0 # 初始化训练损失和正确率for x,y in dataloader: # 获取图片及其标签x,y=x.to(device),y.to(device)# 计算预测误差pred=model(x) # 网络输出loss=loss_fn(pred,y) # 计算网络输出和真实值之间的差距,targets为真实值,计算二者差值即为损失# 反向传播optimizer.zero_grad() # grad属性归零loss.backward() # 反向传播optimizer.step() # 每一步自动更新# 记录acc与losstrain_acc+=(pred.argmax(1)==y).type(torch.float).sum().item()train_loss+=loss.item()train_acc/=sizetrain_loss/=num_batchesreturn train_acc,train_loss
3.3.定义测试函数
# 测试函数
def 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
3.4.正式训练模型
loss_fn=nn.CrossEntropyLoss() # 创建损失函数
learn_rate=1e-4 # 学习率
opt=torch.optim.Adam(model.parameters(),lr=learn_rate)
epochs=50train_acc=[]
train_loss=[]
test_acc=[]
test_loss=[]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)
输出:
Epoch: 1,Train_acc:48.2%,Train_loss:0.699,Test_acc:54.8%,Test_loss:0.690,Lr:1.00E-04
Epoch: 2,Train_acc:57.4%,Train_loss:0.688,Test_acc:74.2%,Test_loss:0.675,Lr:1.00E-04
Epoch: 3,Train_acc:63.6%,Train_loss:0.674,Test_acc:77.4%,Test_loss:0.661,Lr:1.00E-04
Epoch: 4,Train_acc:73.2%,Train_loss:0.659,Test_acc:87.1%,Test_loss:0.648,Lr:1.00E-04
Epoch: 5,Train_acc:73.2%,Train_loss:0.650,Test_acc:90.3%,Test_loss:0.634,Lr:1.00E-04
Epoch: 6,Train_acc:76.1%,Train_loss:0.636,Test_acc:90.3%,Test_loss:0.620,Lr:1.00E-04
Epoch: 7,Train_acc:75.0%,Train_loss:0.630,Test_acc:90.3%,Test_loss:0.607,Lr:1.00E-04
Epoch: 8,Train_acc:78.3%,Train_loss:0.614,Test_acc:87.1%,Test_loss:0.592,Lr:1.00E-04
Epoch: 9,Train_acc:77.6%,Train_loss:0.602,Test_acc:87.1%,Test_loss:0.577,Lr:1.00E-04
Epoch:10,Train_acc:80.5%,Train_loss:0.595,Test_acc:90.3%,Test_loss:0.562,Lr:1.00E-04
Epoch:11,Train_acc:79.0%,Train_loss:0.584,Test_acc:90.3%,Test_loss:0.546,Lr:1.00E-04
Epoch:12,Train_acc:79.4%,Train_loss:0.565,Test_acc:90.3%,Test_loss:0.531,Lr:1.00E-04
Epoch:13,Train_acc:80.9%,Train_loss:0.558,Test_acc:90.3%,Test_loss:0.515,Lr:1.00E-04
Epoch:14,Train_acc:79.8%,Train_loss:0.550,Test_acc:90.3%,Test_loss:0.500,Lr:1.00E-04
Epoch:15,Train_acc:82.4%,Train_loss:0.529,Test_acc:90.3%,Test_loss:0.485,Lr:1.00E-04
Epoch:16,Train_acc:80.9%,Train_loss:0.537,Test_acc:90.3%,Test_loss:0.471,Lr:1.00E-04
Epoch:17,Train_acc:81.2%,Train_loss:0.522,Test_acc:90.3%,Test_loss:0.458,Lr:1.00E-04
Epoch:18,Train_acc:80.9%,Train_loss:0.515,Test_acc:90.3%,Test_loss:0.446,Lr:1.00E-04
Epoch:19,Train_acc:82.4%,Train_loss:0.503,Test_acc:90.3%,Test_loss:0.432,Lr:1.00E-04
Epoch:20,Train_acc:81.6%,Train_loss:0.494,Test_acc:90.3%,Test_loss:0.419,Lr:1.00E-04
Epoch:21,Train_acc:81.6%,Train_loss:0.499,Test_acc:90.3%,Test_loss:0.405,Lr:1.00E-04
Epoch:22,Train_acc:84.6%,Train_loss:0.470,Test_acc:90.3%,Test_loss:0.394,Lr:1.00E-04
Epoch:23,Train_acc:82.0%,Train_loss:0.475,Test_acc:90.3%,Test_loss:0.383,Lr:1.00E-04
Epoch:24,Train_acc:82.7%,Train_loss:0.464,Test_acc:90.3%,Test_loss:0.374,Lr:1.00E-04
Epoch:25,Train_acc:82.4%,Train_loss:0.448,Test_acc:90.3%,Test_loss:0.365,Lr:1.00E-04
Epoch:26,Train_acc:82.4%,Train_loss:0.438,Test_acc:90.3%,Test_loss:0.356,Lr:1.00E-04
Epoch:27,Train_acc:83.8%,Train_loss:0.418,Test_acc:90.3%,Test_loss:0.350,Lr:1.00E-04
Epoch:28,Train_acc:81.2%,Train_loss:0.448,Test_acc:90.3%,Test_loss:0.344,Lr:1.00E-04
Epoch:29,Train_acc:82.7%,Train_loss:0.402,Test_acc:90.3%,Test_loss:0.340,Lr:1.00E-04
Epoch:30,Train_acc:84.9%,Train_loss:0.412,Test_acc:90.3%,Test_loss:0.336,Lr:1.00E-04
Epoch:31,Train_acc:84.2%,Train_loss:0.394,Test_acc:90.3%,Test_loss:0.333,Lr:1.00E-04
Epoch:32,Train_acc:83.8%,Train_loss:0.420,Test_acc:90.3%,Test_loss:0.330,Lr:1.00E-04
Epoch:33,Train_acc:85.3%,Train_loss:0.400,Test_acc:90.3%,Test_loss:0.326,Lr:1.00E-04
Epoch:34,Train_acc:85.3%,Train_loss:0.407,Test_acc:90.3%,Test_loss:0.325,Lr:1.00E-04
Epoch:35,Train_acc:81.6%,Train_loss:0.420,Test_acc:90.3%,Test_loss:0.322,Lr:1.00E-04
Epoch:36,Train_acc:83.8%,Train_loss:0.399,Test_acc:90.3%,Test_loss:0.321,Lr:1.00E-04
Epoch:37,Train_acc:84.6%,Train_loss:0.394,Test_acc:90.3%,Test_loss:0.318,Lr:1.00E-04
Epoch:38,Train_acc:83.5%,Train_loss:0.358,Test_acc:90.3%,Test_loss:0.317,Lr:1.00E-04
Epoch:39,Train_acc:83.5%,Train_loss:0.405,Test_acc:90.3%,Test_loss:0.317,Lr:1.00E-04
Epoch:40,Train_acc:83.1%,Train_loss:0.382,Test_acc:87.1%,Test_loss:0.317,Lr:1.00E-04
Epoch:41,Train_acc:84.9%,Train_loss:0.357,Test_acc:87.1%,Test_loss:0.314,Lr:1.00E-04
Epoch:42,Train_acc:83.5%,Train_loss:0.407,Test_acc:87.1%,Test_loss:0.311,Lr:1.00E-04
Epoch:43,Train_acc:84.9%,Train_loss:0.402,Test_acc:90.3%,Test_loss:0.309,Lr:1.00E-04
Epoch:44,Train_acc:83.1%,Train_loss:0.360,Test_acc:90.3%,Test_loss:0.307,Lr:1.00E-04
Epoch:45,Train_acc:84.9%,Train_loss:0.368,Test_acc:90.3%,Test_loss:0.306,Lr:1.00E-04
Epoch:46,Train_acc:84.9%,Train_loss:0.380,Test_acc:90.3%,Test_loss:0.307,Lr:1.00E-04
Epoch:47,Train_acc:84.2%,Train_loss:0.362,Test_acc:90.3%,Test_loss:0.308,Lr:1.00E-04
Epoch:48,Train_acc:83.8%,Train_loss:0.351,Test_acc:90.3%,Test_loss:0.307,Lr:1.00E-04
Epoch:49,Train_acc:84.2%,Train_loss:0.372,Test_acc:90.3%,Test_loss:0.305,Lr:1.00E-04
Epoch:50,Train_acc:83.8%,Train_loss:0.418,Test_acc:90.3%,Test_loss:0.303,Lr:1.00E-04
==================== Done ====================
4.模型评估
4.1.Loss与Accuracy图
import matplotlib.pyplot as plt
from datetime import datetime
# 隐藏警告
import warnings
warnings.filterwarnings("ignore") # 忽略警告信息current_time=datetime.now() # 获取当前时间plt.rcParams['font.sans-serif']=['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus']=False # 用来正常显示负号
plt.rcParams['figure.dpi']=300 # 分辨率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.2.混淆矩阵
print("="*20,'输入数据shape为',"="*20)
print("x_test.shape:",x_test.shape)
print("y_test.shape:",y_test.shape)pred=model(x_test.to(device)).argmax(1).cpu().numpy()print("="*20,'输出数据shape为',"="*20)
print("pred.shape:",pred.shape)
输出:
==================== 输入数据shape为 ====================
x_test.shape: torch.Size([31, 13])
y_test.shape: torch.Size([31])
==================== 输出数据shape为 ====================
pred.shape: (31,)
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import confusion_matrix,ConfusionMatrixDisplay# 计算混淆矩阵
cm=confusion_matrix(y_test,pred)plt.figure(figsize=(6,5))
plt.suptitle('')
sns.heatmap(cm,annot=True,fmt="d",cmap="Blues")# 修改字体大小
plt.xticks(fontsize=10)
plt.yticks(fontsize=10)
plt.title("Confusion Matrix",fontsize=12)
plt.xlabel("Predicted Label",fontsize=10)
plt.ylabel("True Label",fontsize=10)# 显示图
plt.tight_layout() # 调整布局防止重叠
plt.show()
输出:
5.心得体会
原模型中全连接层本为
self.fc0=nn.Linear(200,50)
self.fc1=nn.Linear(50,2)
运行后准确率一直卡在87%无法提升,后将全连接层结构修改为 (200,64),运行后准确率达到90%,满足开始的89%以上的要求。