LeNet5模型搭建

文章目录

  • 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,而是用flattenself.fc1 = nn.Linear(120, 84)self.fc2 = nn.Linear(84, 10)def forward(self, x):x = F.relu(self.conv1(x))  # C1x = self.pool1(x)  # S2x = F.relu(self.conv2(x))  # C3x = self.pool2(x)  # S4x = F.relu(self.conv3(x))  # C5x = x.view(-1, 120)  # Flatten the tensor for fully connected layer# view可以替换为如下# x = self.flatten(x)  # Flatten the tensor for fully connected layerx = F.relu(self.fc1(x))  # F6x = self.fc2(x)  # Output layerreturn 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), # C1nn.ReLU(),nn.AvgPool2d(kernel_size=2, stride=2),               # S2nn.Conv2d(6, 16, kernel_size=5, stride=1, padding=0), # C3nn.ReLU(),nn.AvgPool2d(kernel_size=2, stride=2),               # S4nn.Conv2d(16, 120, kernel_size=5, stride=1, padding=0), # C5nn.ReLU())self.fc1 = nn.Linear(120, 84)  # F6self.fc2 = nn.Linear(84, 10)   # Output layerdef forward(self, x):x = self.model(x)              # Pass through sequential layersx = x.view(-1, 120)           # Flatten the tensor for fully connected layer  x = F.relu(self.fc1(x))       # F6x = self.fc2(x)               # Output layerreturn 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+1print("当前为第{}轮次,此时的学习率为{}".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 += 1print("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(),tagprint("预测结果:{},正确结果:{}".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*28torchvision.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*28return 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*28torchvision.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*28return 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/" + nameprint("待测试图像名称:{}".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 ToPILImagefrom LeNet5 import LeNet5# 自定义工具类
class Utils:def reSizeAndShape(self, input):img = Image.open(input)transform = torchvision.transforms.Compose([torchvision.transforms.Resize((28, 28)),  # 修改待测试图片的尺寸为28*28torchvision.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*28return 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/" + nameprint("待测试图像名称:{}".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.rhkb.cn/news/394766.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【最长递增子序列】python刷题记录

R4-dp 目录 常规方法遇到以下序列时就会变得错误 动态规划的思路 单调栈 ps: class Solution:def lengthOfLIS(self, nums: List[int]) -> int:#最简单的方法nlen(nums)if n<2:return nmx1for i in range(n):max_i1for j in range(i1,n):if nums[i]<nums[j]:nums…

RK3568平台(触摸篇)FT5X06驱动程序分析

一.设备树 &i2c1 {status "okay";myft5x06: my-ft5x0638 {compatible "my-ft5x06";reg <0x38>;reset-gpios <&gpio0 RK_PB6 GPIO_ACTIVE_LOW>;interrupt-parent <&gpio3>;interrupts-gpio <&gpio3 RK_PA5 GPI…

大数据-70 Kafka 高级特性 物理存储 日志存储 日志清理: 日志删除与日志压缩

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

K8S资源之NameSpace

作用 隔离资源(默认不隔离网络) 查看所有的NS kubectl get ns创建NS kubectl create ns hello删除NS kubectl delete ns hello

VUE基础快速入门

VUE 和 VUE-Cli VUE 是一种流行的渐进式JavaScript框架&#xff0c;用于构建Web用户界面它具有易学、轻量级、灵活性强、高效率等特点&#xff0c;并且可以与其他库和项目集成是目前最流行的前端框架之一VUE-Cli 称为“VUE脚手架”,它是由VUE官方提供的客户端&#xff0c;专门为…

简单Qt贪吃蛇项目

目录 先看效果 项目介绍 界面一&#xff1a;游戏大厅界面 界面二&#xff1a;关卡选择界面​编辑 界面三&#xff1a;游戏界面 游戏大厅页面 游戏关卡选择页面 游戏房间页面 封装贪吃蛇数据结构 初始化游戏房间界面 设置窗口大小、标题、图标等 蛇的移动 初始化贪…

RocketMQ Dashboard安装

RocketMQ Dashboard 是一个基于 Web 的管理工具&#xff0c;用于监控和管理 RocketMQ 集群。它提供了一个用户友好的界面&#xff0c;使管理员能够轻松地查看和操作 RocketMQ 系统中的各种组件和状态。 主要功能包括&#xff1a; 集群管理: 监控和管理 NameServer 和 Broker …

大数据-65 Kafka 高级特性 分区 Broker自动再平衡 ISR 副本 宕机恢复再重平衡 实测

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

【vulnhub】W34kn3ss 1靶机

安装靶机 下载地址&#xff1a;https://www.vulnhub.com/entry/w34kn3ss-1,270/# 信息收集 靶机扫描 nmap 192.168.93.0/24 打开端口为22、80、443 网址访问 目录扫描 dirsearch -u http://192.168.93.162 在网址后面拼接扫到的目录&#xff0c;在/test目录下发现信息 提…

微型导轨:光学仪器精准定位的支撑者

微型导轨是指宽度在25mm以下的导轨系统&#xff0c;通常由导轨和滑块组成&#xff0c;具有体积小、重量轻、精度高、噪音低、寿命长等特点。主要用于支撑和定位光学元件&#xff0c;如镜子、透镜、滤光片等。微型导轨通过提供高精度的运动控制&#xff0c;‌有利于提高设备的性…

【Tessent IJATG Users Manual】【Ch5】IJTAG Network Insertion

The IJTAG Network Insertion FlowIJTAG Network Insertion ExampleModification of the IJTAG Network Insertion Flow How to Edit or Modify a DftSpecificationEdit or Modify MethodDftSpecification Examples IJTAG Network Insertion 可以将已有的 instrument 连接起来&…

Docker学习(6):Docker Compose部署案例

一、docker-compose部署mysql 1、准备镜像 2、编写my.cnf配置文件 # 服务端参数配置 [mysqld] usermysql # MySQL启动用户 default-storage-engineINNODB # 创建新表时将使用的默认存储引擎 character-set-serverutf8mb4 # 设置mysql服务端默认字符集…

离线+树状数组,ABC253 F - Operations on a Matrix

一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 F - Operations on a Matrix 二、解题报告 1、思路分析 我们通过差分树状数组&#xff0c;可以轻松解决操作1 操作3我们也可以通过树状数组来获取对应列的值 关键是操作2会对操作3造成影响 所以我们先对…

【Linux】yum软件包管理器(使用、生态、yum源切换)

目录 1.yum-软件包管理器&#x1f638;1.1yum使用方法1.2什么是yum&#xff1f;&#x1f638;1.3yum的周边生态1.4yum源切换1.4.1 查看系统本身yum源1.4.2 软件源1.4.3yum源配置 1.yum-软件包管理器 以下操作需要联网的情况下进行 &#x1f638;1.1yum使用方法 安装软件时由于需…

【学习笔记】Day 7

一、进度概述 1、DL-FWI基础入门培训笔记 2、inversionnet_train 试运行——未成功 二、详情 1、InversionNet: 深度学习实现的反演 InversionNet构建了一个具有编码器-解码器结构的卷积神经网络&#xff0c;以模拟地震数据与地下速度结构的对应关系。 &#xff08;一…

03 库的操作

目录 创建查看修改删除备份和恢复查看连接情况 1. 创建 语法 CRATE DATABASE [IF NOT EXISTS] db_name [create_specification [, create_specification] …] create_specification:  CHARACTER SET charset_name  CPLLATE collation_name 说明&#xff1a; 大写的标识关键…

基于YOLOv8的小麦种子品质检测系统

基于YOLOv8的小麦种子品质检测系统 (价格85) 包含 [bad seed, healthy seed, impurity] 3个类 通过PYQT构建UI界面&#xff0c;包含图片检测&#xff0c;视频检测&#xff0c;摄像头实时检测。 &#xff08;该系统可以根据数据训练出的yolov8的权重文件&#xff0c;运用在…

【多线程-从零开始-捌】代码案例2—阻塞队列

什么是阻塞队列 阻塞队里是在普通的队列&#xff08;先进先出队列&#xff09;基础上&#xff0c;做出了扩充 线程安全 标准库中原有的队列 Queue 和其子类&#xff0c;默认都是线程不安全的 具有阻塞特性 如果队列为空&#xff0c;进行出队列操作&#xff0c;此时就会出现阻…

vue2知识点4(组件 全局组件 局部组件 父子组件的生命周期钩子函数 父子组件之间的数据传递 局部路由)

目录 一、组件 1. 介绍 2. 全局组件 使用全局组件 实例和组件之间的数据不互通 组件复用 data函数式和data对象的区别: 注意 3. 局部组件 全局组件和局部组件的区别: 注册多个子组件&#xff08;局部组件&#xff09; 4. 父子组件的生命周期钩子函数 加载渲染过程…