基于LeNet5实现手写数字识别,可视化卷积层。

LeNet5

CNN卷积网络的发展史

1. LetNet5(1998)
2. AlexNet(2012)
3. ZFNet(2013)
4. VGGNet(2014)
5. GoogLeNet(2014)
6. ResNet(2015)
7. DenseNet(2017)
8. EfficientNet(2019)
9. Vision Transformers(2020)
10. 自适应卷积网络(2021)

上面列出了发展到现在CNN的一些经典的网络模型,我将持续不断更新学习上述神经网络的笔记。共勉!
当我开始学现有的神经网络的时候我总是在想,卷积层到底是越多越好呢,还是越少越好?他们是怎么确保自己构建的神经网络模型能够达到最优的?非常好奇每一次卷积之后的图像变成了什么样子。这是本人自己学习LetNet5时的一些笔记,希望对大家有所帮助。并且也希望可以在后续学习中,逐步找到答案。也希望大佬们可以给我一些建议。在笔记最后会有一句话,希望可以帮助到大家。

这里是原论文地址:Gradient-Based Learning Applied to Document Recognition
接下里和你一起详解论文!

这里我就不去详解论文了,有感兴趣的可以点击这个来理解该论文Gradient-Based Learning Applied to Document Recognition

该论文的主要内容是以字符识别为例,证明使用基于梯度的反向传播训练的多层神经网络优于手动提取特征的识别算法

目录:

  • LeNet5
    • CNN卷积网络的发展史
    • 目录:
    • 1. 梯度下降
    • 2. CNN
    • 3. LeNet5网络结构
    • 4. 手写数字识别代码实现
      • (1) 代码实现
        • 1.1 数据预处理
        • 1.2 构建网络模型
          • (1) LeNet5网络结构
          • (2)自建网络模型结构
          • 1.2.1 查看网络模型结构
        • 1.3 训练模型
        • 1.4 测试模型
          • 1.2.2 查看自建网络模型的结构
        • 1.3 训练模型(自建模型)
        • 1.4 测试模型(自建模型)
        • 1.5 预测模型
      • (2) 卷积层可视化

这里我会讲解一些个人觉得比较重要的部分,并且会给出一些代码,方便大家理解。

  • 梯度是如何计算的?
  • 以及他在更新参数中是起到什么作用?

1. 梯度下降

假如你有一个线性模型 f w , b ( x ( i ) ) f_{w,b}(x^{(i)}) fw,b(x(i)):
f w , b ( x ( i ) ) = w x ( i ) + b (1) f_{w,b}(x^{(i)}) = wx^{(i)} + b \tag{1} fw,b(x(i))=wx(i)+b(1)
在线性回归中,通过最小化我们的预测 f w , b ( x ( i ) ) f_{w,b}(x^{(i)}) fw,b(x(i))和实际数据 y ( i ) y^{(i)} y(i)之间的误差来利用输入训练数据来拟合参数 w w w b b b。这个度量被称为代价函数$ J(w,b) 。在训练中,你测量所有训练样本的代价函数 。在训练中,你测量所有训练样本的代价函数 。在训练中,你测量所有训练样本的代价函数x{(i)},y{(i)}$
J ( w , b ) = 1 2 m ∑ i = 0 m − 1 ( f w , b ( x ( i ) ) − y ( i ) ) 2 (2) J(w,b) = \frac{1}{2m} \sum\limits_{i = 0}^{m-1} (f_{w,b}(x^{(i)}) - y^{(i)})^2\tag{2} J(w,b)=2m1i=0m1(fw,b(x(i))y(i))2(2)

当所有预测值和实际值之间的误差 J ( w , b ) J(w,b) J(w,b)最小时,模型拟合层度越好,参数 w w w b b b就达到了最优值。

梯度下降如下所示:

repeat until convergence: { w = w − α ∂ J ( w , b ) ∂ w b = b − α ∂ J ( w , b ) ∂ b } \begin{align*} \text{repeat}&\text{ until convergence:} \; \lbrace \newline \; w &= w - \alpha \frac{\partial J(w,b)}{\partial w} \tag{3} \; \newline b &= b - \alpha \frac{\partial J(w,b)}{\partial b} \newline \rbrace \end{align*} repeatwb} until convergence:{=wαwJ(w,b)=bαbJ(w,b)(3)
其中,参数 w w w b b b同时更新。
梯度定义为:
∂ J ( w , b ) ∂ w = 1 m ∑ i = 0 m − 1 ( f w , b ( x ( i ) ) − y ( i ) ) x ( i ) ∂ J ( w , b ) ∂ b = 1 m ∑ i = 0 m − 1 ( f w , b ( x ( i ) ) − y ( i ) ) \begin{align} \frac{\partial J(w,b)}{\partial w} &= \frac{1}{m} \sum\limits_{i = 0}^{m-1} (f_{w,b}(x^{(i)}) - y^{(i)})x^{(i)} \tag{4}\\ \frac{\partial J(w,b)}{\partial b} &= \frac{1}{m} \sum\limits_{i = 0}^{m-1} (f_{w,b}(x^{(i)}) - y^{(i)}) \tag{5}\\ \end{align} wJ(w,b)bJ(w,b)=m1i=0m1(fw,b(x(i))y(i))x(i)=m1i=0m1(fw,b(x(i))y(i))(4)(5)

在学习过程中我们总会好奇电脑是怎么找到最优解的,上述计算的过程就可以帮助我们实现,在高中我们我们就学过斜率是函数变化的方向放到模型传播中如何理解呢?

模型前向传播得到输出值通过反向传播计算梯度,通过不断重复,让曲线或者点不断的按照我们预期的方向移动。直到梯度为0,就达到了最优解。
这里大家可以想象自己就是一个点,你想要实现一个目标,神经网路喂入了一个值,该值是你要达到的方向,然后该值经过前向传播,反向传播后得到梯度(斜率)就是给你的指引。

graph LR
A[你] -->|1. 神经网路喂入一个值,前向传播| B[目标(输出值)]
B -->|2. 反向传播得到梯度(指引,思考方式)| A
A -->|3. 重复1,2步骤(又喂入一个值,前向传播,反向传播)直到梯度为0(学习到实现目标最优的方法)| B

2. CNN

我们都知道卷积的主要思想是:通过卷积核在图片上滑动,通过小区域的多次卷积,最后组合,这样子不论图片经过旋转,拉伸等操作后,都可以识别出两张图片是否一样,并且可以通过下采样(池化)缩小图片尺寸,减小计算量,最后通过全连接层进行分类。但是我很好奇:

  1. 卷积之后提取的图片是什么样的?
  2. 为什么LeNet5是7层网络,如果少一层会差多少,多一层又会怎样?

接下来我们通过代码来进行可视化分析!

3. LeNet5网络结构

在这里插入图片描述

这里我只给出LetNet5的网络结构图并不做详解,在此篇博客中有详细的讲解大家可以点击这里

在这里插入图片描述

接下来我会用代码实现LetNet5神经网络,并且可视化每一层卷积之后得到的图像。希望可视化之后可以让大家对CNN这个抽象的概念有更清晰的理解。

这里推荐一个可视化卷积的网站感兴趣的可以点击此处进行访问卷积可视化.该网站可供我们查看每一层卷积的图像,以及卷积过程和参数。

4. 手写数字识别代码实现

(1) 代码实现

import torch
import torchvision
from torch.utils.data import DataLoader
import torch.nn as nn
from torchsummary import summary
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
1.1 数据预处理
# 数据预处理
# 1. 将图片转换为Tensor
transform = torchvision.transforms.Compose([torchvision.transforms.ToTensor()])
# 2. 加载数据集
train_data = torchvision.datasets.MNIST('./data',train=True,transform=transform,download=True)
test_data = torchvision.datasets.MNIST('./data',train=False,transform=transform,download=True)
# 3. 创建数据加载器
train_dataloder = DataLoader(train_data,batch_size=64)
test_dataloder = DataLoader(test_data,batch_size=64)
# 4. 计算数据集大小
len_train_data = len(train_data)
len_test_data = len(test_data)print(len_train_data,len_test_data)
60000 10000
1.2 构建网络模型

下面我们编写LetNet5的网络结构,两个卷积层,两个池化层,三个全连接层。

(1) LeNet5网络结构
class MY_LeNet5(nn.Module):def __init__(self):super(MY_LeNet5,self).__init__()self.net = nn.Sequential(nn.Conv2d(in_channels=1,out_channels=6,kernel_size=5,padding=2),nn.ReLU(),nn.MaxPool2d(kernel_size=2,stride=2),nn.Conv2d(6,16,5),nn.ReLU(),nn.MaxPool2d(2,2),nn.Flatten(),nn.Linear(16*5*5,120),nn.ReLU(),nn.Linear(120,84),nn.ReLU(),nn.Linear(84,10))def forward(self,x):return self.net(x)
(2)自建网络模型结构

我们往Lenet5中加入一层卷积核为5*5的卷积层层看看变化

class My_self_Lenet(nn.Module):def __init__(self):super(My_self_Lenet,self).__init__()self.net = nn.Sequential(# Reshape(),nn.Conv2d(in_channels=1,out_channels=6,kernel_size=5,padding=2),nn.ReLU(),nn.MaxPool2d(kernel_size=2,stride=2),nn.Conv2d(6,16,5),nn.ReLU(),nn.MaxPool2d(2,2),nn.Conv2d(16,32,5),nn.ReLU(),nn.Flatten(),nn.Linear(32,120),nn.ReLU(),nn.Linear(120,84),nn.ReLU(),nn.Linear(84,10))def forward(self,x):return self.net(x)
1.2.1 查看网络模型结构

我们可以从以下两个方面来评估模型,分别是:

  • 模型复杂度:
  1. 计算量
  2. 参数
  3. 显存占用
  • 模型性能:
  1. 准确率
  2. 精确度
  3. 召回率
  4. F1分数
  5. MSE
  6. R 2 R^2 R2分数

本次实验值采用了准确率来衡量模型性能,在后续实验中我们将会使用上述方法中的多个来衡量模型性能。下面我们将采用summary来查看网络模型的参数,模型大小

loss = nn.CrossEntropyLoss()
loss = loss.to(device)model_LeNet = MY_LeNet5().to(device)
summary(model_LeNet,(1,28,28))
optimizer = torch.optim.Adam(model_LeNet.parameters(),lr=0.001)
----------------------------------------------------------------Layer (type)               Output Shape         Param #
================================================================Conv2d-1            [-1, 6, 28, 28]             156ReLU-2            [-1, 6, 28, 28]               0MaxPool2d-3            [-1, 6, 14, 14]               0Conv2d-4           [-1, 16, 10, 10]           2,416ReLU-5           [-1, 16, 10, 10]               0MaxPool2d-6             [-1, 16, 5, 5]               0Flatten-7                  [-1, 400]               0Linear-8                  [-1, 120]          48,120ReLU-9                  [-1, 120]               0Linear-10                   [-1, 84]          10,164ReLU-11                   [-1, 84]               0Linear-12                   [-1, 10]             850
================================================================
Total params: 61,706
Trainable params: 61,706
Non-trainable params: 0
----------------------------------------------------------------
Input size (MB): 0.00
Forward/backward pass size (MB): 0.11
Params size (MB): 0.24
Estimated Total Size (MB): 0.35
----------------------------------------------------------------
1.3 训练模型

epoch = 10 
total_train_step = 0
total_train_acc =0
for i in range(epoch):print("----------LeNet5第{}轮训练开始----------".format(i+1))for data in train_dataloder:img,target = dataimg = img.to(device)target = target.to(device)output = model_LeNet(img)loss_value = loss(output,target)optimizer.zero_grad()loss_value.backward()optimizer.step()total_train_step += 1acc = (output.argmax(dim=1) == target).sum()total_train_acc += accif total_train_step % 100 == 0:print("训练次数:{:d},Loss:{:f}".format(total_train_step,loss_value.item()))# 保存模型torch.save(model_LeNet.state_dict(),"./model/LeNet5.pth")print("准确率:",total_train_acc/len_train_data)
----------LeNet5第1轮训练开始----------
训练次数:100,Loss:0.098323
训练次数:200,Loss:0.001862
训练次数:300,Loss:0.002084
训练次数:400,Loss:0.102222
训练次数:500,Loss:0.032754
训练次数:600,Loss:0.053640
...
----------LeNet5第10轮训练开始----------
训练次数:8500,Loss:0.038482
训练次数:8600,Loss:0.131152
训练次数:8700,Loss:0.002011
训练次数:8800,Loss:0.000486
训练次数:8900,Loss:0.000392
训练次数:9000,Loss:0.040055
训练次数:9100,Loss:0.000649
训练次数:9200,Loss:0.010035
训练次数:9300,Loss:0.007745
准确率: tensor(9.9402, device='cuda:0')
1.4 测试模型
model_LeNet.eval()total_test_acc = 0
with torch.no_grad():for data in test_dataloder:img,target = dataimg = img.to(device)target = target.to(device)output = model_LeNet(img)loss_value = loss(output,target)acc = (output.argmax(dim=1) == target).sum()total_test_acc += accprint("准确率:",total_test_acc/len_test_data)
准确率: tensor(0.9888, device='cuda:0')
1.2.2 查看自建网络模型的结构
loss = nn.CrossEntropyLoss()
loss = loss.to(device)model_myself = My_self_Lenet().to(device)
summary(model_myself,(1,28,28))optimizer = torch.optim.Adam(model_myself.parameters(),lr=0.001)
----------------------------------------------------------------Layer (type)               Output Shape         Param #
================================================================Conv2d-1            [-1, 6, 28, 28]             156ReLU-2            [-1, 6, 28, 28]               0MaxPool2d-3            [-1, 6, 14, 14]               0Conv2d-4           [-1, 16, 10, 10]           2,416ReLU-5           [-1, 16, 10, 10]               0MaxPool2d-6             [-1, 16, 5, 5]               0Conv2d-7             [-1, 32, 1, 1]          12,832ReLU-8             [-1, 32, 1, 1]               0Flatten-9                   [-1, 32]               0Linear-10                  [-1, 120]           3,960ReLU-11                  [-1, 120]               0Linear-12                   [-1, 84]          10,164ReLU-13                   [-1, 84]               0Linear-14                   [-1, 10]             850
================================================================
Total params: 30,378
Trainable params: 30,378
Non-trainable params: 0
----------------------------------------------------------------
Input size (MB): 0.00
Forward/backward pass size (MB): 0.11
Params size (MB): 0.12
Estimated Total Size (MB): 0.23
----------------------------------------------------------------

通过summary我们可以发现,增加一层卷积层,我们可以减少参数数量,减少模型的大小。虽说有14层网络但是模型大小却比LeNet5小。

1.3 训练模型(自建模型)

epoch = 10 
total_train_step = 0
total_train_acc =0
for i in range(epoch):print("----------第{}轮训练----------".format(i+1))for data in train_dataloder:img,target = dataimg = img.to(device)target = target.to(device)output = model_myself(img)loss_value = loss(output,target)optimizer.zero_grad()loss_value.backward()optimizer.step()total_train_step += 1acc = (output.argmax(dim=1) == target).sum()total_train_acc += accif total_train_step % 100 == 0:print("训练次数:{:d},Loss:{:f}".format(total_train_step,loss_value.item()))# 保存模型torch.save(model_myself.state_dict(),"./model/self_LeNet.pth")print("准确率:",total_train_acc/len_train_data)
----------第1轮训练----------
训练次数:100,Loss:0.456620
训练次数:200,Loss:0.506838
训练次数:300,Loss:0.346417
训练次数:400,Loss:0.266719
训练次数:500,Loss:0.208805
训练次数:600,Loss:0.300649
训练次数:700,Loss:0.199465
...
----------第10轮训练----------
训练次数:8500,Loss:0.022954
训练次数:8600,Loss:0.044531
训练次数:8700,Loss:0.000516
训练次数:8800,Loss:0.003007
训练次数:8900,Loss:0.012397
训练次数:9000,Loss:0.007868
训练次数:9100,Loss:0.000951
训练次数:9200,Loss:0.044759
训练次数:9300,Loss:0.060102
准确率: tensor(9.7456, device='cuda:0')
1.4 测试模型(自建模型)
#测试模型
model_myself.eval()total_test_acc = 0
with torch.no_grad():for data in test_dataloder:img,target = dataimg = img.to(device)target = target.to(device)output = model_myself(img)loss_value = loss(output,target)acc = (output.argmax(dim=1) == target).sum()total_test_acc += accprint("准确率:",total_test_acc/len_test_data)
准确率: tensor(0.9866, device='cuda:0')

通过准确率我们可以发现,LeNet5的准确率为0.9888,而自建网络模型的准确率为0.9866。差了一点点并不多

1.5 预测模型
import torch
import torchvision.models as models
import matplotlib.pyplot as plt
from PIL import Image
from torchvision import transforms
import torch.nn.functional as F
input_image = Image.open('./image/5.jpg')
plt.imshow(input_image)

在这里插入图片描述


preprocess = transforms.Compose([transforms.Grayscale(),transforms.Resize((28,28)),transforms.ToTensor(),transforms.Normalize(mean=[0.48], std=[0.229]),
])input_tensor = preprocess(input_image)
image = input_tensor.unsqueeze(0) # create a mini-batch as expected by the model
image = image.to(device)model_myself_pr = My_self_Lenet()#将model_myself_pr加载到GPU上
model_myself_pr.to(device)
model_myself_pr.load_state_dict(torch.load("./model/self_LeNet.pth"))#预测图片是否跟结果一致
output = model_myself_pr(image)
print(output.data)
_, predicted = torch.max(output.data, 1)print(predicted)
tensor([[  3.4511, -13.5221, -12.3029,  -3.7890, -28.5683,  14.7039,  18.2915,-36.7622,  -5.3393, -11.0205]], device='cuda:0')
tensor([6], device='cuda:0')
input_image = Image.open('./image/5.jpg')#数据预处理(1. 将图片转换为灰度图;2. 将图片大小调整为28*28;3. 将图片转换为Tensor;4. 对Tensor进行归一化)
preprocess = transforms.Compose([transforms.Grayscale(),transforms.Resize((28,28)),transforms.ToTensor(),transforms.Normalize(mean=[0.48], std=[0.229]),
])input_tensor = preprocess(input_image)
image = input_tensor.unsqueeze(0) # create a mini-batch as expected by the model
image = image.to(device)
print(image.shape)model_LeNet_pr = MY_LeNet5()
#将model2加载到GPU上
model_LeNet_pr.to(device)
model_LeNet_pr.load_state_dict(torch.load("./model/LeNet5.pth"))#预测图片是否跟结果一致
output2 = model_LeNet_pr(image)
print(output2.data)
_, predicted2 = torch.max(output2.data, 1)print(predicted2)
torch.Size([1, 1, 28, 28])
tensor([[  0.4604, -40.1675, -32.2241, -22.7404, -30.2796,  47.7085,  14.1403,-44.4054,   4.5876, -10.1606]], device='cuda:0')
tensor([5], device='cuda:0')

使用两个神经网络模型预测同一张图片数字"5",自建模型错误的预测为了6,而LeNet5模型正确的预测为了5。哈哈哈哈看来增加一层卷积层过度的提取特征并不能帮助模型更好的预测。

这里大家可以自行在网络上下载图片用来预测,重复多次看看哪个模型预测更为准确!

(2) 卷积层可视化

注意这里只可视化了自建网络模型,因为它是3层卷积层。下面让我们一起用代码来实现!

import torch
import torchvision.models as models
import matplotlib.pyplot as plt
from PIL import Image
from torchvision import transforms
import torch.nn.functional as F
input_image = Image.open('./image/2.jpg')
# 查看图片大小
print(input_image.size)# 数据预处理(1. 将图片转换为灰度图;2. 将图片大小调整为28*28;3. 将图片转换为Tensor;4. 对Tensor进行归一化)
preprocess = transforms.Compose([transforms.Grayscale(),transforms.Resize((28,28)),transforms.ToTensor(),transforms.Normalize(mean=[0.48], std=[0.229]),
])
input_tensor = preprocess(input_image)
# 展示图片
plt.imshow(input_tensor.squeeze())
print(input_tensor.shape)# 为了喂入模型,需要增加一个维度
image = input_tensor.unsqueeze(0) # create a mini-batch as expected by the modelimage = image.to(device)
print(image.shape)
(700, 730)
torch.Size([1, 28, 28])
torch.Size([1, 1, 28, 28])

在这里插入图片描述

model_weights = []
conv_layers = []
counter = 0model_children = list(model_myself.children())print(model_children)for i in range(len(model_children[0])):if type(model_children[0][i]) == nn.Conv2d:counter+=1model_weights.append(model_children[0][i].weight)conv_layers.append(model_children[0][i])print(f"Total convolution layers: {counter}")
print(conv_layers)print(model_weights[0].shape)
[Sequential((0): Conv2d(1, 6, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))(1): ReLU()(2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)(3): Conv2d(6, 16, kernel_size=(5, 5), stride=(1, 1))(4): ReLU()(5): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)(6): Conv2d(16, 32, kernel_size=(5, 5), stride=(1, 1))(7): ReLU()(8): Flatten(start_dim=1, end_dim=-1)(9): Linear(in_features=32, out_features=120, bias=True)(10): ReLU()(11): Linear(in_features=120, out_features=84, bias=True)(12): ReLU()(13): Linear(in_features=84, out_features=10, bias=True)
)]
Total convolution layers: 3
[Conv2d(1, 6, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2)), Conv2d(6, 16, kernel_size=(5, 5), stride=(1, 1)), Conv2d(16, 32, kernel_size=(5, 5), stride=(1, 1))]
torch.Size([6, 1, 5, 5])
outputs = []
names = []for layer in conv_layers:    # conv_layers即是存储了所有卷积层的列表image = F.conv2d(image,weight=layer.weight,bias=None,stride=1,padding=0)  # 每个卷积层对image做计算,得到以矩阵形式存储的图片,需要通过matplotlib画出outputs.append(image)names.append(str(layer))
print(len(outputs))for feature_map in outputs:print(feature_map.shape)
3
torch.Size([1, 6, 24, 24])
torch.Size([1, 16, 20, 20])
torch.Size([1, 32, 16, 16])
print(outputs[0].shape)
print(outputs[0].squeeze().shape)
print(torch.sum(outputs[0].squeeze(0),0).shape)
torch.Size([1, 6, 24, 24])
torch.Size([6, 24, 24])
torch.Size([24, 24])

processed = []for feature_map in outputs:feature_map = feature_map.squeeze(0)  # torch.Size([1, 64, 112, 112]) —> torch.Size([64, 112, 112])  去掉第0维 即batch_size维gray_scale = torch.sum(feature_map,0)gray_scale = gray_scale / feature_map.shape[0]  # torch.Size([64, 112, 112]) —> torch.Size([112, 112])   从彩色图片变为黑白图片  压缩64个颜色通道维度,否则feature map太多张processed.append(gray_scale.data.cpu().numpy())  # .data是读取Variable中的tensor  .cpu是把数据转移到cpu上  .numpy是把tensor转为numpyfor fm in processed:print(fm.shape)
(24, 24)
(20, 20)
(16, 16)

import matplotlib.pyplot as pltfig = plt.figure(figsize=(30, 50))for i in range(len(processed)):   # len(processed) = 17a = fig.add_subplot(1,len(processed), i+1)img_plot = plt.imshow(processed[i])a.axis("off")a.set_title(names[i].split('(')[0], fontsize=30)   # names[i].split('(')[0] 结果为Conv2dplt.savefig('resnet18_feature_maps.jpg', bbox_inches='tight')

在这里插入图片描述

通过上面的结果我们我们发现,随着卷积的增加,提取的特征不断放大,第一层卷积我们可以很好的识别出是数字”2“,第二次卷积已经分不清是什么了,提取的好像是数字”2“的边缘特征,第3层已经越来越抽象了。

最后希望本次的可视化可以使大家更好的理解卷积神经网络的工作原理。理解CNN这个抽象的概念!

须知少时凌云志,曾许人间第一流!

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

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

相关文章

【超详细】跑通YOLOv8之深度学习环境配置3-YOLOv8安装

环境配置3下载安装内容如下: 1、配置清华等镜像源 2、创建环境 3、下载安装Pytorch 4、下载安装YOLOv8运行环境 版本:Python3.8(要求>3.8),torch1.12.0cu113(要求>1.8) 1、配置清华等镜…

光数据传送器|光通讯传感器极速版OPT系列尺寸与安装步骤

光数据传送器|光通讯传感器极速版OPT系列是利用可见光及不可见光作为信息载体,无需光纤、网线等有线介质,在空中直接进行信息传输的无线方式通信。驱动光源以可见光及不可见光的高速明暗变化来传输数字信号,以极高光频率双向发射接收光信号&a…

Java Web开篇

Java Web开篇 大纲 整个内容梳理 具体案例 整个内容梳理 这是前端和后端组成的系统的框架结构

C#语言进阶

一、简单数据结构类 1. ArrayList ArrayList是一个 C# 为我们封装好的类,它的本质是一个 object 类型的数组。ArrayList类帮助我们实现了很多方法,比如数组的增删查改 1.1 声明 using System.Collections;ArrayList array new ArrayList(); 1.2 增…

任务通知理论和使用

文章目录 一、任务通知是什么?1.1任务通知的优势1.2任务通知的限制1.3通知状态和通知值 二、任务通知的使用2.1任务通知使用_轻量级信号量2.2任务通知使用_轻量级队列2.3任务通知使用_轻量级事件组 一、任务通知是什么? 我们使用队列、信号量、事件组等…

AI如何改变PPT制作流程?推荐四款ai生成ppt工具

提起PPT,大家的第一反应就是痛苦。经常接触PPT的学生党和打工人,光看到这3个字母,就已经开始头痛了: 1、PPT内容框架与文案挑战重重,任务艰巨,耗费大量精力。 2、PPT的排版技能要求高,并非易事…

centos7中如何全局搜索一下nginx的配置文件?

在CentOS 7中搜索Nginx的配置文件,你可以使用一些常用的命令行工具,比如find、grep等。这些工具可以帮助你在文件系统中查找文件,也可以用来查找Docker容器内部的文件,只要你知道如何访问容器的文件系统。 1. 搜索系统中的Nginx配…

深度剖析深度神经网络(DNN):原理、实现与应用

目录 引言 一、DNN基本原理 二、DNN核心算法原理 三、DNN具体操作步骤 四、代码演示 引言 在人工智能和机器学习的浪潮中,深度神经网络(Deep Neural Network,简称DNN)已经成为了一种非常重要的工具。DNN模仿人脑神经网络的结…

C语言leetcode刷题笔记3

C语言leetcode刷题笔记3 第8题:876.链表的中间结点遍历数节点个数快慢指针 第9题:874.比较含退格的字符串第10题:155.最小栈法1:getMin内部实现查找法2:getmin直接返回值补充:栈的使用例子优化:…

Leetcode2079. 给植物浇水

Every day a Leetcode 题目来源:2079. 给植物浇水 解法1:模拟 sum 记录总步数,初始化为 0。water 表示当前水量,初始化为 capacity。 遍历 plants 数组,设下标为 i 的植物需要的水量 plant plants[i]。 如果 wat…

2023 PWNHUB 3月赛-【tototo】

文章目录 volatile泄露_environ打栈漏洞利用malloc和calloc思路(打_environ)代码 volatile int volatile vInt; 当要求使用 volatile 声明的变量值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数…

孙宇晨对话大公网:香港Web3政策友好环境示范意义重大

日前,全球知名华文媒体大公网发布《湾区web3大有可为》重磅系列报道。报道通过对中国香港与大湾区其他城市Web3政策、行业创新和生态建设等方面的梳理,以及对行业领袖和重要行业机构的走访,全面展现了在大湾区一体化发展的背景下,Web3等数字经济模式在该地区的长远发展潜力。 …