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. 依据数值创建
- 依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)
- 依input形状创建全0张量
torch.zeros_like(input,
dtype=None,
layout=None,
device=None,
requires_grad=False)
- 依size创建全1张量
torch.ones(*size,
out=None,
dtype=None,
layout=torch.strided,
device=None,
requires_grad=False)
- 依input形状创建全1张量
torch.ones_like(input,
dtype=None,
layout=None,
device=None,
requires_grad=False)
- 依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)
- 依input形状创建全为fill_value的张量
torch.ones_like(input,
fill_value,
dtype=None,
layout=None,
device=None,
requires_grad=False)
- 创建等值数列
torch.arange(start=0, # 区间为[start, end)
end,
step,
out=None,
dtype=None,
layout=None,
device=None,
requires_grad=False)
- 创建均分数列
torch.linspace(start, # 区间为[start, end]
end,
steps=100, # 数列长度,而不是步长,步长=(end-start)/(steps-1)
out=None,
dtype=None,
layout=None,
device=None,
requires_grad=False)
- 创建对数数列
torch.logspace(start,
end,
steps=100,
base=10.0,
out=None,
dtype=None,
layout=None,
device=None,
requires_grad=False)
- 创建单位对角矩阵(2维张量)
torch.eye(n, # 矩阵行数
m=None, # 矩阵列数(默认等于行数)
out=None,
dtype=None,
layout=None,
device=None,
requires_grad=False)
4. 依据概率分布创建
- 生成正态分布
torch.normal(mean, std, out=None)
# 当mean和std全为标量时,要加一个size
torch.normal(mean, std, size, out=None)
# 比如:
torch.normal(0., 1., size=(4, ))
- 生成标准正态分布
torch.randn(*size,
out=None,
dtype=None,
layout=None,
device=None,
requires_grad=False)
torch.randn_like() # (mean=0 std=1的)
- 在区间[0, 1)上生成均匀分布
torch.rand(*size,
out=None,
dtype=None,
layout=None,
device=None,
requires_grad=False)
torch.rand_like()
- 在区间[low, high)上生成整数均匀分布
torch.randint(low=10,
high,
size,
out=None,
dtype=None,
layout=None,
device=None,
requires_grad=False)
torch.randint_like()
- 生成从0到n-1的随机排列
torch.randperm(n, # n为张量长度,常用来生成索引
out=None,
dtype=None,
layout=None,
device=None,
requires_grad=False)
- 以input为概率,生成伯努利分布(0-1分布/两点分布)
torch.bernoulli(input, # 概率值
*,
generator=None,
out=None)
Tensor的属性
获取张量的形状:tensor.shape
获取张量的数据类型:tensor.dtype
获取存储张量的设备:tensor.device
关于张量的属性更细致的内容参考 官方文档 Tensor Attributes API
Tensor的操作
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
- 在新创建的维度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维了,原维度往后的全部后移了一维
- 将张量按维度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))
- 将张量按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. 张量索引
- 在维度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))
- 按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])
- 类似 numpy 的索引
选择第一行:tensor[0]
选择第一列:tensor[:, 0]
选择最后一列:tensor[…, -1]
将第二列赋值为0:tensor[:, 1] = 0
3. 张量变换
- 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)))
- 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)
- 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,所以不会被移除
- 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()
- 前三个有什么区别呢?
- torch.add()
torch.add(input, # input:第一个张量
alpha=1, # alpha:乘项因子
other, # other:第二个张量
out=None)
功能:逐元素计算,计算的是一个先乘后加的运算
- torch.addcdiv() 实现
- torch.addcmul() 实现
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)