深度学习 | pytorch(一)张量的创建和常用方法

文章目录

  • 一、张量的创建
    • 1、张量的类型
      • ①整数型
      • ②浮点型
      • ③布尔型张量
      • ④复数类型
    • 2、张量类型的转化
      • ①隐式转化
      • ③使用.float()、.int()对张量进行转化
    • 3、创建高维度的张量
    • 4、零维张量:只包含一个元素,但又不是单独的一个数
    • 4、高维张量
  • 二、张量的形变
    • 1、flatten拉平:将任意维度张量转化为一维张量
    • 2、reshape方法:任意变形
      • ①转化成一维张量
      • ②转化成二维张量
      • ③转化成三维张量
  • 三、特殊张量的创建方法
    • 1、创建特殊取值的张量(返回结果默认为浮点型)
      • ①全零张量 .zeros()
      • ②全1张量 .ones()
      • ③单位矩阵 .eyes()
      • ④对角矩阵 .diag(一维张量)
      • ⑤rand:服从0-1均匀分布的张量
      • ⑥arange/linspace:生成数列
      • ⑦empty:生成未初始化的指定形状矩阵
      • ⑧full:根据指定行装,填充指定数值
    • 2、创建指定形状的数组
    • 3、张量与其他相关类型(数组、列表)之间的转化方法
      • ①.numpy方法:张量转化为数组
      • ②.tolist方法:张量转化为列表
      • ③list函数:张量转化为列表
      • ④.item()方法:转化为数值
  • 四、张量的深拷贝
  • 五、张量的索引
    • 1、切片
      • ①连续采样
      • ②隔行采样
      • ③"…"仅仅是为了方便
    • 2、一维张量的索引
    • 2、二维张量索引
    • 3、三维张量的索引
    • 4、张量的函数索引
      • ①index_select函数,通过指定index来对张量进行索引
      • ②tensor.view()方法
  • 六、分块与合并 merge or split
    • 1、张量的分片函数
      • (1)分块:chunk函数
      • (2)拆分:split函数
    • 2、张量的合并操作
      • (1)拼接函数:cat
      • (2)堆叠函数:stack
  • 七、张量的维度变换
    • 1、masked_select()
    • 2、squeeze函数:删除不必要的维度
    • 3、unsqueeze函数:手动升维
      • (1)调用方法1:torch.unsqueeze(t, dim=n)
        • 注:.shape返回多少个元素,张量就有多少个维度
      • (2)调用方法2:a.unsqueeze(dim=n)
  • 八、扩展: expand 和 repeat效果一样
    • 1、expand:broadcasting #只改变理解方式,不增加数据
    • 2、repeat:memory copied #增加数据
      • ①矩阵的转置 .t
      • ②transpose实现维度两两交换
      • ③permute
      • ④broadcast:扩展维度,扩张时不需要copy数据
  • 九、张量的科学运算
    • 1、数学运算与算子
      • 数学运算的分类
    • 2、不同形状的张量计算

一、张量的创建

导入pytorch包

import torch

查看版本号

torch.__version__

‘1.7.1’

张量的创建方法:torch.tensor()

通过列表创建张量:

t=torch.tensor([1,2])
t

tensor([1, 2])

通过元祖创建张量:

torch.tensor((1,2))

tensor([1, 2])

import numpy as np
a=np.array((1,2))
a

array([1, 2])

通过数组创建张量:

t1=torch.tensor(a)
t1

tensor([1, 2], dtype=torch.int32)

dtype指张量的type,d指dimension

type(t)

torch.Tensor

type(t1)

torch.Tensor

从上面的返回结果可以看出返回结果都是张量,即创建的类型都是张量

1、张量的类型

①整数型

a

array([1, 2])

a.dtype #数组类型

dtype(‘int32’)

t

tensor([1, 2])

t.dtype

torch.int64

t1

tensor([1, 2], dtype=torch.int32)

t1.dtype

torch.int32

张量内部还会细分成具体类型。整数型的数组默认创建int32(整型)类型,而张量则默认创建int64(长整型)类型。

②浮点型

np如果创建浮点型对象,array默认为双精度浮点型float64,而tensor则默认为单精度浮点型float32

np.array([1.1,2.2]).dtype

dtype(‘float64’)

torch.tensor(np.array([1.1,2.2])).dtype#输入双精度浮点型的array,返回结果为双精度浮点型,输出继承了输入的类型

torch.float64

torch.tensor([1.11,2.2]).dtype#输入list创建tensor,返回结果为单精度浮点型

torch.float32

③布尔型张量

t2=torch.tensor([True,False])
t2

tensor([ True, False])

t2.dtype

torch.bool

还可以通过dtype参数,在创建张量时设置输出结果:

torch.tensor([1.1,2.7],dtype=torch.int16)

tensor([1, 2], dtype=torch.int16)

④复数类型

a = torch.tensor(1 + 2j) #1是实部,2是虚部
a

tensor(1.+2.j)

2、张量类型的转化

①隐式转化

转化过程对于用户来说是隐藏的,不明显的,朝向尽可能保留有用信息的方向转化。

和numpy中array相同,当张量各元素属于不同类型时,系统会自动进行隐式转化。

浮点型和整数型的隐式转化

torch.tensor([1.1,2])

tensor([1.1000, 2.0000])

布尔型和数值型的隐式转化

torch.tensor([True,2.0])
torch.tensor([False,2.0])

tensor([0., 2.])

③使用.float()、.int()对张量进行转化

t

tensor([1, 2])

转化为默认浮点型(32位):

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)

当在torch函数中使用dtype参数的时候,需要输入torch.float表示精度。

在使用方法进行类型转化时,方法名称是double。(虽然torch.float和double都表示双精度浮点型)

3、创建高维度的张量

用简单序列创建一维数组

t1 = torch.tensor([1,2])
t1

tensor([1, 2])

使用ndim查看张量的维度:

t1.ndim 

1

使用shape查看形状:

t1.shape 

torch.Size([2])

t1.size() #pytorch中size方法和shape函数返回结果相同

torch.Size([2])

还有两种方法查看张量

len(t1)#返回拥有N-1维元素

2

t1.numel()#返回一共有几个数

2

len([1,2,3])

3

一维张量的len和numel返回结果相同,而更高维则不同

用形状相同的序列构成新序列,进而将其转化为二维张量:
用list的list创建二维数组(list的嵌套):

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

tensor([[1, 2],
[3, 4]])

t2.ndim #2维的张量

2

t2.shape #2行2列l

torch.Size([2, 2])

t2.size()

torch.Size([2, 2])

len(t2)#此处len函数返回结果表示t2是由2个1维张量构成

2

4、零维张量:只包含一个元素,但又不是单独的一个数

t1 = torch.tensor([1])
t1

tensor([1])

t1.ndim

1

t1.shape

torch.Size([1])

t0 = torch.tensor(1)
t0

tensor(1)

t0.ndim

0

t0.shape

torch.Size([])

t0.size()

torch.Size([])

理解零维张量:可将零维张量视为拥有张量属性的单独的数。

例如:张量可以存在于GPU上,但python的原生数值对象不行。零维张量虽然是零维的,但因为是张量,所以可以存在于GPU上。

从学术名称来说,python中的数值是scalars(标量),零维张量是tensor

4、高维张量

一般来说,称三维及三维以上的张量为高维张量。三维张量可以理解为二维数组或矩阵的组合。

a1 = np.array([[1,2,2],[3,4,4]])
a1

array([[1, 2, 2],
[3, 4, 4]])

a2 = np.array([[5,6,6],[7,8,8]])
a2

array([[5, 6, 6],
[7, 8, 8]])

由两个形状相同的二维数组创建一个三维的张量:

t3 = torch.tensor([a1,a2])
t3

tensor([[[1, 2, 2],
[3, 4, 4]],
[[5, 6, 6],
[7, 8, 8]]], dtype=torch.int32)

t3.ndim#查看维度

3

由两个矩阵(元素)构成,每个矩阵都是两行三列构成。torch.size([矩阵个数,行,列]):

t3.shape 

torch.Size([2, 2, 3])

t3.size()

torch.Size([2, 2, 3])

len(t3)

2

t3.numel()#包含

12

二、张量的形变

1、flatten拉平:将任意维度张量转化为一维张量

t2

tensor([[1, 2],
[3, 4]])

t2.flatten() #按行排列,拉平

tensor([1, 2, 3, 4])

t3

tensor([[[1, 2, 2],
[3, 4, 4]],
[[5, 6, 6],
[7, 8, 8]]], dtype=torch.int32)

t3.flatten()

tensor([1, 2, 2, 3, 4, 4, 5, 6, 6, 7, 8, 8], dtype=torch.int32)

如果将零维张量拉平,会变成一维张量:

t0=torch.tensor(1)
t0

tensor(1)

t0.ndim

0

t0.flatten()

tensor([1])

t0.flatten().ndim

1

2、reshape方法:任意变形

t1 = torch.tensor([1,2])
t1.shape

torch.Size([2])

t1.reshape(2,1)

tensor([[1],
[2]])

t1.reshape(2,1).shape

torch.Size([2, 1])

reshape转化后的维度由该方法输入的参数个数决定

①转化成一维张量

t1.reshape(2)

tensor([1, 2])

t1.reshape(2,)#另一种表达

tensor([1, 2])

t1.reshape(2).ndim

1

②转化成二维张量

t1.reshape(1,2)

tensor([[1, 2]])

t1.reshape(1,2).ndim

2

③转化成三维张量

t1.reshape(1,1,2)

tensor([[[1, 2]]])

t1.reshape(1,2,1)

tensor([[[1],
[2]]])

t1.reshape(1,2,1).ndim

3

t3

tensor([[[1, 2, 2],
[3, 4, 4]],
[[5, 6, 6],
[7, 8, 8]]], dtype=torch.int32)

t3.reshape(1,1,12)#利用Reshape的方法把t3拉平

tensor([[[1, 2, 2, 3, 4, 4, 5, 6, 6, 7, 8, 8]]], dtype=torch.int32)

三、特殊张量的创建方法

特殊张量:用于模拟特殊取值的矩阵,如全0矩阵、对角矩阵

1、创建特殊取值的张量(返回结果默认为浮点型)

①全零张量 .zeros()

torch.zeros([2,3]) #2行3列

tensor([[0., 0., 0.],
[0., 0., 0.]])

②全1张量 .ones()

torch.ones([2,3])

tensor([[1., 1., 1.],
[1., 1., 1.]])

③单位矩阵 .eyes()

torch.eye(5)

tensor([[1., 0., 0., 0., 0.],
[0., 1., 0., 0., 0.],
[0., 0., 1., 0., 0.],
[0., 0., 0., 1., 0.],
[0., 0., 0., 0., 1.]])

④对角矩阵 .diag(一维张量)

需要用一维张量去创建

t1

tensor([1, 2])

torch.diag(t1)

tensor([[1, 0],
[0, 2]])

不能直接使用list创建:

torch.diag([1,2])
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

 in 
----> 1 torch.diag([1,2])#不能直接使用list创建


TypeError: diag(): argument 'input' (position 1) must be Tensor, not list

⑤rand:服从0-1均匀分布的张量

torch.randn(2,3)#2行3列的随机数所构成的张量

tensor([[-0.8110, -1.1295, -0.2913],
[-1.1786, -0.8882, 0.2433]])

torch.randn(2,3)

tensor([[-0.9562, -2.0921, -0.1686],
[ 0.4361, 0.8800, 1.3416]])

torch.normal(2,3,size = (2,2)) #均值为2,标准差为3的张量

tensor([[ 3.0653, 6.2467],
[-1.5873, 2.1258]])

torch.randint(1,10,[2,4]) #在1到10之间随机抽取整数,组成两行四列的张量

tensor([[1, 8, 2, 3],
[8, 6, 7, 4]])

⑥arange/linspace:生成数列

torch.arange(5)

tensor([0, 1, 2, 3, 4])

torch.arange(1,5,0.5) #从1到5,每隔0.5取一个数

tensor([1.0000, 1.5000, 2.0000, 2.5000, 3.0000, 3.5000, 4.0000, 4.5000])

torch.linspace(1,5,3) #从1取到5,等距取三个数

tensor([1., 3., 5.])

⑦empty:生成未初始化的指定形状矩阵

torch.empty(2,3)

tensor([[0., 0., 0.],
[0., 0., 0.]])

⑧full:根据指定行装,填充指定数值

torch.full([2,4],2) #2行4列,数值为2

tensor([[2, 2, 2, 2],
[2, 2, 2, 2]])

2、创建指定形状的数组

根据指定对象的形状进行数值填充,只需要在上述函数后面加上_like即可

t1

tensor([1, 2])

t2

tensor([[1, 2],
[3, 4]])

torch.full_like(t1,2) #根据t1的形状,填充数值2

tensor([2, 2])

torch.randint_like(t2,1,10) #在1到10中随机抽取一些整数,并将其填充进t2的形状中去

tensor([[2, 1],
[6, 8]])

torch.zeros_like(t1)

tensor([0, 0])

_like类型转化需要注意转化前后数据类型一致的问题

t1

tensor([1, 2])

torch.randn_like(t1) #t1是整数,而转化后变为浮点数,所以会报错
---------------------------------------------------------------------------

RuntimeError                              Traceback (most recent call last)

 in 
----> 1 torch.randn_like(t1) #t1是整数,而转化后变为浮点数,所以会报错


RuntimeError: "normal_kernel_cpu" not implemented for 'Long'
t10 = torch.tensor([1.1,2.2]) #生成一个新的浮点数张量
t10

tensor([1.1000, 2.2000])

torch.randn_like(t10) #即可进行填充转化

tensor([-1.1196, -2.0447])

3、张量与其他相关类型(数组、列表)之间的转化方法

①.numpy方法:张量转化为数组

t1 = torch.tensor([1,2,3,4,5,6,7,8,9,10])
t1

tensor([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])

t1.numpy()

array([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], dtype=int64)

②.tolist方法:张量转化为列表

t1.tolist()

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

③list函数:张量转化为列表

list(t1)

[tensor(1),
tensor(2),
tensor(3),
tensor(4),
tensor(5),
tensor(6),
tensor(7),
tensor(8),
tensor(9),
tensor(10)]

需要注意的是,构成一维张量的是零维张量,而不是数值。

所以此时转化的列表是由一个个零维张量构成的列表,而不是数值构成的列表

④.item()方法:转化为数值

n = torch.tensor(1)
n

tensor(1)

n.item()

1

只能一个元素一个元素进行转化:

t1.item() 
---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

 in 
----> 1 t1.item() #只能一个元素一个元素进行转化


ValueError: only one element tensors can be converted to Python scalars

四、张量的深拷贝

等号赋值操作实际上是浅拷贝,需要进行深拷贝(使用clone方法)

t1

tensor([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])

t11 = t1
t11

tensor([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])

t1[1]

tensor(2)

t1[1] = 10#对“t1中的第二个元素”进行修改
t1

tensor([ 1, 10, 3, 4, 5, 6, 7, 8, 9, 10])

t11 #t11也会同步发生变化

tensor([ 1, 10, 3, 4, 5, 6, 7, 8, 9, 10])

要使得t11不随t1变化,需要对t11进行深拷贝

t11 = t1.clone()
t11

tensor([ 1, 10, 3, 4, 5, 6, 7, 8, 9, 10])

t1[0] = 100
t1

tensor([100, 10, 3, 4, 5, 6, 7, 8, 9, 10])

t11 #t11不随t1发生改变

tensor([ 1, 10, 3, 4, 5, 6, 7, 8, 9, 10])

五、张量的索引

1、切片

①连续采样

[:] #all

[:n] #从0开始到n

[n:] #从n开始到结束

[start:end] #开始的索引:结束的索引

":"可以理解为→,即 从start→end

正向索引[0,1,2],反向索引[-3,-2,-1]

②隔行采样

[start: end:采样步长step]

③"…"仅仅是为了方便

"…"代表任意多的维度,即全部维度。=a[:, :, :, :]:

a[...].shape 

torch.Size([4, 3, 28, 28])

a[0,...].shape #=a[0,:,:,:]

torch.Size([3, 28, 28])

a[:,0,...].shape #=a[:,0,:,:]

torch.Size([4, 28, 28])

当有"…"出现时,右边的索引需要理解为最右边:

a[...,:2].shape #=a[:,:,:,:2]  

torch.Size([4, 3, 28, 2])

a[0,...,::2].shape

torch.Size([3, 28, 14])

2、一维张量的索引

import torch
import numpy as np
t1 = torch.arange(1,11)
t1

tensor([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])

从左到右,从0开始。张量的索引结果是张量,不是单纯的数值:

t1[0]

tensor(1)

t1[0].item()  # 张量转化成单纯的数,用item()

1

t1[1:8]  # 切片

tensor([2, 3, 4, 5, 6, 7, 8])

t1[1:8:2]

tensor([2, 4, 6, 8])

t1[1::2]#从第二个元素开始索引,一直到结尾,并且每隔两个数取一个

tensor([ 2, 4, 6, 8, 10])

t1[:8:2]#从第一个元素开始索引,一直到第九个元素,并且每隔两个数取一个

tensor([1, 3, 5, 7])

t1[9:1:-1]#step必须大于0
---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

 in 
----> 1 t1[9:1:-1]#step必须大于0


ValueError: step must be greater than zero

2、二维张量索引

t2 = torch.arange(1,10).reshape(3,3)
t2

tensor([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])

[第1行,第2列的元素]:

t2[0,1]   

tensor(2)

第一行,每隔两个元素取一列:

t2[0,::2] 

tensor([1, 3])

第一行第1个和第三个元素:

t2[0,[0,2]] 

tensor([1, 3])

每隔2行取一个,每隔2列取一个:

t2[::2,::2] 

tensor([[1, 3],
[7, 9]])

第一行和第3行的第2列元素:

t2[[0,2],1] 

tensor([2, 8])

3、三维张量的索引

t3 = torch.arange(1,28).reshape(3,3,3)
t3,t3.shape

(tensor([[[ 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, 27]]]),
torch.Size([3, 3, 3]))

索引第二个矩阵中,第二行,第二列的元素:

t3[1,1,1]

tensor(14)

索引第二个矩阵,行和列都是每隔两个取一个:

t3[1,::2,::2] 

tensor([[10, 12],
[16, 18]])

每隔两个取一个矩阵,对于矩阵来说,行和列都是每隔两个取一个:

t3[::2,::2,::2] 

tensor([[[ 1, 3],
[ 7, 9]],
[[19, 21],
[25, 27]]])

4、张量的函数索引

①index_select函数,通过指定index来对张量进行索引

t1

tensor([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])

t1.ndim

1

indices = torch.tensor([1,2])
indices

tensor([1, 2])

torch.index_select(t1,0,indices) #对t1索引值为1和2的元素  (输入张量,维度,索引)

tensor([2, 3])

t2 = torch.arange(12).reshape(4,3)
t2,t2.shape

(tensor([[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8],
[ 9, 10, 11]]),
torch.Size([4, 3]))

indices

tensor([1, 2])

torch.index_select(t2,0,indices)   #

tensor([[3, 4, 5],
[6, 7, 8]])

torch.index_select(t2,1,indices) 

tensor([[ 1, 2],
[ 4, 5],
[ 7, 8],
[10, 11]])

②tensor.view()方法

返回一个类似视图的结果

通过.view还可以改变对象的结构

“视图”的作用是节省空间。接下来切分向量的很多方法,返回结果都是“视图”,而不是新生成的一个对象。

t = torch.arange(6).reshape(2,3)
t

tensor([[0, 1, 2],
[3, 4, 5]])

te = t.view(3,2) #构建一个数据相同但图像不同的“视图”
te

tensor([[0, 1],
[2, 3],
[4, 5]])

t[0]=1 #只修改一个,t和t.view()的值都会变化
t,te

(tensor([[1, 1, 1],
[3, 4, 5]]),
tensor([[1, 1],
[1, 3],
[4, 5]]))

tr = t.view(1,2,3) #维度也可以修改
tr

tensor([[[1, 1, 1],
[3, 4, 5]]])

六、分块与合并 merge or split

1、张量的分片函数

(1)分块:chunk函数

按照某维度对张量进行均匀切分,并且返回结果是原张量的视图view。

t2 = torch.arange(12).reshape(4,3)
t2

tensor([[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8],
[ 9, 10, 11]])

在第0个维度上(按行),进行4等分。返回结果是一个视图,不是一个新对象:

tc = torch.chunk(t2,4,dim=0) 
tc 

(tensor([[0, 1, 2]]),
tensor([[3, 4, 5]]),
tensor([[6, 7, 8]]),
tensor([[ 9, 10, 11]]))

tc[0]

tensor([[0, 1, 2]])

tc[0][0]

tensor([0, 1, 2])

tc[0][0][0]

tensor(0)

修改tc的值,原张量也发生了变化

tc[0][0][0] = 1
t2

tensor([[ 1, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8],
[ 9, 10, 11]])

当原张量不能均分时,chunk不会报错,但会返回其他均分的结果

torch.chunk(t2,3,dim=0)  #次一级均分结果

(tensor([[1, 1, 2],
[3, 4, 5]]),
tensor([[ 6, 7, 8],
[ 9, 10, 11]]))

len(torch.chunk(t2,3,dim=0))

2

len(torch.chunk(t2,5,dim=0))#次一级均分结果

4

(2)拆分:split函数

按长度len进行拆分,既能进行均分,也能进行自定义均分。当然,需要注意的是,split和chunk函数的返回结果一样,都是view。

t2 = torch.arange(12).reshape(4,3)
t2

tensor([[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8],
[ 9, 10, 11]])

第二个参数只输入一个数值时表示均分,第三个参数表示维度:

torch.split(t2,2,0)  

(tensor([[0, 1, 2],
[3, 4, 5]]),
tensor([[ 6, 7, 8],
[ 9, 10, 11]]))

第二个参数输入一个序列时,表示按序列数值进行均分。并且1+3 == 4:
[1,3]表示“第一块长度为1,第二块长度为3

torch.split(t2,[1,3],0)

(tensor([[0, 1, 2]]),
tensor([[ 3, 4, 5],
[ 6, 7, 8],
[ 9, 10, 11]]))

torch.split(t2,[1,1,2],0)

(tensor([[0, 1, 2]]),
tensor([[3, 4, 5]]),
tensor([[ 6, 7, 8],
[ 9, 10, 11]]))

torch.split(t2,[1,2],1)

(tensor([[0],
[3],
[6],
[9]]),
tensor([[ 1, 2],
[ 4, 5],
[ 7, 8],
[10, 11]]))

c.shape为torch.Size([2, 32, 8]),第零维长度一共才2,但是要把长度拆分成2,就无法拆分:

aa,bb = c.split(2,dim=0) 

ValueError Traceback (most recent call last)
in
----> 1 aa,bb = c.split(2,dim=0) #长度一共才2,但是要把长度拆分成2,就无法拆分
ValueError: not enough values to unpack (expected 2, got 1)

2、张量的合并操作

(1)拼接函数:cat

a = torch.zeros(2,3)
a

tensor([[0., 0., 0.],
[0., 0., 0.]])

b = torch.ones(2,3)
b

tensor([[1., 1., 1.],
[1., 1., 1.]])

c = torch.zeros(3,3)
c

tensor([[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]])

默认按行(dim=0)【记住二维张量dim=0表示行】进行拼接:

torch.cat([a,b]) 

tensor([[0., 0., 0.],
[0., 0., 0.],
[1., 1., 1.],
[1., 1., 1.]])

按列进行拼接:

torch.cat([a,b],dim=1)

tensor([[0., 0., 0., 1., 1., 1.],
[0., 0., 0., 1., 1., 1.]])

合并时要确保a和b的dimension一样,其他维度数值一致。形状不匹配时会报错:

torch.cat([a,c],dim=1)
---------------------------------------------------------------------------

RuntimeError                              Traceback (most recent call last)

 in 
----> 1 torch.cat([a,c],dim=1)#形状不匹配时会报错


RuntimeError: Sizes of tensors must match except in dimension 1. Got 2 and 3 in dimension 0 (The offending index is 1)

(2)堆叠函数:stack

torch.stack([a,b])

tensor([[[0., 0., 0.],
[0., 0., 0.]],
[[1., 1., 1.],
[1., 1., 1.]]])

构成三维张量:

torch.stack([a,b]).shape  

torch.Size([2, 2, 3])

维度不匹配:

torch.stack([a,c]) 
---------------------------------------------------------------------------

RuntimeError                              Traceback (most recent call last)

 in 
----> 1 torch.stack([a,c])


RuntimeError: stack expects each tensor to be equal size, but got [2, 3] at entry 0 and [3, 3] at entry 1

七、张量的维度变换

1、masked_select()

x = torch.randn(3,4)
x

tensor([[ 0.6480, 1.5947, 0.6264, 0.6051],
[ 1.6784, 0.2768, -1.8780, -0.1133],
[-0.6442, 0.8570, 0.1677, 0.2378]])

ge(0.5)表示“>=0.5”:

mask = x.ge(0.5)
mask

tensor([[ True, True, True, True],
[ True, False, False, False],
[False, True, False, False]])

取出">=0.5"的数据:

torch.masked_select(x,mask) 

tensor([0.6480, 1.5947, 0.6264, 0.6051, 1.6784, 0.8570])

torch.masked_select(x,mask).shape

torch.Size([6])

2、squeeze函数:删除不必要的维度

squeeze()把dimension=1的全部去掉:

t = torch.zeros(1,1,3,1)
t,t.shape

(tensor([[[[0.],
[0.],
[0.]]]]),
torch.Size([1, 1, 3, 1]))

torch.squeeze(t).shape#剔除所有属性为1的维度

torch.Size([3])

t1 = torch.zeros(1,1,3,2,1,2)
t1.shape

torch.Size([1, 1, 3, 2, 1, 2])

torch.squeeze(t1).shape

torch.Size([3, 2, 2])

例:torch.Size([1, 32, 1, 1])四维

b.squeeze().shape #把dimension=1的全部去掉

torch.Size([32])

b.squeeze(0).shape #挤压掉第0维

torch.Size([32, 1, 1])

b.squeeze(-1).shape  #挤压掉最后一维

torch.Size([1, 32, 1])

b.squeeze(1).shape #挤压掉第一维

torch.Size([1, 32, 1, 1])

b.squeeze(-4).shape #挤压掉-4维

torch.Size([32, 1, 1])

3、unsqueeze函数:手动升维

(1)调用方法1:torch.unsqueeze(t, dim=n)

t = torch.zeros(1,2,1,2)
t.shape  #四维

torch.Size([1, 2, 1, 2])

在第0位索引上升高一个维度变成五维:

torch.unsqueeze(t,dim=0).shape 

torch.Size([1, 1, 2, 1, 2])

注:.shape返回多少个元素,张量就有多少个维度

(2)调用方法2:a.unsqueeze(dim=n)

a.shape

torch.Size([4, 1, 28, 28])

对a升高维度。参数可以为(-5 ~ 0 ~ 4)。

在0索引前面插入了一个额外的维度,可以理解成第几组:

a.unsqueeze(0).shape 

torch.Size([1, 4, 1, 28, 28])

在末尾插入一个额外的维度,可以理解为像素的属性:

a.unsqueeze(-1).shape 

torch.Size([4, 1, 28, 28, 1])

a.unsqueeze(4).shape 

torch.Size([4, 1, 28, 28, 1])

a.unsqueeze(-4).shape

torch.Size([4, 1, 1, 28, 28])

a.unsqueeze(-5).shape

torch.Size([1, 4, 1, 28, 28])

a.unsqueeze(5).shape #超过了维度允许范围

IndexError Traceback (most recent call last)
in
----> 1 a.unsqueeze(5).shape #超过了维度允许范围
IndexError: Dimension out of range (expected to be in range of [-5, 4], but got 5)

a = torch.tensor([1.2,2.3])
a

tensor([1.2000, 2.3000])

a.shape

torch.Size([2])

a.unsqueeze(-1)

tensor([[1.2000],
[2.3000]])

a.unsqueeze(-1).shape

torch.Size([2, 1])

a.unsqueeze(0)

tensor([[1.2000, 2.3000]])

a.unsqueeze(0).shape

torch.Size([1, 2])

b = torch.rand(32)
b.shape

torch.Size([32])

f = torch.rand(4,32,14,14)

bias相当于给每个channel上所有像素增加一个偏置

b = b.unsqueeze(1).unsqueeze(2).unsqueeze(0)
b.shape

torch.Size([1, 32, 1, 1])

八、扩展: expand 和 repeat效果一样

1、expand:broadcasting #只改变理解方式,不增加数据

①expand仅限于原来维度是1的可以改变,原来不为1的数要复制:

b = torch.rand(1,32,1,1)
b.expand(4,32,14,14).shape   

torch.Size([4, 32, 14, 14])

②-1表示维度保持不变

b.expand(-1,32,-1,-1).shape 

torch.Size([1, 32, 1, 1])

③-4是bug,生成的结果是无意义的:

b.expand(-1,32,-1,-4).shape 

torch.Size([1, 32, 1, -4])

2、repeat:memory copied #增加数据

b.shape

torch.Size([1, 32, 1, 1])

repeat的参数表示重复的次数:4表示对0维重复4次,32表示对1维重复32次。

b.repeat(4,32,1,1).shape 

torch.Size([4, 1024, 1, 1])

b.repeat(4,1,1,1).shape

torch.Size([4, 32, 1, 1])

b.repeat(4,1,32,32).shape

torch.Size([4, 32, 32, 32])

①矩阵的转置 .t

a = torch.randn(3,4)
a.shape

torch.Size([3, 4])

a.t()
a.t().shape

torch.Size([4, 3])

②transpose实现维度两两交换

a = torch.rand(4,3,32,32)
a1 = a.transpose(1,3).contiguous().view(4,3*32*32).view(4,3,32,32)

#这种写法会造成数据污染,是错误的,最后一个view里面应该是(4,32,32,3),然后用transpose变回去

#正确写法:要把维度信息跟踪住,view的时候要记住维度信息,展开的时候要按维度展开,不要错乱

a2 = a.transpose(1,3).contiguous().view(4,3*32*32).view(4,32,32,3).transpose(1,3)

#transpose包含了要交换的两个维度[b,c,h,w]→[b,w,h,c]
#数据的维度顺序必须与存储顺序一致,用.contiguous把数据变成连续的
#.view(4,33232) [b,whc]
#.view(4,3,32,32) [b,w,h,c]
#.transpose(1,3) [b,c,g,w]

a1.shape,a2.shape

(torch.Size([4, 3, 32, 32]), torch.Size([4, 3, 32, 32]))

torch.all(torch.eq(a,a1))

tensor(False)

torch.all(torch.eq(a,a2))

tensor(True)

③permute

a = torch.rand(4,3,28,28)
a.transpose(1,3).shape

torch.Size([4, 28, 28, 3])

b = torch.rand(4,3,28,32)             #b c h w
b.transpose(1,3).shape                #b w h c   交换一、三维

torch.Size([4, 32, 28, 3])

b.transpose(1,3).transpose(1,2).shape #b h w c

torch.Size([4, 28, 32, 3])

#通过上述几步,实现了把 [b,c,h,w] 变成 [b,h,w,c]
#可以通过permute,直接用索引完成转变

b.permute(0,2,3,1).shape              #b h w c

torch.Size([4, 28, 32, 3])

#[b,h,w,c]是numpy储存图片的格式,需要转变为这种形式才可以导出numpy

④broadcast:扩展维度,扩张时不需要copy数据

1、如果前面没有维度,则需要在前面插入一个新的维度
2、把新加入的维度扩张成为相同的size。例:A,B。B没有内容,为B插入一个新维度,并把B扩张成和A相同的维度
3、经过卷积神经网络,生成feature maps:[4,32,14,14]
4、Bias:32,1,1=>1,32,1,1=>4,32,14,14

why broadcasting?

for actual demanding:

·regulation:[class,student,score],add bias for every students: +5score
·because [4,32,8]can only add with [4,32,8],
·on the contrary,we hope [4,32,8]can add with [5.0](0dimension tensor)

key:Match from last dim(从小维开始匹配)

situation 1:

·if current dim=1,expand to same
    eg.A[4,32,8](four classes,32 students,8 lessons),now we want the score of each lesson be added 5.
    B[1] => B[1,1,1] => B[4,32,8]
A[4,32,14,14]
B[1,32,1,1] => [4,32,14,14]

situation 2:

·if either has no dim,insert one dim and expand to same
    now we just want the score of English be added 5
    B[1,1,8] => [4,32,8]   (through[0,0,5,0,0,0,0,0])
A[4,32,14,14]
B[14,14] => [1,1,14,14] =>[4,32,14,14]

situation 3:

·NOT broadcasting-able
A[4,32,14,14]
B[2,32,14,14]#dim 0 has distinct dim,NOT size 1,so cannot insert and expand to same

九、张量的科学运算

1、数学运算与算子

作为PyTorch中执行深度学习的基本数据类型,张量(Tensor) 也拥有非常多的数学运算函数和方法,以及对应的一系列计算规则。在PyTorch中, 能够作用与Tensor的运算,被统称作为算子。并且相比于NumPy, PyTorch给出 了更加规范的算子(运算)的分类,从而方便用户在不同场景下调用不同类型的算子(运算)。

数学运算的分类

PyToch总共为Tensor设计了六大类数学运算,分别是:

1.逐点运算(Pointwise Ops) :指的是针对Tensor中每个元素执行的相同运算操作;
2.规约运算(Reduction Ops) :指的是对于某一张量进行操作得出某种总结值;
3.比较运算(Comparison Ops) :指的是对多个张量进行比较运算的相关方法;
4.谱运算(Spectral Ops) :指的是涉及信号处理傅里叶变化的操作;
5.BLAS和LAPACK运算: 指的是基础线性代数程序集(Basic Linear Algeria Subprograms)和线性代数包(Linear Algeria Package)中定义的、主要用于线性代数科学计算的函数和方法;
6.其他运算(Other Ops) :其他未被归类的数学运算。

由于谱运算(Spectral Ops)前期不会涉及,而要理解傅里叶变换本身需要更多额外的数学基础,而很多其他运算,我们在前面介绍张量的基本方法时已经介绍,因此接下来将主要围绕逐点运算、规约运算、比较运算和线性代数运算四块进行讲解,而线性代数部分由于涉及到大量的数学内容,因此将放在本课中单独进行讲解。

2、不同形状的张量计算

广播的特性是在不同形状的张量进行计算时,一个或多个张量通过隐式转化,转化成相同形状的两个张量,从而完成计算的特性。但并非任何两个不同形状的张量都可以通过广播特性进行计算,因此,我们需要了解广播的基本规则及其核心依据。

2.1标量和任意形状的张量

标量可以和任意形状的张量进行计算,计算过程就是标量和张量的每一个元素进行计算。

你可能感兴趣的:(机器学习,pytorch,深度学习,python)