LeNet5模型搭建

news2024/9/25 15:21:23

文章目录

  • LeNet
  • 1 搭建模型
  • 2 训练模型
  • 3 测试模型
    • 3.1 预测一
    • 3.2 预测二

LeNet

LeNet 诞生于 1994 年,是最早的卷积神经网络之一,并且推动了深度学习领域的发展。自从 1988 年开始,在许多次成功的迭代后,这项由 Yann LeCun 完成的开拓性成果被命名为 LeNet5【一般LeNet即指代LeNet-5】。LeNet5 的架构基于这样的观点:(尤其是)图像的特征分布在整张图像上,以及带有可学习参数的卷积是一种用少量参数在多个位置上提取相似特征的有效方式。在那时候,没有 GPU 帮助训练,甚至 CPU 的速度也很慢。因此,能够保存参数以及计算过程是一个关键进展。这和将每个像素用作一个大型多层神经网络的单独输入相反。LeNet5 阐述了那些像素不应该被使用在第一层,因为图像具有很强的空间相关性,而使用图像中独立的像素作为不同的输入特征则利用不到这些相关性。

LeNet介绍_1:https://www.jiqizhixin.com/graph/technologies/6c9baf12-1a32-4c53-8217-8c9f69bd011b

LeNet介绍_2: https://www.analyticsvidhya.com/blog/2021/03/the-architecture-of-lenet-5/

1 搭建模型

在这里插入图片描述

在这里插入图片描述

该网络有 5 层,具有可学习的参数,因此命名为 Lenet-5。它有三组卷积层,结合了平均池化。在卷积层和平均池化层之后,我们有两个完全连接的层。
该模型的输入是 32 X 32 灰度图像,因此通道数为 1。
整体项目结构
在这里插入图片描述

模型搭建写法
方式一: 不使用nn.Sequential

class LeNet5(nn.Module):
    def __init__(self):
        super(LeNet5, self).__init__()
        self.conv1 = nn.Conv2d(1, 6, kernel_size=5, stride=1, padding=2)
        self.pool1 = nn.AvgPool2d(kernel_size=2, stride=2)
        self.conv2 = nn.Conv2d(6, 16, kernel_size=5, stride=1, padding=0)
        self.pool2 = nn.AvgPool2d(kernel_size=2, stride=2)
        self.conv3 = nn.Conv2d(16, 120, kernel_size=5, stride=1, padding=0)
        # self.flatten = nn.Flatten() 如果不使用view,而是用flatten
        self.fc1 = nn.Linear(120, 84)
        self.fc2 = nn.Linear(84, 10)

    def forward(self, x):
        x = F.relu(self.conv1(x))  # C1
        x = self.pool1(x)  # S2
        x = F.relu(self.conv2(x))  # C3
        x = self.pool2(x)  # S4
        x = F.relu(self.conv3(x))  # C5
        x = x.view(-1, 120)  # Flatten the tensor for fully connected layer
        # view可以替换为如下
        # x = self.flatten(x)  # Flatten the tensor for fully connected layer
        x = F.relu(self.fc1(x))  # F6
        x = self.fc2(x)  # Output layer
        return x

方式二:使用nn.Sequential

class LeNet5(nn.Module):
    def __init__(self):
        super(LeNet5, self).__init__()
        self.model = nn.Sequential(
            nn.Conv2d(1, 6, kernel_size=5, stride=1, padding=2), # C1
            nn.ReLU(),
            nn.AvgPool2d(kernel_size=2, stride=2),               # S2
            nn.Conv2d(6, 16, kernel_size=5, stride=1, padding=0), # C3
            nn.ReLU(),
            nn.AvgPool2d(kernel_size=2, stride=2),               # S4
            nn.Conv2d(16, 120, kernel_size=5, stride=1, padding=0), # C5
            nn.ReLU()
        )
        self.fc1 = nn.Linear(120, 84)  # F6
        self.fc2 = nn.Linear(84, 10)   # Output layer

    def forward(self, x):
        x = self.model(x)              # Pass through sequential layers
        x = x.view(-1, 120)           # Flatten the tensor for fully connected layer  
        x = F.relu(self.fc1(x))       # F6
        x = self.fc2(x)               # Output layer
        return x

写法上的对比:依据个人喜好

x = F.relu(self.conv1(x))  # C1
等价于
nn.Conv2d(1, 6, kernel_size=5, stride=1, padding=2), # C1
nn.ReLU()

view和flatten的对比:

  • view 是一个张量方法,用于重新调整张量的形状。它可以用于多种形状变换,包括展平张量。使用 view 展平张量时,需要明确指定新形状。
  • flatten 是 PyTorch 的一个模块类(nn.Flatten)或张量方法(tensor.flatten),专门用于展平操作。它可以自动将输入张量展平为二维张量,保持批次维度不变。

何时使用view:
当需要对张量进行复杂的形状变换时。
需要手动指定新形状。
确保张量是连续存储的。
何时使用flatten:
当只需要展平操作时。
不需要手动计算形状。
自动处理张量的存储连续性。

Softmax函数
Softmax函数是机器学习和深度学习中常用的一种归一化函数,通常用于多分类问题的输出层。它将一个向量中的未归一化数值(logits)转换为一个概率分布。

  • Softmax函数将 logits 映射到 (0, 1) 区间内,并且各类别的概率总和为1,这样可以避免数值上的不稳定性。
  • Softmax函数会放大概率大的类别和概率小的类别之间的差异,从而使模型更容易做出明确的决策。
  • Softmax函数的输出可以解释为每个类别的预测概率,这对决策和解释模型输出非常有用。

注意:
如果在模型的最后一层应用了Softmax,并且使用了nn.CrossEntropyLoss进行训练,那么可能会遇到数值不稳定的问题,因为nn.CrossEntropyLoss已经在内部应用了Softmax。所以:

1、如果在模型的forward中使用了softmax,那么应该使用nn.NLLLoss(负对数似然损失)来训练模型,因为它期望输入的是对数概率。
2、如果在forward中没有使用softmax,那么应该使用nn.CrossEntropyLoss进行训练【nn.CrossEntropyLoss 是 PyTorch 中用于多类分类问题的损失函数。它结合了 nn.LogSoftmax 和 nn.NLLLoss(负对数似然损失),因此可以直接处理未归一化的 logits,并计算出交叉熵损失。】

该模型在forward中并没有直接显示使用softmax函数做归一化处理,因此在训练过程中模型输出的结果就是未归一化的概率分布【未经过 softmax 的 logits】。而是在训练阶段定义了交叉熵作为损失函数。

2 训练模型

mnist数据集:https://yann.lecun.com/exdb/mnist/

MNIST(Modified National Institute of Standards and Technology)数据集是一个经典的手写数字识别数据集,被广泛用于机器学习和计算机视觉领域。
数据集概述
内容:MNIST 数据集包含 70,000 张手写数字图像,这些图像属于 10 个类别(0 到 9),每个类别代表一个数字。
分布:
训练集:60,000 张图像
测试集:10,000 张图像
图像大小:每张图像的尺寸为 28x28 像素。
图像格式:灰度图像,每个像素值是从 0 到 255 的整数,其中 0 代表黑色,255 代表白色,其他值代表不同的灰度级别。
数据集文件结构
MNIST 数据集通常分为四个主要文件:
train-images-idx3-ubyte: 训练集图像数据。
train-labels-idx1-ubyte: 训练集标签数据。
t10k-images-idx3-ubyte: 测试集图像数据。
t10k-labels-idx1-ubyte: 测试集标签数据。
样本图片:
在这里插入图片描述

import torch
from torch import nn
from torch import optim
from LeNet5 import LeNet5
from datetime import datetime
from torch.utils.data import DataLoader
from torch.optim import lr_scheduler
from torchvision import datasets, transforms
import os

# 先判断是否有data目录
if not os.path.exists("../data"):
  os.mkdir("../data")
# 获取Mnist数据
data_train = datasets.MNIST(root="../data", train=True, transform=transforms.ToTensor(), download=True)
data_test = datasets.MNIST(root="../data", train=False, transform=transforms.ToTensor(), download=True)
# 获取数据集的长度
data_train_len = len(data_train)
data_test_len = len(data_test)
# DataLoader加载数据
train_dataLoader = DataLoader(batch_size=16, dataset=data_train, shuffle=True)
test_dataLoader = DataLoader(batch_size=16, dataset=data_test, shuffle=True)

# 定义是否使用GPU
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print("当前设备{}".format(device))

# 实例化模型
my_nn = LeNet5().to(device)
# 定义交叉熵损失函数
lossFn = nn.CrossEntropyLoss()
# 定义优化器 随机梯度下降
start_lr = 0.01
optimizer = optim.SGD(my_nn.parameters(), lr=start_lr, momentum=0.9)
# 学习率衰减 ,每十轮衰减一次 ,衰减为原来的0.1
lr_scheduler = lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.1)


# 定义训练函数
def train_net(dataloader, model, loss_fn, optim, lr_scheduler):
    model.train()
    # 一轮次的损失值、一轮次的正确率、一轮次被分为几个批次
    epoch_loss, epoch_acc, batch_count = 0.0, 0.0, 0
    # 学习率衰减的几轮
    cur_epoch=lr_scheduler.last_epoch+1
    print("当前为第{}轮次,此时的学习率为{}".format(cur_epoch,optim.param_groups[0]['lr']))
    for data in dataloader:
        img, tag = data
        # 将数据移动到设备上【cpu或者gpu,看支持情况】
        img, tag = img.to(device), tag.to(device)
        # 记录该批次模型训练输出结果
        output = model(img)
        # 记录该批次的损失值
        batch_loss = loss_fn(output, tag)
        # 对 output 张量的每一行取最大值和对应的索引。_ 保存最大值,pred 保存最大值的索引,也就是预测的类别。
        pred_num, pred = torch.max(output, axis=1)
        # 将正确的预测数量除以总的样本数量,得到当前批次的准确率
        cur_acc = torch.sum(tag == pred) / output.shape[0]
        # 梯度清零,pytorch框架需要手动调用
        optim.zero_grad()
        # 反向传播计算梯度
        batch_loss.backward()
        # 更新模型参数
        optim.step()
        # 累加批次损失值
        epoch_loss += batch_loss.item()
        # 累加批次正确率
        epoch_acc += cur_acc.item()
        # 记录总批次数
        batch_count += 1
    """
    lr_scheduler.step() 有一个内部计数器来跟踪训练进度[轮次]。
    每次调用 step() 方法时,StepLR 会检查当前的 epoch 数量是否达到了 step_size 的倍数。
    如果是,它会按照设定的 gamma 参数更新学习率;如果不是,它不会调整学习率。
    """
    # 每个epoch结束后,更新学习率
    lr_scheduler.step()
    print("Train_Avg_Batch_Loss:{} ".format(epoch_loss / batch_count))
    print("Train_Avg_Batch_Acc: {}".format(epoch_acc / batch_count))


# 定义模型测试方法
def test_net(dataloader, model, loss_fn):
    model.eval()
    # 一轮次的损失值、一轮次的正确率、一轮次被分为几个批次
    epoch_loss, epoch_acc, batch_count = 0.0, 0.0, 0
    # 禁用梯度计算,节省内存和加速计算【在test阶段,是不需要梯度计算的】
    with torch.no_grad():
        for data in dataloader:
            img, tag = data
            # 将数据移动到设备上【cpu或者gpu,看支持情况】
            img, tag = img.to(device), tag.to(device)
            # 记录该批次模型训练输出结果
            output = model(img)
            # 记录该批次的损失值
            batch_loss = loss_fn(output, tag)
            # 对 output 张量的每一行取最大值和对应的索引。pred_num存最大值,pred 保存最大值的索引,也就是预测的类别。
            pred_num, pred = torch.max(output, axis=1)
            # 将正确的预测数量除以总的样本数量,得到当前批次的准确率
            cur_acc = torch.sum(tag == pred) / output.shape[0]
            # 累加批次损失值
            epoch_loss += batch_loss.item()
            # 累加批次正确率
            epoch_acc += cur_acc.item()
            # 记录总批次数
            batch_count += 1
        print("Test_Avg_Batch_Loss:{} ".format(epoch_loss / batch_count))
        print("Test_Avg_Batch_Acc: {}".format(epoch_acc / batch_count))
    # 返回批次正确率
    return epoch_acc / batch_count


"""
只保存两个模型:
    1、测试结果最好的那一个模型
    2、最后的那一个模型
"""
# 开始训练模型
epoch = 20
max_acc = 0.0
# 计时
start_time=datetime.now()
for i in range(epoch):
    print("-----第{}轮训练开始-----".format(i + 1))
    train_net(train_dataLoader, my_nn, lossFn, optimizer,lr_scheduler)
    acc = test_net(test_dataLoader, my_nn, lossFn)
    if acc > max_acc:
        save_dir = "../save_models"
        if not os.path.exists(save_dir):
            os.mkdir(save_dir)
        max_acc = acc
        # 保存模型[只保留模型参数]
        torch.save(my_nn.state_dict(), "../save_models/MaxAccModel.pth")
        print("save max_acc ok!")
    if i + 1 == epoch:
        torch.save(my_nn.state_dict(), "../save_models/LastModel.pth")
        print("save last ok!")
end_time=datetime.now()
print("start_time:{}".format(start_time))
print("end_time:{}".format(end_time))
print("{}训练总用时:{}".format(device,end_time-start_time))
print("Done!")

3 测试模型

ToPILImage 是 torchvision.transforms 中的一个类,用于将张量(Tensor)转换为 PIL 图像。
它不仅适用于灰度图像,也适用于彩色图像。
具体来说,它的作用是将 PyTorch 张量转换为
Python Imaging Library (PIL) 的图像对象,以便进行图像显示或保存。
灰度图像:如果输入的张量是 [1, H, W],它会被解释为灰度图像。
彩色图像:如果输入的张量是 [3, H, W],它会被解释为 RGB 彩色图像。
使用步骤:

from torchvision.transforms import ToPILImage
showImg = ToPILImage()
showImg(img).show()

img=Variable(torch.unsqueeze(img,dim=0).float(),requires_grad=False).to(device)
1、torch.unsqueeze(img, dim=0) 在第 0 维增加一个维度,变成 [1, C, H, W]。这相当于添加了一个 batch 维度,
因为神经网络通常需要输入形状为 [N, C, H, W] 的数据,其中 N 是批次大小。
2、将张量的数据类型转换为 float 类型。这是因为神经网络的输入通常要求是浮点数。
3、Variable 是 PyTorch 的一个类,用于封装张量。尽管在新版的 PyTorch 中直接使用张量即可,但这种用法仍然适用。
4、requires_grad=False 表示在这次操作中不需要计算梯度。因为我们只是在进行推理,不需要反向传播。
5、to(device)将张量移动到指定设备(CPU 或 GPU)
新版可以直接写作:img=torch.unsqueeze(img, dim=0).float().to(device)

3.1 预测一

用mnist测试数据集去做预测【也称为:推理】

import torch
from torch.autograd import Variable
from torchvision import datasets
from torchvision.transforms import transforms
from LeNet5 import LeNet5
from torchvision.transforms import ToPILImage

# 获取Mnist数据
data_test = datasets.MNIST(root="../data", train=False, transform=transforms.ToTensor(), download=True)

# 加载模型
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
LeNet5_Model = LeNet5().to(device)
LeNet5_Model.load_state_dict(torch.load("../save_models/MaxAccModel.pth", map_location=device))

# 可视化图片
"""
ToPILImage 是 torchvision.transforms 中的一个类,用于将张量(Tensor)转换为 PIL 图像。
它不仅适用于灰度图像,也适用于彩色图像。
具体来说,它的作用是将 PyTorch 张量转换为 
Python Imaging Library (PIL) 的图像对象,以便进行图像显示或保存。
灰度图像:如果输入的张量是 [1, H, W],它会被解释为灰度图像。
彩色图像:如果输入的张量是 [3, H, W],它会被解释为 RGB 彩色图像。
"""
showImg = ToPILImage()

# 预测标签【要不要都行,】
tags = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

for i in range(5):
    img,tag=data_test[i][0],data_test[i][1]
    showImg(img).show()
    """
    1、torch.unsqueeze(img, dim=0) 在第 0 维增加一个维度,变成 [1, C, H, W]。这相当于添加了一个 batch 维度,
    因为神经网络通常需要输入形状为 [N, C, H, W] 的数据,其中 N 是批次大小。
    2、将张量的数据类型转换为 float 类型。这是因为神经网络的输入通常要求是浮点数。
    3、Variable 是 PyTorch 的一个类,用于封装张量。尽管在新版的 PyTorch 中直接使用张量即可,但这种用法仍然适用。
    4、requires_grad=False 表示在这次操作中不需要计算梯度。因为我们只是在进行推理,不需要反向传播。
    5、to(device)将张量移动到指定设备(CPU 或 GPU)
    """
    img=Variable(torch.unsqueeze(img,dim=0).float(),requires_grad=False).to(device)
    # 可改写为: img = torch.unsqueeze(img, dim=0).float().to(device)
    with torch.no_grad():
        pred=LeNet5_Model(img)
        """
        print(pred) ——》
        tensor([[ -8.3555,   1.5206,  -3.0009,   7.4744,   1.2823,  -3.6160, -12.8001,
          20.1332,  -6.3026,   4.4215]])
        """
        # 这里对于mnist数据集,索引正好就是和标签对应——》比如:1索引位置记录的正好是1
        # 所以去获取概率最大的位置的索引值,直接就可以和正确的类别标签作对比
        predicted,actual=pred.argmax(1).item(),tag
        print("预测结果:{},正确结果:{}".format(predicted,actual))

在这里插入图片描述

3.2 预测二

彩色数字图像预测【小白刚学习,纯好奇自己瞎搞,同一个模型到这里预测结果很垃圾
在这里插入图片描述
模型训练是用的灰度图像是1通道的,而这里的待预测图像都是彩色的三通道数据。所以这里不同于上面的mnist数据集那样直接可以用,而是需要对图片数据做出一定的处理之后,才可以。
自定义处理函数

class Utils:
    def reSizeAndShape(self, input):
        img = Image.open(input)
        transform = torchvision.transforms.Compose(
            [
                torchvision.transforms.Resize((28, 28)),  # 修改待测试图片的尺寸为28*28
                torchvision.transforms.ToTensor()  # 将待测试图片转化为可以被接受的tensor类型
            ]
        )
        img = transform(img)
        # print(img.shape) --》torch.Size([3, 28, 28]) 待测图片并非灰度图像,而是彩色三通道的图像
        # 训练模型是灰度图像,通道数为1,所以修改为1通道,那么N值给-1,自动计算填充 (N,C,W,H)
        img = torch.reshape(img, (-1, 1, 28, 28))
        # print(img.shape) ——》torch.Size([3, 1, 28, 28])  原来图像是3*28*28的元素个数,重塑形状必定是保持元素个数不变,因此自然就是3*1*28*28
        return img

1、需要将图片尺寸修改为 28 ∗ 28 28*28 2828的,因为mnist数据集的图片是该尺寸,模型是那该数据集训练的,所以先改图片尺寸。
2、转换图片数据格式,换成模型网络支持的tensor格式
3、重塑图像数据形状,将彩色3通道改为灰度1通道,N给值-1,让其更具计算结果自动填充。(N,C,W,H)

原来图像是 3 ∗ 28 ∗ 28 3*28*28 32828的元素个数,重塑形状必定是保持元素个数不变,因此自然就是 3 ∗ 1 ∗ 28 ∗ 28 3*1*28*28 312828
经此变换,我的理解是:就是将原始数据一张彩色图像的元素,切分成了三份,相当于一张彩色图片数据被分为了三张灰度图像数据。依据下面的输出结果似乎可以得到验证。
print(output) ——》 tensor([[ 0.6269, 0.4243, 1.3495, 0.0457, -0.2867, -0.1191, -1.6458, 1.4099, -0.7365, -0.2978],
[ 0.6269, 0.4243, 1.3495, 0.0457, -0.2867, -0.1191, -1.6458, 1.4099,-0.7365, -0.2978],
[ 0.6269, 0.4243, 1.3495, 0.0457, -0.2867, -0.1191, -1.6458, 1.4099, -0.7365, -0.2978]], grad_fn=<AddmmBackward0>)

import torch
import torchvision
from PIL import Image
from LeNet5 import LeNet5


# 自定义工具类
class Utils:
    def reSizeAndShape(self, input):
        img = Image.open(input)
        transform = torchvision.transforms.Compose(
            [
                torchvision.transforms.Resize((28, 28)),  # 修改待测试图片的尺寸为28*28
                torchvision.transforms.ToTensor()  # 将待测试图片转化为可以被接受的tensor类型
            ]
        )
        img = transform(img)
        # print(img.shape) --》torch.Size([3, 28, 28]) 待测图片并非灰度图像,而是彩色三通道的图像
        # 训练模型是灰度图像,通道数为1,所以修改为1通道,那么N值给-1,自动计算填充 (N,C,W,H)
        img = torch.reshape(img, (-1, 1, 28, 28))
        # print(img.shape) ——》torch.Size([3, 1, 28, 28])  原来图像是3*28*28的元素个数,重塑形状必定是保持元素个数不变,因此自然就是3*1*28*28
        return img


# 待测试图片
imgs = [
    "1.png", "3_1.png", "4_3.png", "5.png",
    "3.png", "4.png", "4_2.png", "6.png","7.png","8.png"
]
# 加载模型
LeNet5_Model = LeNet5()
LeNet5_Model.load_state_dict(torch.load("../save_models/MaxAccModel.pth", map_location=torch.device('cpu')))
# 实例化工具类
utils = Utils()
# 测试
for name in imgs:
    img_path = "../test_imgs/" + name
    print("待测试图像名称:{}".format(name.split(".")[0]))
    # 对图片做出处理
    img = utils.reSizeAndShape(img_path)
    # 放入模型预测
    output = LeNet5_Model(img)
    # print(output.shape) ——》torch.Size([3, 10])
    """
    该结果就是与上面定义的图像处理函数有关,reshape之后,就是将原始数据,切分成了三份,相当于一张彩色图片数据被分为了三张灰度图像数据
    # print(output)  ——》 
    tensor([[ 0.6269,  0.4243,  1.3495,  0.0457, -0.2867, -0.1191, -1.6458,  1.4099,
         -0.7365, -0.2978],
        [ 0.6269,  0.4243,  1.3495,  0.0457, -0.2867, -0.1191, -1.6458,  1.4099,
         -0.7365, -0.2978],
        [ 0.6269,  0.4243,  1.3495,  0.0457, -0.2867, -0.1191, -1.6458,  1.4099,
         -0.7365, -0.2978]], grad_fn=<AddmmBackward0>)
    """
    print(output.argmax(1)) # ——》tensor([3, 3, 3]) 横向取出最低维的概率最大的索引
    # print("图片预测结果为:{}".format(output.argmax(1)[0].item()))
    print("\n=================\n")

结果:很差劲,就对一个,哈哈。
在这里插入图片描述

借助ToPILImage去将分割后的张量转换为图片,似乎和我想的将一张图片数据分成三份灰度的,会产生杂乱图片的结果不一样。【比较懵,待后续学习去理解】
在这里插入图片描述

import torch
import torchvision
from PIL import Image
from torchvision.transforms import ToPILImage

from LeNet5 import LeNet5


# 自定义工具类
class Utils:
    def reSizeAndShape(self, input):
        img = Image.open(input)
        transform = torchvision.transforms.Compose(
            [
                torchvision.transforms.Resize((28, 28)),  # 修改待测试图片的尺寸为28*28
                torchvision.transforms.ToTensor()  # 将待测试图片转化为可以被接受的tensor类型
            ]
        )
        img = transform(img)
        # print(img.shape) --》torch.Size([3, 28, 28]) 待测图片并非灰度图像,而是彩色三通道的图像
        # 训练模型是灰度图像,通道数为1,所以修改为1通道,那么N值给-1,自动计算填充 (N,C,W,H)
        img = torch.reshape(img, (-1, 1, 28, 28))
        # print(img.shape) ——》torch.Size([3, 1, 28, 28])  原来图像是3*28*28的元素个数,重塑形状必定是保持元素个数不变,因此自然就是3*1*28*28
        return img


# 待测试图片
imgs = [
    "3_1.png"
]
# 加载模型
LeNet5_Model = LeNet5()
LeNet5_Model.load_state_dict(torch.load("../save_models/MaxAccModel.pth", map_location=torch.device('cpu')))
# 实例化工具类
utils = Utils()
showImg = ToPILImage()
# 测试
for name in imgs:
    img_path = "../test_imgs/" + name
    print("待测试图像名称:{}".format(name.split(".")[0]))
    # 对图片做出处理
    img = utils.reSizeAndShape(img_path)
    for i in range(3):
        # print(img[i])
        # print(img[i].shape)
        showImg(img[i]).show()
    # 放入模型预测
    output = LeNet5_Model(img)
    print(output.argmax(1)) # ——》tensor([3, 3, 3]) 横向取出最低维的概率最大的索引
    # print("图片预测结果为:{}".format(output.argmax(1)[0].item()))
    print("\n=================\n")

结果是这样的:不是乱的,而是将彩色进行了灰度化处理,而且预测也是错的【tensor[8,4,2],里面就是预测的标签,三个都不对】

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1992951.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

阿里云ECS之AMD实例

阿里云ECS之AMD实例 计算型实例规格族c8a关于AMD实例的使用AMD的应用场景 关于AMD实例&#xff0c;AMD实例也是云服务器ECS中的一种&#xff0c;AMD实例是阿里云新推出的重磅产品&#xff0c;基于AMD EPYCTM微处理器架构&#xff0c;该架构属于x86架构。目前已知的AMD实例规格族…

【RISC-V设计-09】- RISC-V处理器设计K0A之CIC

【RISC-V设计-09】- RISC-V处理器设计K0A之CIC 文章目录 【RISC-V设计-09】- RISC-V处理器设计K0A之CIC1.简介2.顶层设计3.端口说明4.代码设计5.仲裁代码6.总结 1.简介 核内中断控制器&#xff08;Core Interrupt Controller&#xff0c;简称CIC&#xff09;是管理和仲裁中断的…

SQL注入实例(sqli-labs/less-18)

0、初始页面 先使用brup爆破密码&#xff0c;账号admin&#xff0c;密码admin 1、确定闭合字符 判断注入点在post请求参数的User-agent处 闭合字符为单引号 2、爆库名 3、爆表名 4、爆列名 5、查询最终目标 在index.php中有这么一句 $insert"INSERT INTO security.uage…

MySQL基础——数据库客户端,数据库服务器,数据库(目录)

什么是数据库 具体&#xff1a;mysqld服务器结合内部的存储引擎所实现对数据库文件中的数据增删查改的一套方案 ——在磁盘或内存中存储特定结构组织的数据----在磁盘中存储的一套数据方案&#xff0c; 你给我字段或要求&#xff0c;我直接给你结果就行 整体&#xff1a;用户通…

gradio之进度条

输出控件显示进度&#xff0c;进度结束显示控件结果 import gradio as gr import timedef slowly_reverse(word, progressgr.Progress()):progress(0, desc"Starting")time.sleep(1)progress(0.05)new_string ""for letter in progress.tqdm(word, desc&…

概率论原理精解【9】

文章目录 集类拓扑空间基 参考文献 集类 C是一个集类&#xff08;以G的某些子集为元素的集合称为G的集类&#xff09;。 A i ∈ C , ∩ i 1 n A i ∈ C , 此为有限交封闭 C 所得集类 C ∩ f A_i \in C,\cap_{i1}^nA_i \in C,此为有限交封闭C所得集类C_{\cap f} Ai​∈C,∩i1n…

2024年黑龙江公安招聘报名流程(建议电脑)

2024年黑龙江省公安机关人民警察专项招录公告&#xff08;2810人&#xff09; 报名时间&#xff1a;2024年8月16日9:00至8月20日17:00 查询资格审查结果时间&#xff1a;2024年8月16日9:00至8月20日17:00 缴费确认时间&#xff1a;2024年8月16日9:00至8月21日17:00 网上打印准考…

程序员保碗之策

欢迎来到 破晓的历程的 博客 ⛺️不负时光&#xff0c;不负己✈️ 文章目录 1. **持续深化技术实力&#xff1a;从基础到前沿**夯实基础紧跟技术前沿跨领域融合 2. **提升实战能力&#xff1a;从项目到产品**参与实际项目自主实践产品思维 3. **建立个人品牌与影响力&#xff…

代码随想录算法训练营day36:动态规划04:1049.最后一块石头的重量II;494.目标和;474.一和零

1049.最后一块石头的重量II 力扣题目链接(opens new window) 有一堆石头&#xff0c;每块石头的重量都是正整数。 每一回合&#xff0c;从中选出任意两块石头&#xff0c;然后将它们一起粉碎。假设石头的重量分别为 x 和 y&#xff0c;且 x < y。那么粉碎的可能结果如下&…

WEB应用(十五)---文件包含

文件包含的概念 在各种开发语言中都提供了内置的文件包含函数&#xff0c;可以使得开发人员在一个代码文件中直接包含&#xff08;引入&#xff09;另外一个代码文件。 由于文件包含可以达到复用和方便修改的目的&#xff0c;在代码设计中常常使用。 大多数情况下&#xff0…

构建生成工具cmake的使用(2)

一.制作库文件 Linux下动态库是以 .lib开头&#xff0c;以 .so为结尾。静态库以 .lib 开头&#xff0c;.a为结尾。 Windows下动态库以dll为结尾&#xff0c;以 .lib为结尾 1.1.cmake编写生成动态库 我们想把add.cc和sub.cc打成一个动态库 …

Spring Boot 3.x Rest API最佳实践之统一响应结构

上一篇&#xff1a;Spring Boot 3.x Rest API最佳实践之API实现 下一篇&#xff1a;Spring Boot 3.x Rest API统一异常处理最佳实践 前面我们完成了电商示例API的设计和简单实现&#xff0c;本小节在此基础上完成统一响应结构的实战。 文章目录 定义Response响应体拦截Rest A…

【Day04】0基础微信小程序入门-学习笔记

文章目录 基础加强学习目标自定义组件1. 创建与引用2. 样式3. 数据、方法和属性4. 数据监听器5. 纯数据字段6. 组件生命周期6.1 created6.2 attached&#xff08;使用最多&#xff09;6.3 detached6.4 定义生命周期函数 7. 组件所在页面的生命周期7.1 定义使用7.2 生成随机的RG…

【QGroundControl二次开发】九. QGC地面站替换自定义MAVLINK协议

一. 生成自定义mavlink协议 具体操作参考之前文章->【QGroundControl二次开发】四.QGC自定义MAVLink消息 -------------------------下面为具体操作--------------------------- 在mavlink源码下的mavlink-master\message_definitions\v1.0\common.xml加入自定义协议&…

(javaweb)请求响应postman

目录 一.请求响应概述 二.请求参数的接收及封装 1.postman 2. 简单参数 3.实体参数 4.数组集合参数 5.日期参数 6.JSON参数 二.设置响应数据 一.请求响应概述 1.Tomcat又称为servlet容器 前端浏览器发起请求携带http请求数据&#xff0c;web服务器负责请求协议的解析&a…

网站开发涉及到的技术内容介绍——后端PHP(1)

一、PHP简介 PHP(全称:Hypertext Preprocessor (超文本预处理器))是一种创建动态交互性网站的服务器端脚本语言( PHP代码可以放在HTML文档中的任何位置;且PHP 脚本是在服务器上运行,然后将纯 HTML 结果发送回浏览器)且PHP 是免费的,并且使用非常广泛。同类的后端语言有…

Element学习(布局组件、案例操作)(4)

1、页面整体的布局 2、找到这种布局对应的代码&#xff08;复制——>粘贴到标签<div>中&#xff09; <el-container><el-header>Header</el-header><el-container><el-aside width"200px">Aside</el-aside><el-main…

8.8C++作业

在类结构体中&#xff0c;运用 成员函数或友元函数 实现算术运算符重载 #include <iostream>using namespace std;class Stu {friend const Stu operator/(const Stu &R,const Stu &L);friend const Stu operator%(const Stu &R,const Stu &L); private…

【2024年电赛H题自动行驶小车】省一学长手把手从零教你学MSPM0

一、前前言 第十二届浙江省大学生电子设计竞赛终于落下帷幕了&#xff0c;名单已公示&#xff0c;几家欢喜几家愁&#xff1f;我觉得每一位经历过电赛的朋友都称得上英雄&#xff0c;我想我们所学到的并非是“省一等”或“成功参赛奖”一个头衔能囊括的&#xff0c;相信真正有…

一篇带你了解kickstart无人值守以及pxe实现服务器自动部署

目录 使用背景 pxe原理 一、kickstart无人值守实验 实验环境&#xff1a; kickstart脚本制作&#xff1a; http服务 DCHP服务 定位需要装的机器上&#xff08;已使用光盘&#xff09; 二、PXE实验 实验环境&#xff1a;在上个实验环境的基础上 使用已经做好的dhcp&am…