基于深度学习的新冠病毒人数预测的回归项目实战
·
一.概述
本项目为基于深度学习的新冠病毒人数预测的回归项目,数据有两千多份,有93个特征值,用来自学回顾,项目来源来自kaggle
链接地址:https://www.kaggle.com/competitions/ml2021spring-hw1
二.主要代码
1.获取数据集
分别获取训练集、验证集和测试集的数据,需要注意训练集和测试集,训练集的x和y分别对应1到92列和最后一列。(第0列为id)。这里把训练集分成训练集和验证集,用来判断模型的好坏,找到最好的模型。
获取数据集的函数必须严格按照父类的模式

class CovidDataset(Dataset):
def __init__(self, file_path, mode="train"): # 默认是获取训练集
self.mode = mode
with open(file_path, "r") as f: # 以只读的方式打开
ori_data = list(csv.reader(f)) # ori_data是一个迭代器,可以用来逐行读取或逐列读取,这里将其转为列表
csv_data = np.array(ori_data[1:])[:, 1:].astype(float) # 列表转为二维numpy数组
indices = [] # 用来存储和将要返回的数据下标
if mode == "train": # 训练集
# indices = [i for i in range(csv_data)] # 这行代码和下面的代码是一个意思
for i in range(len(csv_data)):
if i % 5 != 0:
indices.append(i)
x = torch.tensor(csv_data[indices, :-1]) # 获取indices这些行,再除去最后一列的要预测的值
self.y = torch.tensor(csv_data[indices, -1]) # y只有最后一列,这里的y是一维的
elif mode == "val": # 验证集
for i in range(len(csv_data)):
if i % 5 == 0:
indices.append(i)
x = torch.tensor(csv_data[indices, :-1]) # 获取indices这些行,再除去最后一列的要预测的值
self.y = torch.tensor(csv_data[indices, -1])
else: # 测试集
for i in range(len(csv_data)):
indices.append(i)
x = torch.tensor(csv_data[indices]) # 获取indices这些行,测试集取全部的列
# 对每一列的数据进行标准化,消除同一行的不同特征之间的权重影响问题
# dim=0为按列,就是对每个特征值进行操作
self.x = (x - x.mean(dim=0, keepdim=True)) / x.std(dim=0, keepdim=True)
def __getitem__(self, idx): # 给一个idx下标,返回这一行的数据
if self.mode != "test":
return self.x[idx].float(), self.y[idx].float()
else:
return self.x[idx].float()
def __len__(self): # 返回获取到的数据集的长度
return len(self.x)
train_dataset = CovidDataset(train_file, "train") # 训练集
val_dataset = CovidDataset(train_file, "val") # 验证集
test_dataset = CovidDataset(test_file, "test") # 测试集
train_loader = DataLoader(train_dataset, batch_size=16, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size=16, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=1, shuffle=False) # 测试集的batch——size一般为1 且不可以打乱,不然答案顺序就乱了
2.拿数据
train_file = "covid.train.csv" # 训练文件
test_file = "covid.test.csv" # 测试文件
3.定义自己的模型
这里要特别注意linner的维度问题,全连接,前一层的输出维度为后一层的输入维度。由于最后得到的维度为二维,所以要用x.squeeze(1)将其转变为一维,用来和y进行计算
class Mymodel(nn.Module):
def __init__(self, inDim): # inDim为输入维度
super(Mymodel, self).__init__() # 重新定义父类的初始化函数
self.fc1 = nn.Linear(inDim, 100)
self.relu1 = nn.ReLU()
self.fc2 = nn.Linear(100, 1)
def forward(self, x): # 模型前向传递过程
x = self.fc1(x) # 通过第一层全连接
x = self.relu1(x) # 通过第二层全连接
x = self.fc2(x) # 通过第三层全连接
return x.squeeze(1) # 返回一维的数据,用来和y做计算
4训练模型
开始训练模型,分别传入模型 ,训练集,验证集,设备(我这里是cpu),训练次数,优化器,损失函数,用来保存最好的模型的路径。模型的好坏不是呈直线上升和直线下降,是不规则的曲线,所以要保存预测值最好的模型。还要注意模型的模式,分为测试和验证
梯度回传:
train_batch_loss = loss(pred, target) # 计算损失 train_batch_loss.backward() # 梯度回传、
优化器:
# para -= para.grad * lr # 不能写成 para = para - para.grad*lr # para.grad.zero_() optimizer.step() # 相当于上一个的更新模型的作用,自动更新模型 optimizer.zero_grad() # 梯度清零
具体来说就是梯度回传是计算导数值,优化器是要知道要更新那个参数,然后去执行,至于到底更新多少数值,不是优化器的工作
def train_val(model, train_loader, val_loader, device, epochs, optimizer, loss, save_path):
model = model.to(device) # 把模型放在cpu上
plt_train_loss = []
plt_val_loss = []
min_val_loss = 9999999999 # 记录最小的loss值
for epoch in range(epochs):
train_loss = 0.0 # 记录每一轮的训练loss
val_loss = 0.0 # 记录每一轮的验证loss
start_time = time.time()
# 将模型调味训练模式
# 对于有些模型的某些节点只在训练集上才会有用,随意要让模型知道现在是在训练还是测试
model.train()
for batch_x, batch_y in train_loader:
x, target = batch_x.to(device), batch_y.to(device)
pred = model(x) # 得到预测值
train_batch_loss = loss(pred, target) # 计算损失
train_batch_loss.backward() # 梯度回传
# 下面两行优化器相当于:
# para -= para.grad * lr # 不能写成 para = para - para.grad*lr
# para.grad.zero_()
optimizer.step() # 相当于上一个的更新模型的作用,自动更新模型
optimizer.zero_grad() # 梯度清零
# train_loss += train_batch_loss.cpu().item() # 把数据放在cpu上把损失的数值取出来
train_loss += train_batch_loss.cpu().item() # 测试原本就在cpu上运行
plt_train_loss.append(train_loss / train_loader.dataset.__len__()) # 记录每一轮的loss的平均值
# 训练一轮,验证一轮,调整为验证模式
model.eval()
with torch.no_grad(): # 验证的时候不进行模型优化,也不进行梯度回传,只用来验证效果
for batch_x, batch_y in val_loader:
x, target = batch_x.to(device), batch_y.to(device)
pred = model(x)
val_batch_loss = loss(pred, target)
val_loss += val_batch_loss.cpu().item()
plt_val_loss.append(val_loss / val_loader.dataset.__len__())
if val_loss < min_val_loss:
# torch.save(model, save_path) # 把好的模型保存起来
torch.save(model.state_dict(), save_path)
min_val_loss = val_loss
# 打印训练结果。 注意python语法, %2.2f 表示小数位为2的浮点数, 后面可以对应。
print('[%03d/%03d] %2.2f sec(s) TrainLoss : %.6f | ValLoss: %.6f' % \
(epoch, epochs, time.time() - start_time, plt_train_loss[-1], plt_val_loss[-1]))
plt.plot(plt_train_loss)
plt.plot(plt_val_loss)
plt.title("loss")
plt.legend(["train", "val"])
plt.show()
5.验证测试集,生成数据
这里我的模型只能以这种方式加载到设备上
def evaluate(save_path, device, test_loader, ans_path):
# model = torch.load(save_path).to(device)
# 不知道为什么上面那样不可以?????????????????????????????????
# 总之这里的意思是把最好的模型、最好的参数从文件中取出来,放到设备上
state_dict = torch.load(save_path, map_location=device)
model.load_state_dict(state_dict)
model.to(device)
ans = [] # 用来存储模型预测出来的值
with torch.no_grad():
for x in test_loader:
pred = model.forward(x.to(device))
ans.append(pred.cpu().item())
# print(ans)
# 打开文件,以写的方式,不进行换行(答案格式要求)
with open(ans_path, "w", newline="") as f:
csv_writer = csv.writer((f))
csv_writer.writerow(["id", "tested_positive"])
for i in range(len(ans)):
csv_writer.writerow([str(i), str(ans[i])])
print("文件被保存到" + ans_path)
6.程序开始
1.设置超参数
# 设置超参数
config = {
"lr": 0.001,
"momentum": 0.9, # 动量越大表示向前缓冲的越多
"epochs": 20,
"save_path": "model_save/model.pth",
"ans_path": "pred.csv"
}
2.开始执行
device = "cuda" if torch.cuda.is_available() else "cpu" # 查看自己的设备,我的是cpu
# print(device)
# 维度为93(案例要求)
dim = 93
# 把模型放在cpu上
model = Mymodel(dim).to(device)
# 不用自己写loss函数,直接用官方的(参考上节课的loss)
# def maeLoss(pre_y, y):
# return torch.sum(abs(pre_y - y)) / len(y)
loss = nn.MSELoss() # 相当于上面的loss函数,只是函数不一样,这是是均方误差,上面是平均值误差
# 不用自己写优化器,直接用官方的(参考上节课的sgd)
# 模型的参数,学习率,动量(可以理解为惯性)
# para -= para.grad * lr # 不能写成para = para - para.grad*lr
# para.grad.zero_() # 这个没有动量
optimizer = optim.SGD(model.parameters(), lr=config["lr"], momentum=config["momentum"]) # 优化器,相当于上面两行代码
# 开始训练
# 模型,训练集,验证机,设备(cpu),超参数,优化器,损失函数,保存路径(用来保存最好的模型)
# 模型的好坏不是呈直线上升和直线下降,是不规则的曲线,所以要保存预测值最好的模型
train_val(model, train_loader, val_loader, device, config["epochs"], optimizer, loss, config["save_path"])
# 开始预测,生成预测值,保存
evaluate(config["save_path"], device, test_loader, config["ans_path"])
三.回顾
这个项目总体上说就是根据给出的数据生成一个模型,用来预测最终的结果。最重要的是要学会计算模型中的参数的数量

每个神经元有一个b,并且对应了前一层的全连接数量,如图所示,从而计算出整个模型中的参数的数量。
更多推荐

所有评论(0)