目录数据处理部分模型部分超参部分模型运行完整代码一般一个简单的神经网络项目包括以下几个部分数据处理部分模型部分除了模型以外的超参部分一般包括学习率优化器损失函数等等数据处理部分接下来对三个集合作以区分训练集(Training Set)相当于学生的“课本和课后作业”。这是模型真正用来“学习”的数据。模型通过反复观看这些数据来寻找数据中的规律并不断调整自己内部的参数就像学生看书背知识点一样验证集(Validation Set)学生的“模拟考试”。在模型学习的过程中我们需要不时地测验一下它学得怎么样有没有“死记硬背”也就是过拟合再用在验证集中表现最好的模型去参加比赛也就是去测试集预测测试集(Test Set)相当于学生的“期末大考”。当模型完全训练好所有的参数和策略都固定下来后用来评估这个模型在面对从未见过的真实数据时到底表现如何评估泛化能力注意对训练集验证集的划分要随机首先导入几个需要使用的包import time import matplotlib.pyplot as plt import torch import numpy as np # 矩阵处理包 import csv # 处理读取文件 import pandas as pd # 同上 from torch.utils.data import DataLoader, Dataset # 与数据处理相关 import torch.nn as nn from torch import optim数据处理一般就是给一个数据地址或者数据内容然后根据这个输入得到输出XY。这一般都是通过一个dataset类来实现的dataset一般有三个函数initgetitemleninit初始化作用是把数据读进来一般要给一个数据地址然后把数据放在列表XY中方便日后使用。getitem取数据作用是给它一个下标idx根据这个下标去索引数据X[idx]Y[idx]。len告诉你数据有多长。class CovidDataset(Dataset): # 这里定义了一个名为 CovidDataset 的新类。 # 括号里的 Dataset 表示它继承了 PyTorch 中专门用来处理数据的基础类。 # 这意味着你的 CovidDataset 将具备处理深度学习数据的一些基本特性。 def __init__(self, file_path, modetrain): with open(file_path,r) as f: # 使用 with 的好处是读取完毕后系统会自动帮你把文件关掉防止内存泄漏。 ori_data list(csv.reader(f)) # csv.reader(f) 会逐行读取 CSV 文件中的内容 csv_data np.array(ori_data[1:])[:,1:].astype(float) # 去掉第一行第一列同时将数据类型转换为float if mode train: # 如果数据为训练集 indices [i for i in range(len(csv_data)) if i % 5 ! 0] # 逢五取一即每五个数字取其中一个作为验证集剩下四个作为训练集 self.y torch.tensor(csv_data[indices, -1]) elif mode val: # 如果数据为验证集 indices [i for i in range(len(csv_data)) if i % 5 0] self.y torch.tensor(csv_data[indices, -1]) else: # 如果是测试集则包括所有的数据不需要划分 indices [i for i in range(len(csv_data))] data torch.tensor(csv_data[indices, :-1]) self.data (data - data.mean(dim0,keepdimTrue))/data.std(dim0,keepdimTrue) # 对数据进行归一化 # 不同的数据规模不一样比如有的是0-1有的是0-100。所以要进行归一化 self.mode mode def __getitem__(self, idx): # 给一个下标返回一组数据 if self.mode ! test: # 如果不是测试集而是训练集测试集 return self.data[idx].float(), self.y[idx].float() # .float()是为了将64位浮点数转换为32位加快计算速度 else: # 如果是测试集 return self.data[idx].float() def __len__(self): return len(self.data)SGD随机梯度下降法随机取一批数据计算loss值然后沿着梯度下降的方向前进。在深度学习中模型通常不能也不应该一次性把所有数据都看完而是一小批一小批地学。DataLoader的核心作用就是帮你把数据“打包”并按要求“喂”给模型。虽然上面的数据集里的__getitem__可以根据索引idx每次提取1条数据但现代 GPU 计算能力很强一次只算1条太慢了。而DataLoader会自动调用__getitem__帮你把多条数据拼成一个“批次”Batch让模型可以一次性处理多个数据极大地提高训练速度。batch_size 16 train_loader DataLoader(train_dataset, batch_sizebatch_size,shuffleTrue) # 划分训练集喂给模型 val_loader DataLoader(val_dataset, batch_sizebatch_size,shuffleTrue) # 划分验证集喂给模型 # 上面两条命令都是加载一批数据并且打乱数据模型部分接下来定义模型模型则包括两个函数一个是init另一个是forward:init模型初始化规定这个模型长什么样子forward向前传播规定了数据如何通过模型。class MyModel(nn.Module): # nn.Module 是 PyTorch 提供的一个超级基础类包含了所有神经网络模型该有的基本功能。 # 当你写 class MyModel(nn.Module) 时你是在告诉 Python # “我要创建一个叫 MyModel 的新模型并且它要继承 nn.Module 的所有能力。” # 只要继承了它你的模型就自动拥有了追踪权重参数、 # 在 CPU 和 GPU 之间自由切换.to(device)、保存和加载模型权重等高级功能。 def __init__(self, inDim): # inDim表示输入维度 # 这是 Python 类的初始化函数。当你以后在代码里写 model MyModel(10) 的时候 # 就会自动触发这个函数并且把 10 传给 inDim。 # 通常我们会在这个函数里面定义模型包含哪些层比如全连接层、卷积层等。 super(MyModel, self).__init__() # super 代表“父类”也就是 nn.Module。这行代码的意思是 # 在初始化我自己的 MyModel 之前必须先去调用并执行一次 # 父类 nn.Module 的初始化函数。 self.fc1 nn.Linear(inDim, 64) self.relu1 nn.ReLU() self.fc2 nn.Linear(64, 1) # 定义模型框架 def forward(self, x): # 模型向前传播过程 x self.fc1(x) x self.relu1(x) x self.fc2(x) if len(x.size()) 1: # x.size()获取当前张量 x 的形状比如 [32, 1]。 # len(x.size()) 1判断这个形状是不是多维的。 # 如果是 [32, 1]长度就是 2满足大于 1 的条件。 return x.squeeze(1) # x.squeeze(1)这是核心魔法 squeeze 的英文是“挤压”。 # 它的作用是把指定位置上长度为 1 的那个多余维度“挤掉”。 # 这里的 1 是索引值。在 (32, 1) 中索引 0 的位置是 32索引 1 的位置是 1。 # x.squeeze(1) 就是把索引为 1 的那个维度挤掉。 # 于是(32, 1) 瞬间被压扁成了 (32,) return x # 模型向前传播过程然后是模型的训练和验证过程的函数def train_val(model, train_loader, val_loader, device, epochs, optimizer, loss, save_path): model model.to(device) # 把整个神经网络模型包括它内部所有的网络层、权重和参数搬移到指定的硬件设备上。 plt_train_loss [] # 记录loss值的列表 plt_val_loss [] # 记录loss值的列表 min_val_loss 99999999999999 # 将最小loss值设置为无穷大 for epoch in range(epochs): # 开始训练 train_loss 0.0 val_loss 0.0 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_bat_loss loss(pred,target) # 用得到的预测值计算loss train_bat_loss.backward() # 向后传播 optimizer.step() # 更新模型 optimizer.zero_grad() # 既然梯度已经用于更新模型了接下来就可以清零了 train_loss train_bat_loss.cpu().item() # 记录一下 plt_train_loss.append((train_loss / train_loader.__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_bat_loss loss(pred, target) val_loss val_bat_loss.cpu().item() plt_val_loss.append(val_loss / val_loader.__len__()) if val_loss min_val_loss: # 如果这一轮的loss比以往的loss都要小 torch.save(model,save_path) min_val_loss val_loss 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()完成了训练和验证部分我们还缺少对模型的测试结果超参部分我们把超参数写在一个字典config里面方便调用train_file covid.train.csv test_file covid.test.csv train_dataset CovidDataset(train_file, train) val_dataset CovidDataset(train_file, val) # 注意验证集是从训练集里面分出去的一小部分 test_dataset CovidDataset(test_file, test) batch_size 16 train_loader DataLoader(train_dataset, batch_sizebatch_size,shuffleTrue) # 划分训练集喂给模型 val_loader DataLoader(val_dataset, batch_sizebatch_size,shuffleTrue) # 划分验证集喂给模型 # 上面两条命令都是加载一批数据并且打乱数据 model MyModel(inDim93) # predy model(batch_x) device cuda if torch.cuda.is_available() else cpu # print(device) config { lr:0.001, # 学习率 epochs:20, # 轮次 momentum:0.9, # 动量 save_path:model_save/best_model.pth # 保存路径 }模型运行通过调用上面所写的代码来运行模型model MyModel(inDim93).to(device) loss nn.MSELoss() optimizer optim.SGD(model.parameters(), lrconfig[lr], momentumconfig[momentum]) train_val(model, train_loader, val_loader, device, config[epochs], optimizer, loss, config[save_path])完整代码import time import matplotlib.pyplot as plt import torch import numpy as np # 矩阵处理包 import csv # 处理读取文件 import pandas as pd # 同上 from torch.utils.data import DataLoader, Dataset # 与数据处理相关 import torch.nn as nn from torch import optim # 引用优化器 class CovidDataset(Dataset): # 这里定义了一个名为 CovidDataset 的新类。 # 括号里的 Dataset 表示它继承了 PyTorch 中专门用来处理数据的基础类。 # 这意味着你的 CovidDataset 将具备处理深度学习数据的一些基本特性。 def __init__(self, file_path, modetrain): with open(file_path,r) as f: # 使用 with 的好处是读取完毕后系统会自动帮你把文件关掉防止内存泄漏。 ori_data list(csv.reader(f)) # csv.reader(f) 会逐行读取 CSV 文件中的内容 csv_data np.array(ori_data[1:])[:,1:].astype(float) # 去掉第一行第一列同时将数据类型转换为float if mode train: # 如果数据为训练集 indices [i for i in range(len(csv_data)) if i % 5 ! 0] # 逢五取一即每五个数字取其中一个作为验证集剩下四个作为训练集 data torch.tensor(csv_data[indices, :-1]) self.y torch.tensor(csv_data[indices, -1]) elif mode val: # 如果数据为验证集 indices [i for i in range(len(csv_data)) if i % 5 0] data torch.tensor(csv_data[indices, :-1]) self.y torch.tensor(csv_data[indices, -1]) else: # 如果是测试集则包括所有的数据不需要划分 indices [i for i in range(len(csv_data))] data torch.tensor(csv_data[indices]) self.data (data - data.mean(dim0,keepdimTrue))/data.std(dim0,keepdimTrue) # 对数据进行归一化 # 不同的数据规模不一样比如有的是0-1有的是0-100。所以要进行归一化 self.mode mode def __getitem__(self, idx): # 给一个下标返回一组数据 if self.mode ! test: # 如果不是测试集而是训练集测试集 return self.data[idx].float(), self.y[idx].float() # .float()是为了将64位浮点数转换为32位加快计算速度 else: # 如果是测试集 return self.data[idx].float() def __len__(self): return len(self.data) class MyModel(nn.Module): # nn.Module 是 PyTorch 提供的一个超级基础类包含了所有神经网络模型该有的基本功能。 # 当你写 class MyModel(nn.Module) 时你是在告诉 Python # “我要创建一个叫 MyModel 的新模型并且它要继承 nn.Module 的所有能力。” # 只要继承了它你的模型就自动拥有了追踪权重参数、 # 在 CPU 和 GPU 之间自由切换.to(device)、保存和加载模型权重等高级功能。 def __init__(self, inDim): # inDim表示输入维度 # 这是 Python 类的初始化函数。当你以后在代码里写 model MyModel(10) 的时候 # 就会自动触发这个函数并且把 10 传给 inDim。 # 通常我们会在这个函数里面定义模型包含哪些层比如全连接层、卷积层等。 super(MyModel, self).__init__() # super 代表“父类”也就是 nn.Module。这行代码的意思是 # 在初始化我自己的 MyModel 之前必须先去调用并执行一次 # 父类 nn.Module 的初始化函数。 self.fc1 nn.Linear(inDim, 64) self.relu1 nn.ReLU() self.fc2 nn.Linear(64, 1) # 定义模型框架 def forward(self, x): # 模型向前传播过程 x self.fc1(x) x self.relu1(x) x self.fc2(x) if len(x.size()) 1: # x.size()获取当前张量 x 的形状比如 [32, 1]。 # len(x.size()) 1判断这个形状是不是多维的。 # 如果是 [32, 1]长度就是 2满足大于 1 的条件。 return x.squeeze(1) # x.squeeze(1)这是核心魔法 squeeze 的英文是“挤压”。 # 它的作用是把指定位置上长度为 1 的那个多余维度“挤掉”。 # 这里的 1 是索引值。在 (32, 1) 中索引 0 的位置是 32索引 1 的位置是 1。 # x.squeeze(1) 就是把索引为 1 的那个维度挤掉。 # 于是(32, 1) 瞬间被压扁成了 (32,) return x # 模型向前传播过程 def train_val(model, train_loader, val_loader, device, epochs, optimizer, loss, save_path): model model.to(device) # 把整个神经网络模型包括它内部所有的网络层、权重和参数搬移到指定的硬件设备上。 plt_train_loss [] # 记录loss值的列表 plt_val_loss [] # 记录loss值的列表 min_val_loss 99999999999999 # 将最小loss值设置为无穷大 for epoch in range(epochs): # 开始训练 train_loss 0.0 val_loss 0.0 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_bat_loss loss(pred,target) # 用得到的预测值计算loss train_bat_loss.backward() # 向后传播 optimizer.step() # 更新模型 optimizer.zero_grad() # 既然梯度已经用于更新模型了接下来就可以清零了 train_loss train_bat_loss.cpu().item() # 记录一下 plt_train_loss.append((train_loss / train_loader.__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_bat_loss loss(pred, target) val_loss val_bat_loss.cpu().item() plt_val_loss.append(val_loss / val_loader.__len__()) if val_loss min_val_loss: # 如果这一轮的loss比以往的loss都要小 torch.save(model,save_path) min_val_loss val_loss 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() def evaluate(save_path, test_loader, device, real_path): # 得出测试结果文件 model torch.load(save_path).to(device) # 加载模型并放入设备 real [] # 记录结果的列表 with torch.no_grad(): for x in test_loader: pred model(x.to(device)) real.append(pred.cpu().item()) print(real) with open(real_path, w, newline) as f: csvWriter csv.writer(f) csvWriter.writerow([id,tested_positive]) for i, value in enumerate(real): csvWriter.writerow([str(i), str(value)]) print(文件已经保存到{}.format(real_path)) train_file covid.train.csv test_file covid.test.csv train_dataset CovidDataset(train_file, train) val_dataset CovidDataset(train_file, val) # 注意验证集是从训练集里面分出去的一小部分 test_dataset CovidDataset(test_file, test) batch_size 16 train_loader DataLoader(train_dataset, batch_sizebatch_size,shuffleTrue) # 划分训练集喂给模型 val_loader DataLoader(val_dataset, batch_sizebatch_size,shuffleTrue) # 划分验证集喂给模型 # 上面两条命令都是加载一批数据并且打乱数据 test_loader DataLoader(test_dataset, batch_size1,shuffleFalse) # 测试集不可以打乱 model MyModel(inDim93) # predy model(batch_x) device cuda if torch.cuda.is_available() else cpu # print(device) config { lr:0.001, # 学习率 epochs:20, # 轮次 momentum:0.9, # 动量 save_path:model_save/best_model.pth, # 保存路径 real_path:pred.csv } model MyModel(inDim93).to(device) loss nn.MSELoss() optimizer optim.SGD(model.parameters(), lrconfig[lr], momentumconfig[momentum]) train_val(model, train_loader, val_loader, device, config[epochs], optimizer, loss, config[save_path]) evaluate(config[save_path], test_loader, device, config[real_path])
李哥考研第三节:回归实战
目录数据处理部分模型部分超参部分模型运行完整代码一般一个简单的神经网络项目包括以下几个部分数据处理部分模型部分除了模型以外的超参部分一般包括学习率优化器损失函数等等数据处理部分接下来对三个集合作以区分训练集(Training Set)相当于学生的“课本和课后作业”。这是模型真正用来“学习”的数据。模型通过反复观看这些数据来寻找数据中的规律并不断调整自己内部的参数就像学生看书背知识点一样验证集(Validation Set)学生的“模拟考试”。在模型学习的过程中我们需要不时地测验一下它学得怎么样有没有“死记硬背”也就是过拟合再用在验证集中表现最好的模型去参加比赛也就是去测试集预测测试集(Test Set)相当于学生的“期末大考”。当模型完全训练好所有的参数和策略都固定下来后用来评估这个模型在面对从未见过的真实数据时到底表现如何评估泛化能力注意对训练集验证集的划分要随机首先导入几个需要使用的包import time import matplotlib.pyplot as plt import torch import numpy as np # 矩阵处理包 import csv # 处理读取文件 import pandas as pd # 同上 from torch.utils.data import DataLoader, Dataset # 与数据处理相关 import torch.nn as nn from torch import optim数据处理一般就是给一个数据地址或者数据内容然后根据这个输入得到输出XY。这一般都是通过一个dataset类来实现的dataset一般有三个函数initgetitemleninit初始化作用是把数据读进来一般要给一个数据地址然后把数据放在列表XY中方便日后使用。getitem取数据作用是给它一个下标idx根据这个下标去索引数据X[idx]Y[idx]。len告诉你数据有多长。class CovidDataset(Dataset): # 这里定义了一个名为 CovidDataset 的新类。 # 括号里的 Dataset 表示它继承了 PyTorch 中专门用来处理数据的基础类。 # 这意味着你的 CovidDataset 将具备处理深度学习数据的一些基本特性。 def __init__(self, file_path, modetrain): with open(file_path,r) as f: # 使用 with 的好处是读取完毕后系统会自动帮你把文件关掉防止内存泄漏。 ori_data list(csv.reader(f)) # csv.reader(f) 会逐行读取 CSV 文件中的内容 csv_data np.array(ori_data[1:])[:,1:].astype(float) # 去掉第一行第一列同时将数据类型转换为float if mode train: # 如果数据为训练集 indices [i for i in range(len(csv_data)) if i % 5 ! 0] # 逢五取一即每五个数字取其中一个作为验证集剩下四个作为训练集 self.y torch.tensor(csv_data[indices, -1]) elif mode val: # 如果数据为验证集 indices [i for i in range(len(csv_data)) if i % 5 0] self.y torch.tensor(csv_data[indices, -1]) else: # 如果是测试集则包括所有的数据不需要划分 indices [i for i in range(len(csv_data))] data torch.tensor(csv_data[indices, :-1]) self.data (data - data.mean(dim0,keepdimTrue))/data.std(dim0,keepdimTrue) # 对数据进行归一化 # 不同的数据规模不一样比如有的是0-1有的是0-100。所以要进行归一化 self.mode mode def __getitem__(self, idx): # 给一个下标返回一组数据 if self.mode ! test: # 如果不是测试集而是训练集测试集 return self.data[idx].float(), self.y[idx].float() # .float()是为了将64位浮点数转换为32位加快计算速度 else: # 如果是测试集 return self.data[idx].float() def __len__(self): return len(self.data)SGD随机梯度下降法随机取一批数据计算loss值然后沿着梯度下降的方向前进。在深度学习中模型通常不能也不应该一次性把所有数据都看完而是一小批一小批地学。DataLoader的核心作用就是帮你把数据“打包”并按要求“喂”给模型。虽然上面的数据集里的__getitem__可以根据索引idx每次提取1条数据但现代 GPU 计算能力很强一次只算1条太慢了。而DataLoader会自动调用__getitem__帮你把多条数据拼成一个“批次”Batch让模型可以一次性处理多个数据极大地提高训练速度。batch_size 16 train_loader DataLoader(train_dataset, batch_sizebatch_size,shuffleTrue) # 划分训练集喂给模型 val_loader DataLoader(val_dataset, batch_sizebatch_size,shuffleTrue) # 划分验证集喂给模型 # 上面两条命令都是加载一批数据并且打乱数据模型部分接下来定义模型模型则包括两个函数一个是init另一个是forward:init模型初始化规定这个模型长什么样子forward向前传播规定了数据如何通过模型。class MyModel(nn.Module): # nn.Module 是 PyTorch 提供的一个超级基础类包含了所有神经网络模型该有的基本功能。 # 当你写 class MyModel(nn.Module) 时你是在告诉 Python # “我要创建一个叫 MyModel 的新模型并且它要继承 nn.Module 的所有能力。” # 只要继承了它你的模型就自动拥有了追踪权重参数、 # 在 CPU 和 GPU 之间自由切换.to(device)、保存和加载模型权重等高级功能。 def __init__(self, inDim): # inDim表示输入维度 # 这是 Python 类的初始化函数。当你以后在代码里写 model MyModel(10) 的时候 # 就会自动触发这个函数并且把 10 传给 inDim。 # 通常我们会在这个函数里面定义模型包含哪些层比如全连接层、卷积层等。 super(MyModel, self).__init__() # super 代表“父类”也就是 nn.Module。这行代码的意思是 # 在初始化我自己的 MyModel 之前必须先去调用并执行一次 # 父类 nn.Module 的初始化函数。 self.fc1 nn.Linear(inDim, 64) self.relu1 nn.ReLU() self.fc2 nn.Linear(64, 1) # 定义模型框架 def forward(self, x): # 模型向前传播过程 x self.fc1(x) x self.relu1(x) x self.fc2(x) if len(x.size()) 1: # x.size()获取当前张量 x 的形状比如 [32, 1]。 # len(x.size()) 1判断这个形状是不是多维的。 # 如果是 [32, 1]长度就是 2满足大于 1 的条件。 return x.squeeze(1) # x.squeeze(1)这是核心魔法 squeeze 的英文是“挤压”。 # 它的作用是把指定位置上长度为 1 的那个多余维度“挤掉”。 # 这里的 1 是索引值。在 (32, 1) 中索引 0 的位置是 32索引 1 的位置是 1。 # x.squeeze(1) 就是把索引为 1 的那个维度挤掉。 # 于是(32, 1) 瞬间被压扁成了 (32,) return x # 模型向前传播过程然后是模型的训练和验证过程的函数def train_val(model, train_loader, val_loader, device, epochs, optimizer, loss, save_path): model model.to(device) # 把整个神经网络模型包括它内部所有的网络层、权重和参数搬移到指定的硬件设备上。 plt_train_loss [] # 记录loss值的列表 plt_val_loss [] # 记录loss值的列表 min_val_loss 99999999999999 # 将最小loss值设置为无穷大 for epoch in range(epochs): # 开始训练 train_loss 0.0 val_loss 0.0 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_bat_loss loss(pred,target) # 用得到的预测值计算loss train_bat_loss.backward() # 向后传播 optimizer.step() # 更新模型 optimizer.zero_grad() # 既然梯度已经用于更新模型了接下来就可以清零了 train_loss train_bat_loss.cpu().item() # 记录一下 plt_train_loss.append((train_loss / train_loader.__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_bat_loss loss(pred, target) val_loss val_bat_loss.cpu().item() plt_val_loss.append(val_loss / val_loader.__len__()) if val_loss min_val_loss: # 如果这一轮的loss比以往的loss都要小 torch.save(model,save_path) min_val_loss val_loss 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()完成了训练和验证部分我们还缺少对模型的测试结果超参部分我们把超参数写在一个字典config里面方便调用train_file covid.train.csv test_file covid.test.csv train_dataset CovidDataset(train_file, train) val_dataset CovidDataset(train_file, val) # 注意验证集是从训练集里面分出去的一小部分 test_dataset CovidDataset(test_file, test) batch_size 16 train_loader DataLoader(train_dataset, batch_sizebatch_size,shuffleTrue) # 划分训练集喂给模型 val_loader DataLoader(val_dataset, batch_sizebatch_size,shuffleTrue) # 划分验证集喂给模型 # 上面两条命令都是加载一批数据并且打乱数据 model MyModel(inDim93) # predy model(batch_x) device cuda if torch.cuda.is_available() else cpu # print(device) config { lr:0.001, # 学习率 epochs:20, # 轮次 momentum:0.9, # 动量 save_path:model_save/best_model.pth # 保存路径 }模型运行通过调用上面所写的代码来运行模型model MyModel(inDim93).to(device) loss nn.MSELoss() optimizer optim.SGD(model.parameters(), lrconfig[lr], momentumconfig[momentum]) train_val(model, train_loader, val_loader, device, config[epochs], optimizer, loss, config[save_path])完整代码import time import matplotlib.pyplot as plt import torch import numpy as np # 矩阵处理包 import csv # 处理读取文件 import pandas as pd # 同上 from torch.utils.data import DataLoader, Dataset # 与数据处理相关 import torch.nn as nn from torch import optim # 引用优化器 class CovidDataset(Dataset): # 这里定义了一个名为 CovidDataset 的新类。 # 括号里的 Dataset 表示它继承了 PyTorch 中专门用来处理数据的基础类。 # 这意味着你的 CovidDataset 将具备处理深度学习数据的一些基本特性。 def __init__(self, file_path, modetrain): with open(file_path,r) as f: # 使用 with 的好处是读取完毕后系统会自动帮你把文件关掉防止内存泄漏。 ori_data list(csv.reader(f)) # csv.reader(f) 会逐行读取 CSV 文件中的内容 csv_data np.array(ori_data[1:])[:,1:].astype(float) # 去掉第一行第一列同时将数据类型转换为float if mode train: # 如果数据为训练集 indices [i for i in range(len(csv_data)) if i % 5 ! 0] # 逢五取一即每五个数字取其中一个作为验证集剩下四个作为训练集 data torch.tensor(csv_data[indices, :-1]) self.y torch.tensor(csv_data[indices, -1]) elif mode val: # 如果数据为验证集 indices [i for i in range(len(csv_data)) if i % 5 0] data torch.tensor(csv_data[indices, :-1]) self.y torch.tensor(csv_data[indices, -1]) else: # 如果是测试集则包括所有的数据不需要划分 indices [i for i in range(len(csv_data))] data torch.tensor(csv_data[indices]) self.data (data - data.mean(dim0,keepdimTrue))/data.std(dim0,keepdimTrue) # 对数据进行归一化 # 不同的数据规模不一样比如有的是0-1有的是0-100。所以要进行归一化 self.mode mode def __getitem__(self, idx): # 给一个下标返回一组数据 if self.mode ! test: # 如果不是测试集而是训练集测试集 return self.data[idx].float(), self.y[idx].float() # .float()是为了将64位浮点数转换为32位加快计算速度 else: # 如果是测试集 return self.data[idx].float() def __len__(self): return len(self.data) class MyModel(nn.Module): # nn.Module 是 PyTorch 提供的一个超级基础类包含了所有神经网络模型该有的基本功能。 # 当你写 class MyModel(nn.Module) 时你是在告诉 Python # “我要创建一个叫 MyModel 的新模型并且它要继承 nn.Module 的所有能力。” # 只要继承了它你的模型就自动拥有了追踪权重参数、 # 在 CPU 和 GPU 之间自由切换.to(device)、保存和加载模型权重等高级功能。 def __init__(self, inDim): # inDim表示输入维度 # 这是 Python 类的初始化函数。当你以后在代码里写 model MyModel(10) 的时候 # 就会自动触发这个函数并且把 10 传给 inDim。 # 通常我们会在这个函数里面定义模型包含哪些层比如全连接层、卷积层等。 super(MyModel, self).__init__() # super 代表“父类”也就是 nn.Module。这行代码的意思是 # 在初始化我自己的 MyModel 之前必须先去调用并执行一次 # 父类 nn.Module 的初始化函数。 self.fc1 nn.Linear(inDim, 64) self.relu1 nn.ReLU() self.fc2 nn.Linear(64, 1) # 定义模型框架 def forward(self, x): # 模型向前传播过程 x self.fc1(x) x self.relu1(x) x self.fc2(x) if len(x.size()) 1: # x.size()获取当前张量 x 的形状比如 [32, 1]。 # len(x.size()) 1判断这个形状是不是多维的。 # 如果是 [32, 1]长度就是 2满足大于 1 的条件。 return x.squeeze(1) # x.squeeze(1)这是核心魔法 squeeze 的英文是“挤压”。 # 它的作用是把指定位置上长度为 1 的那个多余维度“挤掉”。 # 这里的 1 是索引值。在 (32, 1) 中索引 0 的位置是 32索引 1 的位置是 1。 # x.squeeze(1) 就是把索引为 1 的那个维度挤掉。 # 于是(32, 1) 瞬间被压扁成了 (32,) return x # 模型向前传播过程 def train_val(model, train_loader, val_loader, device, epochs, optimizer, loss, save_path): model model.to(device) # 把整个神经网络模型包括它内部所有的网络层、权重和参数搬移到指定的硬件设备上。 plt_train_loss [] # 记录loss值的列表 plt_val_loss [] # 记录loss值的列表 min_val_loss 99999999999999 # 将最小loss值设置为无穷大 for epoch in range(epochs): # 开始训练 train_loss 0.0 val_loss 0.0 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_bat_loss loss(pred,target) # 用得到的预测值计算loss train_bat_loss.backward() # 向后传播 optimizer.step() # 更新模型 optimizer.zero_grad() # 既然梯度已经用于更新模型了接下来就可以清零了 train_loss train_bat_loss.cpu().item() # 记录一下 plt_train_loss.append((train_loss / train_loader.__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_bat_loss loss(pred, target) val_loss val_bat_loss.cpu().item() plt_val_loss.append(val_loss / val_loader.__len__()) if val_loss min_val_loss: # 如果这一轮的loss比以往的loss都要小 torch.save(model,save_path) min_val_loss val_loss 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() def evaluate(save_path, test_loader, device, real_path): # 得出测试结果文件 model torch.load(save_path).to(device) # 加载模型并放入设备 real [] # 记录结果的列表 with torch.no_grad(): for x in test_loader: pred model(x.to(device)) real.append(pred.cpu().item()) print(real) with open(real_path, w, newline) as f: csvWriter csv.writer(f) csvWriter.writerow([id,tested_positive]) for i, value in enumerate(real): csvWriter.writerow([str(i), str(value)]) print(文件已经保存到{}.format(real_path)) train_file covid.train.csv test_file covid.test.csv train_dataset CovidDataset(train_file, train) val_dataset CovidDataset(train_file, val) # 注意验证集是从训练集里面分出去的一小部分 test_dataset CovidDataset(test_file, test) batch_size 16 train_loader DataLoader(train_dataset, batch_sizebatch_size,shuffleTrue) # 划分训练集喂给模型 val_loader DataLoader(val_dataset, batch_sizebatch_size,shuffleTrue) # 划分验证集喂给模型 # 上面两条命令都是加载一批数据并且打乱数据 test_loader DataLoader(test_dataset, batch_size1,shuffleFalse) # 测试集不可以打乱 model MyModel(inDim93) # predy model(batch_x) device cuda if torch.cuda.is_available() else cpu # print(device) config { lr:0.001, # 学习率 epochs:20, # 轮次 momentum:0.9, # 动量 save_path:model_save/best_model.pth, # 保存路径 real_path:pred.csv } model MyModel(inDim93).to(device) loss nn.MSELoss() optimizer optim.SGD(model.parameters(), lrconfig[lr], momentumconfig[momentum]) train_val(model, train_loader, val_loader, device, config[epochs], optimizer, loss, config[save_path]) evaluate(config[save_path], test_loader, device, config[real_path])