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

深度学习框架PyTorch——从入门到精通(10)PyTorch张量简介

这部分是 PyTorch介绍——YouTube系列的内容,每一节都对应一个youtube视频。(可能跟之前的有一定的重复)

  • 创建张量
    • 随机张量和种子
    • 张量形状
    • 张量数据类型
  • 使用PyTorch张量进行数学与逻辑运算
    • 简单介绍——张量广播
    • 关于张量更多的数学操作
    • 原地修改张量
  • 复制张量
  • 迁移到加速器
  • 操作张量形状
    • 改变维度数量
  • NumPy 桥接

本节YouTube视频地址:点击这里
张量是PyTorch中的核心数据抽象。

首先,让我们导入PyTorch模块。我们还将添加Python的数学模块以方便一些示例。

import torch
import math

创建张量

创建张量的最简单方法是使用torch.empty()调用:

x = torch.empty(3, 4)
print(type(x))
print(x)
# 输出
<class 'torch.Tensor'>
tensor([[ 6.2754e+13,  7.0065e-45,  1.4013e-45,  0.0000e+00],[-9.3681e+11,  3.0634e-41,  0.0000e+00,  0.0000e+00],[ 1.5457e+03,  3.0635e-41,  2.5256e-12,  4.5895e-41]])

让我们回顾一下我们刚刚做的:

  • 我们使用附加到torch模块的众多工厂方法之一创建了一个张量。
  • 张量本身是二维的,有3行4列。
  • 返回的对象类型为torch.Tensor,它是torch.FloatTensor的别名;默认情况下,PyTorch张量填充有32位浮点数。(更多关于下面的数据类型。)
  • 打印张量时,您可能会看到一些随机的值。torch.empty()调用为张量分配内存,但不使用任何值初始化它-因此您看到的是分配时内存中的任何内容。

关于张量及其维数和术语的简要说明:

  • 您有时会看到一个称为向量的一维张量。
  • 同样,二维张量通常称为矩阵。
  • 任何超过二维的东西通常被称为张量。

通常情况下,您需要使用一些值初始化张量。常见情况是全零、全一或随机值,torch模块为所有这些提供工厂方法:

zeros = torch.zeros(2, 3)
print(zeros)ones = torch.ones(2, 3)
print(ones)torch.manual_seed(1729)
random = torch.rand(2, 3)
print(random)# 输出
tensor([[0., 0., 0.],[0., 0., 0.]])
tensor([[1., 1., 1.],[1., 1., 1.]])
tensor([[0.3126, 0.3791, 0.3087],[0.0736, 0.4216, 0.0691]])

功能方法都做了你所期望的——我们有一个充满零的张量,另一个张量充满一,还有一个张量是随机值(在0到1之间)。

随机张量和种子

说到随机张量,您是否注意到在它前面调用了torch.manual_seed()?使用随机值初始化张量(例如模型的学习权重)很常见,但有时——尤其是在研究环境中——您需要一些结果可重复性的保证。手动设置随机数生成器的种子是做到这一点的方法。让我们更仔细地看看:

torch.manual_seed(1729)
random1 = torch.rand(2, 3)
print(random1)random2 = torch.rand(2, 3)
print(random2)torch.manual_seed(1729)
random3 = torch.rand(2, 3)
print(random3)random4 = torch.rand(2, 3)
print(random4)# 输出
tensor([[0.3126, 0.3791, 0.3087],[0.0736, 0.4216, 0.0691]])
tensor([[0.2332, 0.4047, 0.2162],[0.9927, 0.4128, 0.5938]])
tensor([[0.3126, 0.3791, 0.3087],[0.0736, 0.4216, 0.0691]])
tensor([[0.2332, 0.4047, 0.2162],[0.9927, 0.4128, 0.5938]])

您应该在上面看到的是random1和random3携带相同的值,random2和random4也是如此。手动设置RNG的种子会重置它,因此在大多数设置中,取决于随机数的相同计算应该会提供相同的结果。

有关详细信息,请参阅PyTorch关于可复现性的文档

张量形状

通常,当您对两个或多个张量执行操作时,它们需要具有相同的形状-也就是说,具有相同数量的维度和每个维度中相同数量的单元格。为此,我们有torch.*_like()方法:

x = torch.empty(2, 2, 3)
print(x.shape)
print(x)empty_like_x = torch.empty_like(x)
print(empty_like_x.shape)
print(empty_like_x)zeros_like_x = torch.zeros_like(x)
print(zeros_like_x.shape)
print(zeros_like_x)ones_like_x = torch.ones_like(x)
print(ones_like_x.shape)
print(ones_like_x)rand_like_x = torch.rand_like(x)
print(rand_like_x.shape)
print(rand_like_x)# 输出
torch.Size([2, 2, 3])
tensor([[[5.5989e+07, 3.0635e-41, 3.4774e-15],[3.0635e-41, 8.9683e-44, 0.0000e+00]],[[1.5695e-43, 0.0000e+00,        nan],[3.0631e-41, 0.0000e+00, 1.4013e-45]]])
torch.Size([2, 2, 3])
tensor([[[ 1.5582e+03,  3.0635e-41,  4.0625e+04],[ 3.0635e-41,  1.1210e-43,  0.0000e+00]],[[ 8.9683e-44,  0.0000e+00,  2.2417e+24],[ 3.0631e-41, -9.3681e+11,  3.0634e-41]]])
torch.Size([2, 2, 3])
tensor([[[0., 0., 0.],[0., 0., 0.]],[[0., 0., 0.],[0., 0., 0.]]])
torch.Size([2, 2, 3])
tensor([[[1., 1., 1.],[1., 1., 1.]],[[1., 1., 1.],[1., 1., 1.]]])
torch.Size([2, 2, 3])
tensor([[[0.6128, 0.1519, 0.0453],[0.5035, 0.9978, 0.3884]],[[0.6929, 0.1703, 0.1384],[0.4759, 0.7481, 0.0361]]])

上面代码中的第一个新内容是在张量上使用.shape属性。该属性包含张量每个维度的范围列表——在我们的例子中,x是形状为2 x 2 x 3的三维张量。

下面,我们调用.empty_like(),.zeros_like(),.ones_like().rand_like()方法。使用.shape属性,我们可以验证这些方法中的每一个都返回一个张量的维数和范围。

最后一种创建张量的方法(我们将要介绍的)是直接从一个PyTorch集合中指定其数据:

some_constants = torch.tensor([[3.1415926, 2.71828], [1.61803, 0.0072897]])
print(some_constants)some_integers = torch.tensor((2, 3, 5, 7, 11, 13, 17, 19))
print(some_integers)more_integers = torch.tensor(((2, 4, 6), [3, 6, 9]))
print(more_integers)# 输出
tensor([[3.1416, 2.7183],[1.6180, 0.0073]])
tensor([ 2,  3,  5,  7, 11, 13, 17, 19])
tensor([[2, 4, 6],[3, 6, 9]])

如果您已经在Python元组或列表中有数据,使用torch.tensor()是创建一个张量的最直接方法。如上所示,嵌套集合将创建多维张量。
注意torch.tensor()是 创建数据的副本。

张量数据类型

有几种方法可以设置张量的数据类型:

a = torch.ones((2, 3), dtype=torch.int16)
print(a)b = torch.rand((2, 3), dtype=torch.float64) * 20.
print(b)c = b.to(torch.int32)
print(c)# 输出
tensor([[1, 1, 1],[1, 1, 1]], dtype=torch.int16)
tensor([[ 0.9956,  1.4148,  5.8364],[11.2406, 11.2083, 11.6692]], dtype=torch.float64)
tensor([[ 0,  1,  5],[11, 11, 11]], dtype=torch.int32)

设置张量的基础数据类型的最简单方法是在创建时使用可选参数。在上面单元格的第一行,我们为张量a设置了dtype=torch.int16。当我们打印a时,我们可以看到它充满了1而不是1.(Python的微妙之处是这是一个整数类型而不是浮点。)

关于打印张量 a 的另一件需要注意的事情是,与我们将数据类型(dtype)设为默认值(32位浮点数)时的情况不同,打印这个张量时也会指明它的数据类型。

你可能还发现,我们从将张量的形状指定为一系列整数参数,到将这些参数分组。这不是绝对必要的——PyTorch将一系列有限的、未标记的整数参数作为张量形状——但是当添加可选参数时,它可以使您的意图更具可读性。

使用PyTorch张量进行数学与逻辑运算

现在你知道了一些创建张量的方法…你能用它们做什么?

让我们先看看基本算术,以及张量如何与简单标量相互作用:

ones = torch.zeros(2, 2) + 1
twos = torch.ones(2, 2) * 2
threes = (torch.ones(2, 2) * 7 - 1) / 2
fours = twos ** 2
sqrt2s = twos ** 0.5print(ones)
print(twos)
print(threes)
print(fours)
print(sqrt2s)# 输出
tensor([[1., 1.],[1., 1.]])
tensor([[2., 2.],[2., 2.]])
tensor([[3., 3.],[3., 3.]])
tensor([[4., 4.],[4., 4.]])
tensor([[1.4142, 1.4142],[1.4142, 1.4142]])

正如您在上面看到的,张量和标量之间的算术运算,例如加法、减法、乘法、除法,和指数分布在张量的每个元素上。

因为这些操作的输出将是张量,您可以使用通常的运算符优先级规则将它们链接在一起,如我们创建张量threes的那一行。

两个张量之间的类似操作也表现得像你直觉上期望的那样:

powers2 = twos ** torch.tensor([[1, 2], [3, 4]])
print(powers2)fives = ones + fours
print(fives)dozens = threes * fours
print(dozens)# 输出
tensor([[ 2.,  4.],[ 8., 16.]])
tensor([[5., 5.],[5., 5.]])
tensor([[12., 12.],[12., 12.]])

这里需要注意的是,前面编解码器中的所有张量都是相同的形状。如果形状不同,当我们试图对张量进行二进制运算时会发生什么?

:以下代码运行时会抛出错误。这是故意的。

a = torch.rand(2, 3)
b = torch.rand(3, 2)print(a * b)

在一般情况下,您不能以这种方式对不同形状的张量进行操作,即使在像上面的单元格这样的情况下,张量具有相同数量的元素。

简单介绍——张量广播

如果你熟悉 NumPy 的多维数组(ndarray)中的广播语义,你会发现同样的规则也适用于此处。

同形状规则的一个例外是张量广播。下面是一个例子:

rand = torch.rand(2, 4)
doubled = rand * (torch.ones(1, 4) * 2)print(rand)
print(doubled)# 输出
tensor([[0.6146, 0.5999, 0.5013, 0.9397],[0.8656, 0.5207, 0.6865, 0.3614]])
tensor([[1.2291, 1.1998, 1.0026, 1.8793],[1.7312, 1.0413, 1.3730, 0.7228]])

这里有什么诀窍?我们是如何可以将2x4张量乘以1x4张量?

广播是一种在形状上具有相似性的张量之间执行运算的方式。在上述示例中,那个一行四列的张量与那个两行四列张量的两行都进行了乘法运算。

这在深度学习中是一项重要的运算。常见的例子是将一个包含学习权重的张量与一批输入张量相乘,将该运算分别应用于这批数据中的每个实例,然后返回一个形状相同的张量 —— 就如同我们上面的那个(2,4)*(1,4)的例子,返回了一个形状为(2,4)的张量一样。

广播规则是:

  • 每个张量必须至少有一个维度-不存在空张量。
  • 比较两个张量的维度大小:
    • 每个维度的大小必须相等,或者
    • 其中一个维度的大小必须为 1,或者
    • 某一个维度在其中一个张量中不存在。

当然,形状相同的张量显然是可以进行 “广播” 的,正如你之前所看到的那样。
以下是一些符合上述规则并允许进行广播的情况示例:

# 创建一个形状为(4, 3, 2)且元素全为1的张量a
a =     torch.ones(4, 3, 2)# 将张量a与一个形状为(3, 2)的随机张量相乘,第三个维度和第二个维度与a的对应维度相同,缺少第一个维度
b = a * torch.rand(   3, 2) # 3rd & 2nd dims identical to a, dim 1 absent
print(b)# 将张量a与一个形状为(3, 1)的随机张量相乘,第三个维度大小为1,第二个维度与a的第二个维度相同
c = a * torch.rand(   3, 1) # 3rd dim = 1, 2nd dim identical to a
print(c)# 将张量a与一个形状为(1, 2)的随机张量相乘,第三个维度与a的第三个维度相同,第二个维度大小为1
d = a * torch.rand(   1, 2) # 3rd dim identical to a, 2nd dim = 1
print(d)# 输出
tensor([[[0.6493, 0.2633],[0.4762, 0.0548],[0.2024, 0.5731]],[[0.6493, 0.2633],[0.4762, 0.0548],[0.2024, 0.5731]],[[0.6493, 0.2633],[0.4762, 0.0548],[0.2024, 0.5731]],[[0.6493, 0.2633],[0.4762, 0.0548],[0.2024, 0.5731]]])
tensor([[[0.7191, 0.7191],[0.4067, 0.4067],[0.7301, 0.7301]],[[0.7191, 0.7191],[0.4067, 0.4067],[0.7301, 0.7301]],[[0.7191, 0.7191],[0.4067, 0.4067],[0.7301, 0.7301]],[[0.7191, 0.7191],[0.4067, 0.4067],[0.7301, 0.7301]]])
tensor([[[0.6276, 0.7357],[0.6276, 0.7357],[0.6276, 0.7357]],[[0.6276, 0.7357],[0.6276, 0.7357],[0.6276, 0.7357]],[[0.6276, 0.7357],[0.6276, 0.7357],[0.6276, 0.7357]],[[0.6276, 0.7357],[0.6276, 0.7357],[0.6276, 0.7357]]])

仔细查看上面每个张量的值:

  • 创建b的乘法操作在a的每个“层”上广播(注意这里是四层,因为是432的矩阵,最外层一共有四个。这四个个,彼此相同)。
  • 对于c,操作被广播到a的每一层和每一行(因为乘的是三行一列,所以列之间传播,导致结果相同。一共有两列,两列相同。所有看上去是一行内,两个数是一样的)
  • 对于d,我们切换了它——是一行两列。现在是“行”在四层和两列之间相同的。

以下是一些尝试广播失败的例子:

# 这部分代码是错误的,运行会报错
a =     torch.ones(4, 3, 2)b = a * torch.rand(4, 3)    # dimensions must match last-to-firstc = a * torch.rand(   2, 3) # both 3rd & 2nd dims differentd = a * torch.rand((0, ))   # can't broadcast with an empty tensor

关于张量更多的数学操作

PyTorch张量有超过300个可以在它们上执行的操作。

以下是从一些主要的运算类别中选取的一小部分示例:

import torch
import math# 常见函数
a = torch.rand(2, 4) * 2 - 1
print('常见函数:')
print(torch.abs(a))  # 计算张量a中每个元素的绝对值
print(torch.ceil(a))  # 对张量a中每个元素向上取整
print(torch.floor(a))  # 对张量a中每个元素向下取整
print(torch.clamp(a, -0.5, 0.5))  # 将张量a中每个元素限制在-0.5到0.5之间# 三角函数及其反函数
angles = torch.tensor([0, math.pi / 4, math.pi / 2, 3 * math.pi / 4])
sines = torch.sin(angles)  # 计算角度的正弦值
inverses = torch.asin(sines)  # 计算正弦值的反正弦值
print('\n正弦值和反正弦值:')
print(angles)
print(sines)
print(inverses)# 按位运算
print('\n按位异或:')
b = torch.tensor([1, 5, 11])
c = torch.tensor([2, 7, 10])
print(torch.bitwise_xor(b, c))  # 对张量b和c进行按位异或运算# 比较操作:
print('\n广播式的、逐元素的相等比较:')
d = torch.tensor([[1., 2.], [3., 4.]])
e = torch.ones(1, 2)  # 许多比较操作都支持广播!
print(torch.eq(d, e))  # 返回一个布尔类型的张量,表示d和e对应元素是否相等# 归约操作:
print('\n归约操作:')
print(torch.max(d))  # 返回一个单元素张量,表示张量d中的最大值
print(torch.max(d).item())  # 从返回的张量中提取值
print(torch.mean(d))  # 计算张量d的平均值
print(torch.std(d))  # 计算张量d的标准差
print(torch.prod(d))  # 计算张量d中所有元素的乘积
print(torch.unique(torch.tensor([1, 2, 1, 2, 1, 2])))  # 过滤出唯一元素# 向量和线性代数运算
v1 = torch.tensor([1., 0., 0.])  # x单位向量
v2 = torch.tensor([0., 1., 0.])  # y单位向量
m1 = torch.rand(2, 2)  # 随机矩阵
m2 = torch.tensor([[3., 0.], [0., 3.]])  # 三倍的单位矩阵print('\n向量和矩阵:')
print(torch.linalg.cross(v2, v1))  # y单位向量与x单位向量的叉积,结果为负的z单位向量(v1 x v2 == -v2 x v1)
print(m1)
m3 = torch.linalg.matmul(m1, m2)
print(m3)  # 结果是三倍的m1
print(torch.linalg.svd(m3))  # 对矩阵m3进行奇异值分解# 输出
Common functions:
tensor([[0.9238, 0.5724, 0.0791, 0.2629],[0.1986, 0.4439, 0.6434, 0.4776]])
tensor([[-0., -0., 1., -0.],[-0., 1., 1., -0.]])
tensor([[-1., -1.,  0., -1.],[-1.,  0.,  0., -1.]])
tensor([[-0.5000, -0.5000,  0.0791, -0.2629],[-0.1986,  0.4439,  0.5000, -0.4776]])Sine and arcsine:
tensor([0.0000, 0.7854, 1.5708, 2.3562])
tensor([0.0000, 0.7071, 1.0000, 0.7071])
tensor([0.0000, 0.7854, 1.5708, 0.7854])Bitwise XOR:
tensor([3, 2, 1])Broadcasted, element-wise equality comparison:
tensor([[ True, False],[False, False]])Reduction ops:
tensor(4.)
4.0
tensor(2.5000)
tensor(1.2910)
tensor(24.)
tensor([1, 2])Vectors & Matrices:
tensor([ 0.,  0., -1.])
tensor([[0.7375, 0.8328],[0.8444, 0.2941]])
tensor([[2.2125, 2.4985],[2.5332, 0.8822]])
torch.return_types.linalg_svd(
U=tensor([[-0.7889, -0.6145],[-0.6145,  0.7889]]),
S=tensor([4.1498, 1.0548]),
Vh=tensor([[-0.7957, -0.6056],[ 0.6056, -0.7957]]))

这只是运算的一个小示例。想要了解更多细节以及数学函数的完整列表,请查看相关文档。想要了解更多细节以及线性代数运算的完整列表,请查看这份文档。

原地修改张量

张量上的大多数二进制操作将返回第三个新张量。当我们说c = a * b(其中a和b是张量)时,新张量c将占据与其他张量不同的内存区域。

不过,有时候你可能希望原地修改一个张量——例如,当你进行逐元素计算,并且可以丢弃中间值的时候。为此,大多数数学函数都有一个带有下划线(_)后缀的版本,该版本可以原地修改张量。

例如:

import torch
import matha = torch.tensor([0, math.pi / 4, math.pi / 2, 3 * math.pi / 4])
print('a:')
print(a)
print(torch.sin(a))  # 此操作在内存中创建一个新的张量
print(a)  # a 没有改变b = torch.tensor([0, math.pi / 4, math.pi / 2, 3 * math.pi / 4])
print('\nb:')
print(b)
print(torch.sin_(b))  # 注意下划线
print(b)  # b 已经改变# 输出
a:
tensor([0.0000, 0.7854, 1.5708, 2.3562])
tensor([0.0000, 0.7071, 1.0000, 0.7071])
tensor([0.0000, 0.7854, 1.5708, 2.3562])b:
tensor([0.0000, 0.7854, 1.5708, 2.3562])
tensor([0.0000, 0.7071, 1.0000, 0.7071])
tensor([0.0000, 0.7071, 1.0000, 0.7071])

对于算术运算,有些函数的行为类似:

a = torch.ones(2, 2)
b = torch.rand(2, 2)print('Before:')
print(a)
print(b)
print('\nAfter adding:')
print(a.add_(b))
print(a)
print(b)
print('\nAfter multiplying')
print(b.mul_(b))
print(b)
# 输出
Before:
tensor([[1., 1.],[1., 1.]])
tensor([[0.3788, 0.4567],[0.0649, 0.6677]])After adding:
tensor([[1.3788, 1.4567],[1.0649, 1.6677]])
tensor([[1.3788, 1.4567],[1.0649, 1.6677]])
tensor([[0.3788, 0.4567],[0.0649, 0.6677]])After multiplying
tensor([[0.1435, 0.2086],[0.0042, 0.4459]])
tensor([[0.1435, 0.2086],[0.0042, 0.4459]])

请注意,这些原地算术函数是 torch.Tensor 对象的方法,不像许多其他函数(例如 torch.sin())那样附属于 torch 模块。从 a.add_(b) 可以看出,调用该方法的张量会在原地被修改。

还有另一种方法可以将计算结果存储在现有的、已分配好的张量中。到目前为止我们所见到的许多方法和函数——包括创建张量的方法!——都有一个 out 参数,它允许你指定一个张量来接收输出结果。如果指定的 out 张量具有正确的形状和数据类型,那么计算结果就可以存储其中,而无需重新分配内存:

import torcha = torch.rand(2, 2)
b = torch.rand(2, 2)
c = torch.zeros(2, 2)
old_id = id(c)print(c)
# 将a和b矩阵相乘的结果存储到c中
d = torch.matmul(a, b, out=c)
print(c)  # c的内容已改变# 验证c和d是同一个对象,而非仅仅值相等
assert c is d
# 确保新的c和旧的c是同一个对象
assert id(c) == old_id# 生成一个2x2的随机张量,并将结果存储到c中,创建操作也适用out参数
torch.rand(2, 2, out=c)
print(c)  # c的内容再次改变
# 仍然是同一个对象
assert id(c) == old_id# 输出
tensor([[0., 0.],[0., 0.]])
tensor([[0.3653, 0.8699],[0.2364, 0.3604]])
tensor([[0.0776, 0.4004],[0.9877, 0.0352]])

复制张量

与Python中的任何对象一样,将张量分配给变量会使张量的标签可变,并且不会复制它。例如:

a = torch.ones(2, 2)
b = aa[0][1] = 561  # we change a...
print(b)       # ...and b is also altered# 输出
tensor([[  1., 561.],[  1.,   1.]])

但是,如果您想要单独的数据副本来处理怎么办?clone()方法适合您:

a = torch.ones(2, 2)
b = a.clone()assert b is not a      # different objects in memory...
print(torch.eq(a, b))  # ...but still with the same contents!a[0][1] = 561          # a changes...
print(b)               # ...but b is still all ones# 输出
tensor([[True, True],[True, True]])
tensor([[1., 1.],[1., 1.]])

使用 clone() 时,有一个重要的事项需要注意。如果源张量启用了自动求导(autograd),那么克隆后的张量也会启用。关于自动求导,我们会在相关视频中深入探讨,但如果你想先了解个大概,可以接着往下看。

在很多情况下,这正是你所期望的。例如,若你的模型在其 forward() 方法中有多条计算路径,且原始张量及其克隆张量都对模型的输出有贡献,那么为了让模型能够学习,你需要为这两个张量都开启自动求导。如果源张量启用了自动求导(如果它是一组学习权重,或者是由涉及这些权重的计算得出的,通常就会启用),那么你就能得到预期的结果。

另一方面,如果你进行的计算中,原始张量及其克隆张量都不需要跟踪梯度,那么只要源张量关闭了自动求导,就没问题了。

不过,还有第三种情况:假设你在模型的 forward() 函数中进行计算,默认情况下所有操作都开启了梯度计算,但你想在计算过程中提取一些值来生成某些指标。在这种情况下,你不希望源张量的克隆副本跟踪梯度——关闭自动求导的历史跟踪可以提高性能。为此,你可以在源张量上使用 .detach() 方法:

a = torch.rand(2, 2, requires_grad=True) # turn on autograd
print(a)b = a.clone()
print(b)c = a.detach().clone()
print(c)print(a)# 输出
tensor([[0.0905, 0.4485],[0.8740, 0.2526]], requires_grad=True)
tensor([[0.0905, 0.4485],[0.8740, 0.2526]], grad_fn=<CloneBackward0>)
tensor([[0.0905, 0.4485],[0.8740, 0.2526]])
tensor([[0.0905, 0.4485],[0.8740, 0.2526]], requires_grad=True)

这里发生了什么呢?

  • 我们创建了一个张量 a,并将 requires_grad=True 开启。我们还未介绍过这个可选参数,不过会在自动求导这一单元进行讲解。

  • 当我们打印 a 时,它会告知我们 requires_grad=True 这个属性——这意味着自动求导和计算历史跟踪功能已开启。

  • 我们对 a 进行克隆并将其标记为 b。当我们打印 b 时,可以看到它正在跟踪其计算历史——它继承了 a 的自动求导设置,并添加到了计算历史中。

  • 我们将 a 克隆到 c,但首先调用了 detach() 方法。

  • 打印 c 时,我们看不到计算历史,也没有 requires_grad=True

detach() 方法将张量从其计算历史中分离出来。它的作用是“后续操作就当作自动求导是关闭的”。它在不改变 a 的情况下实现了这一点——你可以看到,当我们最后再次打印 a 时,它仍然保留着 requires_grad=True 属性。

迁移到加速器

PyTorch 的一大优势在于它能在诸如 CUDA、MPS、MTIA 或 XPU 等加速器上实现强大的加速功能。到目前为止,我们所做的一切操作都是在 CPU 上进行的。那么我们如何迁移到更快的硬件上呢?

首先,我们应该使用 is_available() 方法来检查加速器是否可用。
注意:如果你没有可用的加速器,那么本节中可执行的代码单元将不会执行任何与加速器相关的代码。

if torch.accelerator.is_available():print('We have an accelerator!')
else:print('Sorry, CPU only.')
# 输出(至少应该是)
We have an accelerator!

一旦我们确定有一个或多个加速器可用,我们就需要把数据放置到加速器能够访问的地方。你的 CPU 对计算机内存(RAM)中的数据进行计算。而你的加速器有其专用的内存。每当你想在某个设备上执行计算时,你都必须将该计算所需的所有数据移动到该设备可访问的内存中。(通俗来讲,“将数据移动到 GPU 可访问的内存中” 常简称为 “将数据移动到 GPU 上”。)

有多种方法可以将数据传输到目标设备上。你可以在创建数据时就进行操作:

if torch.accelerator.is_available():gpu_rand = torch.rand(2, 2, device=torch.accelerator.current_accelerator())print(gpu_rand)
else:print('Sorry, CPU only.')
# 输出
tensor([[0.3344, 0.2640],[0.2119, 0.0582]], device='cuda:0')

默认情况下,新的张量是在 CPU 上创建的,所以当我们想要在加速器上创建张量时,就必须使用可选的 device 参数进行指定。当我们打印新创建的张量时可以看到,PyTorch 会告知我们该张量所在的设备(如果不是在 CPU 上的话)。

你可以使用 torch.accelerator.device_count() 来查询加速器的数量。如果你有多个加速器,你可以通过索引来指定它们,以 CUDA 为例:device='cuda:0'device='cuda:1' 等等。

从编码实践的角度来看,在各处都用字符串常量来指定设备是相当不可靠的。在理想情况下,无论你的代码是在 CPU 还是在加速器硬件上运行,都应该能够稳定地执行。你可以通过创建一个设备句柄来实现这一点,这样就可以将这个句柄传递给你的张量,而不是使用字符串:

my_device = torch.accelerator.current_accelerator() if torch.accelerator.is_available() else torch.device('cpu')
print('Device: {}'.format(my_device))x = torch.rand(2, 2, device=my_device)
print(x)# 输出
Device: cuda
tensor([[0.0024, 0.6778],[0.2441, 0.6812]], device='cuda:0')

如果你已有一个位于某一设备上的张量,你可以使用 to() 方法将其移动到另一个设备。下面这行代码会在 CPU 上创建一个张量,然后将它移动到你在上一个代码块中获取的设备句柄所对应的设备上。

y = torch.rand(2, 2)
y = y.to(my_device)

要知道,若要对两个或更多张量进行计算,所有这些张量必须位于同一设备上,这一点很重要。不管你是否有可用的加速器设备,下面的代码都会抛出运行时错误,以 CUDA 为例:

x = torch.rand(2, 2)
y = torch.rand(2, 2, device='cuda')
z = x + y  # 会抛出异常

操作张量形状

有时,你需要改变张量的形状。下面,我们来看看几种常见的情况以及如何处理它们。

改变维度数量

你可能需要改变维度数量的一种情况是,向模型传入单个输入实例。PyTorch 模型通常期望输入是批量形式的。

例如,假设有一个处理 3 x 226 x 226 图像的模型——这是一个具有 3 个颜色通道、边长为 226 像素的正方形图像。当你加载并转换图像后,会得到一个形状为 (3, 226, 226) 的张量。然而,你的模型期望的输入形状是 (N, 3, 226, 226),其中 N 是批量中的图像数量。那么,如何创建一个批量大小为 1 的输入呢?

a = torch.rand(3, 226, 226)
b = a.unsqueeze(0)print(a.shape)
print(b.shape)

输出结果:

torch.Size([3, 226, 226])
torch.Size([1, 3, 226, 226])

unsqueeze() 方法会添加一个大小为 1 的维度。unsqueeze(0) 会将其作为新的第 0 维度添加进来——现在你就有了一个批量大小为 1 的数据批次!

既然这是 “扩充维度(unsqueezing)”,那么 “压缩维度(squeezing)” 又是什么意思呢?我们利用了这样一个事实,即任何大小为 1 的维度都不会改变张量中元素的数量。

c = torch.rand(1, 1, 1, 1, 1)
print(c)

输出:

tensor([[[[[0.2347]]]]])

继续上面的例子,假设模型对于每个输入的输出是一个 20 维的向量。那么你会期望输出的形状是 (N, 20),其中 N 是输入批次中实例的数量。这意味着对于我们的单输入批次,我们将得到一个形状为 (1, 20) 的输出。

如果你想用这个输出进行一些非批量的计算——比如只期望一个 20 维向量的计算,那该怎么办呢?

a = torch.rand(1, 20)
print(a.shape)
print(a)b = a.squeeze(0)
print(b.shape)
print(b)c = torch.rand(2, 2)
print(c.shape)d = c.squeeze(0)
print(d.shape)

输出:

torch.Size([1, 20])
tensor([[0.1899, 0.4067, 0.1519, 0.1506, 0.9585, 0.7756, 0.8973, 0.4929, 0.2367,0.8194, 0.4509, 0.2690, 0.8381, 0.8207, 0.6818, 0.5057, 0.9335, 0.9769,0.2792, 0.3277]])
torch.Size([20])
tensor([0.1899, 0.4067, 0.1519, 0.1506, 0.9585, 0.7756, 0.8973, 0.4929, 0.2367,0.8194, 0.4509, 0.2690, 0.8381, 0.8207, 0.6818, 0.5057, 0.9335, 0.9769,0.2792, 0.3277])
torch.Size([2, 2])
torch.Size([2, 2])

从形状可以看出,我们的二维张量现在变成了一维张量,并且如果你仔细观察上面代码块的输出,你会发现打印 a 时由于多了一个维度,会显示 “额外” 的一组方括号 []

你只能压缩大小为 1 的维度。看看上面我们尝试压缩 c 中大小为 2 的维度的情况,得到的形状和开始时一样。对 squeeze()unsqueeze() 的调用只能作用于大小为 1 的维度,因为否则就会改变张量中元素的数量。

另一个可能用到 unsqueeze() 的地方是为了便于广播。回顾上面的例子,我们有如下代码:

a = torch.ones(4, 3, 2)c = a * torch.rand(   3, 1) # 第 3 维度 = 1,第 2 维度与 a 的相同
print(c)

其实际效果是在第 0 维和第 2 维上进行广播操作,使得随机的 3 x 1 张量与 a 中每个 3 元素的列进行逐元素相乘。

如果随机向量只是一个 3 元素的向量呢?我们将失去进行广播的能力,因为根据广播规则,最后的维度将不匹配。unsqueeze() 可以解决这个问题:

a = torch.ones(4, 3, 2)
b = torch.rand(   3)     # 尝试计算 a * b 会引发运行时错误
c = b.unsqueeze(1)       # 转换为二维张量,在末尾添加新维度
print(c.shape)
print(a * c)             # 广播又能正常工作了!

输出:

torch.Size([3, 1])
tensor([[[0.1891, 0.1891],[0.3952, 0.3952],[0.9176, 0.9176]],[[0.1891, 0.1891],[0.3952, 0.3952],[0.9176, 0.9176]],[[0.1891, 0.1891],[0.3952, 0.3952],[0.9176, 0.9176]],[[0.1891, 0.1891],[0.3952, 0.3952],[0.9176, 0.9176]]])

queeze()unsqueeze() 方法也有原地操作版本,即 squeeze_()unsqueeze_()

batch_me = torch.rand(3, 226, 226)
print(batch_me.shape)
batch_me.unsqueeze_(0)
print(batch_me.shape)

输出:

torch.Size([3, 226, 226])
torch.Size([1, 3, 226, 226])

有时你可能想要更彻底地改变张量的形状,同时仍然保留元素的数量及其内容。其中一种情况出现在模型的卷积层和线性层之间的接口处——这在图像分类模型中很常见。卷积核会产生一个形状为 特征数 x 宽度 x 高度 的输出张量,但接下来的线性层期望的是一维输入。reshape() 可以为你完成这个操作,前提是你请求的维度所包含的元素数量与输入张量的元素数量相同:

output3d = torch.rand(6, 20, 20)
print(output3d.shape)input1d = output3d.reshape(6 * 20 * 20)
print(input1d.shape)# 也可以将其作为 torch 模块的方法调用:
print(torch.reshape(output3d, (6 * 20 * 20,)).shape)

输出:

torch.Size([6, 20, 20])
torch.Size([2400])
torch.Size([2400])

注意

上面代码块最后一行中的 (6 * 20 * 20,) 参数是因为 PyTorch 在指定张量形状时期望是一个元组——但是当形状是方法的第一个参数时,它允许我们偷懒,只使用一系列整数。在这里,我们必须添加括号和逗号,以使方法确信这确实是一个单元素元组。

在可能的情况下,reshape() 会返回要改变形状的张量的一个视图——也就是说,一个单独的张量对象,它指向相同的底层内存区域。这一点很重要:这意味着对源张量所做的任何更改都将反映在该张量的视图中,除非你对其进行 clone() 操作。

在一些超出本介绍范围的情况下,reshape() 必须返回一个携带数据副本的张量。有关更多信息,请参阅文档。

NumPy 桥接

在上面关于广播的部分中,提到过 PyTorch 的广播语义与 NumPy 的是兼容的——但 PyTorch 和 NumPy 之间的关系远不止于此。

如果你有现有的机器学习或科学代码,其中数据存储在 NumPy 的 ndarray 中,你可能希望将相同的数据表示为 PyTorch 张量,无论是为了利用 PyTorch 的 GPU 加速功能,还是其用于构建机器学习模型的高效抽象。在 ndarray 和 PyTorch 张量之间进行转换很容易:

import numpy as npnumpy_array = np.ones((2, 3))
print(numpy_array)pytorch_tensor = torch.from_numpy(numpy_array)
print(pytorch_tensor)

输出:

[[1. 1. 1.][1. 1. 1.]]
tensor([[1., 1., 1.],[1., 1., 1.]], dtype=torch.float64)

PyTorch 创建了一个与 NumPy 数组形状相同且包含相同数据的张量,甚至保留了 NumPy 默认的 64 位浮点数数据类型。

转换也可以很容易地反向进行:

pytorch_rand = torch.rand(2, 3)
print(pytorch_rand)numpy_rand = pytorch_rand.numpy()
print(numpy_rand)

输出:

tensor([[0.8716, 0.2459, 0.3499],[0.2853, 0.9091, 0.5695]])
[[0.87163675 0.2458961  0.34993553][0.2853077  0.90905803 0.5695162 ]]

重要的是要知道,这些转换后的对象与它们的源对象使用相同的底层内存,这意味着对其中一个对象的更改会反映在另一个对象上:

numpy_array[1, 1] = 23
print(pytorch_tensor)pytorch_rand[1, 1] = 17
print(numpy_rand)

输出:

tensor([[ 1.,  1.,  1.],[ 1., 23.,  1.]], dtype=torch.float64)
[[ 0.87163675  0.2458961   0.34993553][ 0.2853077  17.          0.5695162 ]]

相关文章:

深度学习框架PyTorch——从入门到精通(10)PyTorch张量简介

这部分是 PyTorch介绍——YouTube系列的内容&#xff0c;每一节都对应一个youtube视频。&#xff08;可能跟之前的有一定的重复&#xff09; 创建张量随机张量和种子张量形状张量数据类型 使用PyTorch张量进行数学与逻辑运算简单介绍——张量广播关于张量更多的数学操作原地修改…...

Springboot 集成 Flowable 6.8.0

1. 创建 Spring Boot 项目 通过 Spring Initializr&#xff08;https://start.spring.io/ &#xff09;创建一个基础的 Spring Boot 项目&#xff0c;添加以下依赖&#xff1a; Spring WebSpring Data JPAMySQL DriverLombok&#xff08;可选&#xff0c;用于简化代码&#x…...

Vue3 项目通过 docxtemplater 插件动态渲染 .docx 文档(带图片)预览,并导出

Vue3 项目通过 docxtemplater 插件动态渲染 .docx 文档&#xff08;带图片&#xff09;预览&#xff0c;并导出 预览安装插件示例代码项目目录结构截图实际效果截图 动态渲染 .docx 文档&#xff08;带图片&#xff09;&#xff0c;预览、导出安装插件docx 模板文件内容完整代码…...

实验一、Linux环境下实现进度条小程序:深入解析核心实现与关键技术细节

目录 引言&#xff1a;为什么需要进度条&#xff1f;环境准备与项目结构分析原理剖析&#xff1a;从终端输出到动态刷新代码逐行解析&#xff08;附完整代码&#xff09; 4.1 头文件与宏定义4.2 进度条的动态构建逻辑4.3 关键转义字符&#xff1a;\r与\n的深度对比4.4 缓冲机制…...

生活电子常识——cmd不能使用anaconda的python环境,导致输入python打开应用商店

前言 电脑已经安装了anaconda,从自带的Anaconda Prompt (Anaconda3)中是可以识别python环境的&#xff0c;然而切换到cmd时&#xff0c;突然发现cmd中无法识别anaconda的python环境&#xff0c;竟然打开了应用商店让我安装Python&#xff0c;这当然是不对的。 解决 这是因为…...

TypeScript中的声明合并:与JavaScript的对比与实践指南

引言 在大型项目开发中&#xff0c;代码的可维护性和可扩展性至关重要。TypeScript作为JavaScript的超集&#xff0c;通过静态类型系统带来了更强大的代码组织能力。其中声明合并&#xff08;Declaration Merging&#xff09; 是TypeScript独有的重要特性&#xff0c;本文将深…...

数据结构初阶-二叉树链式

目录 1.概念与结构 2.二叉数链式的实现 2.1遍历规则 2.2申请内存空间 2.3手动构建一棵二叉树 2.4二叉树结点的个数 2.5二叉树叶子结点的个数 2.6二叉树第K层结点个数 2.7二叉树的高度 2.8二叉树中查找值为x的结点 2.9二叉树的销毁 3.层序遍历 3.1概念 3.2层序遍历…...

2024年认证杯SPSSPRO杯数学建模B题(第二阶段)神经外科手术的定位与导航全过程文档及程序

2024年认证杯SPSSPRO杯数学建模 B题 神经外科手术的定位与导航 原题再现&#xff1a; 人的大脑结构非常复杂&#xff0c;内部交织密布着神经和血管&#xff0c;所以在大脑内做手术具有非常高的精细和复杂程度。例如神经外科的肿瘤切除手术或血肿清除手术&#xff0c;通常需要…...

Linux程序性能分析

为什么程序会慢&#xff1f; 在深入工具和方法之前&#xff0c;我们先来聊聊为什么程序会慢。一个程序主要在三个方面消耗资源&#xff1a; CPU时间 - 计算太多、算法效率低 内存使用 - 内存泄漏、频繁申请释放内存 I/O操作 - 文件读写、网络通信太频繁 今天我们主要聚焦C…...

【开题报告+论文+源码】基于SpringBoot的智能安全与急救知识科普系统设计与实现

项目背景与意义 在全球范围内&#xff0c;安全与急救知识的普及已成为提升公众安全素养、减少意外伤害发生率、提高突发事件应对能力的重要举措。尤其是在当今社会&#xff0c;人们面临的生活、工作环境日益复杂&#xff0c;交通事故、火灾、溺水、突发疾病等各种意外事件的发生…...

Linux shift 命令使用详解

简介 在 Bash 脚本中&#xff0c;shift 命令用于将命令行参数向左移动&#xff0c;有效地丢弃第一个参数并将其他参数向下移动。 基础语法 shift [N]N&#xff08;可选&#xff09;→ 要移动的位置数。默认值为 1 示例用法 移动参数 #!/bin/bash echo "Before shift…...

【C++网络编程】第5篇:UDP与广播通信

一、UDP协议核心特性 1. UDP vs TCP ​特性 ​UDP​TCP连接方式无连接面向连接&#xff08;三次握手&#xff09;可靠性不保证数据到达或顺序可靠传输&#xff08;超时重传、顺序控制&#xff09;传输效率低延迟&#xff0c;高吞吐相对较低&#xff08;因握手和确认机制&…...

C++11QT复习 (五)

文章目录 **Day6-2 成员访问运算符重载&#xff08;2025.03.25&#xff09;****1. 复习****2. 成员访问运算符重载****2.1 箭头运算符 (->) 重载****(1) 语法** **2.2 解引用运算符 (*) 重载****(1) 语法** **3. 代码分析****3.1 代码结构****3.2 代码解析****(1) Data 类**…...

Python项目-基于Python的网络爬虫与数据可视化系统

1. 项目简介 在当今数据驱动的时代&#xff0c;网络爬虫和数据可视化已成为获取、分析和展示信息的重要工具。本文将详细介绍如何使用Python构建一个完整的网络爬虫与数据可视化系统&#xff0c;该系统能够自动从互联网收集数据&#xff0c;进行处理分析&#xff0c;并通过直观…...

SpringCloud Zuul 使用教程

SpringCloud Zuul 使用教程 目录 Zuul 简介环境准备搭建 Zuul 网关 • 3.1 Maven 依赖 • 3.2 配置文件 • 3.3 启动类注解基本路由配置 • 4.1 简单路由 • 4.2 基于路径的路由 • 4.3 基于服务的路由Zuul 高级配置 • 5.1 过滤器配置 • 5.2 限流与熔断 • 5.3 负载均衡 •…...

介绍一款基于MinerU的PDF翻译工具

一。简介 Fast pdf translate是一款pdf翻译软件&#xff0c;基于MinerU实现pdf转markdown的功能&#xff0c;接着对markdown进行分割&#xff0c; 送给大模型翻译&#xff0c;最后组装翻译结果并由pypandoc生成结果pdf。 git地址&#xff1a; https://github.com/kv1830/fast…...

轻量级TLS反向代理工具TLS-reverse-proxy:打造安全通信桥梁

在数字化浪潮席卷全球的今天&#xff0c;数据隐私与传输安全已成为企业及个人的核心关切。TLS&#xff08;传输层安全协议&#xff09;作为互联网通信的"隐形卫士"&#xff0c;承担着保护数据在传输过程中不被窃取或篡改的重要使命。然而&#xff0c;对于许多传统服务…...

SQL问题分析与诊断(8)——前提

8.1. 前提 与其他关系库类似&#xff0c;SQL Server中&#xff0c;当我们对存在性能问题的SQL语句进行分析和诊断时&#xff0c;除了获取该SQL语句本身外&#xff0c;还需要获取SQL语句相应的查询计划及其相关的数据环境。这里&#xff0c;所谓数据环境&#xff0c;具体是指SQ…...

关于cmd中出现无法识别某某指令的问题

今天来解决以下这个比较常见的问题&#xff0c;安装各种软件都可能会发生&#xff0c;一般是安装时没勾选注册环境变量&#xff0c;导致cmd无法识别该指令。例如mysql&#xff0c;git等&#xff0c;一般初学者可能不太清楚。 解决这类问题最主要的是了解环境变量的概念&#x…...

如何处理不同输入类型(例如邮箱、电话号码)的验证?

处理不同输入类型(如邮箱、电话号码)的验证可以通过多种方法实现,包括使用 HTML5 内置验证、JavaScript/jQuery 自定义验证和正则表达式。以下是一些常用的验证方法和示例。 1. 使用 HTML5 内置验证 HTML5 提供了一些内置的输入类型,可以自动处理基本的验证。 示例 <…...

Redis集群哨兵相关面试题

目录 1.Redis 主从复制的实现原理是什么? 详解 补充增量同步 replication buffer repl backlog buffer 2.Redis 主从复制的常见拓扑结构有哪些? 3.Redis 复制延迟的常见原因有哪些? 4.Redis 的哨兵机制是什么? 主观下线和客观下线 哨兵leader如何选出来的&#x…...

【CXX-Qt】4.1 extern “RustQt“

QObjects Properties Methods Signals #[cxx_qt::bridge] mod ffi {extern "RustQt" {} }extern “RustQt” 部分是 CXX-Qt 桥接的核心&#xff0c;用于声明 Rust 类型和签名&#xff0c;使其可用于 Qt 和 C。 CXX-Qt 代码生成器使用你的 extern “RustQt” 部…...

当 0 编程基础,用 ChatGPT 和 Cursor 开发同一应用时… |AI 开发初体验

求人不如求己。 事情是这样的&#xff0c;前段时间&#xff0c;我看了本书&#xff0c;书里介绍了款应用&#xff0c;能计算财富自由价格&#xff0c;还能制定退休计划。 结果&#xff0c;我迫不及待去下载这个应用时&#xff0c;发现这应用功能残缺&#xff0c;完全不可用。 …...

如何排查C++程序的CPU占用过高的问题

文章目录 可能的原因程序设计的BUG系统资源问题恶意软件硬件问题 通常步骤一个简单的问题代码在windows平台上如何排查Windows Process ExplorerWinDBG 在Linux平台如何排查使用TOP GDBPerf 可能的原因 程序设计的BUG 有死循环低效算法与数据结构滥用自旋锁频繁的系统调用&a…...

数据库练习

完善t_hero表 -- 添加作者字段 alter table t_hero add author varchar(100);-- 更新数据update t_hero set author "曹雪芹" where id 1; update t_hero set author "曹雪芹" where id 2; update t_hero set author "曹雪芹" where id…...

nodejs-原型污染链

还是老规矩&#xff0c;边写边学&#xff0c;先分享两篇文章 深入理解 JavaScript Prototype 污染攻击 | 离别歌 《JavaScript百炼成仙》 全书知识点整理-CSDN博客 Ctfshow web入门 nodejs篇 web334-web344_web334 ctfshow-CSDN博客 334-js审计 var express require(expr…...

无人机与AI技术结合的突破性应用场景

1. 自主导航与动态避障 技术栈&#xff1a;SLAM 强化学习 (PPO算法) 代码示例&#xff08;Python PyTorch&#xff09;&#xff1a; import torch class DronePPO(torch.nn.Module):def __init__(self):super().__init__()self.actor torch.nn.Sequential(torch.nn.Linear…...

jsBridge在vue中使用

创建jsBridge.js /* eslint-disable */ function connectWebViewJavascriptBridge (callback) {if (window.WebViewJavascriptBridge) {callback(window.WebViewJavascriptBridge)} else {document.addEventListener(WebViewJavascriptBridgeReady, function () { callback(wi…...

Windows下docker使用教程

docker安装 镜像制作镜像加载容器创建更新镜像导出镜像 Windows10安装dockerdocker image制作docker 镜像加载docker 容器创建更新imageimage 导出为.tar文件 #以Windows10 、11为例 linux和Windows区别在于docker安装的程序是哪个操作系统的&#xff0c;后面的内容其实不变 …...

iOS:GCD信号量、同步、异步的使用方法

信号量的详细用法&#xff0c;可以用此方法进行队列管理 -(void)dispatchSignal{//crate的value表示&#xff0c;最多几个资源可访问dispatch_semaphore_t semaphore dispatch_semaphore_create(3);dispatch_queue_t quene dispatch_get_global_queue(DISPATCH_QUEUE_PRIORI…...

Nginx相关漏洞解析

一、CRLF注入漏洞 原理&#xff1a;Nginx将传入的url进行解码&#xff0c;对其中的%0a%0d替换成换行符&#xff0c;导致后面的数据注入至头部&#xff0c;造成CRLF 注入漏洞 1、开环境 2、访问网站&#xff0c;并抓包 3、构造请求头 %0ASet-cookie:JSPSESSID%3D1 这样就可以…...

SpringCloud构建一个服务步骤

Spring Cloud是一个用于构建分布式系统的开源框架&#xff0c;可以帮助开发者快速构建各种云原生应用。下面是一个简单的步骤&#xff0c;展示如何使用Spring Cloud构建一个服务&#xff1a; 创建一个Spring Boot项目&#xff1a;首先需要创建一个Spring Boot项目作为基础。可以…...

MySQL中怎么分析性能?

MySQL中主要有4种方式可以分析数据库性能&#xff0c;分别是慢查询日志&#xff0c;profile&#xff0c;Com_xxx和explain。 慢查询日志 先用下面命令查询慢查询日志是否开启&#xff0c; show variables like slow_query_log;# 一般默认都是以下结果 ---------------------…...

MinGW与使用VScode写C语言适配

压缩包 通过网盘分享的文件&#xff1a;MinGW.zip 链接: https://pan.baidu.com/s/1QB-Zkuk2lCIZuVSHc-5T6A 提取码: 2c2q 需要下载的插件 1.翻译 找到VScode页面&#xff0c;从上数第4个&#xff0c;点击扩展&#xff08;以下通此&#xff09; 搜索---Chinese--点击---安装--o…...

k8s存储介绍(五)PV与PVC

在 Kubernetes&#xff08;k8s&#xff09;中&#xff0c;持久化存储&#xff08;Persistent Storage&#xff09;是一个非常重要的概念&#xff0c;因为 Pod 本身是无状态的&#xff0c;重启后会丢失数据。为了支持有状态应用&#xff0c;Kubernetes 提供了持久化存储的机制&a…...

LangChain开发(五)消息管理与聊天历史存储

文章目录 消息存储在内存使用单参数session_id配置会话唯一键 消息持久化到redis安装redis依赖安装redis调用聊天接口&#xff0c;看Redis是否存储历史记录 裁剪消息总结记忆源码地址参考资料 消息存储在内存 我们展示一个简单的示例&#xff0c;其中聊天历史保存在内存中&…...

HTML 表单处理进阶:验证与提交机制的学习心得与进度(一)

引言 在前端开发的广袤领域中&#xff0c;HTML 表单处理堪称基石般的存在&#xff0c;是构建交互性 Web 应用不可或缺的关键环节。从日常频繁使用的登录注册表单&#xff0c;到功能多样的搜索栏、反馈表单&#xff0c;HTML 表单如同桥梁&#xff0c;紧密连接着用户与 Web 应用…...

【文献25/03/26】Hyperspectral Image Transformer Classification Networks

高光谱图像Transformer分类网络 Hyperspectral Image Transformer Classification Networks | IEEE Journals & Magazine | IEEE Xplore 摘要 高光谱图像&#xff08;HSI&#xff09;分类是地球观测任务中的一项重要工作。 卷积神经网络&#xff08;CNN&#xff09;凭借…...

数字转换(c++)

【题目描述】 如果一个数 xx 的约数和 yy &#xff08;不包括他本身&#xff09;比他本身小&#xff0c;那么 xx 可以变成 yy &#xff0c;yy 也可以变成 xx 。例如 44 可以变为 33 &#xff0c;11 可以变为 77 。限定所有数字变换在不超过 nn 的正整数范围内进行&#xff0c;…...

WPF ContentPresenter详解2

ContentPresenter与ContentControl的区别 ContentControl 和 ContentPresenter 是 WPF 中两个相关的控件&#xff0c;但它们在用途和功能上有一些关键的区别。理解这两者的区别和联系有助于更好地设计和开发用户界面。 1. 类层次结构 ContentControl&#xff1a;位于 WPF 控件…...

【git】认识git的本地仓库

1.创建本地仓库 git init2. 配置本地仓库 git config user.name xxx git config user.email xxx3. 认识本地仓库 创建完本地仓库后&#xff0c;目录下会有一个.git文件&#xff0c;这个就是本地仓库 而创建本地仓库的目录叫做工作区&#xff0c;我们不能对.git文件进行任何手…...

正则表达式基本语法和Java中的简单使用

先来个例子 public static final Pattern CHINESE_PATTERN Pattern.compile("[\\u4e00-\\u9fa5]"); / 检测字符串是否包含汉字 String text "Hello 世界"; boolean hasChinese CHINESE_PATTERN.matcher(text).find(); // 返回 true// 提取所有汉字 Mat…...

【大模型】什么是循环神经网络(RNNs)

在人工智能&#xff08;AI&#xff09;的世界里&#xff0c;**循环神经网络&#xff08;Recurrent Neural Networks, RNNs&#xff09;**是一种非常强大的工具&#xff0c;特别适合处理序列数据。无论是语言、时间序列还是音乐&#xff0c;RNNs都能帮助我们理解和预测这些数据的…...

Leetcode 交错字符串

java solution class Solution {public boolean isInterleave(String s1, String s2, String s3) {//首先获取这三个字符串的长度int m s1.length();int n s2.length();int l s3.length();if(m n ! l) return false;//创建dp数组,dp[i][j]其含义是s3的前ij个字符是否可以由…...

Vue动态绑定:文本框、单选按钮、下拉列表、多选按钮

Vue 指令系列文章: 《Vue插值:双大括号标签、v-text、v-html、v-bind 指令》 《Vue指令:v-cloak、v-once、v-pre 指令》 《Vue条件判断:v-if、v-else、v-else-if、v-show 指令》 《Vue循环遍历:v-for 指令》 《Vue事件处理:v-on 指令》 《Vue表单元素绑定:v-model 指令》…...

MySQL - 数据库基础操作

SQL语句 结构化查询语言(Structured Query Language)&#xff0c;在关系型数据库上执行数据操作、数据检索以及数据维护的标准语言。 分类 DDL 数据定义语言(Data Definition Language)&#xff0c;定义对数据库对象(库、表、列、索引)的操作。 DML 数据操作语言(Data Manip…...

从入门到精通:SQL注入防御与攻防实战——红队如何突破,蓝队如何应对!

引言&#xff1a;为什么SQL注入攻击依然如此强大&#xff1f; SQL注入&#xff08;SQL Injection&#xff09;是最古老且最常见的Web应用漏洞之一。尽管很多公司和组织都已经采取了WAF、防火墙、数据库隔离等防护措施&#xff0c;但SQL注入依然在许多情况下能够突破防线&#…...

关于优麒麟ukylin如何更换清华源以及ubuntu24.04安装gcc-i686-linux-gnu找不到包的问题

打算把这个文章当成一个调试Linux bug的汇总&#xff0c;会持续更新 1、关于优麒麟ukylin如何更换清华源 &#xff08;1&#xff09;首先打开命令行&#xff0c;切换root权限 su root 输入密码 如果第一次使用ubuntu会提示密码不正确&#xff0c;输入 sudo passwd root …...

Spring Boot 自定义 Starter 组件的技术指南

1、简述 Spring Boot 通过 Starter 机制&#xff0c;让开发者可以快速集成第三方组件。在企业级开发中&#xff0c;我们常常需要封装自己的 Starter 组件&#xff0c;以提高代码复用性&#xff0c;简化配置&#xff0c;并实现可插拔的模块化开发。 Spring Boot Starter 机制 …...

基于Spring Boot的ONLY在线商城系统设计与实现的设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…...