Tensor张量

  • 什么是Tensor
  • Tensor的创建
  • 1. 直接创建
  • 2. 通过numpy创建
  • 3. 依据数值创建
  • 4. 依据概率分布创建
  • Tensor的属性
  • Tensor的操作
  • 1. 张量拼接与切分
  • 2. 张量索引
  • 3. 张量变换
  • 4. 将张量移动到GPU
  • Tensor的数学运算
  • 1. 加减乘除
  • 2. 对、指、幂函数
  • 3. 三角函数


什么是Tensor

张量是一种特殊的数据结构,与数组和矩阵非常相似。在 PyTorch 中,我们使用张量对模型的输入和输出以及模型的参数进行编码。
张量相当于一个多维数组。

torch.Tensor(dtype, shape, device, data, requires_grad, 
			grad, grad_fn, is_leaf)

requires_grad:指示是否需要梯度
grad:data的梯度
grad_fn:创建Tensor的function
is_leaf:是否是叶子节点(张量)

Tensor的创建

1. 直接创建

张量可以直接从数据中创建。数据类型是自动推断的。
从data创建,可以是list,numpy。

torch.tensor(data,
			dtype=None,  # 默认与data一致
			device=None,  # cuda/cpu
			requires_grad=False,  # 是否需要梯度
			pin_memory=False)  # 是否存于锁页内存,通常为False

例如:

data = [[1, 2],[3, 4]]
x_data = torch.tensor(data)

2. 通过numpy创建

从numpy创建的tensor源于ndarray共享内存,当修改其中一个的数据,另一个也会被改动。

np_array = np.array(data)
x_np = torch.from_numpy(np_array)
  • 让tensor和numpy共享底层内存
t = torch.ones(5)
print(f"t: {t}")
n = t.numpy()
print(f"n: {n}")  # n和t是一样的
t.add_(1)
print(f"t: {t}")
print(f"n: {n}")  # t更改后,n的值依然跟t一样

3. 依据数值创建

  1. 依size创建全0张量
torch.zeros(*size,  # 如(3,3)、(3,224,224)
			out=None,  # 输出的张量,将生成张量赋值给一个张量
			dtype=None,
			layout=torch.strided,  # 内存中的布局形式,有strided、sparse_coo等
			device=None,
			requires_grad=False)
  1. 依input形状创建全0张量
torch.zeros_like(input,
				dtype=None,
				layout=None,
				device=None,
				requires_grad=False)
  1. 依size创建全1张量
torch.ones(*size,
			out=None,
			dtype=None,
			layout=torch.strided,
			device=None,
			requires_grad=False)
  1. 依input形状创建全1张量
torch.ones_like(input,
				dtype=None,
				layout=None,
				device=None,
				requires_grad=False)
  1. 依size创建全为fill_value的张量
torch.full(*size,
			fill_value,
			out=None,
			dtype=None,
			layout=torch.strided,
			device=None,
			requires_grad=False)
# 比如:
torch.full((3, 3), 10)
  1. 依input形状创建全为fill_value的张量
torch.ones_like(input,
				fill_value,
				dtype=None,
				layout=None,
				device=None,
				requires_grad=False)
  1. 创建等值数列
torch.arange(start=0,  # 区间为[start, end)
			end,
			step,
			out=None,
			dtype=None,
			layout=None,
			device=None,
			requires_grad=False)
  1. 创建均分数列
torch.linspace(start,  # 区间为[start, end]
				end,
				steps=100,  # 数列长度,而不是步长,步长=(end-start)/(steps-1)
				out=None,
				dtype=None,
				layout=None,
				device=None,
				requires_grad=False)
  1. 创建对数数列
torch.logspace(start,
				end,
				steps=100,
				base=10.0,
				out=None,
				dtype=None,
				layout=None,
				device=None,
				requires_grad=False)
  1. 创建单位对角矩阵(2维张量)
torch.eye(n,  # 矩阵行数
		m=None,  # 矩阵列数(默认等于行数)
		out=None,
		dtype=None,
		layout=None,
		device=None,
		requires_grad=False)

4. 依据概率分布创建

  1. 生成正态分布
torch.normal(mean, std, out=None)
# 当mean和std全为标量时,要加一个size
torch.normal(mean, std, size, out=None)
# 比如:
torch.normal(0., 1., size=(4, ))
  1. 生成标准正态分布
torch.randn(*size, 
			out=None,
			dtype=None,
			layout=None,
			device=None,
			requires_grad=False)
torch.randn_like()  # (mean=0 std=1的)
  1. 在区间[0, 1)上生成均匀分布
torch.rand(*size,
			out=None,
			dtype=None,
			layout=None,
			device=None,
			requires_grad=False)

torch.rand_like()
  1. 在区间[low, high)上生成整数均匀分布
torch.randint(low=10,
			high,
			size,
			out=None,
			dtype=None,
			layout=None,
			device=None,
			requires_grad=False)

torch.randint_like()
  1. 生成从0到n-1的随机排列
torch.randperm(n,  # n为张量长度,常用来生成索引
			out=None,
			dtype=None,
			layout=None,
			device=None,
			requires_grad=False)
  1. 以input为概率,生成伯努利分布(0-1分布/两点分布)
torch.bernoulli(input,  # 概率值
				*, 
				generator=None, 
				out=None)

Tensor的属性

获取张量的形状:tensor.shape
获取张量的数据类型:tensor.dtype
获取存储张量的设备:tensor.device

关于张量的属性更细致的内容参考 官方文档 Tensor Attributes API

Tensor的操作

1. 张量拼接与切分

  1. 将张量按维度dim拼接
torch.cat(tensors,  # 张量序列
		dim=0,  # 要拼接的维度
		out=None)

比如:

t = torch.ones((2, 3))
t_0 = torch.cat([t, t], dim=0)  # ([4, 3]) 在2这个维度上拼接,2+2=4
t_1 = torch.cat([t, t], dim=1)  # ([2, 6]) 3+3=6
t_2 = torch.cat([t, t, t], dim=1)  # ([2, 9]) 3+3+3=9
  1. 在新创建的维度dim上进行拼接
    所有张量大小必须相同
torch.stack(tensors, dim=0, out=None)

比如:

t = torch.ones((2, 3))
t_3 = torch.stack([t, t], dim=2)  # ([2, 3, 2]) 在后面加了一个维度
t_4 = torch.stack([t, t], dim=0)  # ([2, 2, 3]) 因为已有0维了,原维度往后的全部后移了一维
  1. 将张量按维度dim进行平均切分
torch.chunk(input,  # 要切分的张量
			chunks,  # 要切分的份数
			dim=0)  # 要切分的维度
# 返回值:张量列表(若不能整除,最后一份张量小于其他张量)

比如:

a = torch.ones((2, 7))  # 先把7/3向上取整得到3,前几份每份为3
list_of_tensors = torch.chunk(a, dim=1, chunks=3)  # ([2, 3]), ([2, 3]), ([2, 1])
for idx, t in enumerate(list_of_tensors):
	print("第{}个张量:{}, shape is {}".format(idx+1, t, t.shape))
  1. 将张量按dim维度切分
torch.split(tensor,  # 要切分的张量
			split_size_or_sections,  # 为int时,表示每一份的长度;为list时,按list元素切分
			dim=0)

比如:

t = torch.ones((2, 5))
list_of_tensors = torch.split(t, [2, 1, 2], dim=1)  # ([2, 2]), ([2, 1]), ([2, 2])
for idx, t in enumerate(list_of_tensors):
	print("第{}个张量:{}, shape is {}".format(idx+1, t, t.shape))
list_of_tensors = torch.split(t, [2, 1, 1], dim=1)  # 2+1+1 != 5,会报错

2. 张量索引

  1. 在维度dim上,按index索引数据
torch.index_select(input,  # 要索引的张量
				dim,  # 要索引的维度
				index,  # 要索引数据的序号
				out=None)
# 返回值:依index索引数据拼接的张量

比如:

t = torch.randint(0, 9, size=(3, 3))  # tensor([[4, 5, 0],
#												[5, 7, 1],
#												[2, 5, 8]])
idx = torch.tensor([0, 2], dtype=torch.long)    # 64位整型,必须这样设定
t_select = torch.index_select(t, dim=0, index=idx)  # tensor([[4, 5, 0],															#															  [2, 5, 8]])
print("t:\n{}\nt_select:\n{}".format(t, t_select))
  1. 按mask中的True索引
torch.masked_select(input,  # 要索引的张量
					mask,  # 与input同形状的布尔类型张量
					out=None)
# 返回值:一维张量
# 因为不能确定有多少元素是True,所以不能确定返回的形状,就统一返回一维张量

比如:

t = torch.randint(0, 9, size=(3, 3))
mask = t.ge(5)  # 生成>=5的布尔型张量(形状不变)
# ge:大于等于>=   gt: 大于>  le:小于等于<=  lt:小于<
t_select = torch.masked_select(t, mask)  # tensor([5, 5, 7, 5, 8])
  1. 类似 numpy 的索引
    选择第一行:tensor[0]
    选择第一列:tensor[:, 0]
    选择最后一列:tensor[…, -1]
    将第二列赋值为0:tensor[:, 1] = 0

3. 张量变换

  1. torch.reshape()
torch.reshape(input,   # input:要变换的张量
			  shape)  # shape:新张量的形状

功能:变换张量形状
注意事项:当张量在内存中是连续时,新张量与input共享数据内存

t = torch.randperm(8)
    t_reshape = torch.reshape(t, (2, 4))    # 大小要与之前的匹配,否则会报错
    # t_reshape = torch.reshape(t, (-1, 2, 2))  # -1的意思是不需要关心这个维度,根据别的维度计算而来
    print("t:{}\nt_reshape:\n{}".format(t, t_reshape))

    t[0] = 1024
    print("t:{}\nt_reshape:\n{}".format(t, t_reshape))
    print("t.data 内存地址:{}".format(id(t.data)))  # id()是获取内存地址
    print("t_reshape.data 内存地址:{}".format(id(t_reshape.data)))
  1. torch.transpose()
torch.transpose(input,  # input:要变换的张量
				dim0,  # dim0、dim1:要交换的维度
				dim1)

功能:交换张量的两个维度
例如:

# torch.transpose
    t = torch.rand((2, 3, 4))
    t_transpose = torch.transpose(t, dim0=1, dim1=2)    # c*h*w     h*w*c
    print("t shape:{}\nt_transpose shape: {}".format(t.shape, t_transpose.shape))

经常在图像前期的预处理中使用,比如读取进来的图像可能是chw的,把它变换成hwc,把channel放到最后
3. torch.t()

torch.t(input)

功能:二维张量的转置,对矩阵而言,等价于

torch.transpose(input, 0, 1)
  1. torch.squeeze()
torch.squeeze(input,
			  dim=None,
			  out=None)

功能:压缩长度为1的维度(轴)
dim若为None,移除所有长度为1的轴;若指定维度,当且仅当该轴长度为1时,可以被移除
例如:

t = torch.rand((1, 2, 3, 1))
    t_sq = torch.squeeze(t)  # 移除后形状变为([2, 3])
    t_0 = torch.squeeze(t, dim=0)  # ([2, 3, 1])
    t_1 = torch.squeeze(t, dim=1)  # ([1, 2, 3, 1]),由于第一个轴的长度不是1,所以不会被移除
  1. torch.unsqueeze()
torch.unsqueeze(input,
				dim,  # 扩展的维度
				out=None)

功能:依据dim扩展维度,dim上的长度为1
比如要扩展形状为([2, 3])的张量,dim的取值范围使[-3, 2],不能在3及以上的维度扩展。

4. 将张量移动到GPU

# We move our tensor to the GPU if available
if torch.cuda.is_available():
    tensor = tensor.to("cuda")

Tensor的数学运算

1. 加减乘除

  • 有如下这些常用函数:
torch.add()
torch.addcdiv()
torch.addcmul()
torch.sub()
torch.div()
torch.mul()
  • 前三个有什么区别呢?
  1. torch.add()
torch.add(input,  # input:第一个张量
		  alpha=1,  # alpha:乘项因子
		  other,  # other:第二个张量
		  out=None)

功能:逐元素计算pytorch 创建张量能否用表达式 pytorch张量操作_python,计算的是一个先乘后加的运算

  1. torch.addcdiv() 实现pytorch 创建张量能否用表达式 pytorch张量操作_pytorch_02
  2. torch.addcmul() 实现pytorch 创建张量能否用表达式 pytorch张量操作_pytorch_03
torch.addcmul(input,
			  value=1,
			  tensor1,
			  tensor2,
			  out=None)
  • 关于乘法运算,@和torch.matmul()功能一样,*和torch.mul()功能一样
# This computes the matrix multiplication between two tensors. y1, y2, y3 will have the same value
# ``tensor.T`` returns the transpose of a tensor
y1 = tensor @ tensor.T
y2 = tensor.matmul(tensor.T)

y3 = torch.rand_like(y1)
torch.matmul(tensor, tensor.T, out=y3)


# This computes the element-wise product. z1, z2, z3 will have the same value
z1 = tensor * tensor
z2 = tensor.mul(tensor)

z3 = torch.rand_like(tensor)
torch.mul(tensor, tensor, out=z3)
  • 单元素张量可以通过item()转化成Python数值
tensor = torch.ones(4, 4)
agg = tensor.sum()
agg_item = agg.item()
print(agg_item, type(agg_item))
  • In-place操作
    函数的后缀是下划线_,表示这个函数是就地操作的(结果存储到操作数中)
    例如:x.t_()x.copy_()x.add_() 就地操作可以节省一些内存,但在计算导数时可能会出现问题,因为会立即丢失历史计算数据,所以不推荐使用。

2. 对、指、幂函数

torch.log(input, out=None)
torch.log10(input, out=None)
torch.log2(input, out=None)
torch.exp(input, out=None)
torch.pow()

3. 三角函数

torch.abs(input, out=None)
torch.acos(input, out=None)
torch.cosh(input, out=None)
torch.cos(input, out=None)
torch.asin(input, out=None)
torch.atan(input, out=None)
torch.atan2(input, other, out=None)