【pytorch】常用代码

文章目录

  • 条件与概率
    • torch.tensor()
    • torch.rand()
    • torch.randn()
    • torch.randint()
    • torch.multinominal()
  • 逻辑运算
    • torch.argmax()
    • torch.max()
    • torch.sum()
    • torch.tanh()
    • torch.pow()
  • 功能性操作 torch.nn.functional
    • F.normalize()
    • F.elu()
    • F.relu()
    • F.softmax()
  • 张量计算
    • torch.zeros()
    • torch.from_numpy()
    • torch.stack() 张量堆叠
    • torch.Tensor()
    • squeeze()
    • tensor.repeat()
    • .unsqueece()
    • torch.cat()
    • .view()
    • action.flatten()
  • 模型构建
    • torch.nn
    • nn.ModuleList()
    • nn.Sequential() 构建序贯模型
    • nn.ModuleList() 与 nn.Sequential()的区别
    • 自定义模型
    • super(son_class_name, self).__init__()
  • 层结构
    • nn.Conv2d()
    • nn.Linear()
    • nn.Embeding()
    • indices = torch.stack((torch.arange(action.shape[0]), action.sequeeze()), dim=0)
  • 设置梯度
    • optimizer = optim.Adam()
    • with torch.no_grad():
    • y_tensor = x_tensor.detach()
    • detach()与with torch.no_grad(): 的区别
    • optimzer.zero_grad()、optimzier.step()
  • 损失计算
    • nn.L1lose
    • nn.MSELoss()
    • nn.CrossEntropyLoss()
    • loss_ = loss_.sum() / loss_.flatten().shape[0]
  • 模型保存与加载
    • torch.save()
  • 设备
    • cuda
  • 一些补充
    • 查看输入输出维度
    • 动态图与静态图
    • random.seed(42)

条件与概率

torch.tensor()

torch.tensor() 用于创建一个新的张量(Tensor)。
该函数可以接收多种类型的输入,包括列表、元组、NumPy 数组等,并将它们转换为 PyTorch Tensor

当调用 torch.tensor() 时,可以选择性地传递一些额外的参数以定制Tensor
可以通过 dtype 参数来指定你想要的数据类型,如 torch.float32torch.int64 等。
可以通过 device 参数来指定 Tensor应该被创建在哪个设备上,比如 CPU 或 GPU。

import torch# 创建一个包含数字的列表,并将其转换为 Tensor
tensor_list = torch.tensor([1, 2, 3])
print(tensor_list)# 创建一个 NumPy 数组,并将其转换为 Tensor
numpy_array = torch.tensor(np.array([4, 5, 6]))
print(numpy_array)# 创建一个具有特定数据类型和张量的 Tensor
specific_dtype_tensor = torch.tensor([7, 8, 9], dtype=torch.float32)
print(specific_dtype_tensor)# 创建一个位于 GPU 上的 Tensor
gpu_tensor = torch.tensor([10, 11, 12], device='cuda')
print(gpu_tensor)

输出结果

tensor([1, 2, 3])
tensor([4, 5, 6], dtype=torch.int32)
tensor([7., 8., 9.])
tensor([10, 11, 12])

值得注意的是,如果传递给 torch.tensor() 的输入已经是一个 Tensor,那么该函数将会尝试重用输入 Tensor 的内存空间,而不是创建一个新的 Tensor。这样做可以提高效率,但可能会改变原始 Tensor 的数据。因此,如果你不希望修改原始 Tensor,你应该先对其进行复制。

torch.rand()

torch.rand() 是 PyTorch 中的一个函数,用于生成一个填充了随机数的张量。这些随机数是从均匀分布中抽取的,范围从 0 到 1。

以下是 torch.rand() 的一些基本用法:

import torch# 生成一个形状为 (2, 3) 的随机张量
x = torch.rand(2, 3)
print(x)# 生成一个形状为 (2, 3, 4) 的随机张量
y = torch.rand(2, 3, 4)
print(y)# 生成一个形状为 (2, 3) 的随机张量,并且指定数据类型为 float64
z = torch.rand(2, 3, dtype=torch.float64)
print(z)

输出结果

tensor([[0.7737, 0.1984, 0.5063],[0.0678, 0.2937, 0.5074]])tensor([[[0.8139, 0.4975, 0.5486, 0.8712],[0.8165, 0.5168, 0.0253, 0.9529],[0.6147, 0.7410, 0.5361, 0.8880]],[[0.1819, 0.3584, 0.7158, 0.2515],[0.7295, 0.8665, 0.0207, 0.3815],[0.5506, 0.8611, 0.1727, 0.0158]]])tensor([[0.5108, 0.7117, 0.2116],[0.7108, 0.4592, 0.5483]], dtype=torch.float64)

torch.randn()

torch.randn 是 PyTorch 中的一个函数,用于生成符合标准正态分布(均值为0,方差为1)的张量。

以下是一些使用 torch.randn 的例子:

import torch# 生成一个形状为 (2, 3) 的张量
x = torch.randn(2, 3)
print(x)# 生成一个形状为 (2, 3, 4) 的张量
y = torch.randn(2, 3, 4)
print(y)# 生成一个形状为 (2, 3, 4, 5) 的张量
z = torch.randn(2, 3, 4, 5)
print(z)

在这些例子中,我们都使用了 torch.randn 来生成张量。函数的第一个参数是一个元组,表示要生成的张量的形状。

需要注意的是,torch.randn 生成的张量中的元素是随机的,每次运行程序时都可能得到不同的结果。这是因为 torch.randn 使用的是伪随机数生成器,其种子默认为当前时间。

torch.randint()

生成一个从lowhigh的随机数

dic = torch.randint(low=0, high=7,size=(1,))

torch.multinominal()

torch.multinomial 是 PyTorch 中的一个函数,用于按多项式分布从指定的张量中进行采样。它可以用来进行随机抽样,其中每个元素根据其相对概率进行抽取。

这是 torch.multinomial() 的常用形式:

torch.multinomial(input, num_samples, replacement=False, generator=None)
  • input 是一个张量,包含了每个样本的概率分布。它的形状可以是 (N, K),其中 N 是样本数,K 是类别数。
  • num_samples 是需要采样的样本数。
  • replacement 表示是否允许重复抽样。默认情况下,不允许重复抽样。
  • generator 是一个随机数生成器对象。默认情况下,使用全局默认的随机数生成器。

下面是一个示例:

import torch# 创建一个概率分布张量
probs = torch.tensor([[0.2, 0.3, 0.5]])# 从概率分布中抽取3个样本
samples = torch.multinomial(probs, num_samples=3)print(samples)

输出结果可能为:

tensor([[1, 2, 0]])

在上面的例子中,我们创建了一个概率分布张量 probs,其中包含了一个样本的概率分布。然后我们使用 torch.multinomial() 从概率分布中抽取了3个样本。抽取的样本被存储在张量 samples 中。

请注意,输出的样本是按照概率分布进行采样的,并且每个样本对应一个索引值,表示该样本属于的类别。

逻辑运算

torch.argmax()

torch.argmax() 是 PyTorch 中的一个函数,用于返回给定输入张量(tensor)中最大值的索引,常用在需要确定某个维度上最大值的位置时。比如在进行分类任务中,对softmax 的结果进行 取最大值对应的索引

下面是 torch.argmax() 的基本用法:

import torch# 创建一个随机的二维张量
x = torch.rand(3, 4)
print(x)# 获取每一行最大元素的索引
row_indices = torch.argmax(x, dim=1)
print(row_indices)# 获取整个张量最大元素的索引
max_index = torch.argmax(x)
print(max_index)# 指定dim参数为0表示按列查找最大值索引
column_indices = torch.argmax(x, dim=0)
print(column_indices)

输出结果

tensor([[0.9540, 0.4029, 0.9523, 0.6004],[0.4379, 0.5691, 0.2361, 0.7110],[0.7628, 0.6789, 0.5160, 0.3752]])
tensor([0, 3, 0])
tensor(0)
tensor([0, 2, 0, 1])

在这个例子中,我们首先创建了一个形状为 (3, 4) 的随机张量 x。然后使用 torch.argmax() 来找到每行和每列的最大元素索引。

  • 当不指定 dim 参数时,默认在整个张量上寻找最大值索引。
  • 当指定 dim=0dim=1 时,分别按照列或行的方向寻找最大值索引。

此外,torch.argmax() 还可以接受其他参数,例如 keepdim
当设置 keepdim=True 时,输出的张量将保持原张量的维度不变,默认为False;从上面的代码可以看出keepdim=False输出的结果会压缩张量维度,而keepdim=True会保留原先张量的维度结构。
示例

import torch# 创建一个随机的二维张量
x = torch.rand(3, 4)
print(x)# 获取每一行最大元素的索引
row_indices = torch.argmax(x, dim=1,keepdim=True)

输出结果

tensor([[0.8178, 0.7284, 0.3020, 0.4067],[0.5941, 0.0325, 0.0488, 0.4419],[0.2692, 0.0472, 0.5445, 0.5994]])
tensor([[0],[0],[3]])

torch.max()

torch.argmax()torch.max() 是 PyTorch 中的两个函数,用于在张量中寻找最大值的索引和最大值本身。

torch.argmax() 函数返回张量的最大值所在的索引。它的语法如下:

torch.argmax(input, dim=None, keepdim=False, *, out=None, dtype=None)

其中:

  • input:输入张量。
  • dim:指定要沿着哪个维度进行最大值索引的计算。如果不指定 dim,则默认为全局最大值,返回的索引是一个扁平化的一维张量。
  • keepdim:指定是否保持输出张量的尺寸与输入张量尺寸相同。默认为 False,即输出张量会缩小为一维。
  • out(可选):输出张量的可选目标位置。
  • dtype(可选):输出张量的数据类型。

下面是一个例子,展示如何使用 torch.argmax() 函数:

import torchx = torch.tensor([[1, 2, 3],[4, 5, 6],[7, 8, 9]])max_values, indices = torch.max(x, dim=1)
print(max_values)  # 输出张量的最大值
print(indices)  # 最大值所在的索引

输出结果:

tensor([3, 6, 9])
tensor([2, 2, 2])

在这个例子中,我们创建了一个形状为 (3, 3) 的张量 x。通过指定 dim=1,我们在每一行中找到最大值的索引和最大值本身。

相比之下,torch.max() 函数返回张量的最大值而不是索引。它的语法如下:

torch.max(input, dim=None, keepdim=False, *, out=None)

参数与 torch.argmax() 函数相似,不同之处是它只返回最大值本身,而不是索引。

下面是一个使用 torch.max() 函数的例子:

import torchx = torch.tensor([[1, 2, 3],[4, 5, 6],[7, 8, 9]])max_value = torch.max(x)
print(max_value)  # 输出张量的最大值

输出结果:

tensor(9)

在这个例子中,我们得到了张量 x 中的最大值,而不关心其所在的索引。

torch.sum()

torch.tanh()

torch.pow()

功能性操作 torch.nn.functional

F.normalize()

import torch.nn.functional as F
x = F.normalize(x)
y = F.elu(self.conv1(x))

F.elu()

x = F.normalize(x)
y = F.elu(self.conv1(x))

F.relu()

F.softmax()

张量计算

torch.zeros()

torch.from_numpy()

torch.from_numpy() 是 PyTorch 中的一个函数,用于将 NumPy 数组转换为 PyTorch 张量(Tensor)。它创建了一个与给定NumPy数组共享数据的张量,而不需要复制数据。

下面是使用 torch.from_numpy() 的示例:

import numpy as np
import torch# 创建一个 NumPy 数组
arr = np.array([1, 2, 3, 4, 5])# 使用 torch.from_numpy() 将 NumPy 数组转换为张量
tensor = torch.from_numpy(arr)print(tensor)

输出结果为:

tensor([1, 2, 3, 4, 5], dtype=torch.int32)

torch.from_numpy() 返回的张量将与原始 NumPy 数组共享数据内存,因此对其中一个的修改会影响另一个。这样可以在 NumPy 数组和 PyTorch 张量之间进行数据交互,无需数据复制就能提高效率。

需要注意的是,torch.from_numpy() 只能用于将数据类型为 np.ndarray 的 NumPy 数组转换为张量。如果想将其他类型的数组或类似数组的对象转换为张量,可以使用 torch.Tensor() 来创建张量,并将其作为参数传递给该函数。

torch.stack() 张量堆叠

torch.stack() 是 PyTorch 中的一个函数,用于将一组张量沿着一个新的维度进行堆叠。

语法如下:

torch.stack(tensors, dim=0, *, out=None)

参数说明:

  • tensors:要堆叠的一组张量,可以是相同形状的张量或者具有相同形状的可迭代对象。
  • dim:指定要沿着的新维度的索引。默认值是0,表示在新的零号维度上堆叠张量。
  • out(可选):输出张量的可选目标位置,如果不为 None,则结果将被赋值给它。

堆叠操作会创建一个新的张量,将输入张量按照指定的维度进行堆叠。结果张量的形状将是输入张量形状的扩展。如果输入张量的形状是 (n1, n2, ..., ni),则结果张量的形状将是 (n1, n2, ..., ni, num_tensors),其中 num_tensors 是堆叠的张量数量。

以下是一个例子,展示如何使用 torch.stack() 函数:

import torchx = torch.tensor([1, 2, 3])
y = torch.tensor([4, 5, 6])
z = torch.tensor([7, 8, 9])stacked = torch.stack([x, y, z])
print(stacked)

输出结果:

tensor([[1, 2, 3],[4, 5, 6],[7, 8, 9]])

在这个例子中,我们创建了三个形状相同的张量 xyz,然后使用 torch.stack() 函数将它们在零号维度上堆叠,创建了一个形状为(3, 3)的新张量 stacked

torch.stack() 函数主要用于将已经是张量的数据进行堆叠操作。它要求输入的数据是张量或具有相同形状的可迭代对象,然后沿着指定的维度进行堆叠。

如果你有一组已经是张量的数据,并且希望将它们沿着某个维度进行堆叠,那么 torch.stack() 是很方便的函数。

例如,在上面提供的例子中,我们创建了三个张量 xyz,然后使用 torch.stack() 将它们在零号维度上堆叠,生成了新的形状为 (3, 3) 的张量。

但是如果你有非张量的数据,想要将它们转换为张量并进行堆叠,可以使用其他函数或方法,比如 torch.Tensor()

torch.Tensor()

torch.Tensor() 是 PyTorch 中用于创建张量(Tensor)的构造函数。它可以接受各种不同的参数来创建不同类型和形状的张量。

下面是使用 torch.Tensor() 创建张量的示例:

import torch# 创建一个空的张量
empty_tensor = torch.Tensor()
print(empty_tensor)# 创建一个指定形状的张量
ones_tensor = torch.Tensor(2, 3)
print(ones_tensor)# 创建一个张量并用指定值填充
data_tensor = torch.Tensor([1, 2, 3, 4, 5])
print(data_tensor)

输出结果为:

tensor([])
tensor([[1., 1., 1.],[1., 1., 1.]])
tensor([1., 2., 3., 4., 5.])

在上述示例中:

  • 第一个示例创建了一个空的张量 empty_tensor
  • 第二个示例创建了一个形状为 2x3 的张量 ones_tensor,并将其元素初始化为默认值 1.0。
  • 第三个示例通过传递一个 Python 列表作为参数,来创建一个张量 data_tensor,其中包含列表中的元素。

torch.Tensor() 构造函数还可以接受其他参数,如 dtype 来指定张量的数据类型,以及 device 来指定在哪个设备上存储张量(如 CPU 或 GPU)。

需要注意的是,与 torch.from_numpy() 不同,torch.Tensor() 不会与其他数据类型(如 NumPy 数组)共享内存。它会创建一个新的张量,并根据提供的数据或形状进行初始化。

squeeze()

squeeze()是一个用于操作张量的函数。它的作用是去除张量中维度大小为1的维度,从而降低维度的数量。具体来说,squeeze()函数会将张量中维度大小为1的维度压缩掉,使得张量变得更紧凑。

例如,如果一个张量的形状为(1, 3, 1, 5),其中有两个维度的大小为1,那么使用squeeze()函数后,张量的形状将变为(3, 5),即去掉了维度大小为1的维度。

需要注意的是,squeeze()函数只会压缩维度大小为1的维度,对于其他维度,它不会产生任何影响。如果想要压缩指定维度,可以使用squeeze(dim)函数,其中dim是要压缩的维度的索引。

tensor.repeat()

repeat 是一个张量的方法,用于沿指定的维度重复张量的元素。它的参数是一个元组,包含了每个维度上重复的次数。

state_.repeat((N, 1, 1)) 表示沿着维度0重复 state_ 的元素 N 次。维度0通常用于表示样本或批次的维度。而后面两个维度1和1保持不变,即不进行重复。

具体来说,假设 state_ 的形状为 (A, B, C),那么重复后的结果的形状为 (N*A, B, C),其中 N 是重复的次数。

下面是一个简单的示例:

import torch# 创建一个形状为 (2, 3, 4) 的张量
x = torch.tensor([[[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12]],[[13, 14, 15, 16],[17, 18, 19, 20],[21, 22, 23, 24]]])# 沿维度0重复2次
y = x.repeat((2, 1, 1))print(y)

输出结果为:

tensor([[[ 1,  2,  3,  4],[ 5,  6,  7,  8],[ 9, 10, 11, 12]],[[13, 14, 15, 16],[17, 18, 19, 20],[21, 22, 23, 24]],[[ 1,  2,  3,  4],[ 5,  6,  7,  8],[ 9, 10, 11, 12]],[[13, 14, 15, 16],[17, 18, 19, 20],[21, 22, 23, 24]]])

在上面的例子中,我们创建了一个形状为 (2, 3, 4) 的张量 x,然后使用 x.repeat((2, 1, 1)) 对其进行重复。结果是一个形状为 (4, 3, 4) 的新张量 y,其中 x 的内容在维度0上重复了2次。

.unsqueece()

在 PyTorch 中,.unsqueeze(dim=1) 是一个方法,它可以在指定的维度上对张量进行扩展,使其维度增加 1。具体而言,.unsqueeze(dim=1) 可以在给定维度上插入一个大小为 1 的维度。

以下是一个示例,展示了如何使用 .unsqueeze(dim=1) 方法:

import torchx = torch.tensor([1, 2, 3])unsqueeze_x = x.unsqueeze(dim=1)
print(unsqueeze_x)

输出结果:

tensor([[1],[2],[3]])

在这个例子中,我们创建了一个形状为 (3,) 的张量 x。通过使用 .unsqueeze(dim=1),我们在维度 1 上插入了一个新的维度,使得张量 unsqueeze_x 的形状变为 (3, 1)

这样做有时会对某些操作或模型有帮助,特别是在需要扩展维度以进行广播操作或与其他形状不匹配的张量进行计算时。

以下代码为构建批量做准备

return torch.from_numpy(downscale_obs(state, to_gray=True)).float().unsqueeze(dim=0)

torch.cat()

torch.cat() 是 PyTorch 中用于连接(拼接)张量的函数。它可以将多个张量沿指定的维度进行拼接。

torch.cat() 的语法如下:

torch.cat(tensors, dim=0, *, out=None)

其中:

  • tensors:是一个需要拼接的张量序列,可以是一个列表或元组。
  • dim:指定拼接的维度,即在哪个维度上进行拼接。默认为 0,表示在第一个维度上拼接。
  • out(可选):输出张量的可选目标位置。

下面是一个示例,展示了如何使用 torch.cat() 拼接张量:

import torchx1 = torch.tensor([[1, 2],[3, 4]])x2 = torch.tensor([[5, 6],[7, 8]])# 沿着行维度拼接张量
merged_tensor = torch.cat((x1, x2), dim=0)
print(merged_tensor)

输出结果:

tensor([[1, 2],[3, 4],[5, 6],[7, 8]])

在这个例子中,我们有两个形状为 (2, 2) 的张量 x1x2。通过使用 torch.cat((x1, x2), dim=0),我们沿着行维度(维度 0)拼接了这两个张量,得到了形状为 (4, 2) 的合并张量。

需要注意的是,所有要拼接的张量在除了指定维度外的其他维度上的大小必须相同。

torch.cat() 还可以在其他维度上进行拼接,只需根据需要更改 dim 参数即可。

.view()

在 PyTorch 中,.view() 是一个方法,用于调整张量的形状。.view() 可以通过改变张量的维度来重新组织数据。

具体而言,.view() 方法可以用来改变张量的形状,但需要注意的是,调整后的形状的元素数量必须与原始形状的元素数量保持一致。

下面是一个示例,展示如何使用 .view() 方法来重新组织张量的形状:

import torchx = torch.tensor([[1, 2],[3, 4],[5, 6]])reshaped_x = x.view(2, 3)  # 将 x 调整为 2 行 3 列的形状
print(reshaped_x)

输出结果:

tensor([[1, 2, 3],[4, 5, 6]])

在这个例子中,我们创建了一个形状为 (3, 2) 的张量 x。通过使用 .view(2, 3),我们将 x 的形状调整为 2 行 3 列的形状。

需要注意的是,调整后的形状必须满足元素数量匹配的要求。在这个例子中,原始张量 x 包含了 6 个元素,调整后的形状 (2, 3) 也必须保持 6 个元素。

总之,.view() 方法可以用于调整张量的形状,以便与各种计算和模型的输入或输出对齐。

action.flatten()

模型构建

torch.nn

nn.ModuleList()

nn.ModuleList 是 PyTorch 中的一个类,它是 nn.Module 的子类,用于创建一个动态的模块列表。这意味着你可以像操作普通 Python 列表那样添加或删除模块,而且 PyTorch 会自动将这些模块注册到整个网络中,并更新网络的参数。

以下是一个简单的例子,展示了如何使用 nn.ModuleList

import torch
import torch.nn as nn# 创建一个 ModuleList 对象
my_list = nn.ModuleList([nn.Linear(10, 5), nn.ReLU(), nn.Linear(5, 1)])# 打印 ModuleList 对象
print(my_list)

在这个例子中,我们首先创建了一个 nn.ModuleList 对象,并向其中添加了两个 nn.Linear 层和一个 nn.ReLU 激活函数。然后,我们打印了这个 ModuleList 对象,可以看到它包含了这三个模块。

nn.ModuleList 的一个常见用途是在构建更复杂的神经网络结构时,可以将一组相关的层组合在一起作为一个模块,这样可以使网络的结构更加清晰和易于管理。
比如Transformers中的Encoder部分

class Encoder(nn.Module):def __init__(self, src_vocab_size,embed_size,num_layers,heads,device,forward_expansion,dropout,max_length):super(Encoder, self).__init__()self.embed_size = embed_sizeself.device = deviceself.word_embedding = nn.Embedding(src_vocab_size, embed_size) self.position_embedding = nn.Embedding(max_length, embed_size)def forward(self, x, mask):N, seq_lengh = x.shapepositions = torch.arange(0, seq_lengh).expand(N, seq_lengh).to(self.device)out = self.dropout(self.word_embedding(x) + self.position_embedding(positions))for layer in self.layers:out = layer(out, out, out, mask)return out

注意看forward()方法中的for layer in self.layers:。因为nn.ModuleList() 并没有提供类似 nn.Sequential 那样的整体前向传播接口,所以我们必须显式地遍历 ModuleList 并调用每个模块。

nn.Sequential() 构建序贯模型

import torch
from torch import nnmodel = nn.Sequential(torch.nn.Linear(l1,l2),torch.nn.ReLU(),torch.nn.Linear(l2,l3),torch.nn.ReLU(),torch.nn.Linear(l3,l4))

nn.ModuleList() 与 nn.Sequential()的区别

nn.ModuleListnn.Sequential 都是 PyTorch 中用于构建神经网络结构的容器模块,但它们之间存在一些关键的区别:

  1. 顺序访问 vs 键值访问:nn.Sequential 是一个有序的容器,只能按照添加的顺序访问模块。而 nn.ModuleList 则更像一个普通的 Python 列表,支持基于索引或键名的访问方式。

  2. 可变性:nn.Sequential 是一个不可变容器,一旦添加了模块,就不能更改顺序。而 nn.ModuleList 则是可变的,可以在任意位置插入或删除模块。

  3. 注册参数:两者都会自动注册其包含的模块及其参数,这意味着你可以直接通过模型访问这些参数,例如使用 model.parameters() 方法。

  4. 前向传播:nn.Sequential 会自动构建一条从输入到输出的前向传播路径,你只需要将输入传递给 Sequential 对象即可。而 nn.ModuleList 则需要你自己定义前向传播逻辑,将输入依次传递给每个模块。

  5. 使用场景:nn.Sequential 适合于那些模块间没有复杂依赖关系的简单网络结构,而 nn.ModuleList 则适用于那些需要灵活控制模块顺序和访问方式的复杂网络结构。

总的来说,选择使用哪一个取决于你的具体需求。如果你需要一个有序且不可变的容器,并且不需要手动定义前向传播逻辑,那么 nn.Sequential 可能是更好的选择。反之,如果你需要更灵活的容器来组织你的网络结构,那么 nn.ModuleList 将更适合你。

代码体现两者的区别,展示了 nn.ModuleListnn.Sequential 的区别:

import torch
import torch.nn as nn# 使用 nn.Sequential
seq_model = nn.Sequential(nn.Linear(10, 5),nn.ReLU(),nn.Linear(5, 1)
)# 使用 nn.ModuleList
mod_list_model = nn.ModuleList([nn.Linear(10, 5),nn.ReLU(),nn.Linear(5, 1)
])# 打印两种模型的前向传播结果
input = torch.randn(1, 10)
print("Sequential Model Output:", seq_model(input))
print("ModuleList Model Output:", mod_list_model[0](input))  # 注意这里只调用了第一个模块

在这个例子中,我们首先创建了一个 nn.Sequential 模型和一个 nn.ModuleList 模型,它们都包含了相同的三个模块:一个线性层,一个 ReLU 激活函数,以及另一个线性层。

然后,我们打印了两个模型的前向传播结果。对于 nn.Sequential 模型,我们只需要将输入传递给模型,模型就会自动按照添加的顺序执行每个模块的前向传播。而对于 nn.ModuleList 模型,我们需要自己遍历模型,将输入逐个传递给每个模块。

需要注意的是,nn.ModuleList 并没有提供类似 nn.Sequential 那样的整体前向传播接口,所以我们必须显式地遍历 ModuleList 并调用每个模块。

当最后一行代码改为print("ModuleList Model Output:", mod_list_model(input) 会引发错误NotImplementedError: Module [ModuleList] is missing the required "forward" function 说明其前向传播需要我们遍历实现

自定义模型

好奇心机制的编码器模型

class Phi(nn.Module):def __init__(self):super(Phi, self).__init__()self.conv1 = nn.Conv2d(3, 32, kernel_size=(3,3), stride=2, padding=1)self.conv2 = nn.Conv2d(32,32, kernel_size=(3,3), stride=2, padding=1)self.conv3 = nn.Conv2d(32,32, kernel_size=(3,3), stride=2, padding=1)self.conv4 = nn.Conv2d(32,32, kernel_size=(3,3), stride=2, padding=1)def forward(self,x):x = F.normalize(x)y = F.elu(self.conv1(x))y = F.elu(self.conv2(y))y = F.elu(self.conv3(y))y = F.elu(self.conv4(y))y = y.flatten(start_dim=1)return y

好奇心机制的正向模型

class Fnet(nn.Module):def __init__(self):super(Fner, self).__init__()self.linear1 = nn.Linear(300,256)self.linear2 = nn.Linear(256,288)def forward(self, state, action):action_ = torch.zeros(action.shape[0],12)indices = torch.stack((torch.arange(action.shape[0], action.squeeze()), dim=0)indices = indices.tolist()action_[indices] = 1x = torch.cat((state,action_), dim=1)y = F.relu(self.linear1(x))y = self.linear2((y)return y

好奇心机制的DQN模型

class Qnetwork(nn.Module):def __init__(self):super(Qnetwork, self).__init__()self.conv1 = nn.Conv2d(in_channels=3, out_channels=32, kernel_size=(3, 3), stride=2, padding=1)self.conv2 = nn.Conv2d(32, 32, kernel_size=(3, 3), stride=2, padding=1)self.conv3 = nn.Conv2d(32, 32, kernel_size=(3, 3), stride=2, padding=1)self.conv4 = nn.Conv2d(32, 32, kernel_size=(3, 3), stride=2, padding=1)self.linear1 = nn.Linear(288, 100)self.linear2 = nn.Linear(100, 12)def forward(self, x):x = F.normalize(x)y = F.elu(self.conv1(x))y = F.elu(self.conv2(y))y = F.elu(self.conv3(y))y = F.elu(self.conv4(y))y = y.flatten(start_dim=2)y = y.view(y.shape[0], -1, 32)y = y.flatten(start_dim=1)y = F.elu(self.linear1(y))y = self.linear2(y)return y

总结就是,自定义模块总要部分在于__init__()方法与forward()方法

class Class_Name(nn.Module):def __init__(self, params):super(Class_Name, self).__init__()self.params = paramsdef forward(self,x):return y

super(son_class_name, self).init()

```python
class Phi(nn.Module):def __init__(self):super(Phi, self).__init__()self.conv1 = nn.Conv2d(3, 32, kernel_size=(3,3), stride=2, padding=1)self.conv2 = nn.Conv2d(32,32, kernel_size=(3,3), stride=2, padding=1)self.conv3 = nn.Conv2d(32,32, kernel_size=(3,3), stride=2, padding=1)self.conv4 = nn.Conv2d(32,32, kernel_size=(3,3), stride=2, padding=1)def forward(self,x):x = F.normalize(x)y = F.elu(self.conv1(x))y = F.elu(self.conv2(y))y = F.elu(self.conv3(y))y = F.elu(self.conv4(y))y = y.flatten(start_dim=1)return y

super(Phi, self).__init__() 这行代码是在 Python 中调用父类(在这里是 nn.Module)的构造函数。这是在子类中初始化父类的标准做法。

在 Python 中,当我们创建一个新的类时,我们可以继承一个或多个现有的类。在这种情况下,新的类被称为子类,而被继承的类被称为父类。

在 PyTorch 中,nn.Module 是所有神经网络模块的基类。当你创建一个新的神经网络模型时,通常会继承 nn.Module。在你的模型的构造函数中,你需要调用 super().__init__() 来确保 nn.Module 的构造函数被正确地调用。这是因为 nn.Module 的构造函数负责一些必要的初始化工作,比如设置一些属性等。

所以,super(Phi, self).__init__() 这行代码的作用就是确保 nn.Module 的构造函数被正确地调用,从而让你的 Phi 类能够正常工作。

层结构

nn.Conv2d()

from torch import nnself.conv1 = nn.Conv2d(3, 32, kernel_size=(3,3), stride=2, padding=1)

nn.Linear()

from torch import nnself.linear1 = nn.Linear(576,256)

nn.Embeding()

nn.Embedding() 是 PyTorch 中的一个模块,用于创建一个嵌入层,这是一种常见的神经网络结构,常用于处理离散的类别变量。

嵌入层的主要思想是将每个类别映射到一个固定大小的向量,这个过程也被称为“嵌入”。这种处理方式在许多任务中都非常有用,例如自然语言处理中的词嵌入,推荐系统中的物品嵌入等。

import torch
import torch.nn as nn# 假设我们有10个类别,每个类别将被嵌入到一个大小为3的向量中
embedding = nn.Embedding(num_embeddings=10, embedding_dim=3)# 创建一个包含三个类别的张量
input = torch.tensor([1, 2, 3])# 将输入张量送入嵌入层
output = embedding(input)print(output)

输出结果

tensor([[ 0.7962, -1.8463,  0.2988],[-1.0745,  1.5030, -0.2322],[-1.4668, -0.2942,  2.2095]], grad_fn=<EmbeddingBackward0>)

首先,实例 nn.Embedding() ,其中 num_embeddings 参数指定了类别的数量,embedding_dim 参数指定了嵌入向量的大小。
然后,我们创建了一个包含三个类别的张量,并将其送入嵌入层。
最后, 打印出了嵌入层的输出,这是一个形状为 (3, 3) 的张量,其中的每一行都是一个类别的嵌入向量。

nn.Embedding() 是 PyTorch 深度学习框架中的一个嵌入层模块,它的主要功能是将离散的类别变量转换为连续的向量表示。这一过程在自然语言处理和其他领域中非常重要,因为它允许模型捕捉到类别之间的语义关系。

嵌入层的工作原理

  1. nn.Embedding() 本身并不包含任何预训练的知识或模型,其权重是随机初始化的。在训练过程中,通过与其它神经网络层结合使用,并通过学习数据集中的模式来逐渐调整其内部的权重。在训练过程中,嵌入层会学习这些向量,使得相似的词在向量空间中更加接近。例如,“king"和"queen”、"man"和"woman"等词的向量会在向量空间中彼此靠近

  2. 嵌入层的工作原理类似于一个字典或查找表,它内部维护着一个固定大小的矩阵,矩阵的每一行对应一个类别的嵌入向量。当我们将一个类别索引传递给嵌入层时,它会返回对应的嵌入向量。

  3. 假设我们有一个词汇表大小为1000的词嵌入层,嵌入维度为5。那么,嵌入层内部会存储着1000个长度为5的向量。当我们输入一个索引为50的词时,嵌入层会返回第50个长度为5的向量。当输入的类别数量超出num_embedings参数设定的值的时候,会报错IndexError: index out of range in self

  4. 在实际应用中,嵌入层通常与其它神经网络层结合使用,例如全连接层或循环神经网络层,以实现更复杂的任务,如文本分类、情感分析等。

indices = torch.stack((torch.arange(action.shape[0]), action.sequeeze()), dim=0)

代码 indices = torch.stack((torch.arange(action.shape[0]), action.squeeze()), dim=0) 的作用是创建一个张量 indices,该张量的形状是 (2, N),其中 Naction 张量的第一个维度的大小。

具体解释如下:

  • torch.arange(action.shape[0]) 生成一个从 0 到 action.shape[0]-1 的整数张量,形状为 (action.shape[0],)。这表示生成一个从 0 到 action.shape[0]-1 的数列。
  • action.squeeze()action 张量中的尺寸为 1 的维度进行压缩,即去除尺寸为 1 的维度。如果 action 张量的尺寸为 (1, M),则压缩后的形状为 (M,)
  • torch.stack((torch.arange(action.shape[0]), action.squeeze()), dim=0)torch.arange(action.shape[0])action.squeeze() 张量按照维度 0 进行堆叠,生成一个形状为 (2, N) 的张量 indices。其中,第一行是 torch.arange(action.shape[0]),第二行是 action.squeeze()

请注意,在使用上述代码之前,确保 action 张量已经定义和赋值。另外,如果 action 张量的维度或尺寸不符合要求,可能会导致代码执行错误。

设置梯度

optimizer = optim.Adam()

optimizer = torch.optim.Adam(params=model.parameters(), lr=learning_rate) 用于创建一个 Adam 优化器,用于优化模型的参数。

Adam 是一种常用的优化算法,用于调整模型的参数以最小化损失函数。

  • params 指定需要进行训练的权重,model.parameters() 返回模型中所有需要训练的参数。这些参数将被传递给 Adam 优化器,用于更新它们的值。

  • lr=learning_rate 参数指定了学习率(learning rate),它控制着每次参数更新的大小。学习率越大,则参数更新越大;学习率越小,则参数更新越小。可以根据具体问题和经验来选择合适的学习率。

以下是一个示例,展示如何使用 Adam 优化器进行参数更新:

import torch
import torch.optim as optim# 定义模型
model = MyModel()# 定义优化器
learning_rate = 0.001
optimizer = optim.Adam(model.parameters(), lr=learning_rate)# 迭代训练
for epoch in range(num_epochs):# 前向传播outputs = model(inputs)loss = loss_function(outputs, labels)# 梯度清零optimizer.zero_grad()# 反向传播loss.backward()# 参数更新optimizer.step()

在上述代码中,我们首先定义了一个模型 MyModel(),然后使用 optim.Adam() 创建了一个 Adam 优化器。在训练过程中,我们迭代多个 epoch,在每个 epoch 中进行前向传播、计算损失、反向传播等步骤。最后,调用 optimizer.step() 来实际更新模型的参数。

重要的几个部分

  • optimizer = optim.Adam(model.parameters(), lr=learning_rate)创建优化器
  • loss = loss_function(outputs, labels) 计算损失
  • optimizer.zero_grad() 梯度清零
  • loss.backward() 误差反向传播
  • optimizer.step() 优化器更新参数

Adam 优化器根据计算得到的梯度和学习率来更新模型的参数。通过反复迭代和更新,我们可以让模型逐渐优化,并找到损失函数最小化的参数。

with torch.no_grad():

with torch.no_grad() 是在 PyTorch 中的一个上下文管理器(context manager),用于禁用梯度计算。在这个上下文中,所有的操作都不会被记录梯度,从而减少了内存消耗和加快了计算速度。

通常情况下,PyTorch 中的张量会自动跟踪并记录梯度信息,以便进行自动求导和反向传播。但是,在某些情况下,我们可能只想进行前向计算或推理,并且不需要计算梯度,这时就可以使用 torch.no_grad() 来暂时禁用梯度计算。

下面是使用 with torch.no_grad() 的示例:

import torch# 创建张量并设置 requires_grad=True 来跟踪梯度
x = torch.tensor([1.0, 2.0, 3.0], requires_grad=True)# 在上下文中进行前向计算,但不记录梯度
with torch.no_grad():y = x * 2z = y + 1print(y)  # 不会进行梯度计算
print(z)  # 不会进行梯度计算

输出结果为:

tensor([2., 4., 6.])
tensor([3., 5., 7.])

在这个示例中,x 是一个张量,并通过设置 requires_grad=True 来告知 PyTorch 跟踪梯度。然后,在 with torch.no_grad() 的上下文中,我们进行了一系列的计算,但这些计算不会被记录梯度。

torch.no_grad() 可以与训练循环或评估模型时的推理过程结合使用。在推理过程中,我们不需要计算梯度,因为只关心模型的输出而不是梯度值。

y_tensor = x_tensor.detach()

在 PyTorch 中,detach() 是一个用于切断反向传播关系的方法,用于从计算图中分离出张量,并返回一个新的不具有梯度信息的张量。具体来说,detach() 会返回一个与原始张量共享数据存储的新张量,但新张量不参与梯度计算,也不会被反向传播。

detach() 的主要作用包括:

  • 切断梯度反向传播关系,使得从一个张量开始的计算不会影响到原始张量的梯度计算。
  • 生成一个不需要梯度的张量,用于一些只需计算前向传播结果的任务。

以下是 detach() 方法的使用示例:

import torchx = torch.randn(3, requires_grad=True)
y = x.detach()  # 分离 x,得到一个不需要梯度的张量 y# 进行前向传播计算
z = y * 2
loss = z.sum()# 反向传播
loss.backward()print(x.grad)  # 输出为 None,由于 y 分离自 x,连接断开,因此 x 的梯度为 None

在上述示例中,我们首先创建一个张量 x,并将 requires_grad 设置为 True,表示我们希望计算关于 x 的梯度。然后,我们使用 detach() 方法,将 x 分离为 y,得到一个新的不需要梯度的张量。接下来,我们进行一系列前向传播计算,并计算出一个损失 loss。最后,通过调用 loss.backward() 来进行反向传播,但由于 y 已经分离自 x,因此 x 的梯度为 None。

通过使用 detach() 方法,我们可以在某些情况下切断计算图,从而在不需要梯度的情况下进行前向传播或忽略部分张量的反向传播。

detach()与with torch.no_grad(): 的区别

with torch.no_grad()detach() 都是在 PyTorch 中用于控制梯度计算和反向传播的机制,但它们有一些区别。
detach()with torch.no_grad(): 都是在PyTorch中进行梯度计算的上下文管理,但它们之间有一些关键的区别。

  1. 作用域:with torch.no_grad(): 是一个上下文管理器,它会在进入代码块时禁用梯度计算,并在退出代码块时重新启用。这意味着你在 with torch.no_grad(): 代码块内进行的任何操作都不会影响梯度,常用在模型训练完成后的推理阶段。而 detach() 方法则会立即返回一个新的Tensor,这个新的Tensor不会参与到当前的梯度计算中。 这对于只需要前向传播而无需计算梯度的情况非常有用。示例如下:

    import torchx = torch.randn(3, requires_grad=True)with torch.no_grad():y = x * 2z = y.mean()print(y.requires_grad)  # False,y 不需要梯度
    print(z.requires_grad)  # False,z 不需要梯度
    
  2. 返回值:detach() 方法会返回一个新的Tensor,这个新的Tensor和原来的Tensor共享相同的内存空间,但不会被计算图所追踪。而 with torch.no_grad(): 并不会返回任何值,它只是改变了当前代码块的梯度计算状态。

    import torchx = torch.randn(3, requires_grad=True)y = x.detach()print(y.requires_grad)  # False,y 不需要梯度
    
  3. 灵活性:detach() 方法可以让你更灵活地控制哪些Tensor需要参与梯度计算,哪些不需要。而 with torch.no_grad(): 则是一次性地为整个代码块禁用梯度计算,不提供这种细粒度的控制。

总的来说,detach()with torch.no_grad(): 都可以在不进行梯度计算的情况下处理Tensor,但它们的适用场景和使用方式有所不同。在选择使用哪一个时,需要根据具体的应用场景和需求来决定。

optimzer.zero_grad()、optimzier.step()

optimizer.zero_grad() 的目的是将之前迭代中的梯度清零,而不是完全清除梯度。它将参数的梯度设置为零,以便在接下来的反向传播过程中,新的梯度可以正确地累积。这是为了避免梯度的累积干扰或错误更新模型参数。

反向传播是依赖梯度的。清零梯度只是为了确保每次迭代时,梯度是新计算的,并且不受之前迭代的影响。

具体流程如下:

调用 optimizer.zero_grad() 清零梯度。
通过前向传播计算出模型的输出。
根据模型输出和目标数据计算出损失值。
调用 loss.backward() 执行反向传播,计算模型参数的梯度。
调用 optimizer.step() 根据梯度更新模型的参数

optimizer.zero_grad()
loss = loss_fn(X,Y)
# loss_list.append(loss.item()) # 保存输出loss
loss.backward()
optimzier.step()

损失计算

nn.L1lose

https://blog.51cto.com/u_15274944/4999058

nn.MSELoss()

forward_loss = nn.MSELoss(reduction='none')
qloss = nn.MSELoss()

nn.CrossEntropyLoss()

inverse_loss = nn.CrossEntropyLoss(reduction='none')

loss_ = loss_.sum() / loss_.flatten().shape[0]

代码 loss_ = loss_.sum() / loss_.flatten().shape[0] 的作用是将张量 loss_ 进行求和并计算平均值,从而得到一个标量值作为最终的损失。

具体解释如下:

  • loss_.sum() 对张量 loss_ 进行求和操作,将所有元素相加得到一个标量值。
  • loss_.flatten() 将张量 loss_ 展平为一维张量。
  • loss_.flatten().shape[0] 获取展平后张量的第一个维度的大小,在这里即为张量 loss_ 中元素的总个数。
  • loss_.sum() / loss_.flatten().shape[0] 计算求和后的值除以元素总个数,从而得到平均值。

通过执行上述代码,将张量 loss_ 进行求和并计算平均值,将结果存储在 loss_ 中。现在,loss_ 是一个标量值,表示了损失的平均值。

请注意,在使用上述代码之前,确保 loss_ 张量已经定义和赋值。另外,如果 loss_ 张量的维度或尺寸不符合要求,可能会导致代码执行错误。

模型保存与加载

torch.save()

在PyTorch中,模型的保存和加载可以通过两种方式实现:

  1. 保存整个模型:使用torch.save(model, "models/dongtai.pt"),这种方式会将模型的整个结构以及其中的参数一同保存下来。当需要加载这个模型时,可以直接使用model = torch.load("models/dongtai.pt"),这样就可以得到一个完全相同的模型副本,包括它的结构和参数。
# 模型保存
torch.save(model,"models/model_weights.pt")
# 模型加载,带模型结构
mode = torch.load("models/model_weights.pt")
  1. 保存模型的参数状态字典:
    使用torch.save(model.state_dict(), "models/dongtai_state_dict.pt"),这种方式只会保存模型的参数状态,而不包括模型的结构。
    当需要加载这个模型时,需要先生成一个新的模型实例,然后使用model.load_state_dict(torch.load("models/dongtai_state_dict.pt"))来加载参数。
# 模型保存
torch.save(model.state_dict(),"models/model_weights.pt")
# 实例模型
model = Model_class()
# 加载模型权重
weights = torch.load("models/model_weights.pt")
model.load_state_dict(weights)

设备

cuda

参考

  1. cuda是否可用
    import torch
    print(torch.cuda.is_available()) # 输出为True 或者 False
    
  2. GPU 设备的数量
    print("available gpu devices: {}".format(torch.cuda.device_count()))
    
  3. GPU设备的名称
    print("gpu device name: {}".format(torch.cuda.get_device_name(torch.device("cuda:0"))))
    

一些补充

查看输入输出维度

对于保存了整个模型的

Qmodel = torch.load('ICM_Qmodel.pt',map_location=torch.device('cpu'))
for key, value in Qmodel.state_dict().items():print(key,value.size(),sep="  ")

对于只保存了模型权重

state_dict = torch.load('ICM_Qmodel.pt')
for key, value in state_dict.items():print(key,value.size(),sep="  ")

两者的输出结果相同,只是模型的保存方式不同,对应的查看方式有略微差别
在这里插入图片描述

动态图与静态图

静态图和动态图的概念,它们是深度学习框架中执行计算图的不同方式:

  • 静态图:静态图是指在运行前就构建好整个计算图,然后在运行时仅执行计算图。静态图通常更高效,因为它允许编译器和硬件优化图执行。然而,静态图不太灵活,因为一旦图构建完成,就很难进行修改。TensorFlow早期版本主要使用静态图。

  • 动态图:动态图是指每次运行时都会构建计算图,并且在运行时执行。动态图提供了更大的灵活性,因为它允许在运行时更改模型结构。动态图通常更适合研究和原型设计,因为它更容易调试和理解。PyTorch主要使用动态图。

random.seed(42)

random.seed()函数使用给定值初始化随机数生成器。

种子被赋予一个整数值,以确保伪随机生成的结果是可重现的。通过重新使用种子值,只要不是多线程,相同的序列应该可以在运行之间重现。可重复性是一个非常重要的概念,它确保任何重新运行代码的人都能获得完全相同的输出。

random.seed(42) 的意义是什么?
其实是一种流行文化,是一种计算机领域的默认传统,在道格拉斯·亚当斯 1979 年广受欢迎的科幻小说《银河系漫游指南》中,在书的最后,超级计算机Deep Thought揭示了“生命、宇宙和一切”这个重大问题的答案是 42
https://zhuanlan.zhihu.com/p/458809368

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

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

相关文章

python Matplotlib Tkinter-->tab切换1

环境 python:python-3.12.0-amd64 包: matplotlib 3.8.2 pillow 10.1.0 import matplotlib.pyplot as plt from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk import tkinter as tk import tkinter.messagebox as messagebox import …

实验室储样瓶耐强酸强碱PFA材质试剂瓶适用新材料半导体

PFA&#xff0c;全名可溶性聚四氟乙烯&#xff0c;试剂瓶又叫取样瓶、样品瓶、广口瓶、储样瓶等。主要用于痕量分析、同位素分析等实验室&#xff0c;广泛应用于新兴的半导体、新材料、多晶硅、硅材、微电子等行业。 规格参考&#xff1a;30ml、60ml、100ml、125ml、250ml、30…

MATLAB环境下一种改进的瞬时频率(IF)估计方法

相对于频率成分单一、周期性强的平稳信号来说&#xff0c;具有非平稳、非周期、非可积特性的非平稳信号更普遍地存在于自然界中。调频信号作为非平稳信号的一种&#xff0c;由于其频率时变、距离分辨率高、截获率低等特性&#xff0c;被广泛应用于雷达、地震勘测等领域。调频信…

三、系统知识笔记-计算机系统基础知识

一、计算机系统概述 计算机系统是指用于数据管理的计算机硬件、软件及网络组成的系统。 它是按人的要求接收和存储信息&#xff0c;自动进行数据处理和计算&#xff0c;并输出结果信息的机器系统。 冯诺依曼体系计算机结构&#xff1a; 1.1计算机硬件组成 冯诺依曼计算机结…

pclpy 最小二乘法拟合平面

pclpy 最小二乘法拟合平面 一、算法原理二、代码三、结果1.左边原点云、右边最小二乘法拟合平面后点云投影 四、相关数据 一、算法原理 平面方程的一般表达式为&#xff1a; A x B y C z D 0 ( C ≠ 0 ) Ax By Cz D 0 \quad (C\neq0) AxByCzD0(C0) 即&#xff1a; …

Elasticsearch:了解人工智能搜索算法

作者&#xff1a;来自 Elastic Jessica Taylor, Aditya Tripathi 人工智能工具无处不在&#xff0c;其原因并不神秘。 他们可以执行各种各样的任务并找到许多日常问题的解决方案。 但这些应用程序的好坏取决于它们的人工智能搜索算法。 简单来说&#xff0c;人工智能搜索算法是…

QT信号槽实现分析

1.宏定义 qt中引入了MOC来反射&#xff0c;编译阶段变成 MOC–>预处理–>编译–>汇编–>链接 1-1、Q_OBJECT 这个宏定义了一系列代码&#xff0c;包括元对象和处理的函数 #define Q_OBJECT \public: \QT_WARNING_PUSH \Q_OBJECT_NO_OVERRIDE_WARNING \static c…

位运算03 不用加号的加法[C++]

图源&#xff1a;文心一言 上机题目练习整理&#xff0c;位运算&#xff0c;供小伙伴们参考~&#x1f95d;&#x1f95d; 网页版目录在页面的右上角↗~&#x1f95d;&#x1f95d; 第1版&#xff1a;在力扣新手村刷题的记录~&#x1f9e9;&#x1f9e9; 编辑&#xff1a;梅…

二叉树与堆

目录 1.树概念及结构 1.1树的概念 1.2 树的相关概念 1.3 树的表示 1.4 树在实际中的运用&#xff08;表示文件系统的目录树结构&#xff09; 2.二叉树概念及结构 2.1概念 2.2现实中的二叉树&#xff1a; 2.3 特殊的二叉树&#xff1a; 2.4 二叉树的性质 2.5 二叉树的…

高性能 Kafka 及常见面试题

Kafka 是一种分布式的&#xff0c;基于发布/订阅的消息系统&#xff0c;原本开发自 LinkedIn&#xff0c;用作 LinkedIn 的事件流&#xff08;Event Stream&#xff09;和运营数据处理管道&#xff08;Pipeline&#xff09;的基础。 基础原理详解可见 Kafka 基本架构及原理 基础…

【大数据】Flink SQL 语法篇(四):Group 聚合、Over 聚合

Flink SQL 语法篇&#xff08;四&#xff09;&#xff1a;Group 聚合、Over 聚合 1.Group 聚合1.1 基础概念1.2 窗口聚合和 Group 聚合1.3 SQL 语义1.4 Group 聚合支持 Grouping sets、Rollup、Cube 2.Over 聚合2.1 时间区间聚合2.2 行数聚合 1.Group 聚合 1.1 基础概念 Grou…

019 Spring Boot+Vue 电影院会员管理系统(源代码+数据库+文档)

部分代码地址&#xff1a; https://github.com/XinChennn/xc019-cinema 一、系统介绍 cinema项目是一套电影院会员管理系统&#xff0c;使用前后端分离架构开发包含管理员、会员管理、会员卡管理、电影票、消费记录、数据统计等模块 二、所用技术 后端技术栈&#xff1a; …

【Flink精讲】Flink组件通信

主要指三个进程中的通讯 CliFrontendYarnJobClusterEntrypointTaskExecutorRunner Flink内部节点之间的通讯使用Akka&#xff0c;比如JobManager和TaskManager之间。而operator之间的数据传输是利用Netty。 RPC是统称&#xff0c;Akka&#xff0c;Netty是实现 Akka与Ac…

热闹元宵进行中,如何利用VR全景展示民宿品牌形象?

错峰出游闹元宵&#xff0c;元宵节恰逢周末&#xff0c;而且还是春节假期返工之后的首个休息日&#xff0c;不少人都想通过短途度假来缓解“节后综合征”。两位数的特价机票、打折的各种酒店让你实现“旅行自由”&#xff0c;那么如何知道特价酒店服务好不好呢&#xff1f;先别…

Docker Volume

"Ice in my vein" Docker Volume(存储卷) 什么是存储卷? 存储卷就是: “将宿主机的本地文件系统中存在的某个目录&#xff0c;与容器内部的文件系统上的某一目录建立绑定关系”。 存储卷与容器本身的联合文件系统&#xff1f; 在宿主机上的这个与容器形成绑定关系…

实用区块链应用:去中心化投票系统的部署与实施

一、需求分析背景 随着技术的发展&#xff0c;传统的投票系统面临着越来越多的挑战&#xff0c;如中心化控制、透明度不足和易受攻击等问题。为了解决这些问题&#xff0c;我们可以利用区块链技术去中心化、透明性和安全性来构建一个去中心化投票系统。这样的系统能够确保投票过…

vue2.0及起步(前端面试知识积累)

1、需要了解的vue概要知识 1、vue是什么&#xff1f; 一套用于构建用户界面的渐进式JavaScript框架。 为什么vue被称为是渐进式JS框架&#xff1f; 答&#xff1a;Vue允许开发者在不同的项目中以渐进式的方式使用它&#xff0c;这种渐进式表现在以下的方面&#xff1a; 逐步采…

数据可视化--了解数据可视化和Excel数据可视化

目录 1.1科学可视化&#xff1a; 可视化是模式、关系、异常 1.2三基色原理&#xff1a; 三基色:红色、绿色和蓝色 1.3Excel数据可视化 1.3.1 excel数据分析-13个图表可视化技巧 1.3.2 excel数据分析-28个常用可视化图表&#xff08;video&#xff09; 1.3.3Excel可视化…

Java面试——锁

​ 公平锁&#xff1a; 是指多个线程按照申请锁的顺序来获取锁&#xff0c;有点先来后到的意思。在并发环境中&#xff0c;每个线程在获取锁时会先查看此锁维护的队列&#xff0c;如果为空&#xff0c;或者当前线程是等待队列的第一个&#xff0c;就占有锁&#xff0c;否则就会…

Apache Doris 发展历程、技术特性及云原生时代的未来规划

本文节选自《基础软件之路&#xff1a;企业级实践及开源之路》一书&#xff0c;该书集结了中国几乎所有主流基础软件企业的实践案例&#xff0c;由 28 位知名专家共同编写&#xff0c;系统剖析了基础软件发展趋势、四大基础软件&#xff08;数据库、操作系统、编程语言与中间件…