Pytorch(笔记2)

广播机制

在某些情况下,即使形状不同,我们仍然可以通过调用 广播机制(broadcasting mechanism)来执行按元素操作。这种机制的工作方式如下:

  1. 通过适当复制元素来扩展一个或两个数组,以便在转换之后,两个张量具有相同的形状;
  2. 对生成的数组执行按元素操作。

你尝试对两个形状不匹配的张量进行元素级(element-wise)操作时,如加法、减法、乘法等,PyTorch会尝试进行广播(broadcasting)以使得这两个张量在形状上兼容。广播是一种强大的机制,它允许NumPy和PyTorch等库对形状不同的数组进行数值计算。

a = torch.arange(3).reshape((3, 1))
b = torch.arange(4).reshape((1, 4))
print(a+b)
tensor([[0, 1, 2, 3],
        [1, 2, 3, 4],
        [2, 3, 4, 5]]) 
# 注释总结:  
# a和b的形状分别是(3, 1)和(1, 4),但由于PyTorch的广播规则,我们可以将它们相加。  
# 结果是一个(3, 4)的张量,其中每个元素都是a和b中对应元素的和。

索引和切片

在任何其他Python数组中一样,张量中的元素可以通过索引访问。与任何Python数组一样:第一个元素的索引是0,最后一个元素索引是‐1;可以指定范围以包含第一个元素和最后一个之前的元素。

我们可以用[-1]选择最后一个元素,可以用[1:3]选择第二个和第三个元素:

import torch  
  
# 创建一个从0到15(包括15)的一维张量,并将其重新整形为4x4的二维张量  
a = torch.arange(16).reshape((4, 4))  
  
# 打印a的最后一行,即索引为3的行  
print(a[-1])  # 输出:tensor([12, 13, 14, 15])  
  
# 打印a的前三行,即索引为0, 1, 2的行  
print(a[0:3])  # 输出:  
# tensor([[ 0,  1,  2,  3],  
#         [ 4,  5,  6,  7],  
#         [ 8,  9, 10, 11]])  
  
# 打印a的第三行,即索引为2的行  
print(a[2])  # 输出:tensor([ 8,  9, 10, 11])  
  
# 打印a中索引为(2, 3)的元素,即第三行第四列的元素  
print(a[2,3])  # 输出:tensor(11)  
  
# 打印a中从第三行到最后一行的所有行(即索引为2, 3的行)  
print(a[2:,:])  # 输出:  
# tensor([[ 8,  9, 10, 11],  
#         [12, 13, 14, 15]])  
  
# 打印a中从第一行到第二行的所有行(即索引为0, 1的行),以及这些行的所有列  
print(a[0:2,:])  # 输出:  
# tensor([[0, 1, 2, 3],  
#         [4, 5, 6, 7]])

节省内存

运行一些操作可能会导致为新结果分配内存。例如,如果我们用Y = X + Y,我们将取消引用Y指向的张量,而是指向新分配的内存处的张量。

import torch  
import numpy as np  # 注意:虽然导入了numpy,但在这段代码中并没有使用numpy  
  
a = torch.arange(16).reshape((4, 4))  
b = torch.arange(16).reshape((4, 4))  
  
# 记录a的原始id(内存地址)  
before = id(a)  
 
c = a + b  
# 打印c的内存地址是否与a的原始内存地址相同  
# 因为+运算符在PyTorch中返回一个新的张量,所以id(c)与before是不同的  
print(id(c) == before)  # 输出应为False  
  
# 使用加法赋值运算符+=修改a的值,使其为a和b的和  
a += b  
# 打印a的内存地址是否与其原始内存地址相同  
# 在PyTorch中,+=运算符可能会原地修改a(取决于具体的实现和上下文),但即使原地修改,  
# Python的id函数也可能不会改变(因为对象本身还在同一个内存位置),但内部数据可能已经改变  
print(id(a) == before)  # 输出可能为True(但这不保证在所有PyTorch版本和所有环境中都如此)  
  
# 使用切片操作[:]来“原地”修改a的值,但实际上这仍然是一个赋值操作  
# 它首先计算a+b的结果,然后将这个结果赋值给a的切片(即整个a)  
# 这与+=的效果类似,但它更明确地表示了我们想要替换整个a的内容  
a[:] = a + b  
# 再次打印a的内存地址是否与其原始内存地址相同  
# 同样,id(a)可能保持不变,但a的内容已经改变  
print(id(a) == before)  # 输出可能为True(但这不保证在所有PyTorch版本和所有环境中都如此)

PyTorch张量(tensor)和NumPy数组(array)之间的转换

import torch  # 导入PyTorch库  
  
a = torch.arange(16).reshape((4, 4))  
  
# 使用a的.numpy()方法将其从PyTorch张量转换为NumPy数组  
c = a.numpy()  
print(type(c))  # 打印c的类型,输出应为<class 'numpy.ndarray'>,表示c是一个NumPy数组  
  
# 使用torch.from_numpy(c)将NumPy数组c转换回PyTorch张量  
c = torch.from_numpy(c)  
print(type(c))  # 打印c的类型,输出应为<class 'torch.Tensor'>,表示c现在是一个PyTorch张量  
  
# 注释总结:  
# 这段代码展示了如何在PyTorch张量和NumPy数组之间进行转换。  
# 首先,使用.numpy()方法将PyTorch张量转换为NumPy数组。  
# 然后,使用torch.from_numpy()函数将NumPy数组转换回PyTorch张量。  
# 转换过程中,数据的值保持不变,但数据类型(即Python中的类)会发生变化。

思考

当使用三维张量(或其他更高维度的张量)替换广播机制中按元素操作的两个张量时,结果是否与预期相同?

import torch  
  
# 创建一个形状为 (2, 1, 3) 的三维张量 a  
a = torch.arange(6).reshape((2, 1, 3))  
print(a)  
# tensor([[[0, 1, 2]],  
#         [[3, 4, 5]]])  
  
# 创建一个形状为 (1, 4, 1) 的三维张量 b  
b = torch.arange(4).reshape((1, 4, 1)) * 10  
print(b)  
# tensor([[[ 0],  
#          [10],  
#          [20],  
#          [30]]])  
  
# 尝试将 a 和 b 相加  
c = a + b  
  
# 广播规则将允许这样的加法,因为:  
# 1. 在第一个维度上(从0开始),a 有 2,b 有 1,1 可以广播到 2  
# 2. 在第二个维度上,a 有 1,b 有 4,1 可以广播到 4  
# 3. 在第三个维度上,a 有 3,b 有 1,1 可以广播到 3  
  
print(c)  
# tensor([[[ 0, 10, 20],  
#          [ 1, 11, 21],  
#          [ 2, 12, 22],  
#          [ 3, 13, 23]],  
#  
#         [[ 3, 13, 23],  
#          [ 4, 14, 24],  
#          [ 5, 15, 25],  
#          [ 6, 16, 26]]])

归纳起来,确保两个张量大小匹配或满足广播规则的方法有:

  • 确保两个张量的形状完全相同。
  • 如果两个张量的形状在某些维度上不同,检查是否满足广播规则,即这些维度的大小是否相等或其中一个大小为1。
  • 如果不满足上述条件,你可能需要调整张量的形状(如使用reshape、view、squeeze、unsqueeze等方法)或重新考虑你的操作,以确保它们能够兼容。

自律每一天,fighting

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值