参考:《20天吃掉那只pytorch》
4、张量
张量是pytroch中最重要的数据类型,神经网络中操作的数据都是张量。输入的图片是一个张量,中间的隐藏层也是张量,最后输出的结果也是张量。
所以懂得张量的基本操作就成了pytroch的基本功。
张量是一个多维数组,维度可以从0到n
如果维度为0那么就是一个常数,如果维度为1那么就是一个向量,如果维度为2那么就是一个矩阵,如果维度为3就是一个立方体,如果维度为4 …
4.0 张量的介绍
Pytorch的基本数据结构是张量。 张量即多维数组。
Pytorch的张量和numpy中的array很类似。两者可以通过代码进行相互转换
本章我们主要介绍张量的数据类型、张量的维度、张量的尺寸、张量和numpy数组等基本概念。
4.0.1 张量的数据类型与创建
张量的数据类型和numpy.array基本一一对应,但是不支持str类型
包括:
torch.float64(torch.double),
torch.float32(torch.float),
torch.float16,
torch.int64(torch.long),
torch.int32(torch.int),
torch.int16,
torch.int8,
torch.uint8,
torch.bool
一般神经网络模型使用的是torch.float32类型
(1)根据传入的参数,自动指定类型
# 可以通过传入的数据,自动判断类型
import numpy as np
import torch
# 自动推断数据类型
i = torch.tensor(1)
print(i,i.dtype) # tensor(1) torch.int64
x = torch.tensor(2.0)
print(x,x.dtype) # tensor(2.) torch.float32
b = torch.tensor(True)
print(b,b.dtype) # tensor(True) torch.bool
(2)指定张量的类型
# 指定数据类型
i = torch.tensor(1,dtype = torch.int32)
print(i,i.dtype) # tensor(1, dtype=torch.int32) torch.int32
x = torch.tensor(2.0,dtype = torch.double)
print(x,x.dtype) # tensor(2., dtype=torch.float64) torch.float64
(3)使用特定类型的构造函数
注意:要大写。 且i = torch.IntTensor(1)是生成一个维度为1的随机数
i = torch.IntTensor(1)
print(i,i.dtype) # tensor([5], dtype=torch.int32) torch.int32
x = torch.Tensor(np.array(2.0))
print(x,x.dtype) #等价于torch.FloatTensor
b = torch.BoolTensor(np.array([1,0,2,0]))
print(b,b.dtype) # tensor([ True, False, True, False]) torch.bool
(4)不同类型进行转换
i = torch.tensor(1)
print(i,i.dtype) # tensor(1) torch.int64
x = i.float()
print(x,x.dtype) #调用 float方法转换成浮点类型 tensor(1.) torch.float32
y = i.type(torch.float)
print(y,y.dtype) #使用type函数转换成浮点类型 tensor(1.) torch.float32
z = i.type_as(x)
print(z,z.dtype) #使用type_as方法转换成某个Tensor相同类型 tensor(1.) torch.float32
(5)张量的创建
x = torch.empty(5,3)
x = torch.rand(5,3) ## 平均分布
x = torch.randn(5,3) ## 标准正态分布
x = torch.zeros(5,3,dtype=torch.long)
x = torch.ones(5,3) ## 全部都是1 , 构建一个5行3列的矩阵
4.0.2 张量的维度
不同类型的数据可以用不同维度(dimension)的张量来表示
标量为0维度张量
向量为1维度张量
矩阵为2维度张量
彩色图像有长、宽、以及rgb通道,可以表示为3维张量。
视频还有时间维,可以表示为4维度张量
总结起来:有几层中括号,就是多少维的张量
(1)0维张量
scalar = torch.tensor(True)
print(scalar) #tensor(True)
print(scalar.dim()) #0
(2) 1维张量
vector = torch.tensor([1.0,2.0,3.0,4.0]) #向量
print(vector) # tensor([1., 2., 3., 4.])
print(vector.dim()) # 1
(3) 2维张量
matrix = torch.tensor([[1.0,2.0],[3.0,4.0]]) #矩阵, 2维张量
print(matrix)
print(matrix.dim())
(4) 3维张量
tensor3 = torch.tensor([[[1.0,2.0],[3.0,4.0]],[[5.0,6.0],[7.0,8.0]]]) # 3维张量
print(tensor3)
print(tensor3.dim())
(5) 4维张量
tensor4 = torch.tensor([[[[1.0,1.0],[2.0,2.0]],[[3.0,3.0],[4.0,4.0]]],
[[[5.0,5.0],[6.0,6.0]],[[7.0,7.0],[8.0,8.0]]]]) # 4维张量
print(tensor4)
print(tensor4.dim())
4.0.3 张量的尺寸(改变、交换、扩展)
可以使用shape属性,或者size()方法查看张量在每一维的长度
可以使用view方法改变张量的尺寸
如果view方法改变尺寸失败,可以使用reshape方法
注!!view操作有时会报错
当张量tensor使用view操作时会报错。因为tensor在内存中存储并不是连续的(比如经过转置操作),但是使用view时,必须保证tensor在内存中是连续的,这就会导致错误。解决方法有两种
(1)使用reshape() reshape并没有要求连续
(2)使用contiguous()方法, 让tensor变成连续的 a.contiguous().view(...)
(1)查看张量的尺寸
scalar = torch.tensor(True)
print(scalar.size()) #torch.Size([]) 表示0维
print(scalar.shape) #torch.Size([])
vector = torch.tensor([1.0,2.0,3.0,4.0])
print(vector.size()) #torch.Size([4])
print(vector.shape) #torch.Size([4])
matrix = torch.tensor([[1.0,2.0],[3.0,4.0]])
print(matrix.size()) #torch.Size([2, 2])
(2) 使用view改变张量尺寸
vector = torch.arange(0,12)
print(vector) # tensor([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
print(vector.shape) #torch.Size([12])
matrix34 = vector.view(3,4)
print(matrix34)
print(matrix34.shape) # torch.Size([3, 4])
matrix43 = vector.view(4,-1) #-1表示该位置长度由程序自动推断
print(matrix43)
print(matrix43.shape) # torch.Size([4, 3])
(3) 使用reshape改变张量尺寸
有些操作会让张量存储结构扭曲,直接使用view会失败,可以用reshape方法
matrix26 = torch.arange(0,12).view(2,6)
print(matrix26)
# tensor([[ 0, 1, 2, 3, 4, 5],
# [ 6, 7, 8, 9, 10, 11]])
print(matrix26.shape) #torch.Size([2, 6])
# 转置操作让张量存储结构扭曲,变成不连续的
matrix62 = matrix26.t()
print(matrix62.is_contiguous())
# 直接使用view方法会失败,可以使用reshape方法
#matrix34 = matrix62.view(3,4) #error!
matrix34 = matrix62.reshape(3,4) #等价于matrix34 = matrix62.contiguous().view(3,4)
print(matrix34)
(4)交换尺寸
x = torch.randn(5,3,2)
y = x.permute(2,1,0)
print(x.size()) # 得到(2,3,5)
z = x.permute(2,0,1)
print(x.size()) # 得到 (2,5,3)
(5)扩展尺寸
x = torch.randn(3,5)
print(x.size()) #输出(3,5)
y = x.unsqueeze(0)
print(y.size()) #在第0个维度加1 , 输出(1,3,5)
y = x.unsqueeze(-1)
print(y.size()) # 在最后一个维度加1 输出(3,5,1)
y = x.unsqueeze(1)
print(y.size()) #在第1个维度加1, 输出(3,1,5)
(6) 合并通道 [1,3,4,5]=>[1,3,20]
x = torch.randn(1,3,4,5)
# 从下标2开始合并
x.flatten(2).size() # [1,3,20]
# 从下标1开始合并
x.flatten(1).size() # [1,60]
# 从下标0开始合并
x.flatten(0).size() # [60]
4.0.4 张量的运算
张量的运算包括加减乘除等,均值方差等
(1)张量的基本运算,加减乘除
x = torch.rand(5,3)
y = torch.rand(5,3)
print(x+y)
print(torch.add(x,y))
result = torch.empty(5,3)
torch.add(x,y,out=result)
y.add_(x)
# 将对应元素相加
x = torch.ones(2,2)
y = x+2
# x中的每个元素都加上2
x = torch.ones(2,2)
y = torch.ones(2,2)
z = x*y
# 对应元素相乘
(2)张量的高级运算
x = torch.randn(5,3)
y = x.mean()
print(y.item())
# 计算x中所有元素,然后得到均值
x = torch.rand(5,3)
print(x.sum())
# 计算得到总和
# @是用来对tensor进行矩阵相乘的:
import torch
d = 2
n=50
X = torch.randn(n,d)
true_w = torch.tensor([[-1.0],[2.0]])
y = X @ true_w + torch.randn(n,1)*0.1
print(X.shape)
print(y.shape)
print(true_w.shape)
# *用来对tensor进行矩阵进行逐元素相乘:
x = torch.tensor([[1,2],[3,4]])
y = torch.tensor([[2,1],[4,3]])
c = x*y
print("x_shape",x.shape)
print("y_shape",y.shape)
print("c_shape",c.shape)
print(c)
4.0.5 张量的取值
张量的下标是从0开始的
(1)取出特定维度的数据
x = torch.rand(5,3)
print(x[:,1]) #取出第二个维度index为1的数据。对于矩阵来说就是第二列(下标从0开始)。
tensor中只有一个数据,直接取出
x = torch.randn(1)
print(x.item())
4.0.6 张量和numpy数组
可以用numpy方法从Tensor得到numpy数组,也可以用torch.from_numpy从numpy数组得到Tensor
注:这两种方法,Tensor和numpy是共享数据内存的,如果改变其中一个,另一个值也会发送改变
如果有需要,可以用张量的clone方法拷贝张量,中断这种关联。
此外,还可以使用item方法从标量张量得到对应的python数值
使用tolist方法从张量得到对应的Python数值列表
import numpy as np
import torch
(1)numpy => Tensor
#torch.from_numpy函数从numpy数组得到Tensor
arr = np.zeros(3)
tensor = torch.from_numpy(arr)
print("before add 1:")
print(arr)
print(tensor)
print("\nafter add 1:")
np.add(arr,1, out = arr) #给 arr增加1,tensor也随之改变
print(arr)
print(tensor)
# before add 1:
# [0. 0. 0.]
# tensor([0., 0., 0.], dtype=torch.float64)
# after add 1:
# [1. 1. 1.]
# tensor([1., 1., 1.], dtype=torch.float64)
(2)Tensor=>numpy
# numpy方法从Tensor得到numpy数组
tensor = torch.zeros(3)
arr = tensor.numpy()
print("before add 1:")
print(tensor)
print(arr)
print("\nafter add 1:")
#使用带下划线的方法表示计算结果会返回给调用 张量
tensor.add_(1) #给 tensor增加1,arr也随之改变
#或: torch.add(tensor,1,out = tensor)
print(tensor)
print(arr)
# before add 1:
# tensor([0., 0., 0.])
# [0. 0. 0.]
# after add 1:
# tensor([1., 1., 1.])
# [1. 1. 1.]
(3)使用clone方法
# 可以用clone() 方法拷贝张量,中断这种关联
tensor = torch.zeros(3)
#使用clone方法拷贝张量, 拷贝后的张量和原始张量内存独立
arr = tensor.clone().numpy() # 也可以使用tensor.data.numpy()
print("before add 1:")
print(tensor)
print(arr)
print("\nafter add 1:")
#使用 带下划线的方法表示计算结果会返回给调用 张量
tensor.add_(1) #给 tensor增加1,arr不再随之改变
print(tensor)
print(arr)
# before add 1:
# tensor([0., 0., 0.])
# [0. 0. 0.]
# after add 1:
# tensor([1., 1., 1.])
# [0. 0. 0.]
(4) 使用item方法
# item方法和tolist方法可以将张量转换成Python数值和数值列表
scalar = torch.tensor(1.0)
s = scalar.item()
print(s)
print(type(s))
(5)使用tolist方法
把tensor转换为python中的list类型
tensor = torch.rand(2,2)
t = tensor.tolist()
print(t)
print(type(t))
4.1、张量的结构操作
张量的操作包括
(1)张量的结构操作
(2)张量的数学运算
张量的结构操作:张量创建、索引切片、维度变换、合并分割
张量数学运算:标量运算、向量运算、矩阵运算。还会介绍张量运算的广播机制
4.1.1 创建张量
张量创建和numpy中创建array的方法很像
import numpy as np
import torch
a = torch.tensor([1,2,3],dtype=torch.float)
print(a) # tensor([1., 2., 3.])
b = torch.arange(1,10,step=2)
print(b) # tensor([1, 3, 5, 7, 9])
# torch.linspace(a,b,c), 数值范围从a到b,总共切c份
c = torch.linspace(0.0,2*3.14,10)
print(c) # tensor([0.0000, 0.6978, 1.3956, 2.0933, 2.7911, 3.4889, 4.1867, 4.8844, 5.5822,6.2800])
c = torch.linspace(0,10,5) #从0到10,总共且5份
print(c)
# tensor([ 0.0000, 2.5000, 5.0000, 7.5000, 10.0000])
d = torch.zeros((3,3))
print(d)
# tensor([[0., 0., 0.],
# [0., 0., 0.],
# [0., 0., 0.]])
a = torch.ones((3,3),dtype = torch.int)
print(a)
# tensor([[1, 1, 1],
# [1, 1, 1],
# [1, 1, 1]], dtype=torch.int32)
b = torch.zeros_like(a,dtype=torch.float) #复制一个形状一样的张量,不过都设置为0
print(b)
# tensor([[0., 0., 0.],
# [0., 0., 0.],
# [0., 0., 0.]])
torch.fill_(b,5) #给b中的每个元素都填充5
print(b)
# tensor([[5., 5., 5.],
# [5., 5., 5.],
# [5., 5., 5.]])
#均匀随机分布
torch.manual_seed(0)
minval,maxval = 0,10
a = minval + (maxval-minval)*torch.rand([5])
print(a)
# tensor([4.9626, 7.6822, 0.8848, 1.3203, 3.0742])
#正态分布随机
b = torch.normal(mean = torch.zeros(3,3), std = torch.ones(3,3))
print(b)
# tensor([[-1.3836, 0.2459, -0.1312],
# [-0.1785, -0.5959, 0.2739],
# [ 0.5679, -0.6731, -1.2095]])
#正态分布随机
mean,std = 2,5
c = std*torch.randn((3,3))+mean
print(c)
# tensor([[ 8.7204, 13.9161, -0.8323],
# [ -3.7681, -10.5115, 6.3778],
# [-11.3628, 1.8433, 4.4939]])
#整数随机排列
d = torch.randperm(20)
print(d)
# tensor([ 5, 15, 19, 10, 7, 17, 0, 4, 12, 16, 14, 13, 1, 3, 9, 6, 18, 2,8, 11])
#特殊矩阵
I = torch.eye(3,3) #单位矩阵
print(I)
t = torch.diag(torch.tensor([1,2,3])) #对角矩阵
print(t)
# tensor([[1., 0., 0.],
# [0., 1., 0.],
# [0., 0., 1.]])
# tensor([[1, 0, 0],
# [0, 2, 0],
# [0, 0, 3]])
4.1.2 规则的索引切片
张量的切片方式和numpy几乎是一样的。切片时支持缺省参数和省略号。
可以通过索引和切分对部分元素进行修改。
此外,对于不规则的切片提取,可以使用torch.index_select
,torch.masked_select
,torch.take
如果要通过修改张量的某些元素得到新的张量,可以使用torch.where
,torch.masked_fill
,torch.index_fill
#均匀随机分布
torch.manual_seed(0)
minval,maxval = 0,10
t = torch.floor(minval + (maxval-minval)*torch.rand([5,5])).int()
print(t)
# tensor([[4, 7, 0, 1, 3],
# [6, 4, 8, 4, 6],
# [3, 4, 0, 1, 2],
# [5, 6, 8, 1, 2],
# [6, 9, 3, 8, 4]], dtype=torch.int32)
# 第0行
print(t[0])
# tensor([4, 7, 0, 1, 3], dtype=torch.int32)
#倒数第一行
print(t[-1])
# tensor([6, 9, 3, 8, 4], dtype=torch.int32)
#第1行第3列
print(t[1,3])
print(t[1][3])
# tensor(4, dtype=torch.int32)
# tensor(4, dtype=torch.int32)
#第1行至第3行
print(t[1:4,:]) #左闭右开
# tensor([[6, 4, 8, 4, 6],
# [3, 4, 0, 1, 2],
# [5, 6, 8, 1, 2]], dtype=torch.int32)
#第1行至最后一行,第0列到最后一列每隔两列取一列
# 1:4 表示[1,4)行 , :4:2等价于0:4:2 表示[0,4)列,间隔为2
print(t[1:4,:4:2])
# tensor([[6, 8],
# [3, 0],
# [5, 8]], dtype=torch.int32)
#可以使用索引和切片修改部分元素
x = torch.tensor([[1,2],[3,4]],dtype = torch.float32,requires_grad=True)
x.data[1,:] = torch.tensor([0.0,0.0])
print(x)
# tensor([[1., 2.],
# [0., 0.]], requires_grad=True)
4.1.3 修改张量的形状
a = torch.arange(27).view(3,3,3)
print(a)
# tensor([[[ 0, 1, 2],
# [ 3, 4, 5],
# [ 6, 7, 8]],
# [[ 9, 10, 11],
# [12, 13, 14],
# [15, 16, 17]],
# [[18, 19, 20],
# [21, 22, 23],
# [24, 25, 26]]])
#省略号可以表示多个冒号
print(a[...,1])
# tensor([[ 1, 4, 7],
# [10, 13, 16],
# [19, 22, 25]])
4.1.4 不规则的索引切片
对于不规则的切片提取,可以使用torch.index_select
,torch.take
,torch.gather
,torch.masked_select
例子:有4个班级,每个班级有10个学生,每个学生有7门课程。可以用一个4×10×7的张量来表示。
minval=0
maxval=100
scores = torch.floor(minval + (maxval-minval)*torch.rand([4,10,7])).int()
print(scores)
tensor([[[55, 95, 3, 18, 37, 30, 93],
[17, 26, 15, 3, 20, 92, 72],
[74, 52, 24, 58, 3, 13, 24],
[81, 79, 27, 48, 81, 99, 69],
[56, 83, 20, 59, 11, 15, 24],
[72, 70, 20, 65, 77, 43, 51],
[61, 81, 98, 11, 31, 69, 91],
[93, 94, 59, 6, 54, 18, 3],
[94, 88, 0, 59, 41, 41, 27],
[69, 20, 68, 75, 85, 68, 0]],
[[17, 74, 60, 10, 21, 97, 83],
[28, 37, 2, 49, 12, 11, 47],
[57, 29, 79, 19, 95, 84, 7],
[37, 52, 57, 61, 69, 52, 25],
[73, 2, 20, 37, 25, 32, 9],
[39, 60, 17, 47, 85, 44, 51],
[45, 60, 81, 97, 81, 97, 46],
[ 5, 26, 84, 49, 25, 11, 3],
[ 7, 39, 77, 77, 1, 81, 10],
[39, 29, 40, 40, 5, 6, 42]],
[[50, 27, 68, 4, 46, 93, 29],
[95, 68, 4, 81, 44, 27, 89],
[ 9, 55, 39, 85, 63, 74, 67],
[37, 39, 8, 77, 89, 84, 14],
[52, 14, 22, 20, 67, 20, 48],
[52, 82, 12, 15, 20, 84, 32],
[92, 68, 56, 49, 40, 56, 38],
[49, 56, 10, 23, 90, 9, 46],
[99, 68, 51, 6, 74, 14, 35],
[33, 42, 50, 91, 56, 94, 80]],
[[18, 72, 14, 28, 64, 66, 87],
[33, 50, 75, 1, 86, 8, 50],
[41, 23, 56, 91, 35, 20, 31],
[ 0, 72, 25, 16, 21, 78, 76],
[88, 68, 33, 36, 64, 91, 63],
[26, 26, 2, 60, 21, 5, 93],
[17, 44, 64, 51, 16, 9, 89],
[58, 91, 33, 64, 38, 47, 19],
[66, 65, 48, 38, 19, 84, 12],
[70, 33, 25, 58, 24, 61, 59]]], dtype=torch.int32)
torch.index_select 的使用
#抽取每个班级第0个学生,第5个学生,第9个学生的全部成绩
# dim=1 表示对第1个维度(下标从0开始)进行抽取
# index=torch.tensor([0,5,9]) , 表示抽取下标为0,5,9的数据
torch.index_select(scores,dim = 1,index = torch.tensor([0,5,9]))
#抽取每个班级第0个学生,第5个学生,第9个学生的第1门课程,第3门课程,第6门课程成绩
# 先抽取每个班级第0个学生,第5个学生,第9个学生的全部成绩
temp_tensor = torch.index_select(scores,dim = 1,index = torch.tensor([0,5,9]))
# 在此基础上,再抽取第1门,第3门,第6门课程
q = torch.index_select(temp_tensor,dim2,index=torch.tensor([1,3,6]))
print(q)
torch.take的使用
#抽取第0个班级第0个学生的第0门课程,第2个班级的第4个学生的第1门课程,第3个班级的第9个学生第6门课程成绩
#take将输入拉成一维数组,然后通过下标确定
# 0*10*7 + 0 表示第0个班级,第0个学生,第0门成绩
# 2*10*7 + 4*7 + 1 表示第2个班级,第4个学生,第1门课程
# 3*10*7 + 9*7 + 6 表示第3个班级,第9个学生,第6门课程
s = torch.take(scores,torch.tensor([0*10*7+0, 2*10*7+4*7+1, 3*10*7+9*7+6]))
torch.masked_select 的使用
#抽取分数大于等于80分的分数(布尔索引)
#结果是1维张量
g = torch.masked_select(scores,scores>=80)
print(g)
以上方法仅能提取张量的部分元素值,但不能更改张量的部分元素值得到新的张量。
如果要通过修改张量的部分元素值得到新的张量,可以使用torch.where
,torch.index_fill
和torch.masked_fll
(1)torch.where 可以理解为if的张量版本
(2) torch.index_fill 的选取元素逻辑和torch.index_select相同
(3) torch.masked_fill 的选取元素逻辑和 torch.masked_select 相同
#如果分数大于60分,赋值成1,否则赋值成0
ifpass = torch.where(scores>60,torch.tensor(1),torch.tensor(0))
print(ifpass)
#将每个班级第0个学生,第5个学生,第9个学生的全部成绩赋值成满分
torch.index_fill(scores,dim = 1,index = torch.tensor([0,5,9]),value = 100)
#等价于 scores.index_fill(dim = 1,index = torch.tensor([0,5,9]),value = 100)
#将分数小于60分的分数赋值成60分
b = torch.masked_fill(scores,scores<60,60)
#等价于b = scores.masked_fill(scores<60,60)
b
4.1.5 维度变换
维度变换相关函数主要有torch.reshape
(或者调用张量的view方法),torch.squeeze
,torch.unsqueeze
(1)torch.reshape 可以改变张量的形状
(2)torch.squeeze 可以减少维度
(3)torch.unsqueeze 可以增加维度
(4)torch.transpose 可以交换维度
(5)torch.permute 可以交换维度
torch.reshape的使用
# 张量的view方法有时候会调用失败,可以使用reshape方法。
torch.manual_seed(0)
minval,maxval = 0,255
a = (minval + (maxval-minval)*torch.rand([1,3,3,2])).int()
print(a.shape)
print(a)
注!!view操作有时会报错
当张量tensor使用view操作时会报错。因为tensor在内存中存储并不是连续的(比如经过转置操作),但是使用view时,必须保证tensor在内存中是连续的,这就会导致错误。解决方法有两种
(1)使用reshape() reshape并没有要求连续
(2)使用contiguous()方法, 让tensor变成连续的 a.contiguous().view(...)
# 改成 (3,6)形状的张量
b = a.view([3,6]) #torch.reshape(a,[3,6])
print(b.shape)
print(b)
# 改回成 [1,3,3,2] 形状的张量
c = torch.reshape(b,[1,3,3,2]) # b.view([1,3,3,2])
print(c)
torch.squeeze的使用
如果张量在某个维度上只有一个元素,利用torch.squeeze可以消除这个维度。
torch.squeeze和torch.unsqueeze相反
a = torch.tensor([[1.0,2.0]])
s = torch.squeeze(a)
print(a)
print(s)
print(a.shape)
print(s.shape)
# tensor([[1., 2.]])
# tensor([1., 2.])
# torch.Size([1, 2])
# torch.Size([2])
#在第0维插入长度为1的一个维度
d = torch.unsqueeze(s,axis=0)
print(s.shape)
print(d.shape)
# torch.Size([2])
# torch.Size([1, 2])
torch.transpose的使用
交换通道是很有用的,常用在图像存储格式的变换上。
如果是二维的矩阵,通常会调用matrix.t(),等价于torch.transpose(matrix,0,1)
minval=0
maxval=255
# Batch,Height,Width,Channel
data = torch.floor(minval + (maxval-minval)*torch.rand([100,256,256,3])).int()
print(data.shape)
# 转换成 Pytorch默认的图片格式 Batch,Channel,Height,Width
# 需要交换两次
temp_tensor = torch.transpose(data,1,2) #交换1,2维度,(下标从0开始)
data_t = torch.transpose(temp_tensor,1,3) # 交换1,3维度(下标从0开始)
print(data_t.shape)
# torch.Size([100, 256, 256, 3])
# torch.Size([100, 3, 256, 256])
torch.permute的使用
torch.permute也是用于交换张量的维度的,但是比transpose使用更加方便。
permute不需要两次交换,可以一次性搞定
x = torch.randn(5,3,2)
y = x.permute(2,1,0)
print(x.size()) # 得到(2,3,5)
z = x.permute(2,0,1)
print(x.size()) # 得到 (2,5,3)
4.1.6 合并与分割
可以用torch.cat
方法和torch.stack
方法将多个张量合并,可以使用torch.split
方法把一个张量分割成多个张量。
torch.cat的使用
a = torch.tensor([[1.0,2.0],[3.0,4.0]])
b = torch.tensor([[5.0,6.0],[7.0,8.0]])
c = torch.tensor([[9.0,10.0],[11.0,12.0]])
abc_cat = torch.cat([a,b,c],dim = 0)
print(abc_cat.shape)
print(abc_cat)
# torch.Size([6, 2])
# tensor([[ 1., 2.],
# [ 3., 4.],
# [ 5., 6.],
# [ 7., 8.],
# [ 9., 10.],
# [11., 12.]])
torch.cat([a,b,c],axis = 1)
# tensor([[ 1., 2., 5., 6., 9., 10.],
# [ 3., 4., 7., 8., 11., 12.]])
torch.stack的使用
abc_stack = torch.stack([a,b,c],axis = 0) #torch中dim和axis参数名可以混用
print(abc_stack.shape)
print(abc_stack)
# torch.Size([3, 2, 2])
# tensor([[[ 1., 2.],
# [ 3., 4.]],
# [[ 5., 6.],
# [ 7., 8.]],
# [[ 9., 10.],
# [11., 12.]]])
torch.stack([a,b,c],axis = 1)
# tensor([[[ 1., 2.],
# [ 5., 6.],
# [ 9., 10.]],
# [[ 3., 4.],
# [ 7., 8.],
# [11., 12.]]])
torch.split的使用
torch.split是torch.cat的逆运算,可以指定分割分数平均分割,也可以通过指定每份的记录数量进行分割。
print(abc_cat)
# tensor([[ 1., 2.],
# [ 3., 4.],
# [ 5., 6.],
# [ 7., 8.],
# [ 9., 10.],
# [11., 12.]])
a,b,c = torch.split(abc_cat,split_size_or_sections = 2,dim = 0) #每份2个进行分割
print(a)
print(b)
print(c)
print(abc_cat)
p,q,r = torch.split(abc_cat,split_size_or_sections =[4,1,1],dim = 0) #每份分别为[4,1,1]
print(p)
print(q)
print(r)
# tensor([[1., 2.],
# [3., 4.],
# [5., 6.],
# [7., 8.]])
# tensor([[ 9., 10.]])
# tensor([[11., 12.]])
4.2 张量的数学运算
张量的操作包括张量的结构操作和张量的数学运算
张量的结构操作有 张量创建、索引切片、维度变换、合并分割
张量数学运算有 标量运算、向量运算、矩阵运算,还会介绍张量运算的广播机制。
4.2.1 标量运算
标量运算包括
(1)加减乘除乘方
(2)三角函数、指数、对数等
(3)逻辑比较运算符等
标量运算的特点:对张量实施逐元素运算
有些标量运算对常用的数学运算符进行了重载,并且支持类似numpy的广播特性。
import torch
import numpy as np
a = torch.tensor([[1.0,2],[-3,4.0]])
b = torch.tensor([[5.0,6],[7.0,8.0]])
a+b #对应元素相加
# tensor([[ 6., 8.],
# [ 4., 12.]])
a-b # 对应元素相减
# tensor([[ -4., -4.],
# [-10., -4.]])
a*b
# tensor([[ 5., 12.],
# [-21., 32.]])
a/b
# tensor([[ 0.2000, 0.3333],
# [-0.4286, 0.5000]])
a**2
# tensor([[ 1., 4.],
# [ 9., 16.]])
a**(0.5)
# tensor([[1.0000, 1.4142],
# [ nan, 2.0000]])
a%3 #求模
# tensor([[1., 2.],
# [0., 1.]])
a//3 #地板除法
# tensor([[ 0., 0.],
# [-1., 1.]])
a = torch.tensor([[1.0,2],[-3,4.0]])
a>=2 # torch.ge(a,2) #ge: greater_equal缩写
# tensor([[False, True],
# [False, True]])
(a>=2)&(a<=3)
# tensor([[False, True],
# [False, False]])
(a>=2)|(a<=3)
# tensor([[True, True],
# [True, True]])
a==5 #torch.eq(a,5)
# tensor([[False, False],
# [False, False]])
torch.sqrt(a)
# tensor([[1.0000, 1.4142],
# [ nan, 2.0000]])
a = torch.tensor([1.0,8.0])
b = torch.tensor([5.0,6.0])
c = torch.tensor([6.0,7.0])
d = a+b+c
print(d)
# tensor([12., 21.])
print(torch.max(a,b)) # a和b分别取最大值
# tensor([5., 8.])
print(torch.min(a,b))
# tensor([1., 6.])
x = torch.tensor([2.6,-2.7])
print(torch.round(x)) #保留整数部分,四舍五入
print(torch.floor(x)) #保留整数部分,向下归整
print(torch.ceil(x)) #保留整数部分,向上归整
print(torch.trunc(x)) #保留整数部分,向0归整
# tensor([ 3., -3.])
# tensor([ 2., -3.])
# tensor([ 3., -2.])
# tensor([ 2., -2.])
x = torch.tensor([2.6,-2.7])
print(torch.fmod(x,2)) #作除法取余数
print(torch.remainder(x,2)) #作除法取剩余的部分,结果恒正
# tensor([ 0.6000, -0.7000])
# tensor([0.6000, 1.3000])
# 幅值裁剪
x = torch.tensor([0.9,-0.8,100.0,-20.0,0.7])
y = torch.clamp(x,min=-1,max = 1) # 小于-1的都设置为-1,大于1的都设置为1
z = torch.clamp(x,max = 1) # 大于1的都设置为1
print(y)
print(z)
# tensor([ 0.9000, -0.8000, 1.0000, -1.0000, 0.7000])
# tensor([ 0.9000, -0.8000, 1.0000, -20.0000, 0.7000])
4.2.2 向量运算
#统计值
a = torch.arange(1,10).float()
print(torch.sum(a))
print(torch.mean(a))
print(torch.max(a))
print(torch.min(a))
print(torch.prod(a)) #累乘
print(torch.std(a)) #标准差
print(torch.var(a)) #方差
print(torch.median(a)) #中位数
# tensor(45.)
# tensor(5.)
# tensor(9.)
# tensor(1.)
# tensor(362880.)
# tensor(2.7386)
# tensor(7.5000)
# tensor(5.)
#指定维度计算统计值
b = a.view(3,3)
print(b)
print(torch.max(b,dim = 0)) # 取每一列的最大值 , values表示值,indices表示对应的下标
print(torch.max(b,dim = 1)) # 取每一行的最大值
# tensor([[1., 2., 3.],
# [4., 5., 6.],
# [7., 8., 9.]])
# torch.return_types.max(
# values=tensor([7., 8., 9.]),
# indices=tensor([2, 2, 2]))
# torch.return_types.max(
# values=tensor([3., 6., 9.]),
# indices=tensor([2, 2, 2]))
#cum扫描
a = torch.arange(1,10)
print(torch.cumsum(a,0)) # 每一项与前面所有项的累计和, [1,2,3]=>[1,3,6] 因为[1,3,6]等于[1,1+2,1+2+3]
print(torch.cumprod(a,0)) # 每一项和前面所有项的累计积
print(torch.cummax(a,0).values) # 每一项和前面所有项的最大值
print(torch.cummax(a,0).indices)
print(torch.cummin(a,0)) # 每一项和前面所有项的最小值
# tensor([ 1, 3, 6, 10, 15, 21, 28, 36, 45])
# tensor([ 1, 2, 6, 24, 120, 720, 5040, 40320, 362880])
# tensor([1, 2, 3, 4, 5, 6, 7, 8, 9])
# tensor([0, 1, 2, 3, 4, 5, 6, 7, 8])
# torch.return_types.cummin(
# values=tensor([1, 1, 1, 1, 1, 1, 1, 1, 1]),
# indices=tensor([0, 0, 0, 0, 0, 0, 0, 0, 0]))
#torch.sort和torch.topk可以对张量排序
a = torch.tensor([[9,7,8],[1,3,2],[5,6,4]]).float()
print(torch.topk(a,2,dim = 0),"\n") # 取每一列的前2大的值
print(torch.topk(a,2,dim = 1),"\n")
print(torch.sort(a,dim = 1),"\n")
#利用torch.topk可以在Pytorch中实现KNN算法
# torch.return_types.topk(
# values=tensor([[9., 7., 8.],
# [5., 6., 4.]]),
# indices=tensor([[0, 0, 0],
# [2, 2, 2]]))
# torch.return_types.topk(
# values=tensor([[9., 8.],
# [3., 2.],
# [6., 5.]]),
# indices=tensor([[0, 2],
# [1, 2],
# [1, 0]]))
# torch.return_types.sort(
# values=tensor([[7., 8., 9.],
# [1., 2., 3.],
# [4., 5., 6.]]),
# indices=tensor([[1, 2, 0],
# [0, 2, 1],
# [2, 0, 1]]))
4.2.3 矩阵运算
矩阵运算包括:矩阵乘法、矩阵转置、逆矩阵、矩阵求迹,矩阵范数,矩阵行列式,矩阵求特征值,矩阵分解等运算。
#矩阵乘法
a = torch.tensor([[1,2],[3,4]])
b = torch.tensor([[2,0],[0,2]])
print(a@b) #等价于torch.matmul(a,b) 或 torch.mm(a,b)
# tensor([[2, 4],
# [6, 8]])
#矩阵转置
a = torch.tensor([[1.0,2],[3,4]])
print(a.t())
# tensor([[1., 3.],
# [2., 4.]])
#矩阵逆,必须为浮点类型
a = torch.tensor([[1.0,2],[3,4]])
print(torch.inverse(a))
# tensor([[-2.0000, 1.0000],
# [ 1.5000, -0.5000]])
#矩阵求trace
a = torch.tensor([[1.0,2],[3,4]])
print(torch.trace(a))
# tensor(5.)
#矩阵求范数
a = torch.tensor([[1.0,2],[3,4]])
print(torch.norm(a))
# tensor(5.4772)
#矩阵行列式
a = torch.tensor([[1.0,2],[3,4]])
print(torch.det(a))
# tensor(-2.0000)
#矩阵特征值和特征向量
a = torch.tensor([[1.0,2],[-5,4]],dtype = torch.float)
print(torch.eig(a,eigenvectors=True))
#两个特征值分别是 -2.5+2.7839j, 2.5-2.7839j
# torch.return_types.eig(
# eigenvalues=tensor([[ 2.5000, 2.7839],
# [ 2.5000, -2.7839]]),
# eigenvectors=tensor([[ 0.2535, -0.4706],
# [ 0.8452, 0.0000]]))
#矩阵QR分解, 将一个方阵分解为一个正交矩阵q和上三角矩阵r
#QR分解实际上是对矩阵a实施Schmidt正交化得到q
a = torch.tensor([[1.0,2.0],[3.0,4.0]])
q,r = torch.qr(a)
print(q,"\n")
print(r,"\n")
print(q@r)
#矩阵svd分解
#svd分解可以将任意一个矩阵分解为一个正交矩阵u,一个对角阵s和一个正交矩阵v.t()的乘积
#svd常用于矩阵压缩和降维
a=torch.tensor([[1.0,2.0],[3.0,4.0],[5.0,6.0]])
u,s,v = torch.svd(a)
print(u,"\n")
print(s,"\n")
print(v,"\n")
print(u@torch.diag(s)@v.t())
#利用svd分解可以在Pytorch中实现主成分分析降维
# tensor([[-0.2298, 0.8835],
# [-0.5247, 0.2408],
# [-0.8196, -0.4019]])
# tensor([9.5255, 0.5143])
# tensor([[-0.6196, -0.7849],
# [-0.7849, 0.6196]])
# tensor([[1.0000, 2.0000],
# [3.0000, 4.0000],
# [5.0000, 6.0000]])
4.2.4 广播机制
Pytorch的广播规则和numpy是一样的
(1)如果张量的维度不同,将维度较小的张量进行扩展,直到两个张量的维度都一样
(2)如果两个张量在某个维度上的长度是相同的,或者其中一个张量在该维度上的长度为1,那么我们就说这两个张量在该维度上是相容的。
(3)如果两个张量在所有维度上都是相容的,它们就能使用广播。
(4)广播之后,每个维度的长度将取两个张量在该维度长度的较大值。
(5)在任何一个维度上,如果一个张量的长度为1,另一个张量长度大于1,那么在该维度上,就好像是对第一个张量进行了复制。
torch.broadcast_tensors
可以将多个张量根据广播规则转换成相同的维度
a = torch.tensor([1,2,3])
b = torch.tensor([[0,0,0],[1,1,1],[2,2,2]])
print(b + a)
# tensor([[1, 2, 3],
# [2, 3, 4],
# [3, 4, 5]])
a_broad,b_broad = torch.broadcast_tensors(a,b)
print(a_broad,"\n")
print(b_broad,"\n")
print(a_broad + b_broad)
# tensor([[1, 2, 3],
# [1, 2, 3],
# [1, 2, 3]])
# tensor([[0, 0, 0],
# [1, 1, 1],
# [2, 2, 2]])
# tensor([[1, 2, 3],
# [2, 3, 4],
# [3, 4, 5]])