一、Pytorch张量及基本数据类型
1.张量Tensor是Pytorch最基本的操作对象,它表示一个多维矩阵。标量可以称为0维张量,向量可以称为1维张量,矩阵可以称为2维张量,RGB图像可以表示3维张量。张量类似于Numpy的数组,但张量可以在GPU上使用以加速计算。
2.dtype:张量的数据类型,如torch.FloatTensor。Pytorch提供了9种数据类型,共分为3大类:float(16-bit, 32-bit, 64-bit)、integer(unsigned-8-bit, 8-bit, 16-bit, 32-bit, 64-bit)、Boolean。模型参数和数据用的最多类型是float-32-bit。labell常用的类型是integer-64-bit。
Date type | dtype |
32-bit floating point | torch.float32/torch.float |
64-bit floating point | torch.float64/torch.double |
16-bit floating point | torch.float16/torch.half |
8-bit integer(unsigned) | torch.uint8 |
8-bit integer(signed) | torch.int8 |
16-bit integer(signed) | torch.int16/torch.short |
32-bit integer(signed) | torch.int32/torch.int |
64-bit integer(signed) | torch.int64/torch.long |
Boolean | torch.bool |
3.张量的类型转换
- 张量类型的隐式转化 。其和numpy中的array相同,当张量各元素属于不同类型时,系统会自动进行隐式转化。
#浮点型和整数型的隐式转化 torch.tensor([1.1, 2]).dtype 结果为:torch.float32 #布尔型和数值型的隐式转化 torch.tensor([True, 2.0]) 结果为:tensor([1., 2.])
- 张量类型的转化方法。还可以通过.float()、.int()等方法对张量类型进行转化。但是并不会改变张量的原始类型,因为只是调用的张量的方法。
t = torch.tensor(np.array([1, 2])) print(t) 结果为:tensor([1,2]) #转化为默认浮点型 t.float() 结果为:tensor([1., 2.]) #转化为双精度浮点型 t.double() 结果为:tensor([1., 2.], dtype=torch.float64) #并不改变张量的原始类型 t.dtype 结果为:torch.int64 #转化为16位整数型 t.short() 结果为:tensor([1, 2], dtype=torch.int16)
二、张量的创建
1.通过torch.tensor()方法创建张量,可通过多种形式创建,如下:
(1)通过列表创建
t = torch.tensor([1, 2])
(2)通过元组创建
t = torch.tensor((1, 2))
(3)通过numpy数组创建
import numpy as np
a = np.array((1, 2)) #a是一个numpy多维数组
t = torch.tensor(a)
在使用torch.tensor()方法创建张量时会发生数据的复制行为。
2.生成随机矩阵
- 构造一个随机初始化的矩阵:torch.rand()
- 全0矩阵:torch.zeros()
- 全1矩阵:torch.ones()
代码如下(示例):
import torch
#0-1之间均匀分布随机数,2行3列数组
x = torch.rand(2, 3)
print(x)
#0-1之间正态分布随机数,3行4列数组
y = torch.randn(3, 4)
print(y)
#全0数组
z = torch.zeros(2, 3)
print(z)
#全1数组,三维数组,可以理解为两个3*4的矩阵
q = torch.ones(2, 3, 4)
print(q)
三、张量运算
索引切片
- 规则索引切片方式。张量的索引切片方式和numpy、python多维列表几乎一致,都可以通过索引和切片对部分元素进行修改。切片表达式:[start:end:step]
表达式的意义是,从start开始到end结束,每隔step个进行采样。通过[start:end]形式索引,这种方式是左闭右开的方式[start:end),左边默认是从0开始索引。注意负值的索引即表示倒数第几个元素,例如-2就是倒数第二个元素。代码如下(示例):
t = torch.randint(1, 10, [3, 3])
print(t)
结果为:tensor([[8, 2, 9],
[2, 5, 9],
[3, 9, 9]])
#第一行数据
t[0]
结果为:tensor([8, 2, 9])
#第三行第三列数据
t[2][2]
结果为:tensor(9)
#第一行至第三行,全部列
t[0:3, :]
结果为:tensor([[8, 2, 9],
[2, 5, 9],
[3, 9, 9]])
#第一行至第二行
t[0:2, :]
结果为:tensor([[8, 2, 9],
[2, 5, 9]])
#第二行至最后行,最后一列
t[1: , -1]
结果为:tensor([9, 9])
#第一行至最后行,第0列到最后一列每隔两列取一列
t[1: , : : 2]
结果为:tensor([[2, 9],
[3, 9]])
- 对于不规则的切片提取。gather和torch.index_select算子
gather函数定义如下:torch.gather(input, dim, index, *, sparse_grad=False, out=None) -> Tensor
输入input和索引index具有相同数量的维度,即input.shape = index.shape。对于任意维数,只要d != dim, index.size(d) <= input.size(d),即对于可以不用索引维数d上的全部数据。输出out和索引index具有相同的形状。输入和索引不会相互广播。
gather是通过将索引在指定维度dim上的值替换为index的值,但是其他维度索引不变的情况下获取tensor数据。直观上可以理解为对矩阵进行重排,比如对每一行(dim=1)的元素进行变换,比如torch.gather(a, 1, torch.tensor([[1, 2, 0], [1, 2, 0]]))的作用就是对矩阵a每一行的元素,进行permtute(1, 2, 0)操作。
torch.index_select函数作用是返回沿着输入张量的指定维度的指定索引号进行索引的张量子集。函数定义如下:torch.index_select(input, dim, index, *, out=None) -> Tensor
函数返回一个新的张量,它使用数据类型为LongTensor的index中的条目沿维度dim索引输入张量。返回的张量具有与原始张量(输入)相同的维数。维度尺寸与索引长度相同,其他尺寸与原始张量中的尺寸相同。代码如下(示例):
x = torch.randn(3, 4)
x
tensor([[0.1427, 0.0231, -0.5414, -1.0009],
[-0.4664, 0.2647, -0.1228, -1.1068],
[-1.1734, -0.6571, 0.7230, -0.6004]])
indices = torch.tensor([0, 2])
torch.index_select(x, 0, indices)
tensor([[0.1427, 0.0231, -0.5414, -1.0009],
[-1.1734, -0.6571, 0.7230, -0.6004]])
torch.index_select(x, 1, indices)
tensor([[0.1427, -0.5414],
[-0.4664, -0.1228],
[-1.1734, 0.7230]])
2.合并分割
- torch.cat和torch.stack。可以用torch.cat方法和torch.stack方法将多个张量合并,但两者有略微的区别,torch.cat是连接,不会增加维度,而torch.stack是堆叠,会增加一个维度。两者函数定义如下:torch.cat(tensors, dim=0, *, out=None) -> Tensor; torch.stack(tensors, dim=0, *, out=None) -> Tensor。代码如下(示例):
>>> a = torch.arange(0,9).view(3,3) >>> b = torch.arange(10,19).view(3,3) >>> c = torch.arange(20,29).view(3,3) >>> cat_abc = torch.cat([a,b,c], dim=0) >>> print(cat_abc.shape) torch.Size([9, 3]) >>> print(cat_abc) tensor([[ 0, 1, 2], [ 3, 4, 5], [ 6, 7, 8], [10, 11, 12], [13, 14, 15], [16, 17, 18], [20, 21, 22], [23, 24, 25], [26, 27, 28]]) >>> stack_abc = torch.stack([a,b,c], axis=0) # torch中dim和axis参数名可以混用 >>> print(stack_abc.shape) torch.Size([3, 3, 3]) >>> print(stack_abc) tensor([[[ 0, 1, 2], [ 3, 4, 5], [ 6, 7, 8]], [[10, 11, 12], [13, 14, 15], [16, 17, 18]], [[20, 21, 22], [23, 24, 25], [26, 27, 28]]]) >>> chunk_abc = torch.chunk(cat_abc, 3, dim=0) >>> chunk_abc (tensor([[0, 1, 2], [3, 4, 5], [6, 7, 8]]), tensor([[10, 11, 12], [13, 14, 15], [16, 17, 18]]), tensor([[20, 21, 22], [23, 24, 25], [26, 27, 28]]))
-
torch.split和torch.chunk。torch.split()和torch.chunk()可以看作是torch.cat()的逆运算。split()作用是将张量拆分为多个块,每个块都是原始张量的视图。chunk()作用是将tensor按dim(行或列)分割成chunks个tensor块,返回的是一个元组。split()和chunk()函数的定义如下:torch.split(tensor, split_size_or_sections, dim=0); torch.chunk(input, chunks, dim=0)。代码如下(示例):
>>> a = torch.arange(10).reshape(5,2) >>> a tensor([[0, 1], [2, 3], [4, 5], [6, 7], [8, 9]]) >>> torch.split(a, 2) (tensor([[0, 1], [2, 3]]), tensor([[4, 5], [6, 7]]), tensor([[8, 9]])) >>> torch.split(a, [1,4]) (tensor([[0, 1]]), tensor([[2, 3], [4, 5], [6, 7], [8, 9]])) >>> torch.chunk(a, 2, dim=1) (tensor([[0], [2], [4], [6], [8]]), tensor([[1], [3], [5], [7], [9]]))
3.维度变换
3.1、squeeze\unsqueeze维度增减
- squeeze():对tensor进行维度的压缩,去掉维数为1的维度。用法:torch.squeeze(a)将a中所有为1的维度都删除,或者a.squeeze(1)是去掉a中指定的维数为1的维度。
- unsqueeze():对数据维度进行扩充,给指定位置加上维数为1的维度。用法:torch.unsqueeze(a, N),或者a.unsqueeze(N),在a中指定位置N加上一个维数为1的维度。代码如下(示例):
#squeeze用例 a = torch.rand(1, 1, 3, 3) b = torch.squeeze(a) c = a.squeeze(1) print(b.shape) torch.Size([3, 3]) print(c.shape) torch.Size([1, 3, 3]) #unsqueeze用例 x = torch.rand(3, 3) y1 = torch.unsqueeze(x, 0) y2 = x.unsqueeze(0) print(y1.shape) torch.Size([1, 3, 3]) print(y2.shape) torch.Size([1, 3, 3])
3.2、transpose\permute 维度交换
- torch.transpose()只能交换两个维度,而torch.permute()可以自由交换任意位置。代码如下(示例):
import torch input = torch.rand(1,3,28,32) # torch.Size([1, 3, 28, 32] print(b.transpose(1, 3).shape) # torch.Size([1, 32, 28, 3]) print(b.transpose(1, 3).transpose(1, 2).shape) # torch.Size([1, 28, 32, 3]) print(b.permute(0,2,3,1).shape) # torch.Size([1, 28, 32, 3]