PyTorch基础1——张量

参考:《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_selecttorch.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_selecttorch.take,torch.gathertorch.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_filltorch.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.squeezetorch.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]])

  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值