LeNet学习笔记二
视频地址:【Pytorch框架与经典卷积神经网络与实战】 https://www.bilibili.com/video/BV1e34y1M7wR/?p=57&share_source=copy_web&vd_source=7d0117001ba4a138b2924a4a9803d9f5
第三步:测试模板代码
import torch
import torch.utils.data as Data
from torchvision import transforms
from torchvision.datasets import FashionMNIST # FashionMNIST是一个常用的机器学习数据集,类似于经典的MNIST手写数字数据集,但它包含了 10 个不同种类的时尚商品图片。这些商品包括 T 恤衫、裤子、套衫、裙子、外套、凉鞋、衬衫、运动鞋、包和短靴
from model import LeNet
def test_data_process():
test_data = FashionMNIST(root='./data', # 路径
train=False, # FashionMNIST里面分装了有6w张训练数据和1w张测试数据,是否是训练数据为Fslse,每个图片大小为1*28*28
transform=transforms.Compose([transforms.Resize(size=28), transforms.ToTensor()]),
# 将数据转换成张量的形成,方便后面的应用
download=True) #有数据就不再次下载,没有数据就会下载
test_dataloader = Data.DataLoader(dataset=test_data, # Data.DataLoader函数可以批量加载数据,一次可以读取多张图片
batch_size=1, # 一张一张图片去测试
shuffle=True, # 可以打乱数据也可以不打乱
num_workers=0) # 0线程
return test_dataloader
def test_model_process(model,test_dataloader):
device = "cuda" if torch.cuda.is_available() else "cpu" # 设定所用到的设备,有GPU用GPU,没有GPU用CPU
model = model.to(device) # 将模型放入到设备中
# 初始化一些参数
test_corrects = 0.0
test_num = 0
with torch.no_grad(): # 在test中只需要进行前向传播,不需要后向传播,不需要计算梯度,从而节省内存,加快运行速度
for (test_data_x,test_data_y) in test_dataloader:
test_data_x = test_data_x.to(device) # 将特征放入到验证设备中
test_data_y = test_data_y.to(device) # 将标签放到验证设备中
model.eval() # 将模型设置为推理/评估模式,在评估模式下,模型不会更新权重,而是用来进行模型的测试和推断
output = model(test_data_x)
pre_lab = torch.argmax(output, dim=1) #查找每一行中最大值对应的行标;torch.argmax() 函数是 PyTorch 中用于在张量(tensor)中找到最大值的索引的方法。具体地说,它返回张量中沿指定维度(dimension)的最大值的索引
test_corrects += torch.sum(pre_lab==test_data_y.data) # 如果pre_lab==test_data_y.data,即预测的值和真实值相等则为True,那么预测对的时候就会+1,最终的结果就是预测正确多少张图片
test_num += test_data_x.size(0) # 将所有的测试样本进行累加
test_acc = test_corrects.double() / test_num # 计算测试准确率
print("测试的准确率为:", test_acc)
if __name__=="__main__":
model = LeNet() # 加载模型,将模型实例化
model.load_state_dict(torch.load("best_model.pth"))
test_dataloader = test_data_process() # 加载测试数据
test_model_process(model, test_dataloader) # 加载模型测试的函数
device = "cuda" if torch.cuda.is_available() else "cpu"
model = model.to(device)
classes = ["T-shirt/top", "Trouser", "Pullover", "Dress", "Coat", "Sandal", "Shirt", "Sneaker", "Bag", "Ankle boot"]
with torch.no_grad():
for b_x, b_y in test_dataloader:
b_x = b_x.to(device)
b_y = b_y.to(device)
model.eval() # 设置模型为验证模式
output = model(b_x)
result = torch.argmax(output, dim=1)
label = b_y.item()
print("预测值:", classes[result], "-----", "真实值:", classes[label])
学习中的一些疑惑
1.with torch.no_grad():
with torch.no_grad():
是一个上下文管理器(Context Manager),它用于控制 PyTorch 中梯度计算的行为。在这个上下文中,所有的张量操作都不会被记录梯度,也就是说,在这个上下文中,PyTorch 不会计算或存储张量的梯度信息。
通常情况下,当你在训练神经网络时,你希望 PyTorch 能够跟踪每一步操作的梯度,以便使用反向传播算法来更新模型的参数。然而,在某些情况下,比如你只是想要用已经训练好的模型进行推断(inference),而不需要计算梯度时,就可以使用 torch.no_grad()
来临时禁止梯度计算,以提高效率并节省内存。
2.pre_lab = torch.argmax(output, dim=1)
3.test_corrects += torch.sum(pre_lab==test_data_y.data)
4.test_num += test_data_x.size(0)
pre_lab = torch.argmax(output, dim=1)
在深度学习中,特别是在使用神经网络进行分类任务时,模型的输出通常是一个概率分布,表示每个类别的预测概率。对于分类问题,模型最终的输出通常是一个 softmax 激活函数的结果,将原始的分数(scores)转换为概率分布。例如,如果有三个类别,模型的输出可能是类似 [0.2, 0.5, 0.3]
的概率分布,表示第一个类别的概率为 0.2,第二个类别的概率为 0.5,第三个类别的概率为 0.3。
在这种情况下,我们通常希望找到概率分布中具有最高概率的类别,即预测的类别。这就是使用 torch.argmax()
函数的目的。torch.argmax()
函数会返回沿着指定维度(通常是特征维度或者样本维度)的最大值的索引,这正好适用于我们在分类任务中的需求。
在神经网络中,通常情况下,输出张量的维度是 (batch_size, num_classes)
,其中 batch_size
表示当前批次的样本数量,num_classes
表示类别数量。因此,当我们使用 torch.argmax(output, dim=1)
时,我们是在每个样本的维度上寻找最大值的索引,这样就能够得到每个样本的预测类别。
如何确定在哪个维度进行查找?这通常由输出张量的形状决定。在分类任务中,通常输出张量的最后一个维度是表示类别的维度,因此我们选择在倒数第二个维度(通常是第一维度)进行查找,以获得每个样本的预测类别。
test_corrects += torch.sum(pre_lab==test_data_y.data)
-
pre_lab==test_data_y.data
: 这个表达式会生成一个布尔张量,其中对于每个样本,如果预测的类别与真实标签相同,则对应位置为True
,否则为False
。 -
torch.sum(pre_lab==test_data_y.data)
: 这个函数会计算布尔张量中所有True
的数量,也就是当前批次中模型预测正确的样本数量。 -
test_corrects += torch.sum(pre_lab==test_data_y.data)
: 这一行代码是将当前批次中预测正确的样本数量加到之前统计的正确样本数量test_corrects
上。这样,随着代码的执行,test_corrects
就会累积所有批次中预测正确的样本数量。
综合来说,这行代码的作用是将当前批次中预测正确的样本数量加到已经统计的正确样本数量上,以便在评估过程中跟踪模型的性能。
注意:
test_data_y.data:
在PyTorch中,.data
属性通常用于访问张量的底层数据。但需要注意的是,它在最新的版本中已经不推荐使用,因为它有一些潜在的问题,比如不会被自动跟踪到计算图中,可能导致梯度丢失或者不一致性的问题。
在较早的版本中,.data
属性被用来访问原始张量的数据,而不跟踪梯度。但是由于梯度丢失的风险,现在更推荐使用.detach()
方法来分离张量,以保持梯度的正确性。
所以,.data
在当前的实践中已经不再推荐使用,而是推荐使用.detach()
方法。在较旧的代码中,可能仍会看到.data
的使用,但应该尽量避免在新的代码中使用它。.detach()
方法与 .data
类似,也可以用于访问张量的底层数据,但是它会保留梯度跟踪信息,因此更安全
修改后的代码如下:
test_corrects += torch.sum(pre_lab==test_data_y.data) # 原来的
test_corrects += torch.sum(pre_lab == test_data_y.detach()) # 修改后
test_num += test_data_x.size(0)
test_data_x.size(0)
是 PyTorch 中张量对象的方法之一,用于获取张量的第一个维度的大小。在这个特定的上下文中,test_data_x
是一个张量,表示测试数据的输入特征。由于测试数据通常是以批次的形式传入模型的,所以 test_data_x
的第一个维度通常表示批次中样本的数量。
因此,test_data_x.size(0)
就是获取当前批次中样本的数量。这个值通常用来计算批次中的样本数量,从而在训练或评估过程中跟踪处理的数据量。
总之,test_data_x.size(0)
用于获取测试数据输入特征张量中的批次大小,即当前批次中样本的数量。
5.b_y.item()
b_y
是一个张量,表示测试数据的标签。在大多数情况下,测试数据的标签张量应该是一个一维的张量,因此.item()
方法可以用于获取该张量中唯一的元素的数值。
具体来说,b_y.item()
返回的是张量中唯一的一个元素的 Python 数值(即标量),前提是该张量中只包含一个元素。如果张量中包含多个元素,.item()
方法会引发一个错误。
在这种情况下,假设 b_y
是一个包含单个标签的张量(通常是整数),b_y.item()
就是获取这个标签的数值。这个数值通常表示类别标签,比如0、1、2、3等,具体取决于数据集的类别标签的编码方式。