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

【PyTorch】(基础六)---- 搭建卷积神经网络

关于神经网络中激活函数、卷积层、池化层等底层原理,我不会在本文中详解,但是关于pytorch中如何使用对应的方法实现这些层的功能我会进行解释,如果你想要了解一些关于神经网络底层的知识,我十分推荐你去看一下吴恩达老师的深度学习课程,我后续也会推出他的课程视频。

torch.nn 是 PyTorch 中用于构建神经网络的核心模块。它提供了许多常用的神经网络层、激活函数、损失函数、优化器等组件,使得构建和训练神经网络变得非常方便。以下的模块都是在nn中提供的内容,我来进行详解

Module

在pytorch中,想要自定义网络的话,需要使用pytorch.nn中的Module类。Module类是所有神经网络模块的基类,即自己编写的所有模块都需要继承Module类。并重写 __init__forward 方法。在使用_init_ 时又必须先引用调用父类的初始化方法,即super().__init__(),然后在forward 前向传播中使用自定义的网络模块。

【代码示例】 搭建一个自己的module子类,不需要实现神经网络的功能

import torch
from torch import nnclass Mymodule(nn.Module):def __init__(self):super().__init__()def forward(self, input):output = input + 1return outputmymodule = Mymodule()
x = torch.tensor(1)
output = mymodule(x)
print(output)  # 输出tensor(2)

常用的神经网络层

Conv2d卷积层

卷积层用于提取图像中的特征,其主要语法如下:

nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0)
# in_channels : 输入数据的通道数
# out_channels : 输出数据的通道数
# kernel_size : 卷积核的大小
# stride: 步长
# padding: 边缘填充·

【代码示例】对CIFAR-10测试集使用3*3卷积,stride和padding为默认,CIFAR10中数据大小为3*32*32,卷积结果的计算公式为 ⌊ n + 2 p − f s + 1 ⌋ \left\lfloor\frac{n+2p-f}{s}+1\right\rfloor sn+2pf+1 ,其中n为输入的大小,p为padding大小,f为卷积核大小,s为stride大小,可以进行计算得知最终结果应该为3*30*30 ,通过图片检验你的代码结果是否正确

import torchvision
from torch import nn
from torch.nn import Conv2d
from torch.utils.data import DataLoaderdataset = torchvision.datasets.CIFAR10("dataset/val", train=False, transform=torchvision.transforms.ToTensor(),download=True)
dataloader = DataLoader(dataset, batch_size=64)class myModule(nn.Module):def __init__(self):super(myModule, self).__init__()# 在此处写自定义的神经网络层self.conv1 = Conv2d(in_channels=3, out_channels=6, kernel_size=3, stride=1, padding=0)def forward(self, input):# 在此处调用神经网络层input = self.conv1(input)return inputmymodule = myModule()step = 0
for data in dataloader:imgs, targets = dataoutput = mymodule(imgs)print(imgs.shape)    # torch.Size([64, 3, 32, 32])  64为batch_size大小,即每轮读取64张图片print(output.shape)   # torch.Size([64, 6, 30, 30])  -> [xxx, 3, 30, 30]exit(0)

输出结果:

torch.Size([64, 3, 32, 32])  # 先输出通道数,然后才是图像的大小
torch.Size([64, 6, 30, 30])

池化层

池化层主要用于缩小输入的特征数量,池化有平均池化和最大值池化。两者用法相同,现在的神经网络中最大值池化更加常用,其主要语法如下:

MaxPool2d(kernel_size=3, stride=2)
# kernel_size: 池化窗口的大小。它可以是一个整数(表示高和宽相等的正方形窗口)或者一个包含两个整数的元组(kernel_height, kernel_width),分别指定窗口的高度和宽度。# stride: 窗口在输入数据上滑动的步长,默认等于kernel_size。它可以是一个整数(表示高度和宽度上的步长相同)或一个包含两个整数的元组(stride_height, stride_width)。# padding: 在输入的每个边缘填充的大小。这可以是单个整数(表示所有边缘的相同填充)或一个包含两个整数的元组(pad_height, pad_width)。默认为0,意味着没有填充。# return_indices: 如果设置为True,除了返回池化后的输出张量外,还会返回每个最大值在输入张量中的索引。这对于某些操作,如nn.MaxUnpool2d反池化操作,可能很有用。默认为False。# ceil_mode: 当设为True,会计算输出尺寸时使用向上取整,而不是默认的向下取整。这会影响到输出的尺寸计算,特别是在使用非1的步长或填充时。默认为False。

【代码示例】对CIFAR10数据集使用最大池化,并将最终结果进行可视化呈现

import torchvision
from torch import nn
from torch.nn import MaxPool2d
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriterdataset = torchvision.datasets.CIFAR10("dataset/CIFAR10", train=False, download=True,transform=torchvision.transforms.ToTensor())dataloader = DataLoader(dataset, batch_size=64)class myModule(nn.Module):def __init__(self):super().__init__()self.maxpool1 = MaxPool2d(kernel_size=3)def forward(self, input):output = self.maxpool1(input)return outputmyModule = myModule()writer = SummaryWriter("logs_maxpool")
step = 0for data in dataloader:imgs, targets = datawriter.add_images("input", imgs, step)output = myModule(imgs)writer.add_images("output", output, step)step = step + 1writer.close()

运行结果:最大池化相当于是邻近几个像素中只选取一个最大值作为特征,所以呈现结果就是图片减小,变成类似马赛克的效果

在这里插入图片描述

归一化层

归一化层是深度学习中常用的技巧,用于加速训练过程并提高模型的性能。归一化层的主要目的是减少内部协变量偏移(internal covariate shift),即在网络训练过程中,每一层的输入分布会发生变化,这会导致训练不稳定。归一化层通过将每一层的输入分布标准化,使得训练更加稳定和高效。

class torch.nn.BatchNorm2d(num_features, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
# num_features:特征的数量(对于 BatchNorm1d 是输入特征的数量,对于 BatchNorm2d 是通道数)。
# eps:一个小常数,用于防止除零错误,默认值为 1e-05。
# momentum:用于计算运行平均值和方差的动量因子,默认值为 0.1。
# affine:如果为 True,则使用可学习的仿射参数(gamma 和 beta),默认值为 True。
# track_running_stats:如果为 True,则跟踪整个训练过程中的运行平均值和方差,默认值为 True。
# 对 2D 输入进行层归一化
ln = nn.LayerNorm(normalized_shape=(16, 32, 32))

激活层

主要的激活函数有三个,ReLU、Sigmoid和Tanh,这三者对应的方法为nn.ReLU()nn.Sigmoid()nn.Tanh(),没有特殊的参数需求,直接调用即可

【代码示例】实现激活函数并进行可视化

import torch
import torchvision
from torch import nn
from torch.nn import ReLU, Sigmoid
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriterinput = torch.tensor([[1, -0.5],[-1, 3]])input = torch.reshape(input, (-1, 1, 2, 2))
print(input.shape)dataset = torchvision.datasets.CIFAR10("dataset/CIFAR10", train=False, download=True,transform=torchvision.transforms.ToTensor())dataloader = DataLoader(dataset, batch_size=64)class myModule(nn.Module):def __init__(self):super().__init__()self.relu1 = ReLU()self.sigmoid1 = Sigmoid()def forward(self, input):output = self.sigmoid1(input)return outputmymodule = myModule()writer = SummaryWriter("logs_relu")
step = 0
for data in dataloader:imgs, targets = datawriter.add_images("input", imgs, global_step=step)output = mymodule(imgs)writer.add_images("output", output, step)step += 1writer.close()

Linear线性层

线性变换主要有两个函数:

nn.flatten 用来将图片展开为一维信息

nn.Linear 用来进行全连接的线性变换

【代码示例】实现nn.flatten 展开和nn.Linear 线性变换

import torch
import torchvision
from torch import nn
from torch.nn import Linear
from torch.utils.data import DataLoaderdataset = torchvision.datasets.CIFAR10("dataset/CIFAR10", train=False, transform=torchvision.transforms.ToTensor(), download=True)dataloader = DataLoader(dataset, batch_size=64, drop_last=True)class myModule(nn.Module):def __init__(self):super().__init__()# in_feature 为输入特征数量# out_feature 为输出特征数量self.linear1 = Linear(196608, 10)def forward(self, input):output = self.linear1(input)return outputmymodule = myModule()for data in dataloader:imgs, targets = dataprint(imgs.shape)output = torch.flatten(imgs)  # flatten将图片变为一维信息print(output.shape)output = mymodule(output)print(output.shape)

损失函数

主要用到的三种损失函数如下:

  1. CrossEntropyLoss:交叉熵损失,常用于多分类问题,特别是类别相互排斥的情况(每个样本只属于一个类别)。它结合了softmax函数和负对数似然损失,直接从raw logits预测中计算损失。
  2. MSELoss:均方误差损失,广泛应用于回归问题,计算预测值和真实值之间的均方差。
  3. L1Loss:L1范数损失,也称为绝对误差损失,适用于回归任务,鼓励生成稀疏解,因为它对误差的惩罚不如MSELoss敏感。

在PyTorch中的L1Loss以及其他许多损失函数中,reduction参数是用来指定如何将每个样本或批次中元素的损失进行汇总的方式。这个参数决定了最终返回的损失值是单个标量还是保持为向量/张量的形式。reduction参数可以取以下几个值:

  1. ‘none’:不进行约简,即返回的loss是一个与输入形状相同的张量,每个元素对应一个样本的损失。这对于需要对每个样本的损失进行单独分析或者后处理的情况很有用。
  2. ‘mean’(默认值):将所有样本的损失求平均值,得到一个标量。这是最常用的情况,特别是在训练神经网络时,因为这能给出批次内损失的总体表现。
  3. ‘sum’:将所有样本的损失相加,得到一个标量。这种情况下,损失值会随着批次大小的增加而增加,因此在比较不同大小批次的损失时需要注意归一化。

【代码示例】 在上面创建的CIFAR10卷积神经网络中使用交叉熵损失函数观察其预测结果和正确结果之间的差距

import torchvision
from torch import nn
from torch.nn import Sequential, Conv2d, MaxPool2d, Flatten, Linear
from torch.utils.data import DataLoaderdataset = torchvision.datasets.CIFAR10("dataset/CIFAR10", train=False, transform=torchvision.transforms.ToTensor(),download=True)dataloader = DataLoader(dataset, batch_size=1)class myModule(nn.Module):def __init__(self):super().__init__()self.model1 = Sequential(Conv2d(3, 32, 5, padding=2),MaxPool2d(2),Conv2d(32, 32, 5, padding=2),MaxPool2d(2),Conv2d(32, 64, 5, padding=2),MaxPool2d(2),Flatten(),Linear(1024, 64),Linear(64, 10))def forward(self, x):x = self.model1(x)return xloss = nn.CrossEntropyLoss()
mymodule = myModule()
for data in dataloader:imgs, targets = dataoutputs = mymodule(imgs)result_loss = loss(outputs, targets)print(result_loss)

运行结果:

tensor(2.3523, grad_fn=<NllLossBackward0>)
tensor(2.2240, grad_fn=<NllLossBackward0>)
...

优化器

PyTorch提供了多种内置的优化器,用于在训练过程中更快地更新模型参数,主要作用在反向传播过程中以下是一些常用的优化器及其特点:

  1. SGD (Stochastic Gradient Descent):随机梯度下降,是最基础的优化算法,支持动量选项(momentum)。
  2. SGD with Nesterov Momentum:带Nesterov动量的SGD,动量更新时考虑的是未来位置,有时能提供更好的收敛性。
  3. ASGD (Averaged Stochastic Gradient Descent):平均随机梯度下降,会跟踪并最终使用参数的历史平均值进行更新。
  4. Adagrad:自动调整学习率的梯度下降,对每个参数维护一个不同的学习率,适合稀疏数据。
  5. Adadelta:不需要手动设置学习率,基于历史梯度平方的加权平均来调整学习率。
  6. RMSprop:Root Mean Square Propagation,也是基于梯度的平方,但使用指数移动平均来调整学习率。
  7. Adam (Adaptive Moment Estimation):非常流行的优化器,结合了动量和RMSprop的优点,自适应学习率,适用于多种任务。
  8. Adamax:Adam的一个变种,使用无穷范数代替二阶矩估计。
  9. AdamW:修正了Adam中权重衰减(L2惩罚)的实现,使权重衰减独立于学习率缩放。
  10. NAdam:带有Nesterov动量的Adam,结合了Nesterov动量和Adam的优点。
  11. SparseAdam:专门为稀疏梯度优化设计的Adam变体。
  12. L-BFGS:拟牛顿优化方法,适合解决小批量或批大小为1的情况,但内存消耗较大。
  13. RAdam (Rectified Adam):解决了Adam初期学习率过低的问题,通过修正初始阶段的偏差来提高训练稳定性。
  14. Radam:RAdam的另一种实现,同样旨在改进Adam的初期学习行为。

目前只需要知道是最常用的为SGD和Adam即可,我会在后续专门的文章中给大家详细介绍关于优化器地底层原理以及它们的选择

【代码示例】使用随机梯度下降(SGD)实现模型的训练

import torch
import torchvision
from torch import nn
from torch.nn import Sequential, Conv2d, MaxPool2d, Flatten, Linear
from torch.optim.lr_scheduler import StepLR
from torch.utils.data import DataLoaderdataset = torchvision.datasets.CIFAR10("dataset/CIFAR10", train=False, transform=torchvision.transforms.ToTensor(),download=True)dataloader = DataLoader(dataset, batch_size=1)class myModule(nn.Module):def __init__(self):super().__init__()self.model1 = Sequential(Conv2d(3, 32, 5, padding=2),MaxPool2d(2),Conv2d(32, 32, 5, padding=2),MaxPool2d(2),Conv2d(32, 64, 5, padding=2),MaxPool2d(2),Flatten(),Linear(1024, 64),Linear(64, 10))def forward(self, x):x = self.model1(x)return xloss = nn.CrossEntropyLoss()    # 交叉熵损失函数
mymodule = myModule()
optim = torch.optim.SGD(mymodule.parameters(), lr=0.01)    # 随机梯度下降优化器
for epoch in range(20):running_loss = 0.0for data in dataloader:imgs, targets = dataoutputs = mymodule(imgs)result_loss = loss(outputs, targets)optim.zero_grad()   # 每次进行反向传播前,将优化器的梯度置零,避免梯度积累result_loss.backward()    # 根据计算出的损失函数进行反向传播,计算梯度optim.step()  # 更新参数running_loss = running_loss + result_loss   # 累加损失,方便对每一轮的损失进行监控print(running_loss)

运行结果:

tensor(18725.9844, grad_fn=<AddBackward0>)
tensor(16125.2676, grad_fn=<AddBackward0>)
tensor(15378.4766, grad_fn=<AddBackward0>)
tensor(16010.2520, grad_fn=<AddBackward0>)
tensor(17944.1855, grad_fn=<AddBackward0>)
tensor(20080.8281, grad_fn=<AddBackward0>)
tensor(21999.3105, grad_fn=<AddBackward0>)
tensor(23399.3027, grad_fn=<AddBackward0>)
tensor(23927.0293, grad_fn=<AddBackward0>)
tensor(24855.1445, grad_fn=<AddBackward0>)
tensor(25396.4512, grad_fn=<AddBackward0>)

Sequential容器序列

在PyTorch中,nn.Sequential是一个容器模块,用于将多个模块按照顺序串联起来形成一个有序的神经网络结构。这意味着数据会按照添加到nn.Sequential中的顺序依次通过每个模块(层)。在上一个例子中,我们要手写forward中所有的调用,使用nn.Sequential时,你不需要显式地定义前向传播(forward)方法,因为这个容器会自动将输入传递给序列中的第一个模块,然后将前一个模块的输出自动作为下一个模块的输入,依此类推,直到序列结束。这样不仅是不需要手写forward,同时也不需要在Sequential中定义每一层的名字了。这对于定义那些没有分支或者跳连的简单前馈网络特别方便。

【代码示例】使用Sequential改写CIFAR-10官方例子(在下一小节介绍)

import torch
from torch import nn# 搭建神经网络
class myModule(nn.Module):def __init__(self):super().__init__()self.model = nn.Sequential(nn.Conv2d(3, 32, 5, 1, 2),nn.MaxPool2d(2),nn.Conv2d(32, 32, 5, 1, 2),nn.MaxPool2d(2),nn.Conv2d(32, 64, 5, 1, 2),nn.MaxPool2d(2),nn.Flatten(),nn.Linear(64*4*4, 64),nn.Linear(64, 10))def forward(self, x):x = self.model(x)return xif __name__ == '__main__':mymodule = myModule()input = torch.ones((64, 3, 32, 32))output = mymodule(input)print(output.shape)  # 运行结果不变,代码更加简洁

卷积神经网络的简单搭建

相信你还记得我们在上一章中使用到的CIFAR10数据集,官方为我们提供了一个卷积神经网络的架构实现对其进行分类,其架构图如下所示:

在这里插入图片描述

图例说明:a@b*c 中,a为通道数,b和c为图像的宽高

【代码示例】自己动手实现上面的卷积神经网络并测试其输出内容和大小

import torch
from torch import nn# 搭建CIFAR10神经网络
class myModule(nn.Module):def __init__(self):super().__init__()self.my_conv1 = nn.Conv2d(3, 32, 5, 1, 2)self.my_pool1 = nn.MaxPool2d(2)self.my_conv2 = nn.Conv2d(32, 32, 5, 1, 2)self.my_pool2 = nn.MaxPool2d(2)self.my_conv3 = nn.Conv2d(32, 64, 5, 1, 2)self.my_pool3 = nn.MaxPool2d(2)self.my_flat = nn.Flatten()self.my_line1 = nn.Linear(64*4*4, 64)self.my_line2 = nn.Linear(64, 10)def forward(self, input):output = self.my_conv1(input)output = self.my_pool1(output)output = self.my_conv2(output)output = self.my_pool2(output)output = self.my_conv3(output)output = self.my_pool3(output)output = self.my_flat(output)output = self.my_line1(output)output = self.my_line2(output)return outputif __name__ == '__main__':mymodule = myModule()print(mymodule)    # 查看自己创建的结构# 验证最后的输出大小是否为自己想要的input = torch.ones((64, 3, 32, 32))output = mymodule(input)print(output.shape)

运行结果:

myModule((my_conv1): Conv2d(3, 32, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))(my_pool1): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)(my_conv2): Conv2d(32, 32, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))(my_pool2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)(my_conv3): Conv2d(32, 64, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))(my_pool3): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)(my_flat): Flatten(start_dim=1, end_dim=-1)(my_line1): Linear(in_features=1024, out_features=64, bias=True)(my_line2): Linear(in_features=64, out_features=10, bias=True)
)
torch.Size([64, 10])

修改网络模型

现在我们已经会自己搭建一个模型并进行训练,但通常情况下我们都不需要自己从头搭建一个网络,而是在前人研究的基础上,进行改进,得到效果更好的/更适用于当前任务的网络模型,如何修改别人已经搭建好的模型就成为了一个问题。

就像之前用到的CIFAR10之类的数据集一样,pytorch官网也为我们提供了一些公开的网络模型,我们在这里使用vgg16进行演示。

# pretrained表示是否使用预训练好的参数,选择True就是使用作者已经为我们训练好的参数,False则是随机初始化参数,自己从头开始进行训练
vgg16_true = torchvision.models.vgg16(pretrained=True)

此时如果打印我们实例化的vgg16对象,就可以看到它的整个网络结构:

VGG((features): Sequential((0): Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(1): ReLU(inplace=True)(2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(3): ReLU(inplace=True)(4): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)(5): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(6): ReLU(inplace=True)(7): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(8): ReLU(inplace=True)(9): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)(10): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(11): ReLU(inplace=True)(12): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(13): ReLU(inplace=True)(14): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(15): ReLU(inplace=True)(16): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)(17): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(18): ReLU(inplace=True)(19): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(20): ReLU(inplace=True)(21): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(22): ReLU(inplace=True)(23): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)(24): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(25): ReLU(inplace=True)(26): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(27): ReLU(inplace=True)(28): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(29): ReLU(inplace=True)(30): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False))

首先要知道,大部分复杂的神经网络都分为了很多部分,比如:

在这里插入图片描述

首先到达你需要修改网络的正确部分,添加层使用add_module,修改某个层使用它的下表定位后重新制定Module即可

【代码示例】修改下载的vgg16模型并观察其网络结构的变化

import torchvision
from torch import nnvgg16_false = torchvision.models.vgg16(pretrained=False)
vgg16_true = torchvision.models.vgg16(pretrained=True)print(vgg16_true)
print("*"*30)vgg16_true.classifier.add_module('add_linear', nn.Linear(1000, 10))
print(vgg16_true)
print("*"*30)print(vgg16_false)
print("*"*30)
vgg16_false.classifier[6] = nn.Linear(4096, 10)
print(vgg16_false)

模型的保存与读取

当我们写好一个模型,训练好其参数后,谁也不想下次开机又要重新来一遍这个漫长的过程,这个时候就需要将训练好的模型进行保存,并在下一次需要使用它时候进行读取。

模型的保存和读取有两种方法:

  1. 保存模型结构+模型参数

    # 保存
    torch.save(vgg16, "vgg16_method1.pth")# 读取
    model = torch.load("vgg16_method1.pth")
    

    用这个方法得到的模型对象直接就是其网络结构

  2. 保存模型参数

    # 保存   
    torch.save(vgg16.state_dict(), "vgg16_method2.pth")# 读取  需要先创建好对象,然后给其传入参数
    vgg16 = torchvision.models.vgg16(pretrained=False)
    vgg16.load_state_dict(torch.load("vgg16_method2.pth"))
    print(vgg16)  # 需要创建模型并加载参数model = torch.load("vgg16_method2.pth")
    print(model)   # 只读取其参数而没有模型装载就是所有参数的字典
    

推荐使用第二种方法,第一种方法会遇到的问题:

# 保存一个自定义网络   
class myModule(nn.Module):def __init__(self):super().__init__()self.conv1 = nn.Conv2d(3, 64, kernel_size=3)def forward(self, x):x = self.conv1(x)return xmymodule = myModule()
torch.save(mymodule, "mymodule.pth")# 读取
model = torch.load('mymodule.pth')
print(model)

这种情况下在另一个文件中使用第一种方法进行读取就会报错,因为在新的文件中没有定义关于自定义模型的结构。这时候在新的文件中导入自定义模块的类,使用第二种方法创建对象,然后再传参就不会有问题了。

相关文章:

【PyTorch】(基础六)---- 搭建卷积神经网络

关于神经网络中激活函数、卷积层、池化层等底层原理&#xff0c;我不会在本文中详解&#xff0c;但是关于pytorch中如何使用对应的方法实现这些层的功能我会进行解释&#xff0c;如果你想要了解一些关于神经网络底层的知识&#xff0c;我十分推荐你去看一下吴恩达老师的深度学习…...

【JAVA项目】基于ssm的【美食推荐管理系统】

【JAVA项目】基于ssm的【美食推荐管理系统】 技术简介&#xff1a;采用JSP技术、B/S架构、SSM框架、MySQL技术等实现。 系统简介&#xff1a;美食推荐管理系统&#xff0c;在系统首页可以查看首页、热门美食、美食教程、美食店铺、美食社区、美食资讯、我的、跳转到后台等内容。…...

adb 常用命令笔记

adb connect <ip> #连接指定ip adb disconnect <ip> #断开连接指定ip adb devices #查看连接中的设备 adb install <flie> #安装apk adb uninstall <packageName> #卸载app adb -s install <flie> #指定设备安装 adb shell pm list package…...

[代码随想录Day32打卡] 理论基础 509. 斐波那契数 70. 爬楼梯 746. 使用最小花费爬楼梯

理论基础 题型 动归基础&#xff08;这一节就是基础题&#xff09;背包问题打家劫舍股票问题子序列问题 动态规划五部曲 确定dp数组及其下标的含义确定递推公式dp数组如何初始化遍历顺序打印dp数组 509. 斐波那契数 简单~ dp数组及下标含义&#xff1a; dp[i]表示第i各斐…...

android NumberPicker隐藏分割线或修改颜色

在 Android 中&#xff0c;可以通过以下几种方法隐藏 NumberPicker 的分割线&#xff1a; 使用 XML 属性设置 在布局文件中的 NumberPicker 标签内添加 android:selectionDividerHeight"0dp" 属性&#xff0c;将分割线的高度设置为 0&#xff0c;从而达到隐藏分割线…...

7-2 二分查找

输入n值(1<n<1000)、n个非降序排列的整数以及要查找的数x&#xff0c;使用二分查找算法查找x&#xff0c;输出x所在的下标&#xff08;0~n-1&#xff09;及比较次数。若x不存在&#xff0c;输出-1和比较次数。 输入格式: 输入共三行&#xff1a; 第一行是n值&#xff1…...

mid360使用cartorapher进行3d建图导航

1. 添加urdf配置文件&#xff1a; 添加IMU配置关节点和laser关节点 <!-- imu livox --> <joint name"livox_frame_joint" type"fixed"> <parent link"base_link" /> <child link"livox_frame" /> <o…...

Ubuntu安装grafana

需求背景&#xff1a;管理服务器&#xff0c;并在线预警&#xff0c;通知 需求目的&#xff1a; 及时获取服务器状态 技能要求&#xff1a; 1、ubuntu 2、grafana 3、prometheus 4、node 步骤&#xff1a; 一、grafana安装 1、准备系统环境&#xff0c;配置号网络 2、…...

Java版-图论-最短路-Floyd算法

实现描述 网络延迟时间示例 根据上面提示&#xff0c;可以计算出&#xff0c;最大有100个点&#xff0c;最大耗时为100*wi,即最大的耗时为10000&#xff0c;任何耗时计算出来超过这个值可以理解为不可达了&#xff1b;从而得出实现代码里面的&#xff1a; int maxTime 10005…...

可视化建模以及UML期末复习篇----UML图

这是一篇相对较长的文章&#xff0c;如你们所见&#xff0c;比较详细&#xff0c;全长两万字。我不建议你们一次性看完&#xff0c;直接跳目录找你需要的知识点即可。 --------欢迎各位来到我UML国&#xff01; 一、UML图 总共有如下几种&#xff1a; 用例图&#xff08;Use Ca…...

HTML常见标签列表,涵盖了多种用途的标签。

文档结构标签 <!DOCTYPE html>&#xff1a;声明文档类型&#xff0c;告诉浏览器使用HTML5标准。<html>&#xff1a;HTML文档的根元素。<head>&#xff1a;包含文档的元数据&#xff08;meta-data&#xff09;&#xff0c;如标题、字符集、样式表链接、脚本等…...

FPGA 16 ,Verilog中的位宽:深入理解与应用

目录 前言 一. 位宽的基本概念 二. 位宽的定义方法 1. 使用向量变量定义位宽 ① 向量类型及位宽指定 ② 位宽范围及位索引含义 ③ 存储数据与字节数据 2. 使用常量参数定义位宽 3. 使用宏定义位宽 4. 使用[:][-:]操作符定义位宽 1. 详细解释 : 操作符 -: 操作符 …...

vue-生命周期

Vue 的生命周期是指 Vue 实例从创建到销毁期间经历的一系列阶段。每个阶段都有相应的钩子函数&#xff08;Lifecycle Hooks&#xff09;&#xff0c;允许开发者在这些关键时刻执行自定义逻辑。 一、钩子函数 1. 创建阶段 beforeCreate 在实例初始化之后&#xff0c;数据观测 …...

浅谈Kubernetes(K8s)之RC控制器与RS控制器

1.RC控制器 1.1RC概述 Replication Controller 控制器会持续监控正在运行的Pod列表&#xff0c;并保证相应类型的Pod的数量与期望相符合&#xff0c;如果Pod数量过少&#xff0c;它会根据Pod模板创建新的副本&#xff0c;反之则会删除多余副本。通过RC可实现了应用服务的高可用…...

本题要求采用选择法排序,将给定的n个整数从大到小排序后输出。

#include <stdio.h> #define MAXN 10 int main() { int i, index, k, n, temp; int a[MAXN]; scanf("%d", &n); for (i 0; i < n; i) { scanf("%d", &a[i]); } // 外层循环控制排序轮数&#xff0c;一共需要n-1轮 for (k 0; k < n…...

Linux: glibc: 频繁调用new/delete会不会导致内存的碎片

最近同事问了一个问题:频繁调用new/delete会不会导致内存的碎片。 下面是我想到的一些回答, glibc的内存处理机制,是在释放的时候会自动将小块内存整合成大块内存,为接下来满足大块的需求的可能。而且程序也不是一直占着内存不释放(如果是一直不释放,要考虑是不是内存泄漏…...

量子变分算法---损失函数

引子 关于损失函数&#xff0c;我们知道在强化学习中&#xff0c;会有一个函数&#xff0c;用来表示模型每一次行为的分数&#xff0c;通过最大化得分&#xff0c;建立一个正反馈机制&#xff0c;若模型为最优则加分最多&#xff0c;若决策不佳则加很少分或者扣分。而在神经网络…...

计算机的性能评估

目录 计算机的性能评估 确定性能指标 考虑通讯因素 考虑机器过热因素 综合评估模型 动态评估与调整 计算机的性能评估 在分布式计算机系统中,综合考虑各种因素来评估性能是一个复杂但重要的问题。以下是一种可能的方法来综合考虑评估分布式计算机性能,动态地考虑实际情…...

大数据之国产数据库_OceanBase数据库002_在centos7.9上_安装部署OceanBase001_踩坑指南_亲测可用

部署前最好看一下,部署前的要求, 主要是系统 以及系统内核版本,还有比如清理一下缓存等,按照做一做. 这些都是前置条件. 清一下缓存. 也就是说官网给的前置的条件,都要根据说明去执行一遍,如果不执行可能后面安装会报错. 然后用户最好也去创建一个用户. 注意前置...

【ETCD】【源码阅读】深入解析 EtcdServer.run 函数

EtcdServer.run 是 etcd 的核心运行逻辑之一&#xff0c;负责管理 Raft 状态机的应用、事件调度以及集群的核心操作。本文将逐步从源码层面分析 run 函数的逻辑&#xff0c;帮助读者理解其内部机制和设计思想。 函数签名与关键职责 func (s *EtcdServer) run() {... }关键职责…...

springboot/ssm校内订餐系统Java代码web项目美食外卖点餐配送源码

springboot/ssm校内订餐系统Java代码web项目美食外卖点餐配送源码 基于springboot(可改ssm)vue项目 开发语言&#xff1a;Java 框架&#xff1a;springboot/可改ssm vue JDK版本&#xff1a;JDK1.8&#xff08;或11&#xff09; 服务器&#xff1a;tomcat 数据库&#xff…...

floodfill算法

目录 什么是floodfill算法 题目一——733. 图像渲染 - 力扣&#xff08;LeetCode&#xff09; 题目二——200. 岛屿数量 - 力扣&#xff08;LeetCode&#xff09; 题目三——695. 岛屿的最大面积 - 力扣&#xff08;LeetCode&#xff09; 题目四—— 130. 被围绕的区域 …...

【JAVA】六亮增加贴

James Gosling&#xff08;詹姆斯.高斯林&#xff09; Java 语言源于 1991 年 4 月&#xff0c;Sun 公司 James Gosling博士 领导的绿色计划(Green Project) 开始启动&#xff0c;此计划最初的目标是开发一种能够在各种消费性电子产品(如机顶盒、冰箱、收音机等)上运行的程序…...

git提交时出现merge branch main of xxx

git提交时出现merge branch main of xxx 原因&#xff1a; 1、同事commit了一个修改A&#xff0c;push到remote 2、我把这个修改直接pull了下来&#xff08;pull是fetchmerge的操作&#xff0c;自动合并到本地workspace&#xff09; 3、同事因为后续的commit有冲突&#xff0c…...

lstm 输入数据的形状是怎么样的,他有两种输入方式,通过参数 batch_first来设置 默认是False

lstm 输入数据的形状是怎么样的&#xff0c;他有两种输入方式&#xff0c;通过参数 batch_first来设置 默认是False 当batch_firstFalse时&#xff0c;LSTM输入的数据形状通常是一个三维张量&#xff0c;其维度顺序为[sequence_length, batch_size, input_size]。下面是对这些维…...

Apache Doris 数据类型

Apache Doris 已支持的数据类型列表如下&#xff1a; 数值类型​ 类型名存储空间&#xff08;字节&#xff09;描述BOOLEAN1布尔值&#xff0c;0 代表 false&#xff0c;1 代表 true。TINYINT1有符号整数&#xff0c;范围 [-128, 127]。SMALLINT2有符号整数&#xff0c;范围 …...

编译问题 fatal error: rpc/rpc.h: No such file or directory

在编译一些第三方软件的时候&#xff0c;会经常遇到一些文件识别不到的问题&#xff0c;这里整理下做个归总。 目前可能的原因有&#xff08;排序分先后&#xff09;&#xff1a; 文件不存在&#xff1b;文件存在但路径识别不了&#xff1b;…… 这次以常见的编译lmbench测试…...

linux 安装composer

下载composer curl -sS https://getcomposer.org/installer | php下载后设置环境变量&#xff0c;直接通过命令composer -v mv composer.phar /usr/local/bin/composer查看版本看是否安装成功 composer -v...

数据库公共字段自动填充的三种实现方案

背景介绍 在实际项目开发中,我们经常需要处理一些公共字段的自动填充,比如: createTime (创建时间)updateTime (更新时间)createUser (创建人)updateUser (更新人) 这些字段在每个表中都存在,如果每次都手动设置会很麻烦。下面介绍三种常用的解决方案。 方案一&#xff1a;M…...

《MySQL 入门:数据库世界的第一扇门》

一、MySQL 简介 MySQL 是一种开源的关系型数据库管理系统&#xff0c;在数据库领域占据着重要地位。它以其高效查询、高安全性、低成本和扩展性著称&#xff0c;广泛应用于网站、企业级应用、数据分析等领域。 MySQL 具有诸多优点。首先&#xff0c;它成本低&#xff0c;作为…...

网站建设新闻 常识/百度pc网页版入口

更多渗透技能 欢迎搜索公众号&#xff1a;白帽子左一一、Python反序列化 与PHP存在反序列化一样 Python也存在反序列化漏洞、并且Python反序列化更加强大 除了能反序列化当前代码中出现的类&#xff08;包括import引入的模块中的类&#xff09; 还能反序列化types创建的匿…...

部门网站建设的工作领导小组/app开发工具哪个好

在学习《modern c design》的时候&#xff0c;学习到了如何在 compile-time 检测两个类型之间是否可以转化. 这里的转换&#xff0c;既包括 int&#xff0c;long&#xff0c;double 这些数据类型之间的转换&#xff0c;也包括基类和子类之间的转换&#xff08;也就是两个类之前…...

公司网站建设价格/阿里云域名注册官网

前言 纵观神经网络的发展历程&#xff0c;从最原始的MLP&#xff0c;到CNN&#xff0c;到RNN,到LSTM&#xff0c;GRU&#xff0c;再到现在的Attention机制&#xff0c;人们不断的在网络里面加入一些先验知识&#xff0c;使得网络不过于“发散”&#xff0c;能够朝着人们希望的…...

外贸网站建设费用多少/semi是什么意思

PAYJS开通微信分账功能以来&#xff0c;有很多同学咨询相关情况。很多同学关心有没有什么办法&#xff0c;可以让自己的商户号快速开通企业付款功能。这里就介绍下微信分账的具体相关内容&#xff0c;可以完美解决问题。一、什么是微信分账&#xff1f; 微信分账的推出主要有三…...

网站自动售卡怎么做/自媒体怎么做

首先调整磁盘大小为60G 注意: 如果虚拟机做快照是无法调节置备大小的,要先删除快照才能调整 点确定后,重启虚拟机OS[rootdownload1 ~] shutdown -r now[rootdownload1 ~]fdisk -lDisk /dev/sda: 64.4 GB, 64424509440 bytes255 heads, 63 sectors/track, 7832 cylindersUnits …...

电子商务网站建设合同/搜索引擎优化方案

mockjs的简介&#xff1a; 使用mockjs可以事先模拟数据&#xff0c;前提是和后端约定好了数据接口&#xff0c;怎样的数据。使用mock就可以生成你要的数据了&#xff0c;从而实现开发时前后端分离。 其主要功能是&#xff1a; 基于数据模板生成模拟数据。 基于HTML模板生成模…...