基于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.mzph.cn/news/838414.shtml

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

相关文章

单位个人如何向期刊投稿发表文章?

在单位担任信息宣传员一职以来,我深感肩上的责任重大。每月的对外信息宣传投稿不仅是工作的核心,更是衡量我们部门成效的重要指标。起初,我满腔热血,以为只要勤勉努力,将精心撰写的稿件投至各大报社、报纸期刊的官方邮箱,就能顺利登上版面,赢得读者的青睐。然而,现实远比理想骨…

Java入门基础学习笔记23——For循环结构

1、for循环: 控制一段代码反复执行很多次。 2、For循环语句的基本结构: for(初始化表达式;判断表达式;递增(递减)表达式) {循环体语句(重复执行的代码) } 例&#xff1…

基于单片机的智能安防系统设计(32+4G+WIFI版)-设计说明书

设计摘要: 本设计基于STM32单片机,旨在实现一个智能安防系统,主要包括烟雾和温度传感器、人体红外传感器、显示屏、按键、4G模块和WiFi模块等组件。通过这些组件的协作,实现了火灾检测、入侵监测、状态显示、用户交互和远程通信等…

OSG编程指南<二十三>:基于OSG+ImGui制作模型编辑器,实现三轴方向的实时平移、旋转和缩放变化

1、概述 在OSG的开发应用过程中,我们有时候总会纠结于使用MFC还是Qt来嵌入OSG窗口以便于后续的功能开发,毕竟选择一个合适的UI框架,对于后续的开发还是省去很多麻烦的。但对于初学者来说,可能对框架消息机制的不熟悉,尤…

项目8-头像的上传

js实现头像上传并且预览图片功能以及提交 - 掘金 (juejin.cn) 我们简单建立一个表 1.前端知识储备 1.1 addClass的使用 1.基本语法 addClass() 方法向被选元素添加一个或多个类。 该方法不会移除已存在的 class 属性,仅仅添加一个或多个 class 属性。 提示&…

Pathlib,一个不怕迷路的 Python 向导

大家好!我是爱摸鱼的小鸿,关注我,收看每期的编程干货。 一个简单的库,也许能够开启我们的智慧之门, 一个普通的方法,也许能在危急时刻挽救我们于水深火热, 一个新颖的思维方式,也许能…

全网最全的基于电机控制的38类simulink仿真全家桶----新手大礼包

整理了基于电机的38种simulink仿真全家桶,包含多种资料,类型齐全十分适合新手学习使用。包括但是不局限于以下: 1、基于多电平逆变器的无刷直流电机驱动simulink仿真 2、基于负载转矩的感应电机速度控制simulink仿真 3、基于滑膜观测器的永…

OpenAI GPT-4o - 介绍

本文翻译整理自: Hello GPT-4o https://openai.com/index/hello-gpt-4o/ 文章目录 一、关于 GPT-4o二、模型能力三、能力探索四、模型评估1、文本评价2、音频 ASR 性能3、音频翻译性能4、M3Exam 零样本结果5、视觉理解评估6、语言 tokenization 六、模型安全性和局限…

【汇编语言】多文件组织

【汇编语言】多文件组织 文章目录 【汇编语言】多文件组织前言一、8086拓展1.子程序的另外一种写法2.程序的多文件组织 总结 前言 本篇文章将讲到子程序的另一种写法,以及程序的多文件组织。 一、8086拓展 1.子程序的另外一种写法 初始的程序 在这里我们对比一下…

用SwitchHosts模拟本地域名解析访问

一.用SwitchHosts模拟本地域名解析访问 1.下载地址 https://download.csdn.net/download/jinhuding/89313168 2.使用截图

20240511每日运维----聊聊nignx改配置所有的nginx改完unknow

1、改配置所有的nginx改完unknow src/core/nginx.h src/http/ngx_http_header_filter_module.c src/http/ngx_http_special_response.c src/http/v2/ngx_http_v2_filter_module.c 2、make 3、去objs里面把nginx文件替换过去sbin/nginx

地点导航图片怎么做?商场导航图用什么做的?

上海懒图科技以各类商场导购&可视化管理需求为出发点,量身定制专属商场地图,用户自主即可简易快速完成制图并实现应用,地图数据的制作及编辑维护全可视化,通过拖、拉、拽即可快速完成,零地图编辑技能基础要求。绘制…

MySQL深入理解MVCC机制(详解)

深入理解MVCC 1、MVCC定义 MVCC:Multi-Version Concurrency Control,多版本并发控制机制。 在mysql中,为了满足事务的四大特性之一的隔离性,就是当前事务中的查询的数据不受其他事务的增删改操作的影响,因此mysql主要是通过这个…

Lora训练Windows[笔记]

一. 使用kohya_ss的GUI版本(https://github.com/bmaltais/kohya_ss.git) 这个版本跟stable-diffusion-webui的界面很像,只不过是训练模型专用而已,打开的端口同样是7860。 1.双击setup.bat,选择1安装好xformers,pytorch等和cuda…

malloc_consolidate

此文章用于详细介绍malloc_consolidate。 众所周知,fastbin一般是不能合并,但在malloc_consolidate中是个例外。 1.触发机制 首先构造这样的堆块结构 一个0x40的堆块在fastbin中,一个0x110的堆块在unbin中 随后我们尝试分配一个0x300的堆…

[ROS 系列学习教程] 建模与仿真 - URDF 建模实践

ROS 系列学习教程(总目录) 本文目录 一、机器人结构组成二、新建功能包三、编写launch文件四、创建底盘五、添加轮子六、添加其他部件七、解决部分实体位于地面以下的问题 前文介绍了URDF建模与URDF语法,接下来介绍怎么使用URDF从零构建一个机器人模型并在rviz中显示…

Linux网络编程——HTTP协议的理解与运用

目录 前言 一、认识URL 二、认识HTTP样例 三、HTTP的报头内容 1.url 2. Content-Type 3.Method 方法 1.GET方法 2.POST方法 4、状态码 5.cookie和session 前言 我们知道,协议就是一种约定,客户端与服务端统一的用这种约定进行传输数据。我们…

K210开发板MicroPython开发环境搭建

一、安装CanMV IDE开发软件 1、进入如下连接 https://developer.canaan-creative.com/resource 2、点击下载 3、下一步 4、修改安装路径,下一步 5、接受许可下一步 6、下一步 7、安装 8、完成 9、区域①菜单栏:操作文件,使用工具等。…

Hadoop3:HDFS副本节点选择逻辑讲解

一、副本节点选择(机架感知) 说明 第一个副本,因为我们的client可能是web页,也可能是shell终端。 如果是web页,则随机选取一个节点,如果是shell终端,则选择当前shell终端所在的节点。 节点距离最…

Spring编程使用DDD的小把戏

场景 现在流行充血领域层,在原本只存储对象的java类中,增加一些方法去替代原本写在service层的crud, 但是例如service这种一般都是托管给spring的,我们使用的ORM也都托管给spring,这样方便在service层调用mybatis的m…