Broadcast自动扩展

  • 维度扩展
  • 扩展无需复制数据

broadcast原理图

对于一个shape为`torch.size([x1,x2,x3,...,xn])`的`tensor`,靠近

x1那一端为大维度,靠近xn那一端为小维度。那么对于broadcast有: 小维度指定,大维度随意这一规律(因为在大维度上进行广播)

拼接与拆分

  • cat
  • stack
  • split
  • chunk

cat

主要的功能是将tensor拼接起来

1
2
3
4
In [3]: a=torch.rand(4,32,8)
In [4]: b=torch.rand(5,32,8)
In [5]: torch.cat([a,b],dim=0).shape
Out[5]: torch.Size([9, 32, 8])
`dim`参数表示的是在哪个维度上进行合并。如果是`n`维tensor则取值范围是

[0,n)

stack

stack的功能和cat有些像,但是他要求的是所拼接的两个tensor的shape必须完全一样。并且拼接后会在拼接维度之前增加一个维度。

注意:shape必须完全一样

然后在所拼接维度之前会新增加一个维度,用于区分是拼接的哪一块(前一块或后一块)

1
2
3
4
In [3]: a1=torch.rand(5,3,16,32)
In [4]: a2=torch.rand(5,3,16,32)
In [5]: torch.stack([a1,a2],dim=1).shape
Out[5]: torch.Size([5, 2, 3, 16, 32])

split

split按照长度对tensor进行拆分的函数,一般传入两个参数,第一个参数代表的是所拆维度的长度(长度不仅可以用标量表示固定的长度,也可以用list来表示不固定的长度,详情见下面的例子),第二个参数代表的是所拆的维度是第几维度。

1
2
3
4
5
6
7
8
In [3]: c=torch.rand(3,32,8)
In [4]: aa,bb,cc=c.split(1,dim=0)
In [5]: aa.shape,bb.shape,cc.shape
Out[5]: (torch.Size([1, 32, 8]), torch.Size([1, 32, 8]), torch.Size([1, 32, 8]))
In [6]: aa,bb=c.split([2,1],dim=0)
In [7]: aa.shape,bb.shape
Out[7]: (torch.Size([2, 32, 8]), torch.Size([1, 32, 8]))

chunck

chunck按照数量对tensor进行拆分的函数,一般传入两个参数,第一个参数代表的是所拆维度要拆出来的数量,第二个参数代表的是所拆的维度是第几维度。

1
2
3
4
5
In [3]: d=torch.rand(6,32,8)
In [4]: c=torch.rand(3,32,8)
In [5]: aa,bb,cc=d.chunk(3,dim=0)
In [6]: aa.shape,bb.shape,cc.shape
Out[6]: (torch.Size([2, 32, 8]), torch.Size([2, 32, 8]), torch.Size([2, 32, 8]))

数学运算

  • 基本四则运算(add/minus/multiply/divide)
  • 矩阵乘法(Matmul)
  • 乘方运算(Pow)
  • 开方运算(Sqrt/rsqrt)
  • 近似运算(Round)

基本四则运算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
In [3]: a=torch.tensor([[2,4,6],[8,10,12]])
In [4]: b=torch.ones(2,3)*2
In [5]: a+b
Out[5]:
tensor([[ 4., 6., 8.],
[10., 12., 14.]])
In [6]: a-b
Out[6]:
tensor([[ 0., 2., 4.],
[ 6., 8., 10.]])
In [7]: a*b
Out[7]:
tensor([[ 4., 8., 12.],
[16., 20., 24.]])
In [8]: a/b
Out[8]:
tensor([[1., 2., 3.],
[4., 5., 6.]])
In [9]: a/(a/b)
Out[9]:
tensor([[2., 2., 2.],
[2., 2., 2.]])

pytorch中还专门有定义好的四则运算的函数和以上这些运算符号所运算出来的效果相同,他们分别是。

  • torch.add
  • torch.minus
  • torch.mul
  • torch.div
1
2
3
4
5
6
7
8
9
In [10]: torch.all(torch.eq(a+b,torch.add(a,b)))
Out[10]: tensor(True)
In [11]: torch.all(torch.eq(a-b,torch.sub(a,b)))
Out[11]: tensor(True)
In [12]: torch.all(torch.eq(a*b,torch.mul(a,b)))
Out[12]: tensor(True)
In [13]: torch.all(torch.eq(a/b,torch.div(a,b)))
Out[13]: tensor(True)

注意:这里的乘法和除法,不同于矩阵乘法和除法,只是将两个矩阵对应位置的数进行相乘或相除操作。

这里还有一个整除运算符号//没有讲到,好像并没有找到与他对应的函数

矩阵乘法

  • torch.mm(只适用于二维tensor,不推荐)
  • torch.matmul(适用于任意维度的矩阵)
  • @(同上torch.matmul)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
In [3]: a=torch.tensor([[2,4],[8,12]])
In [4]: b=torch.tensor([[1,2],[3,4]])
In [5]: torch.mm(a,b)
Out[5]:
tensor([[14, 20],
[44, 64]])
In [6]: torch.matmul(a,b)
Out[6]:
tensor([[14, 20],
[44, 64]])
In [7]: a@b
Out[7]:
tensor([[14, 20],
[44, 64]])

BP全连接神经网络的一个例子

x@w.t()+bx@w.t()+b

1
2
3
4
In [4]: x=torch.rand(4,784)
In [5]: w=torch.rand(512,784)
In [6]: (x@w.t()).shape
Out[6]: torch.Size([4, 512])

高于2维矩阵乘法的原理

取最小两个维度的内容进行矩阵乘法,保留高维度的的大小。其实相当于是支持了多个矩阵并行相乘

高维度大小不一定要完全一样,但是要符合broadcasting原则,具体见下面的例子

1
2
3
4
5
6
7
8
9
10
In [3]: a=torch.rand(4,3,28,64)
In [4]: b=torch.rand(4,3,64,32)
In [5]: torch.matmul(a,b).shape
Out[5]: torch.Size([4, 3, 28, 32])
In [6]: b=torch.rand(4,1,64,32)
In [7]: torch.matmul(a,b).shape
Out[7]: torch.Size([4, 3, 28, 32])
In [8]: b=torch.rand(4,2,64,32)
In [9]: torch.matmul(a,b).shape
Traceback

乘方运算

  • .pow():括号中写次方数
  • **:同上面的pow函数,乘方运算符
  • sqrt():开平方运算
  • rsqrt():开平方后求倒数。
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
28
29
In [3]: a=torch.full([2,2],3)
In [4]: a
Out[4]:
tensor([[3, 3],
[3, 3]])
In [5]: a.pow(2)
Out[5]:
tensor([[9, 9],
[9, 9]])
In [6]: a**2
Out[6]:
tensor([[9, 9],
[9, 9]])
In [7]: torch.all(torch.eq(a**2,a.pow(2)))
Out[7]: tensor(True)
In [8]: aa=a**2
In [9]: aa.sqrt()
Out[9]:
tensor([[3., 3.],
[3., 3.]])
In [10]: aa.rsqrt()
Out[10]:
tensor([[0.3333, 0.3333],
[0.3333, 0.3333]])
In [11]: aa**(0.5)
Out[11]:
tensor([[3., 3.],
[3., 3.]])

exp和log和log2和log10

看名字应该就大概明白功能了,这里不再赘述,直接上一个例子吧

1
2
3
4
5
6
7
8
9
10
In [14]: a=torch.exp(torch.ones(2,2))
In [15]: a
Out[15]:
tensor([[2.7183, 2.7183],
[2.7183, 2.7183]])
In [16]: torch.log(a)
Out[16]:
tensor([[1., 1.],
[1., 1.]])

1
2
3
4
5
6
7
8
9
10
11
In [18]: a=torch.ones(2,2)*2
In [19]: torch.log2(a)
Out[19]:
tensor([[1., 1.],
[1., 1.]])
In [20]: a=torch.ones(2,2)*100
In [21]: torch.log10(a)
Out[21]:
tensor([[2., 2.],
[2., 2.]])

近似运算

  • .floor():向下取整
  • .ceil():向上取整
  • .round():四舍五入
  • .trunc():裁剪——只保留整数部分
  • /frac():裁剪——只保留小数部分
1
2
3
4
5
6
7
8
9
10
In [3]: a=torch.tensor(3.14)
In [4]: a.floor(),a.ceil(),a.trunc(),a.frac()
Out[4]: (tensor(3.), tensor(4.), tensor(3.), tensor(0.1400))
In [5]: a=torch.tensor(3.499999)
In [6]: a.round()
Out[6]: tensor(3.)
In [7]: a=torch.tensor(3.5)
In [8]: a.round()
Out[8]: tensor(4.)

梯度裁剪※

我们在做和机器学习相关的项目中时,有可能会碰到这种情况,就是梯度太大或太小(接近0)从而导致我们的训练结果并不是非常的理想,因此我们可以采用梯度裁剪的方法,将梯度现有梯度控制在一个范围内,有效避免这类问题。

所使用的函数是clamp,一般传入一个或两个参数。

如果之传入一个参数,该参数表示梯度的最小值,因此比该值小的梯度都会被强行增大到该值。

如果传入两个参数,则两个参数分别表示最小值和最大值,比最小值小的值会强行变为设定的最小值,比最大值大的值会强行变为设定的最大值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
In [3]: grad=torch.rand(2,3)*30-15
In [4]: grad.max()
Out[4]: tensor(6.2306)
In [5]: grad.min()
Out[5]: tensor(-11.8838)
In [6]: grad.median()
Out[6]: tensor(-0.5767)
In [7]: grad.clamp(5)
Out[7]:
tensor([[5.0000, 5.0000, 5.0000],
[5.0000, 5.0000, 6.2306]])
In [8]: grad
Out[8]:
tensor([[ 3.8570, -0.2229, -11.8838],
[ -8.5261, -0.5767, 6.2306]])
In [9]: grad.clamp(0)
Out[9]:
tensor([[3.8570, 0.0000, 0.0000],
[0.0000, 0.0000, 6.2306]])
In [10]: grad.clamp(0,5)
Out[10]:
tensor([[3.8570, 0.0000, 0.0000],
[0.0000, 0.0000, 5.0000]])

统计属性

  • norm:范数
  • mean,sum:平均值,和
  • prod累乘
  • max,min,argmin,argmax:最大值,最小值,最小值所在位置,最大值所在位置
  • kthvalue,topk:求第k个值,求前k个值

norm

一般是1范数和2范数使用的较多,但下面还是会将各个范数的含义进行简要的介绍。

  • 0范数:矩阵中非零元素的个数
  • 1范数:矩阵中各个元素的绝对值之和
  • 2范数:矩阵中各个元素平方和的1/2次方,又被称为Euclidean范数或者Frobenius 范数。
  • p范数:为x向量(或矩阵)各个元素绝对值p次方和的1/p次方。

norm函数一般需要一个或者两个参数,如果只是提供一个参数,则该参数表示的是第几范数,如果提供2个参数,第一个参数意义同上,第二个参数的意义是在哪个维度上进行范数运算。

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
28
29
30
31
32
33
34
35
36
In [3]: a=torch.tensor([1.,2.,3.,4.,5.,6.,7.,8.])
In [4]: b=a.view(2,4)
In [5]: c=a.view(2,2,2)
In [6]: b
Out[6]:
tensor([[1., 2., 3., 4.],
[5., 6., 7., 8.]])
In [7]: c
Out[7]:
tensor([[[1., 2.],
[3., 4.]],
[[5., 6.],
[7., 8.]]])
In [8]: a.norm(1),b.norm(1),c.norm(1)
Out[8]: (tensor(36.), tensor(36.), tensor(36.))
In [9]: a.norm(2),b.norm(2),c.norm(2)
Out[9]: (tensor(14.2829), tensor(14.2829), tensor(14.2829))
In [10]: b.norm(1,dim=1)
Out[10]: tensor([10., 26.])
In [11]: b.norm(1,dim=0)
Out[11]: tensor([ 6., 8., 10., 12.])
In [12]: b.norm(2,dim=1)
Out[12]: tensor([ 5.4772, 13.1909])
In [13]: c.norm(1,dim=0)
Out[13]:
tensor([[ 6., 8.],
[10., 12.]])
In [14]: c.norm(1,dim=1)
Out[14]:
tensor([[ 4., 6.],
[12., 14.]])
In [15]: c.norm(1,dim=2)
Out[15]:
tensor([[ 3., 7.],
[11., 15.]])

mean,max,min,sum,prod

1
2
3
4
5
6
7
8
9
10
11
12
In [3]: a=torch.arange(8).view(2,4).float()
In [4]: a
Out[4]:
tensor([[0., 1., 2., 3.],
[4., 5., 6., 7.]])
In [5]: a.min(),a.max(),a.mean(),a.prod()
Out[5]: (tensor(0.), tensor(7.), tensor(3.5000), tensor(0.))
In [6]: a.sum()
Out[6]: tensor(28.)
In [7]: a.argmax(),a.argmin()
Out[7]: (tensor(7), tensor(0))

可以发现argmaxargmin在寻找最大最小值的时候,是将整个tensor变为了一个一维向量的!如果我们想求每一行的一个最大值或最小值的位置,我们可以采用以下的这种做法。

1
2
3
In [3]: a=torch.randn(4,10)
In [4]: a.argmax(dim=1)
Out[4]: tensor([3, 0, 6, 9])

dim&keepdim

这两个都是函数中的参数,dim的作用在上面我们已经演示过了,这里就不再赘述。keepdim设置的是返回的答案是否要和原来的矩阵保持相同的维度信息。

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
28
In [3]: a=torch.randn(4,10)
In [4]: a.argmax(dim=1)
Out[4]: tensor([3, 0, 6, 9])
In [5]: a.max(dim=1)
Out[5]:
torch.return_types.max(
values=tensor([2.2597, 1.0554, 0.9288, 1.1149]),
indices=tensor([3, 0, 6, 9]))
In [6]: a.argmax(dim=1)
Out[6]: tensor([3, 0, 6, 9])
In [7]: a.max(dim=1,keepdim=True)
Out[7]:
torch.return_types.max(
values=tensor([[2.2597],
[1.0554],
[0.9288],
[1.1149]]),
indices=tensor([[3],
[0],
[6],
[9]]))
In [8]: a.argmax(dim=1,keepdim=True)
Out[8]:
tensor([[3],
[0],
[6],
[9]])

topk,kthvalue

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
28
29
30
31
32
33
34
35
36
37
38
39
In [3]: a=torch.randn(4,10)
In [4]: a.topk(3,dim=1)
Out[4]:
torch.return_types.topk(
values=tensor([[2.0085, 0.5275, 0.4304],
[1.5748, 0.8510, 0.6699],
[1.1440, 0.8921, 0.7946],
[0.9630, 0.8487, 0.8158]]),
indices=tensor([[4, 0, 8],
[9, 3, 5],
[5, 9, 0],
[6, 9, 4]]))
In [5]: a.topk(3,dim=1,largest=False)
Out[5]:
torch.return_types.topk(
values=tensor([[-1.8353, -1.5600, -0.7452],
[-1.1623, -1.0671, -0.2221],
[-1.1269, -0.3777, -0.2676],
[-2.1377, -0.2639, -0.0832]]),
indices=tensor([[2, 3, 9],
[1, 8, 4],
[7, 8, 6],
[8, 1, 5]]))
In [6]: a.kthvalue(1,dim=1)
Out[6]:
torch.return_types.kthvalue(
values=tensor([-1.8353, -1.1623, -1.1269, -2.1377]),
indices=tensor([2, 1, 7, 8]))
In [7]: a.kthvalue(1)
Out[7]:
torch.return_types.kthvalue(
values=tensor([-1.8353, -1.1623, -1.1269, -2.1377]),
indices=tensor([2, 1, 7, 8]))
In [8]: a.kthvalue(9)
Out[8]:
torch.return_types.kthvalue(
values=tensor([0.5275, 0.8510, 0.8921, 0.8487]),
indices=tensor([0, 3, 9, 9]))

对于topk函数默认是从大到小,如果把largest改为False则是从小到大。kthvalue函数默认是从小到大

比较

  • >,>=,<=,!=,==

  • torch.eq(a,b)

以上两种都是比较矩阵对应位置的数,因此返回的值是一个同大小的矩阵。

  • torch.equal(a,b):判断两个矩阵是否完全相等
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
28
29
30
31
32
33
34
In [3]: a=torch.randn(4,10)
In [4]: a>0
Out[4]:
tensor([[ True, True, True, True, True, True, False, False, True, True],
[ True, True, True, True, True, True, False, True, False, False],
[False, True, False, True, False, True, False, True, True, False],
[False, True, False, True, False, False, False, False, True, True]])
In [5]: torch.gt(a,0)
Out[5]:
tensor([[ True, True, True, True, True, True, False, False, True, True],
[ True, True, True, True, True, True, False, True, False, False],
[False, True, False, True, False, True, False, True, True, False],
[False, True, False, True, False, False, False, False, True, True]])
In [6]: a!=0
Out[6]:
tensor([[True, True, True, True, True, True, True, True, True, True],
[True, True, True, True, True, True, True, True, True, True],
[True, True, True, True, True, True, True, True, True, True],
[True, True, True, True, True, True, True, True, True, True]])
In [7]: a=torch.ones(2,3)
In [8]: b=torch.randn(2,3)
In [9]: torch.eq(a,b)
Out[9]:
tensor([[False, False, False],
[False, False, False]])
In [10]: torch.eq(a,a)
Out[10]:
tensor([[True, True, True],
[True, True, True]])
In [11]: torch.equal(a,a)
Out[11]: True
In [12]: torch.equal(a,b)
Out[12]: False

高阶操作

  • where
  • gather

where

torch.where(condition,x,y)

where函数返回一个tensor,这个tensor中的元素不是从x中选出来的就是从y中选出来的,选择判断条件如下所示:

outi={xiif conditioniyiotherwiseout_i=\begin{cases} x_i \quad if\ condition_i\\ y_i\quad otherwise \end{cases}

condition也是一个tensor,和x,y的大小一样。

如果对应位置的conditon值为1,则取x对应位置的数据,否则取y对应位置的数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
In [3]: cond=torch.rand(2,2)
In [4]: a=torch.zeros(2,2)
In [5]: b=torch.ones(2,2)
In [6]: cond
Out[6]:
tensor([[0.5486, 0.6658],
[0.9244, 0.3691]])
In [7]: a
Out[7]:
tensor([[0., 0.],
[0., 0.]])
In [8]: b
Out[8]:
tensor([[1., 1.],
[1., 1.]])
In [9]: torch.where(cond>0.6,a,b)
Out[9]:
tensor([[1., 0.],
[0., 1.]])

虽然我们也可以用Python自带的for循环和if语句来实现同样的功能,但是这样的话由于这些语句运行在CPU上,我们不能享受到PyTorch库中带来的GPU加速的优势,因此会运行的更慢一些。

gather

torch.gather(input,dim,index,out=None)

gather函数实现的功能类似于查表函数,给定一个初始表,再给定一个索引表,自动生成一张按照索引表查完初始表的表。

原理图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
In [3]: prob=torch.randn(4,10)
In [4]: idx=prob.topk(dim=1,k=3)
In [5]: idx=idx[1]
In [6]: idx
Out[6]:
tensor([[9, 6, 7],
[0, 3, 2],
[1, 4, 2],
[1, 5, 6]])
In [7]: label=torch.arange(10)+100
In [8]: label
Out[8]: tensor([100, 101, 102, 103, 104, 105, 106, 107, 108, 109])
In [9]: torch.gather(label.expand(4,10),dim=1,index=idx.long())
Out[9]:
tensor([[109, 106, 107],
[100, 103, 102],
[101, 104, 102],
[101, 105, 106]])

总结

以上便是PyTorch中常用的操作了,后续就是实战环节了。


本站由 @anonymity 使用 Stellar 主题创建。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。