当前位置: 首页 > news >正文

如何使用 pytorch 创建一个神经网络

我已发布在:如何使用 pytorch 创建一个神经网络 SapientialM.Github.io

构建神经网络

1 导入所需包

import os
import torch
from torch import nn
from torch.utils.data import DataLoader
from torchvision import datasets, transforms

2 检查GPU是否可用

device = ("cuda"if torch.cuda.is_available()else "mps"if torch.backends.mps.is_available()else "cpu"
)
print(f"Using {device} device")
print(torch.__version__)
print(torch.cuda.is_available())
print(torch.version.cuda)
Using cuda device
2.2.2+cu121
True
12.1

如果发现GPU不可用,可能是因为torch版本问题(比如我),应该去下载GPU版本。

  • 在CUDA官网找到合适版本的cuda,一般是根据系统平台和显卡版本来选择所需CUDA
  • 查看安装完成是否
# 版本,看CUDA的版本,比如我的是cuda_11.2
nvcc --version
# 驱动,看Driver Version
nvidia-smi
  • 去PyTorch官网找到合适版本的PyTorch,一般是根据开发环境来选择,然后复制所给的Commond去shell下安装即可
# 比如我的命令就是
pip install torch==2.2.2 torchvision==0.17.2 torchaudio==2.2.2 --index-url https://download.pytorch.org/whl/cu121

3 定义我们的神经网络

pytorch里面一切自定义操作基本上都是继承nn.Module类来实现的,你可以先不去深入了解这个类,但是要知道,我们一般都是通过继承和重构nn.Module来定义我们的神经网络。我们一般重构__init__forward这两个方法。根据PyTorch官网的说法:__init__初始化神经网络层;forward层之间的数据操作,也是整个网络的核心。__init__只会定义层,而forward负责将层连接起来。实际上类的初始化参数一般是一些固有属性,我们可以将一些带有训练参数的层放在__init__,而没有训练参数的层是可以加入到forward里面的,或者说我们将没有训练参数的层看作是层之间的数据操作。

当然直接这么说,肯定不是很清晰,我们来看一个官网给的例子:

class NeuralNetwork(nn.Module):def __init__(self):super().__init__()self.flatten = nn.Flatten()self.linear_relu_stack = nn.Sequential(nn.Linear(28*28, 512),nn.ReLU(),nn.Linear(512, 512),nn.ReLU(),nn.Linear(512, 10),)def forward(self, x):x = self.flatten(x)logits = self.linear_relu_stack(x)return logits
model = NeuralNetwork().to(device) # 将网络移入device,并打印结构
print(model)
NeuralNetwork((flatten): Flatten(start_dim=1, end_dim=-1)(linear_relu_stack): Sequential((0): Linear(in_features=784, out_features=512, bias=True)(1): ReLU()(2): Linear(in_features=512, out_features=512, bias=True)(3): ReLU()(4): Linear(in_features=512, out_features=10, bias=True))
)

我们用torchviz可以将神经网络进行一个简单的可视化,当然很多参数可选,这里不一一列举

pip install torchviz
from torchviz import make_dot
X = torch.rand(1, 28, 28, device=device) # 需要对神经网络进行数据输入,才是一个完整的网络
y = model(X)
output = make_dot(y.mean(), params=dict(model.named_parameters())) # 开始绘制
output.format = "png"
output.directory = "."
output.render("torchviz", view=True) # 会在相对路径下保存一个可视化后的图片并打开
'torchviz.png'

看起来可能会比较复杂,也看不懂,我们得需要学会看懂神经网络的结构才能看懂结构图。当然,还有诸如draw_convnet、NNSVG、netron等可视化工具会更加优秀。

4 神经网络模型层

想要构建一个神经网络并进行训练和预测,我们需要去认识神经网络的构成。假定你已经了解过感知机、人工神经网络的基本概念,那么现在就是来了解一下神经网络的模型层。

我们直接分解一下官网所给出的这个模型,这是一个简单的前馈神经网络(Feedforward Neural Network),我们先不去了解它的作用,一点点的分解它,看看它最终实现了什么。

我们先根据官网所说的,取一个大小为 28x28 的 3 张图像的样本小批量作为输入(我们一般将数据的第一个维度看作批量维度并保留):

input_image = torch.rand(3,28,28)
print(input_image.size())
torch.Size([3, 28, 28])

4.1 nn.Flatten

虽然是PyTorch的nn.Flatten,但是Flatten层是神经网络中常见的组成部分。在神经网络的训练和预测过程中,输入数据通常需要经过一系列的处理和转换。在这个过程中,Flatten层能够将多维的输入数据转化为一维的线性形式,以便于神经网络的进一步处理。模型中的nn.Flatten,将我们所输入的2D 28*28 图像转换为一个包含 784 个像素值的连续数组,也就是和它表面的意思一样展平这个高维数组。

nn.Flatten()默认参数是start_dim=1end_dim=-1,如果你想展平所有维度,可以通过设置start_dim=0来实现)

flatten = nn.Flatten()
flat_image = flatten(input_image) # 将输入的图像展平
print(flat_image.size())
torch.Size([3, 784])

在卷积神经网络(CNN)中,Flatten层可以将卷积层提取到的特征图展平,便于进一步的特征处理或分类,也便于输入到全连接层(全连接层通常需要一维的输入,后面会讲到)。在构建复杂网络时,Flatten层可以帮助不同类型的层之间进行连接。总的来说,Flatten层起到了桥梁的作用,使得卷积神经网络的层次结构更加灵活和易于设计,并且确保了从卷积层到全连接层的数据传递顺畅,维持了网络的整体性能和效率。

4.2 nn.Linear

nn.Linear应该耳熟能详,我们称之为线性层(Linear Layer),也可以称为全连接层(Fully Connected Layer)或密集层(Dense Layer)。线性层是一个使用其存储的权重和偏差对输入应用线性变换的模块,也就是对输入数据进行线性变换。线性层对数据的处理方式基本上可以表示为:
y = W x + b y = Wx + b y=Wx+b
,其中 W 是权重矩阵,b 是偏置。向量都是可学习的参数。在神经网络的训练和预测过程中,Linear层的作用是将输入数据通过一组权重进行线性变换,然后添加一个偏置项。简单来说,它能够将输入特征映射到输出特征,从而实现对数据的线性组合和转换。如下图是一个单隐藏层的多层感知机(Multilayer Perceptron),一般称为MLP,隐藏层和输出层均是由线性层和激活函数组成:

MLP

# 定义一个线性层,将28*28维度的向量转换为20维度的向量
layer1 = nn.Linear(in_features=28*28, out_features=20)
hidden1 = layer1(flat_image) 
print(hidden1.size())
torch.Size([3, 20])

在这个例子中,in_features=28*28表示输入特征的维度,out_features=20表示输出特征的维度。nn.Linear层会自动初始化权重和偏置,并在训练过程中通过反向传播算法进行调整。简单理解就是,该线性层的输入是784维,而输出是20维。

4.3 nn.ReLU

ReLU函数,全称Rectified Linear Unit,是人工神经网络中常用的一种激活函数.
讲到这里,我们就讲讲常见的激活函数及其作用。

Sigmoid 激活函数

数学表达式:
σ ( x ) = 1 1 + e − x \sigma(x) = \frac{1}{1 + e^{-x}} σ(x)=1+ex1

作用:

  • 将输入映射到 (0, 1) 之间。
  • 常用于输出层,尤其是在二分类问题中,输出概率值。

优点:

  • 输出范围在 (0, 1) 之间,可以解释为概率。
  • 平滑梯度,有助于梯度下降。

缺点:

  • 容易导致梯度消失问题。
  • 输出不是零中心的,会影响网络的训练效率。
import numpy as np
import matplotlib.pyplot as pltdef sigmoid(x):return 1 / (1 + np.exp(-x))x = np.linspace(-10, 10, 100)
y = sigmoid(x)plt.plot(x, y)
plt.title("Sigmoid Activation Function")
plt.xlabel("x")
plt.ylabel("Sigmoid(x)")
plt.grid()
plt.show()

sigmoid

Tanh 激活函数

数学表达式:
tanh ⁡ ( x ) = e x − e − x e x + e − x \tanh(x) = \frac{e^x - e^{-x}}{e^x + e^{-x}} tanh(x)=ex+exexex

作用:

  • 将输入映射到 (-1, 1) 之间。
  • 常用于隐藏层,提供零中心的输出,有助于训练。

优点:

  • 输出是零中心的,梯度消失问题较轻。

缺点:

  • 仍然存在梯度消失问题。
import numpy as np
import matplotlib.pyplot as pltdef tanh(x):return np.tanh(x)x = np.linspace(-10, 10, 100)
y = tanh(x)plt.plot(x, y)
plt.title("Tanh Activation Function")
plt.xlabel("x")
plt.ylabel("Tanh(x)")
plt.grid()
plt.show()

Tanh

ReLU (Rectified Linear Unit) 激活函数

数学表达式:
ReLU ( x ) = max ⁡ ( 0 , x ) \text{ReLU}(x) = \max(0, x) ReLU(x)=max(0,x)

作用:

  • 将输入小于0的部分设为0,大于0的部分保持不变。
  • 常用于隐藏层,特别是深度神经网络。

优点:

  • 计算简单,收敛速度快。
  • 减少梯度消失问题。

缺点:

  • 输出不是零中心的。
  • 输入小于0时梯度为零,可能导致“神经元死亡”问题。
import numpy as np
import matplotlib.pyplot as pltdef relu(x):return np.maximum(0, x)x = np.linspace(-10, 10, 100)
y = relu(x)plt.plot(x, y)
plt.title("ReLU Activation Function")
plt.xlabel("x")
plt.ylabel("ReLU(x)")
plt.grid()
plt.show()

RELU

Leaky ReLU 激活函数

数学表达式:
Leaky ReLU ( x ) = { x if  x > 0 α x if  x ≤ 0 \text{Leaky ReLU}(x) = \begin{cases} x & \text{if } x > 0 \\ \alpha x & \text{if } x \leq 0 \end{cases} Leaky ReLU(x)={xαxif x>0if x0
其中 (\alpha) 通常是一个很小的常数,如 0.01。

作用:

  • 解决 ReLU 的“神经元死亡”问题。

优点:

  • 输入小于0时仍有较小梯度,避免神经元死亡。

缺点:

  • 计算稍复杂。
import numpy as np
import matplotlib.pyplot as pltdef leaky_relu(x, alpha=0.01):return np.where(x > 0, x, alpha * x)x = np.linspace(-10, 10, 100)
y = leaky_relu(x)plt.plot(x, y)
plt.title("Leaky ReLU Activation Function")
plt.xlabel("x")
plt.ylabel("Leaky ReLU(x)")
plt.grid()
plt.show()

Leaky RELU

Softmax 激活函数

数学表达式:
Softmax ( x i ) = e x i ∑ j e x j \text{Softmax}(x_i) = \frac{e^{x_i}}{\sum_{j} e^{x_j}} Softmax(xi)=jexjexi

作用:

  • 将输入向量转换为概率分布,总和为1。
  • 常用于多分类问题的输出层。

优点:

  • 输出可以解释为概率,便于分类。

缺点:

  • 计算相对复杂,容易导致数值不稳定。
import numpy as np
import matplotlib.pyplot as pltdef softmax(x):e_x = np.exp(x - np.max(x))return e_x / e_x.sum(axis=0)x = np.linspace(-10, 10, 100)
y = softmax(x)plt.plot(x, y)
plt.title("Softmax Activation Function")
plt.xlabel("x")
plt.ylabel("Softmax(x)")
plt.grid()
plt.show()

Softmax

4.4 nn.Sequential

nn.Sequential是 PyTorch 提供的一个容器模块,它按顺序包含其他子模块,便于构建和管理简单的神经网络结构。通过 nn.Sequential,可以方便地将一系列层(如线性层、激活函数、卷积层等)按顺序堆叠在一起,从而简化模型定义和前向传播的代码。简而言之就是一个包裹的顺序容器。

5 理解我们的神经网络

看完这些,我们再来理解这个官网给的例子:

class NeuralNetwork(nn.Module):# 重构 __init__,定义“固有属性”def __init__(self):# 这一步操作是调用父类 nn.Module 的构造函数,确保继承自 nn.Module 的特性正确初始化super().__init__()# 定义一个展开层 flattenself.flatten = nn.Flatten()# 定义一个线性容器,可以简化在forward中的调用self.linear_relu_stack = nn.Sequential(# 容器内包含一个三层网络# 这里的512、10都是研究者根据具体任务和数据集进行调试和优化得到的结果# 熟悉的调参nn.Linear(28*28, 512),nn.ReLU(),nn.Linear(512, 512),nn.ReLU(),nn.Linear(512, 10),)# 重构forward,定义前向传播路径def forward(self, x):# 在这里定义各个层输入输出的顺序,即层在网络里的位置关系x = self.flatten(x)logits = self.linear_relu_stack(x)return logits
model = NeuralNetwork().to(device) # 将网络移入device,并打印结构
print(model)
NeuralNetwork((flatten): Flatten(start_dim=1, end_dim=-1)(linear_relu_stack): Sequential((0): Linear(in_features=784, out_features=512, bias=True)(1): ReLU()(2): Linear(in_features=512, out_features=512, bias=True)(3): ReLU()(4): Linear(in_features=512, out_features=10, bias=True))
)

6 使用我们的网络

主要步骤如下:

  • 定义模型
  • 数据载入
  • 损失函数和优化
  • 训练和评估
  • 预测与可视化

先导入所需包:

import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
import numpy as np
import matplotlib.pyplot as plt

6.1 定义模型

# 定义设备,如果有GPU则使用GPU,否则使用CPU
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")# 定义神经网络模型
class NeuralNetwork(nn.Module):def __init__(self):super().__init__()self.flatten = nn.Flatten()self.linear_relu_stack = nn.Sequential(nn.Linear(28*28, 512),nn.ReLU(),nn.Linear(512, 512),nn.ReLU(),nn.Linear(512, 10),)def forward(self, x):x = self.flatten(x)logits = self.linear_relu_stack(x)return logits# 创建神经网络模型实例,并移动到设备上
model = NeuralNetwork().to(device)
print(model)
NeuralNetwork((flatten): Flatten(start_dim=1, end_dim=-1)(linear_relu_stack): Sequential((0): Linear(in_features=784, out_features=512, bias=True)(1): ReLU()(2): Linear(in_features=512, out_features=512, bias=True)(3): ReLU()(4): Linear(in_features=512, out_features=10, bias=True))
)

6.2 数据载入

我们这次用的模型用于简单图像分类问题,所以可以使用MNIST数据集,导入用的是PyTorch的datasets。

# 加载MNIST数据集并进行预处理
transform = transforms.Compose([# 对图片的常用操作,将图像数据转换为形状为 (C, H, W) 的张量transforms.ToTensor(),# 因为数据集是灰度图像,所以只有单值标准化transforms.Normalize((0.5,), (0.5,))
])# 加载MNIST数据集,并划分训练集和测试集(这里会下载下来)
train_dataset = datasets.MNIST(root='./data', train=True, transform=transform, download=True)
test_dataset = datasets.MNIST(root='./data', train=False, transform=transform)# 定义批量大小和数据加载器
batch_size = 64
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)

6.3 损失函数和优化

损失函数选取交叉熵损失函数(Cross Entropy Loss),它是一种常用的损失函数,能够有效地衡量预测类别和真实类别之间的差异。它能够处理模型输出的logits,并且在计算过程中会自动应用Softmax操作,从而简化代码。

优化器选取随机梯度下降法(Stochastic Gradient Descent, SGD),它是一种简单而有效的优化方法,特别适用于大规模数据集和模型。结合Momentum算法,SGD优化器可以加速收敛并减小震荡,从而在一定程度上提高训练效率和模型性能。

# 定义损失函数
criterion = nn.CrossEntropyLoss()
# 定义优化器
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)

6.4 训练和评估

# 训练模型
# 训练的目的是通过多个训练周期和批次的数据,不断调整模型参数以最小化损失函数,从而提高模型的性能。
def train(model, train_loader, optimizer, criterion, epochs=5):# 某些层(如Dropout和BatchNorm)在训练和评估模式下的行为不同,所以需要显式地设置模型为训练模式。model.train()# 开始训练循环for epoch in range(epochs):# 初始化损失running_loss = 0.0# 遍历训练数据加载器中的每个批次for batch_idx, (data, target) in enumerate(train_loader):# 将数据移动到设备上data, target = data.to(device), target.to(device)# 梯度清零optimizer.zero_grad()# 前向传播,将数据输入模型进行预测output = model(data)# 计算损失loss = criterion(output, target)# 将损失反向传播loss.backward()# 使用优化器更新参数optimizer.step() # 累计损失running_loss += loss.item()if batch_idx % 100 == 99:    # 每100个批次打印一次训练状态print(f'Epoch [{epoch+1}/{epochs}], Step [{batch_idx+1}/{len(train_loader)}], Loss: {running_loss/100:.4f}')running_loss = 0.0# 测试模型
def test(model, test_loader):# 进入评估模式,原因与train同理model.eval()# 累计计数correct = 0total = 0# 测试过程不会进行优化,所以no_grad禁用梯度计算可以加快测试速度with torch.no_grad():for data, target in test_loader:data, target = data.to(device), target.to(device)# 前向传播,将数据输入模型进行预测outputs = model(data)# 获取预测结果_, predicted = torch.max(outputs.data, 1)total += target.size(0)correct += (predicted == target).sum().item()accuracy = 100 * correct / totalprint(f'Test Accuracy: {accuracy:.2f}%')

6.5 预测与可视化

# 可视化预测结果
def visualize_predictions(model, test_loader, num_images=10):# 这里和上面定义的test相似,主要是在执行过程中添加了可视化代码和限制了测试数量model.eval()images_so_far = 0plt.figure(figsize=(12, 8))with torch.no_grad():for i, (inputs, labels) in enumerate(test_loader):inputs = inputs.to(device)labels = labels.to(device)# 获取每张图的预测结果,并将数据绘制出来进行比对outputs = model(inputs)_, preds = torch.max(outputs, 1)for j in range(inputs.size(0)):images_so_far += 1ax = plt.subplot(num_images // 5, 5, images_so_far)ax.axis('off')ax.set_title(f'Predicted: {preds[j]} (Label: {labels[j]})')# imshow用于在绘图窗口中显示图像ax.imshow(inputs.cpu().data[j].numpy().squeeze(), cmap='gray')if images_so_far == num_images:model.train()returnmodel.train()# 执行训练
train(model, train_loader, optimizer, criterion)# 执行测试
test(model, test_loader)# 可视化预测结果
visualize_predictions(model, test_loader, num_images=10)
plt.suptitle('Model Predictions')
plt.tight_layout()
plt.show()
NeuralNetwork((flatten): Flatten(start_dim=1, end_dim=-1)(linear_relu_stack): Sequential((0): Linear(in_features=784, out_features=512, bias=True)(1): ReLU()(2): Linear(in_features=512, out_features=512, bias=True)(3): ReLU()(4): Linear(in_features=512, out_features=10, bias=True))
)
Epoch [1/5], Step [100/938], Loss: 1.1812
Epoch [1/5], Step [200/938], Loss: 0.4243
Epoch [1/5], Step [300/938], Loss: 0.3437
Epoch [1/5], Step [400/938], Loss: 0.3305
Epoch [1/5], Step [500/938], Loss: 0.2820
Epoch [1/5], Step [600/938], Loss: 0.2634
Epoch [1/5], Step [700/938], Loss: 0.2482
Epoch [1/5], Step [800/938], Loss: 0.2131
Epoch [1/5], Step [900/938], Loss: 0.2161
Epoch [2/5], Step [100/938], Loss: 0.1853
Epoch [2/5], Step [200/938], Loss: 0.1658
Epoch [2/5], Step [300/938], Loss: 0.1766
Epoch [2/5], Step [400/938], Loss: 0.1507
Epoch [2/5], Step [500/938], Loss: 0.1606
Epoch [2/5], Step [600/938], Loss: 0.1347
Epoch [2/5], Step [700/938], Loss: 0.1407
Epoch [2/5], Step [800/938], Loss: 0.1371
Epoch [2/5], Step [900/938], Loss: 0.1283
Epoch [3/5], Step [100/938], Loss: 0.1027
Epoch [3/5], Step [200/938], Loss: 0.1169
Epoch [3/5], Step [300/938], Loss: 0.1150
Epoch [3/5], Step [400/938], Loss: 0.1077
Epoch [3/5], Step [500/938], Loss: 0.0986
Epoch [3/5], Step [600/938], Loss: 0.1139
Epoch [3/5], Step [700/938], Loss: 0.1110
Epoch [3/5], Step [800/938], Loss: 0.0986
Epoch [3/5], Step [900/938], Loss: 0.0927
Epoch [4/5], Step [100/938], Loss: 0.0908
Epoch [4/5], Step [200/938], Loss: 0.0834
Epoch [4/5], Step [300/938], Loss: 0.0957
Epoch [4/5], Step [400/938], Loss: 0.0742
Epoch [4/5], Step [500/938], Loss: 0.0873
Epoch [4/5], Step [600/938], Loss: 0.0786
Epoch [4/5], Step [700/938], Loss: 0.0901
Epoch [4/5], Step [800/938], Loss: 0.0828
Epoch [4/5], Step [900/938], Loss: 0.0810
Epoch [5/5], Step [100/938], Loss: 0.0682
Epoch [5/5], Step [200/938], Loss: 0.0729
Epoch [5/5], Step [300/938], Loss: 0.0601
Epoch [5/5], Step [400/938], Loss: 0.0684
Epoch [5/5], Step [500/938], Loss: 0.0755
Epoch [5/5], Step [600/938], Loss: 0.0706
Epoch [5/5], Step [700/938], Loss: 0.0733
Epoch [5/5], Step [800/938], Loss: 0.0579
Epoch [5/5], Step [900/938], Loss: 0.0621
Test Accuracy: 97.45%

在这里插入图片描述

神经网络(尤其是深度神经网络)的一个非常吸引人的特点就是:它们具有很强的通用性,可以通过不同的数据集进行训练,以解决各种不同的任务。我们可以将该模型使用另外的数据集进行训练和测试,仍然有不低的准确率。

import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
import numpy as np
import matplotlib.pyplot as plt# 定义设备,如果有GPU则使用GPU,否则使用CPU
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")# 定义神经网络模型
class NeuralNetwork(nn.Module):def __init__(self):super().__init__()self.flatten = nn.Flatten()self.linear_relu_stack = nn.Sequential(nn.Linear(28*28, 512),nn.ReLU(),nn.Linear(512, 512),nn.ReLU(),nn.Linear(512, 10),)def forward(self, x):x = self.flatten(x)logits = self.linear_relu_stack(x)return logits# 创建神经网络模型实例,并移动到设备上
model = NeuralNetwork().to(device)
print(model)# 加载FashionMNIST数据集并进行预处理
transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.5,), (0.5,))
])train_dataset = datasets.FashionMNIST(root='./data', train=True, transform=transform, download=True)
test_dataset = datasets.FashionMNIST(root='./data', train=False, transform=transform)# 定义批量大小和数据加载器
batch_size = 64
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)# 定义损失函数和优化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)# 训练模型
def train(model, train_loader, optimizer, criterion, epochs=5):model.train()for epoch in range(epochs):running_loss = 0.0for batch_idx, (data, target) in enumerate(train_loader):data, target = data.to(device), target.to(device)optimizer.zero_grad()output = model(data)loss = criterion(output, target)loss.backward()optimizer.step()running_loss += loss.item()if batch_idx % 100 == 99:    # 每100个批次打印一次训练状态print(f'Epoch [{epoch+1}/{epochs}], Step [{batch_idx+1}/{len(train_loader)}], Loss: {running_loss/100:.4f}')running_loss = 0.0# 测试模型
def test(model, test_loader):model.eval()correct = 0total = 0with torch.no_grad():for data, target in test_loader:data, target = data.to(device), target.to(device)outputs = model(data)_, predicted = torch.max(outputs.data, 1)total += target.size(0)correct += (predicted == target).sum().item()accuracy = 100 * correct / totalprint(f'Test Accuracy: {accuracy:.2f}%')# 可视化预测结果
def visualize_predictions(model, test_loader, num_images=10):model.eval()images_so_far = 0plt.figure(figsize=(12, 8))with torch.no_grad():for i, (inputs, labels) in enumerate(test_loader):inputs = inputs.to(device)labels = labels.to(device)outputs = model(inputs)_, preds = torch.max(outputs, 1)for j in range(inputs.size(0)):images_so_far += 1ax = plt.subplot(num_images // 5, 5, images_so_far)ax.axis('off')ax.set_title(f'Predicted: {preds[j]} (Label: {labels[j]})')ax.imshow(inputs.cpu().data[j].numpy().squeeze(), cmap='gray')if images_so_far == num_images:model.train()returnmodel.train()# 执行训练
train(model, train_loader, optimizer, criterion)# 执行测试
test(model, test_loader)# 可视化预测结果
visualize_predictions(model, test_loader, num_images=10)
plt.suptitle('Model Predictions')
plt.tight_layout()
plt.show()
NeuralNetwork((flatten): Flatten(start_dim=1, end_dim=-1)(linear_relu_stack): Sequential((0): Linear(in_features=784, out_features=512, bias=True)(1): ReLU()(2): Linear(in_features=512, out_features=512, bias=True)(3): ReLU()(4): Linear(in_features=512, out_features=10, bias=True))
)
Epoch [1/5], Step [100/938], Loss: 1.1111
Epoch [1/5], Step [200/938], Loss: 0.6047
Epoch [1/5], Step [300/938], Loss: 0.5097
Epoch [1/5], Step [400/938], Loss: 0.4919
Epoch [1/5], Step [500/938], Loss: 0.4808
Epoch [1/5], Step [600/938], Loss: 0.4519
Epoch [1/5], Step [700/938], Loss: 0.4558
Epoch [1/5], Step [800/938], Loss: 0.4473
Epoch [1/5], Step [900/938], Loss: 0.4138
Epoch [2/5], Step [100/938], Loss: 0.3960
Epoch [2/5], Step [200/938], Loss: 0.3889
Epoch [2/5], Step [300/938], Loss: 0.4075
Epoch [2/5], Step [400/938], Loss: 0.3719
Epoch [2/5], Step [500/938], Loss: 0.3819
Epoch [2/5], Step [600/938], Loss: 0.3858
Epoch [2/5], Step [700/938], Loss: 0.3838
Epoch [2/5], Step [800/938], Loss: 0.3564
Epoch [2/5], Step [900/938], Loss: 0.3616
Epoch [3/5], Step [100/938], Loss: 0.3488
Epoch [3/5], Step [200/938], Loss: 0.3507
Epoch [3/5], Step [300/938], Loss: 0.3522
Epoch [3/5], Step [400/938], Loss: 0.3363
Epoch [3/5], Step [500/938], Loss: 0.3375
Epoch [3/5], Step [600/938], Loss: 0.3445
Epoch [3/5], Step [700/938], Loss: 0.3378
Epoch [3/5], Step [800/938], Loss: 0.3208
Epoch [3/5], Step [900/938], Loss: 0.3163
Epoch [4/5], Step [100/938], Loss: 0.3189
Epoch [4/5], Step [200/938], Loss: 0.3005
Epoch [4/5], Step [300/938], Loss: 0.3071
Epoch [4/5], Step [400/938], Loss: 0.3240
Epoch [4/5], Step [500/938], Loss: 0.3147
Epoch [4/5], Step [600/938], Loss: 0.2946
Epoch [4/5], Step [700/938], Loss: 0.3150
Epoch [4/5], Step [800/938], Loss: 0.3024
Epoch [4/5], Step [900/938], Loss: 0.3152
Epoch [5/5], Step [100/938], Loss: 0.2723
Epoch [5/5], Step [200/938], Loss: 0.2969
Epoch [5/5], Step [300/938], Loss: 0.2963
Epoch [5/5], Step [400/938], Loss: 0.2835
Epoch [5/5], Step [500/938], Loss: 0.2910
Epoch [5/5], Step [600/938], Loss: 0.2990
Epoch [5/5], Step [700/938], Loss: 0.2990
Epoch [5/5], Step [800/938], Loss: 0.3039
Epoch [5/5], Step [900/938], Loss: 0.3005
Test Accuracy: 87.60%

在这里插入图片描述

7 优化与调参

显然,同样的模型对于不同的数据集的适配程度是不一样的。对于MNIST数据集准确率可以达到97%,但对于FashionMNIST只能达到86%。所以我们可以来探索一下为什么会有这样的偏差,以及如何优化该模型才能让FashionMNIST也可以达到90%以上的准确率。

7.1 可能的问题

  1. 数据集的复杂性
  • MNIST 数据集:包含手写数字的灰度图像(0-9),这些图像相对简单,特征明显,模式较少。
  • FashionMNIST 数据集:包含服装物品的灰度图像(例如 T 恤、裤子、鞋子等),这些图像的特征更加复杂,类别之间的差异较小。
  1. 模型的复杂性
  • 我们使用的是一个简单的全连接神经网络,它可能足以在 MNIST 数据集上达到高准确率,但在处理更复杂的 FashionMNIST 数据集时会表现不佳。
  1. 超参数调整:
  • 我们的模型可能需要在不同的数据集上进行不同的超参数调整。例如,学习率、批量大小、正则化参数等可能需要重新调整以适应 FashionMNIST 的复杂性。
  1. 数据预处理:
  • 数据的预处理步骤(如标准化、归一化、数据增强等)对不同的数据集可能有不同的效果。我们可能需要针对 FashionMNIST 数据集尝试不同的预处理方法。

7.2 解决方案

7.2.1 增加神经网络层数和神经元容量

通过增加模型的容量,模型能够学习更多的特征。如下,我们添加两个线性层进一步提高模型对复杂特征的处理能力。

class NeuralNetwork_v1(nn.Module):def __init__(self):super().__init__()self.flatten = nn.Flatten()self.linear_relu_stack = nn.Sequential(nn.Linear(28*28, 1024),nn.ReLU(),nn.Linear(1024, 1024),nn.ReLU(),nn.Linear(1024, 512),nn.ReLU(),nn.Linear(512, 512),nn.ReLU(),nn.Linear(512, 10))def forward(self, x):x = self.flatten(x)logits = self.linear_relu_stack(x)return logits

可能因为线性层在处理FashionMNIST数据集时,难以处理和学习更多的特征,在添加了线性层后预测准确率没有明显的提高,仍然是87%左右。所以需要尝试其他的方法。

7.2.2 使用卷积神经网络(CNN)

FashionMNIST 数据集涉及到服装和配件的图像分类,每个图像都是单通道的灰度图像,分辨率为 28x28 像素。尽管 FashionMNIST 数据集相对于真实世界的图像数据集如 CIFAR-10 或 ImageNet 来说较为简单,但仍然涉及到一定程度的空间特征。且如纹理图案、形状轮廓等复杂特征,线性层更加难以处理和识别。所以在局部相关性和空间结构处理占优的情况下,使用卷积神经网络(CNN)来处理是更优的选择。

import torch
import torch.nn as nn
import torch.nn.functional as Fclass ConvNeuralNetwork(nn.Module):def __init__(self):super(ConvNeuralNetwork, self).__init__()# 卷积层定义self.conv1 = nn.Conv2d(1, 32, kernel_size=3, stride=1, padding=1)self.conv2 = nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1)self.conv3 = nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1)# 扁平化层self.flatten = nn.Flatten()# 全连接层self.fc1 = nn.Linear(128*3*3, 512)self.fc2 = nn.Linear(512, 512)self.fc3 = nn.Linear(512, 10)# Dropout 正则化层,用于随机丢弃一定比例的神经元,防止过拟合self.dropout = nn.Dropout(0.4)# 批量归一化层,对每个卷积层的输出进行归一化,有助于加速收敛和提高模型泛化能力self.batch_norm1 = nn.BatchNorm2d(32)self.batch_norm2 = nn.BatchNorm2d(64)self.batch_norm3 = nn.BatchNorm2d(128)# 批量归一化层,对全连接层的输出进行归一化self.batch_norm_fc1 = nn.BatchNorm1d(512)self.batch_norm_fc2 = nn.BatchNorm1d(512)def forward(self, x):# 卷积层和激活函数# 依次进行卷积、ReLU 激活函数、批量归一化和最大池化操作x = self.conv1(x)x = F.relu(self.batch_norm1(x))x = F.max_pool2d(x, 2)x = self.conv2(x)x = F.relu(self.batch_norm2(x))x = F.max_pool2d(x, 2)x = self.conv3(x)x = F.relu(self.batch_norm3(x))x = F.max_pool2d(x, 2)# 进行全连接和正则化x = self.flatten(x)x = self.fc1(x)x = F.relu(self.batch_norm_fc1(x))x = self.dropout(x)x = self.fc2(x)x = F.relu(self.batch_norm_fc2(x))x = self.dropout(x)# 输出层logits = self.fc3(x)return logits# 创建CNN实例,并移动到设备上
model_v1 = ConvNeuralNetwork().to(device)# 定义损失函数和优化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model_v1.parameters(), lr=0.01, momentum=0.9)# train_loader、test_loader 在之前已初始化# 训练模型
def train_v1(model, train_loader, optimizer, criterion, epochs=5):model.train()for epoch in range(epochs):running_loss = 0.0for batch_idx, (data, target) in enumerate(train_loader):data, target = data.to(device), target.to(device)optimizer.zero_grad()output = model(data)loss = criterion(output, target)loss.backward()optimizer.step()running_loss += loss.item()if batch_idx % 100 == 99:    # 每100个批次打印一次训练状态print(f'Epoch [{epoch+1}/{epochs}], Step [{batch_idx+1}/{len(train_loader)}], Loss: {running_loss/100:.4f}')running_loss = 0.0# 获取类别名称
class_names_v1 = train_dataset.classes# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei']  # 使用SimHei字体
plt.rcParams['axes.unicode_minus'] = False  # 解决负号'-'显示为方块的问题def visualize_predictions_v1(model, dataloader, class_names):model.eval()num_classes = len(class_names)confusion_matrix = np.zeros((num_classes, num_classes), dtype=np.int32)with torch.no_grad():for inputs, labels in dataloader:inputs, labels = inputs.to(device), labels.to(device)  # 将数据移动到设备上outputs = model(inputs)_, preds = torch.max(outputs, 1)for t, p in zip(labels.view(-1), preds.view(-1)):confusion_matrix[t.long(), p.long()] += 1# 归一化混淆矩阵confusion_matrix = confusion_matrix.astype('float') / confusion_matrix.sum(axis=1)[:, np.newaxis]# 绘图fig, ax = plt.subplots(figsize=(10, 8))im = ax.imshow(confusion_matrix, interpolation='nearest', cmap=plt.cm.Blues)ax.figure.colorbar(im, ax=ax)ax.set(xticks=np.arange(confusion_matrix.shape[1]),yticks=np.arange(confusion_matrix.shape[0]),xticklabels=class_names, yticklabels=class_names,title='归一化混淆矩阵',ylabel='真实标签',xlabel='预测标签')# 旋转标签并设置对齐plt.setp(ax.get_xticklabels(), rotation=45, ha="right",rotation_mode="anchor")# 遍历数据维度并创建文本注释fmt = '.2f'thresh = confusion_matrix.max() / 2.for i in range(confusion_matrix.shape[0]):for j in range(confusion_matrix.shape[1]):ax.text(j, i, format(confusion_matrix[i, j], fmt),ha="center", va="center",color="white" if confusion_matrix[i, j] > thresh else "black")fig.tight_layout()plt.show()# 执行训练
train_v1(model_v1, train_loader, optimizer, criterion)# 执行测试
test(model_v1, test_loader)# 可视化预测结果
visualize_predictions_v1(model_v1, test_loader, class_names_v1)
plt.suptitle('Model Predictions_v1')
plt.tight_layout()
plt.show()
Epoch [1/5], Step [100/938], Loss: 0.7875
Epoch [1/5], Step [200/938], Loss: 0.4787
Epoch [1/5], Step [300/938], Loss: 0.4455
Epoch [1/5], Step [400/938], Loss: 0.3960
Epoch [1/5], Step [500/938], Loss: 0.3657
Epoch [1/5], Step [600/938], Loss: 0.3557
Epoch [1/5], Step [700/938], Loss: 0.3363
Epoch [1/5], Step [800/938], Loss: 0.3495
Epoch [1/5], Step [900/938], Loss: 0.3140
Epoch [2/5], Step [100/938], Loss: 0.2842
Epoch [2/5], Step [200/938], Loss: 0.3065
Epoch [2/5], Step [300/938], Loss: 0.2671
Epoch [2/5], Step [400/938], Loss: 0.2750
Epoch [2/5], Step [500/938], Loss: 0.2874
Epoch [2/5], Step [600/938], Loss: 0.2722
Epoch [2/5], Step [700/938], Loss: 0.2639
Epoch [2/5], Step [800/938], Loss: 0.2840
Epoch [2/5], Step [900/938], Loss: 0.2630
Epoch [3/5], Step [100/938], Loss: 0.2359
Epoch [3/5], Step [200/938], Loss: 0.2461
Epoch [3/5], Step [300/938], Loss: 0.2350
Epoch [3/5], Step [400/938], Loss: 0.2337
Epoch [3/5], Step [500/938], Loss: 0.2453
Epoch [3/5], Step [600/938], Loss: 0.2247
Epoch [3/5], Step [700/938], Loss: 0.2354
Epoch [3/5], Step [800/938], Loss: 0.2351
Epoch [3/5], Step [900/938], Loss: 0.2333
Epoch [4/5], Step [100/938], Loss: 0.2045
Epoch [4/5], Step [200/938], Loss: 0.2206
Epoch [4/5], Step [300/938], Loss: 0.2161
Epoch [4/5], Step [400/938], Loss: 0.2125
Epoch [4/5], Step [500/938], Loss: 0.2003
Epoch [4/5], Step [600/938], Loss: 0.2060
Epoch [4/5], Step [700/938], Loss: 0.1919
Epoch [4/5], Step [800/938], Loss: 0.2012
Epoch [4/5], Step [900/938], Loss: 0.2138
Epoch [5/5], Step [100/938], Loss: 0.1789
Epoch [5/5], Step [200/938], Loss: 0.1724
Epoch [5/5], Step [300/938], Loss: 0.1737
Epoch [5/5], Step [400/938], Loss: 0.1883
Epoch [5/5], Step [500/938], Loss: 0.1921
Epoch [5/5], Step [600/938], Loss: 0.1982
Epoch [5/5], Step [700/938], Loss: 0.2055
Epoch [5/5], Step [800/938], Loss: 0.1865
Epoch [5/5], Step [900/938], Loss: 0.1930
Test Accuracy: 91.53%

在这里插入图片描述

<Figure size 640x480 with 0 Axes>
7.2.3 调整超参数

超参数是模型训练过程中需要预先设定的参数,学习率、批次大小和迭代次数等都称之为超参数。通过调整这些超参数,我们可以提高模型的性能和准确性。

  • 学习率是最重要的超参数之一。我们可以尝试不同的学习率,观察其对模型性能的影响:
learning_rates = [0.1, 0.01, 0.001]for lr in learning_rates:optimizer = optim.SGD(model.parameters(), lr=lr)# 训练模型并记录性能
  • 当然我们还可以通过学习率调度器在训练过程中动态调整学习率
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.1)for epoch in range(num_epochs):# 训练模型scheduler.step()
  • 批量大小会影响训练的稳定性和速度:
batch_sizes = [32, 64, 128]for batch_size in batch_sizes:train_loader = torch.utils.data.DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=True)# 训练模型并记录性能
  • 不同的优化器可能会对模型的收敛速度和最终性能产生影响。我们可以尝试不同的优化器,如SGD和Adam:
optimizers = {'SGD': optim.SGD(model.parameters(), lr=0.01),'Adam': optim.Adam(model.parameters(), lr=0.01)
}

参考

  • PyTorch-构建神经网络
  • PyTorch中文文档
  • d2L-多层感知机

相关文章:

如何使用 pytorch 创建一个神经网络

我已发布在&#xff1a;如何使用 pytorch 创建一个神经网络 SapientialM.Github.io 构建神经网络 1 导入所需包 import os import torch from torch import nn from torch.utils.data import DataLoader from torchvision import datasets, transforms2 检查GPU是否可用 dev…...

Java版Flink使用指南——定制RabbitMQ数据源的序列化器

大纲 新建工程新增依赖数据对象序列化器接入数据源 测试修改Slot个数打包、提交、运行 工程代码 在《Java版Flink使用指南——从RabbitMQ中队列中接入消息流》一文中&#xff0c;我们从RabbitMQ队列中读取了字符串型数据。如果我们希望读取的数据被自动化转换为一个对象&#x…...

CV每日论文--2024.7.8

1、DisCo-Diff: Enhancing Continuous Diffusion Models with Discrete Latents 中文标题&#xff1a;DisCo-Diff&#xff1a;利用离散潜伏增强连续扩散模型 简介&#xff1a;这篇文章提出了一种新型的离散-连续潜变量扩散模型(DisCo-Diff),旨在改善传统扩散模型(DMs)存在的问…...

【AI大模型】赋能儿童安全:楼层与室内定位实践与未来发展

文章目录 引言第一章&#xff1a;AI与室内定位技术1.1 AI技术概述1.2 室内定位技术概述1.3 楼层定位的挑战与解决方案 第二章&#xff1a;儿童定位与安全监控的需求2.1 儿童安全问题的现状2.2 智能穿戴设备的兴起 第三章&#xff1a;技术实现细节3.1 硬件设计与选择传感器选择与…...

云服务器linux系统安装配置docker

在我们拿到一个纯净的linux系统时&#xff0c;我需要进行一些基础环境的配置 &#xff08;如果是云服务器可以用XShell远程连接&#xff0c;如果连接不上可能是服务器没开放22端口&#xff09; 下面是配置环境的步骤 sudo -s进入root权限&#xff1a;退出使用exit sudo -i进入…...

泰勒雷达图2

matplotlib绘制泰勒雷达图 import matplotlib.pyplot as plt import numpy as np from numpy.core.fromnumeric import shape import pandas as pd import dask.dataframe as dd from matplotlib.projections import PolarAxes import mpl_toolkits.axisartist.floating_axes a…...

数据库容灾 | MySQL MGR与阿里云PolarDB-X Paxos的深度对比

开源生态 众所周知&#xff0c;MySQL主备库&#xff08;两节点&#xff09;一般通过异步复制、半同步复制&#xff08;Semi-Sync&#xff09;来实现数据高可用&#xff0c;但主备架构在机房网络故障、主机hang住等异常场景下&#xff0c;HA切换后大概率就会出现数据不一致的问…...

react根据后端返回数据动态添加路由

以下代码都为部分核心代码 一.根据不同的登录用户&#xff0c;返回不同的权限列表 &#xff0c;以下是三种不同用户限权列表 const pression { //超级管理员BigAdmin: [{key: "screen",icon: "FileOutlined",label: "数据图表",},{key: "…...

机器学习中的可解释性

「AI秘籍」系列课程&#xff1a; 人工智能应用数学基础 人工智能Python基础 人工智能基础核心知识 人工智能BI核心知识 人工智能CV核心知识 为什么我们需要了解模型如何进行预测 我们是否应该始终信任表现良好的模型&#xff1f;模型可能会拒绝你的抵押贷款申请或诊断你患…...

上海慕尼黑电子展开展,启明智显携物联网前沿方案亮相

随着科技创新的浪潮不断涌来&#xff0c;上海慕尼黑电子展在万众瞩目中盛大开幕。本次展会汇聚了全球顶尖的电子产品与技术解决方案&#xff0c;成为业界瞩目的焦点。启明智显作为物联网彩屏显示领域的佼佼者携产品亮相展会&#xff0c;为参展者带来了RTOS、LINUX全系列方案及A…...

Centos7离线安装ElasticSearch7.4.2

一、官网下载相关的安装包 ElasticSearch7.4.2&#xff1a; elasticsearch-7.4.2-linux-x86_64.tar.gz 下载中文分词器&#xff1a; elasticsearch-analysis-ik-7.4.2.zip 二、上传解压文件到服务器 上传到目录&#xff1a;/home/data/elasticsearch 解压文件&#xff1…...

深入理解sklearn中的模型参数优化技术

参数优化是机器学习中的关键步骤&#xff0c;它直接影响模型的性能和泛化能力。在sklearn中&#xff0c;参数优化可以通过多种方式实现&#xff0c;包括网格搜索&#xff08;GridSearchCV&#xff09;、随机搜索&#xff08;RandomizedSearchCV&#xff09;和贝叶斯优化等。本文…...

【Elasticsearch】开源搜索技术的演进与选择:Elasticsearch 与 OpenSearch

开源搜索技术的演进与选择&#xff1a;Elasticsearch 与 OpenSearch 1.历史发展2.OpenSearch 与 Elasticsearch 相同点3.OpenSearch 与 Elasticsearch 不同点3.1 版本大不同3.2 许可证不同3.3 社区不同3.4 功能不同3.5 安全性不同3.6 性能不同3.7 价格不同3.8 两者可相互导入 4…...

欧拉openEuler 22.03 LTS-部署k8sv1.03.1

1.设置ip # vi /etc/sysconfig/network-scripts/ifcfg-ens32 TYPEEthernet PROXY_METHODnone BROWSER_ONLYno BOOTPROTOstatic DEFROUTEyes IPV4_FAILURE_FATALno #IPV6INITyes #IPV6_AUTOCONFyes #IPV6_DEFROUTEyes #IPV6_FAILURE_FATALno #IPV6_ADDR_GEN_MODEeui64 NAMEens1…...

老年生活照护实训室:为养老服务业输送专业人才

本文探讨了老年生活照护实训室在养老服务业专业人才培养中的关键作用。通过详细阐述实训室的功能、教学实践、对学生能力的培养以及面临的挑战和解决方案&#xff0c;强调了其在提升人才素质、满足行业需求方面的重要性&#xff0c;旨在为养老服务业的可持续发展提供有力的人才…...

go语言中使用WaitGroup和channel实现处理多线程问题

WaitGroup 背景 如果将一个任务分为任意个小任务&#xff0c;并且不关心小任务的执行顺序&#xff0c;并且希望等待全部的小任务执行完成后再去操作后面的逻辑&#xff0c;那我推荐你用sync.WaitGRoup 使用方法 比如&#xff0c;有一个任务需要执行 3 个子任务&#xff0c;…...

Open3D 计算点云的平均密度

目录 一、概述 1.1基于领域密度计算原理 1.2应用 二、代码实现 三、实现效果 2.1点云显示 2.2密度计算结果 一、概述 在点云处理中&#xff0c;点的密度通常表示为某个点周围一定区域内的点的数量。高密度区域表示点云较密集&#xff0c;低密度区域表示点云较稀疏。计算…...

C语言之数据在内存中的存储(1),整形与大小端字节序

目录 前言 一、整形数据在内存中的存储 二、大小端字节序 三、大小端字节序的判断 四、字符型数据在内存中的存储 总结 前言 本文主要讲述整型包括字符型是如何在内存中存储的&#xff0c;涉及到大小端字节序这一概念&#xff0c;还有如何判断大小端&#xff0c;希望对大…...

B端全局导航:左侧还是顶部?不是随随便便,有依据在。

一、什么是全局导航 B端系统的全局导航是指在B端系统中的主要导航菜单&#xff0c;它通常位于系统的顶部或左侧&#xff0c;提供了系统中各个模块和功能的入口。全局导航菜单可以帮助用户快速找到和访问系统中的各个功能模块&#xff0c;提高系统的可用性和用户体验。 全局导航…...

什么是海外仓管理自动化?策略及落地实施步骤指南

作为海外仓的管理者&#xff0c;你每天都面临提高海外仓运营效率、降低成本和满足客户需求的问题。海外仓自动化管理技术为这些问题提供了不错的解决思路&#xff0c;不过和任何新技术一样&#xff0c;从策略到落地实施&#xff0c;都有一个对基础逻辑的认识过程。 今天我们整…...

自定义控件三部曲之绘图篇(六)Paint之函数大汇总、ColorMatrix与滤镜效果、setColorFilter

在自定义控件的绘图篇中&#xff0c;Paint 类是核心的组成部分之一&#xff0c;它控制了在 Canvas 上绘制的内容的各种属性&#xff0c;包括颜色、风格、抗锯齿、透明度等等。下面将详细介绍 Paint 的主要功能以及如何使用 ColorMatrix 和 setColorFilter 来实现滤镜效果。 Pa…...

请写sql满足业务:找到连续登录3天以上的用户

为了找到连续登录超过 3 天的用户&#xff0c;我们可以使用 SQL 窗口函数和递归查询来实现。假设有一个 user_logins 表&#xff0c;包含以下字段&#xff1a; user_id&#xff08;用户ID&#xff09;login_date&#xff08;登录日期&#xff09; 假设 login_date 是 DATE 类…...

fatal error: apriltag/apriltag.h: No such file or directory 的 参考解决方法

文章目录 写在前面一、问题描述二、解决方法参考链接 写在前面 自己的测试环境&#xff1a; Ubuntu20.04&#xff0c;ROS-Noteic 一、问题描述 自己编译ROS程序的时候遇到如下问题&#xff1a; fatal error: apriltag/apriltag.h: No such file or directory9 | #include &…...

C++继承(一文说懂)

目录 一&#xff1a; &#x1f525;继承的概念及定义1.1 继承的概念1.2 继承定义1.2.1 定义格式1.2.2 继承关系和访问限定符1.2.3 继承基类成员访问方式的变化 二&#xff1a;&#x1f525;基类和派生类对象赋值转换三&#xff1a;&#x1f525;继承中的作用域四&#xff1a;&a…...

卷积神经网络可视化的探索

文章目录 训练LeNet模型下载FashionMNIST数据训练保存模型 卷积神经网络可视化加载模型一个测试图像不同层对图像处理的可视化第一个卷积层的处理第二个卷积层的处理 卷积神经网络是利用图像空间结构的一种深度学习网络架构&#xff0c;图像在经过卷积层、激活层、池化层、全连…...

RxJava学习记录

文章目录 1. 总览1.1 基本原理1.2 导入包和依赖 2. 操作符2.1 创建操作符2.2 转换操作符2.3 组合操作符2.4 功能操作符 1. 总览 1.1 基本原理 参考文献 构建流&#xff1a;每一步操作都会生成一个新的Observable节点(没错&#xff0c;包括ObserveOn和SubscribeOn线程变换操作…...

Spring Boot Vue 毕设系统讲解 3

目录 项目配置类 项目中配置的相关代码 spring Boot 拦截器相关知识 一、基于URL实现的拦截器&#xff1a; 二、基于注解的拦截器 三、把拦截器添加到配置中&#xff0c;相当于SpringMVC时的配置文件干的事儿&#xff1a; 项目配置类 项目中配置的相关代码 首先定义项目认…...

Spring Boot对接大模型:实战价值与技巧

Spring Boot对接大模型&#xff1a;实战价值与技巧 随着大数据和人工智能技术的飞速发展&#xff0c;大模型&#xff08;Large-scale Models&#xff09;在各个行业中的应用越来越广泛。为了充分利用这些大模型的能力&#xff0c;我们需要将其与现有的应用框架进行对接。Sprin…...

完美解决NameError: name ‘file‘ is not defined的正确解决方法,亲测有效!!!

完美解决NameError: name ‘file’ is not defined的正确解决方法&#xff0c;亲测有效&#xff01;&#xff01;&#xff01; 亲测有效 完美解决NameError: name file is not defined的正确解决方法&#xff0c;亲测有效&#xff01;&#xff01;&#xff01;报错问题解决思路…...

Witness Table 的由来

“Witness Table” 是 Swift 中的一个术语&#xff0c;源于编译原理和类型系统的概念。它被用来表示一种机制&#xff0c;通过这个机制&#xff0c;编译器可以确保某个类型确实实现了它声明遵循的协议中的所有方法和属性。下面是对这个术语的详细解释&#xff1a; 1. 术语来源…...