自动微分
Pytorch支持自动微分,即自动计算梯度,无需人工参与,既可以自动计算一个函数关于一个变量在某一取值下的导数。通过该功能,就可以使用基于梯度的方法对参数(变量)进行优化(也叫学习或训练)。使用Pytorch计算梯度非常容易,仅需要执行tensor.backward() 函数,就可以通过反向传播算法自动完成。
需要注意,为了计算一个函数关于某一个变量的导数,Pytorch要求显示设置该变量(张量)是可求导的,否则默认不能对该变量求导。具体设置方法为:在张量生成时,设置requires_grad=True 。
因此,计算
z
=
(
x
+
y
)
×
(
y
?
2
)
z=(x+y)\times (y-2)
z=(x+y)×(y?2)中,当
x
=
2
,
y
=
3
x=2,y=3
x=2,y=3时,求
d
z
/
d
x
dz/dx
dz/dx和
d
z
/
d
y
dz/dy
dz/dy的值:
import torch
x=torch.tensor([2.],requires_grad=True)
y=torch.tensor([3.],requires_grad=True)
z=(x+y)*(y-2)
print(z)
z.backward()
print(x.grad,y.grad)
我们手动验证:
d
z
d
x
=
y
?
2
,
d
z
d
y
=
x
+
2
y
?
2
\frac{dz}{dx}=y-2,\frac{dz}{dy}=x+2y-2
dxdz?=y?2,dydz?=x+2y?2当
x
=
2
,
y
=
3
x=2,y=3
x=2,y=3时,梯度确实为
1
1
1和
6
6
6。
张量调整形状
参与运算的张量需要满足一定的形状,比如两个矩阵相乘,前一个矩阵的第二维应该和后一个矩阵的第一维相同。Pytorch一共有4种调整张量形状的函数,分别为view,reshape,transpose,permute 。
view函数的参数用于设置新的张量形状,因此,需要保证张量总的元素个数不变:
x=torch.tensor([1,2,3,4,5,6])
print(x.shape)
y=x.view(-1,3)
print(y.size())
进行view操作的张量要求是连续的(Contiguous),可以调用is_conuous 函数判断一个张量是否为连续的。如果张量非连续,则需要先调用contiguous 函数将其变为连续的,才能使用view。为了克服这个缺点,Pytorch提供了reshape,可以对非连续张量调整形状,reshape与view的功能一致。
transpose(转置)函数用于交换张量中的两个维度,参数分别为相应的维序号:
x=torch.tensor([[1,2,3],[4,5,6]])
print(x)
"""
torch.Size([2, 3])
tensor([[1, 2, 3],
[4, 5, 6]])
"""
y=x.transpose(0,1)
print(y)
"""
torch.Size([3, 2])
tensor([[1, 4],
[2, 5],
[3, 6]])
"""
transpose只能同时交换两个维度,若要交换更多维度,需要多次调用该函数,为了便捷,Pytorch提供了permute函数,其需要提供全部维度信息作为参数(即使有些维度无须交换也要提供):
x=torch.tensor([[[1,2,3],[4,5,6]]])
print(x,x.shape)
"""
tensor([[[1, 2, 3],
[4, 5, 6]]]) torch.Size([1, 2, 3])
"""
y=x.permute(2,0,1)
print(y,y.shape)
"""
tensor([[[1, 4]],
[[2, 5]],
[[3, 6]]]) torch.Size([3, 1, 2])
"""
广播机制
在上面的张量运算中,都是假设两个参与运算的张量形状相同。在有些情况下,即使两个张量的形状不同,也可以通过广播机制执行按元素计算。具体的执行规则为:
- 首先,对其中一个或同时对两个张量的元素进行复制,使得两个张量形状相同;
- 然后,在扩展之后的张量上再执行按元素运算。
通常是沿着长度为1的维度进行扩展,示例如下:
x=torch.arange(1,4).view(3,1)
y=torch.arange(4,6).view(1,2)
print(x)
"""
tensor([[1],
[2],
[3]])
"""
print(y)
"""
tensor([[4, 5]])
"""
生成两个张量,形状分别为(3,1)和(1,2),显然,它们不能直接执行按元素运算。因此,在执行按元素运算之前,需要将它们扩展(广播)为形状(3,2)的张量,具体扩展方法为将x 的第1列复制到第2列,将y 的第1行复制到第2行,第3行。
比如,执行加法:
print(x+y)
"""
tensor([[5, 6],
[6, 7],
[7, 8]])
"""
索引与切片
与Python列表类似,Pytorch中也可以对张量进行索引和切片操作,规则也与Python基本一致,即索引值是从0开始的,切片[m:n] 的范围是从m 开始到n 前一个元素结束(左闭右开)。与Python不同,Pytorch可以对张量的任意一个维度进行索引或切片:
x=torch.arange(12).view(3,4)
print(x)
"""
tensor([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
"""
print(x[1,3])
"""
tensor(7)
"""
print(x[1])
"""
tensor([4, 5, 6, 7])
"""
print(x[1:3])
"""
tensor([[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
"""
print(x[:,2])
"""
tensor([ 2, 6, 10])
"""
print(x[:,2:4])
"""
tensor([[ 2, 3],
[ 6, 7],
[10, 11]])
"""
x[:,2:4]=100
print(x)
"""
tensor([[ 0, 1, 100, 100],
[ 4, 5, 100, 100],
[ 8, 9, 100, 100]])
"""
降维与升维
升维通过调用torch.unsqueeze(input,dim,out=None) 函数,对输入张量的dim 位置插入维度1,并返回一个新的张量。与索引相同,dim 的值可以为负数。
降维恰好相反,使用torch.squeeze(input,dim,out=None) 函数:
- 在不指定
dim 时,张量中形状为1的所有维都将被去除。比如输入形状为
(
A
,
1
,
B
,
1
,
C
,
1
,
D
)
(A,1,B,1,C,1,D)
(A,1,B,1,C,1,D)的张量,输出形状变成
(
A
,
B
,
C
,
D
)
(A,B,C,D)
(A,B,C,D); - 当给定
dim 时,降维操作只在给定维度上,比如输入形状为
(
A
,
1
,
B
)
(A,1,B)
(A,1,B),如果设置squeeze(input,dim=0) ,张量形状将保持不变,只有设置squeeze(input,dim=1) ,形状才会变成
(
A
,
B
)
(A,B)
(A,B)。
|