根据美国一项COVID-19的调查40个州,4种COVID可能的症状,8个日常的行为,5个心里方面的状况,和是否呈阳性。现在有连续三天的调查资料作为训练资料,测试资料为根据前两天的资料预测第三天的阳性情况。
训练资料如图:
测试资料:缺第三天的阳性情况,需要进行预测
1.构建神经网络
# 导入torch神经网络工具包
import torch
import torch.nn as nn
# 构建一个简单神经网络
# torch.nn.Sequential是一个Sequential容器,模块将按照构造函数中传递的顺序添加到模块中。
def myModel(input_dim):
model=nn.Sequential(
nn.Linear(input_features=input_dim,output_features=64),
nn.Relu(),
nn.Linear(input_features=64,out_features=1)
)
这样就构建了一个三层的神经网络,其中的64是依据经验设定,第一层为线性层,根据模型的输入特征值,输出64个特征值,第二层为普通的激活函数层,第三层为线性层,输入64个特征值,输出为1,这里指输出为第三天的阳性情况,因此必须是1个特征。
2神经网络的输入
pytorch数据读取的核心是torch.utils.data.DataLoader类。即如果使用pytorch构建神经网络时,那么DNN中传入的数据类型就是DataLoader类。
from torch.utils.data import Dataloader
import torch.utils.data as data
def pre_Dataloader():
dataloader=Dataloader(
dataset=dataset, batch_size=batch_size,
shuffle=(mode == 'train'), drop_last=False,
num_workers=0, pin_memory=True)
# 这里直接输入表格内的数据是有问题的因此,需要先构建一个数据预处理方法使其能够满足于dataloader的数据输入,因此需要继承dataset作为父类,才能输入到dataloader中
# 在训练集和测试集的数据略有不同,因此加一个mode,使下面方法同时都适用
# 字典型数据集是指实现了__getitem__()和__len__()协议,表示从索引到数据样本的映射。
# 需要初始化三个协议
class COVID19Dataset(data.Dataset):
def __int__(self):
# 用于初始化数据
data=xxx
self.data=torch.FloatTensor(data)
def __getitem__(self, index):
# 用于字典型的数据实现,根据标签返回数值
return self.data[index],self.target[index]
def __len__():
return len(self.data)
具体操作如下:
import torch.utils.data as data
import csv
class COVID19Dataset(data.dataset):
def __init__(self,path,mode='train',):
self.mode= mode
with open(path,'r') as fp:
data=list(csv.read(fp)):
# 去掉行标题
data=np.array(data[1:])
# 转换为浮点型
data=data.astype(float)
if mode=='test':
data=data[:,list(range(93))]
self.data=torch.FloatTensor(data)
else:
data=data[:,list(range(93))]
target=data[:,-1]
# 将训练数据分为train data 和Validation data
# 此次分割数据是每十个数据的前9个为训练集,一个验证集进行分割
indices = []
if mode == 'train':
for i in range(len(data)):
if i % 10 != 0:
indices.append(i)
elif mode == 'validation':
for i in range(len(data)):
if i % 10 == 0:
indices.append(i)
self.data=torch.FloatTensor(data[indices])
self.target=torch.FloatTensor(data[indices])
3.进行训练
1.准备一组含有未知参数的公式y=f_θ (x),即上文所构建的DNN网络。2.将数据带入函数中计算得到预测结果y,且已知正确的结果是y^^。3此时需要一个量来评价预测结果和正确结果之间的差值,即损失函数。损失函数越小表明,预测值和真实值之间差异越小。4.为使得损失函数最小,需要不断更新参数θ的值,此时即需要一个优化函数对参数进行更新,使得损失函数的值最小。最终经过反复迭代,得到一个相对合适的θ值,即完成网络的训练。
因此,在训练之前需要定义一些参数:
定义损失函数
loss=nn.MSELoss()
定义优化函数
optimizer=torch.optim.SGD()
定义迭代次数,以及记录迭代时的loss和accuracy的值的变换,并设定迭代停止条件
epoch=0
n_epochs=1000
min_mse=1000
构造训练器
def train(DNN_model,trainingDataloader, validationDataloader):
# 初始化迭代次数
epoch = 0
# 初始化模型
this_model = DNN_model
# 最大迭代次数
n_epochs = config['n_epochs']
# 定义损失函数
loss = nn.MSELoss(reduction='mean')
# 定义优化器
optimizer = torch.optim.SGD(params=this_model.parameters(), lr=0.001, momentum=0.9)
# 记录loss值
loss_record = {'train': [], 'val': []}
min_mse = 1000
while epoch<n_epochs:
this_model.train() # 这句表示开始训练,并无实际影响
for x,y in trainingDataloader:
pred=this_model(x).squeeze(1)
mse_Loss=loss(pred, y)
optimizer.zero_grad()
mse_loss.backward()
optimizer.step()
loss_record['train'].append(mse_loss.detach().item())
# 评估模型
this_model.eval()
total_loss = 0
# 禁用掉参数更新
with torch.no_grad():
for x,y in validationDataloader:
pred=this_model(x).squeeze(1)
mse_Loss=loss(pred, y)
#这里的loss是一个minBatch的平均值,因此要乘以x的长度计算出一个batch里的总值
total_loss+=mse_loss.detach.item()*len(x)
# 计算平均loss
total_loss=total_loss/len(validationDataloader.dataset)
# 完成一个迭代
# 当平均loss更小时,保存此时的模型参数
early_stop_cnt = 0
if total_loss < min_mse:
min_mse = total_loss
print("保存模型epoch{},loss{}".format(epoch+1,min_mse))
torch.save(this_model.state_dict(), config['save_path'])
early_stop_cnt =0
else:
early_stop_cnt +=1
epoch+=1
loss_record['val'].append(total_loss)
# 适当退出
if early_stop_cnt > config['early_stop']:
break
print("神经网络经过{}训练结束".format(epoch))
return min_mse, loss_record
4.保存训练结果,读取训练结果,进行测试