一、引言
代码参考:《深度学习入门之基于PyTorch和TensorFlow的理论与实现》。
代码源可以参考这里。会在实验与学习中加入一些自己的理解。
二、张量部分
1、创建张量。
张量是PyTorch里基本的运算单位,类似NumPy中的数组。但张量可以在GPU版本的PyTorch上运行,可NumPy中的数组只能在CPU版本的PyTorch上运行。
1°创建一个随机初始化的 Tensor 。
x = torch.randn(2,2)
2°直接把 Python 列表构建成 Tensor 。
x = torch.tensor([[1, 2], [3, 4]])
3°创建一个全零 Tensor 。
x = torch.zeros(2,2)
结果如下:
tensor([[0., 0.],
[0., 0.]])
4°基于现有的 Tensor 创建新的 Tensor 。
x = torch.zeros(2,2)
y = torch.ones_like(x)
print(x)
print(y)
torch.ones_like函数和torch.zeros_like函数的基本功能是根据给定张量,生成与其形状相同的全1张量或全0张量。在这两个函数中,还可以指定数据类型、设备、是否计算梯度等信息,可以结合具体场景灵活使用。结果如下。
tensor([[0., 0.],
[0., 0.]])
tensor([[1., 1.],
[1., 1.]])
5°指定 Tensor 数据类型。
x = torch.ones(2, 2, dtype=torch.long)
print(x)
返回结果。
tensor([[0., 0.],
[0., 0.]])
tensor([[1., 1.],
[1., 1.]])
2、Terson 数学运算。
1°两个 Tensor 相加。
x = torch.ones(2,2)
y = torch.ones(2,2)
z = x + y #等价于z = torch.add(x, y)
print(z)
返回结果。
tensor([[2., 2.],
[2., 2.]])
2°使用._add()实现相加后的替换。
x = torch.ones(2,2)
y = torch.ones(2,2)
y.add_(x)
print(y)
返回结果。
tensor([[2., 2.],
[2., 2.]])
3°Tenosr乘法。包含对应元素相乘、矩阵相乘两种形式。
对应元素相乘。
x = torch.tensor([[1, 2], [3, 4]])
y = torch.tensor([[1, 2], [3, 4]])
x.mul(y)
返回结果。
tensor([[ 1, 4],
[ 9, 16]])
矩阵相乘。这种形式是比较常见的。
x = torch.tensor([[1, 2], [3, 4]])
y = torch.tensor([[1, 2], [3, 4]])
x.mm(y)
返回结果。
tensor([[ 7, 10],
[15, 22]])
3、张量与NumPy数组。
Tensor 与 NumPy 数组可以互相转换,两者共享内存位置,一个改变,另一个也随之改变。运行前,先把NumPy导入,import numpy as np。
1°Tensor 转化为 NumPy 数组。
a = torch.ones(2,2)
b = a.numpy()
print(type(a))
print(type(b))
print(a)
print(b)
把 Tensor 类型数据,转为 NumPy 。从打印结果,也可以看出,a是一个 Tensor 类型数据,用 tensor 表示了,而b已经是 NumPy 类型了。
<class 'torch.Tensor'>
<class 'numpy.ndarray'>
tensor([[1., 1.],
[1., 1.]])
[[1. 1.]
[1. 1.]]
2°此时,如果Tensor发生改变,对应的NumPy数组也有相同的变化。
a.add_(1)
print(a)
print(b)
结果如下。
tensor([[2., 2.],
[2., 2.]])
[[2. 2.]
[2. 2.]]
3°NumPy 转化为 Tensor 数组。
a = np.array([[1, 1], [1, 1]])
b = torch.from_numpy(a)
print(type(a))
print(type(b))
print(a)
print(b)
返回结果。
<class 'numpy.ndarray'>
<class 'torch.Tensor'>
[[1 1]
[1 1]]
tensor([[1, 1],
[1, 1]], dtype=torch.int32)
4°如果 NumPy 数组发生改变,对应的 Tensor 也有相同的变化。
np.add(a, 1, out=a)
print(a)
print(b)
返回结果。
[[2 2]
[2 2]]
tensor([[2, 2],
[2, 2]], dtype=torch.int32)
5°新建张量默认保存在CPU中,如果安装了GPU版本的PyTorch,就可以将张量转移到GPU里,代码如下。
a = torch.ones(2,2)
# 检查是否可以使用GPU
if torch.cuda.is_available():
a_cuda = a.cuda()
print(a_cuda)
由于我装的是CPU版本的PyTorch,所以这里不会执行if语句。如果安装了GPU,a_cuda的打印结果如下:
tensor([[1., 1.],
[1., 1.]], device='cuda:0')
三、自动求导
构建深度学习模型的基本流程为搭建计算图、求损失函数,然后计算损失函数对模型参数的导数,最后用梯度下降法等来更新参数。搭建计算图的过程,称为“正向传播”,需要手动搭建,和本身设计模型的结构相关。而由损失函数求导的过程,称为“反向传播”,是一件辛苦事。
深度学习的算法本质,在于反向传播求导数。这个功能是通过PyTorch的自动求导(autograd)模块实现的。对于张量的所有操作,自动求导模块都能为它们自动提供微分,避免了手动计算导数的复杂过程,可以节约时间。
如果要让张量使用自动求导的功能,需要在定义张量的时候,设置参数tensor.requries_grad=True。否则,默认情况下,张量是没有求导功能的。
x = torch.ones(2, 2)
y = torch.ones(2, 2, requires_grad=True)
print(x.requires_grad) # 使用x.requires_grad调用变量的属性值
print(y.requires_grad)
x.requires_grad_(False) # 使用x.requires_grad_调用内置的函数,来改变属性
print(x.requires_grad)
使用print把requires_grad结果打印下来,也可以发现默认情况下,张量确实没有求导功能。想改变这个属性,可以使用tensor.requires_grad_()方法。
False
True
False
1°返回值是标量的情况。
print(x)
print(y)
z = x + y
print(z)
z = z.mean()
print(z)
输出定义为张量x和张量y每个元素相加后求平均的结果,是一个一维数据。在PyTorch中,每个通过函数计算得到的变量,都有一个.grad_fn属性,grad_fn是用来记录变量是怎么来的,方便计算梯度,比如:y = x*3,grad_fn就记录了y由x计算的过程。
tensor([[1., 1.],
[1., 1.]])
tensor([[1., 1.],
[1., 1.]], requires_grad=True)
tensor([[2., 2.],
[2., 2.]], grad_fn=<AddBackward0>)
tensor(2., grad_fn=<MeanBackward0>) #坑点待补
2°如果此时,需要计算z对x的偏导数,以及z对y的偏导数。首先要对z使用.backward()来定义反向传播,代码为z.backward()。
z.backward(torch.ones_like(z)) #定义反向传播
#直接使用x.grad来计算z对x的偏导,y.grad来计算z对y的偏导。
print(x.grad)
print(y.grad)
可以看到,只需简单两句,就完成了PyTorch的自动求导功能了。
tensor([[0.2500, 0.2500],
[0.2500, 0.2500]])
tensor([[0.2500, 0.2500],
[0.2500, 0.2500]])
#对结果进行验证,是对的,具体参考“标量对矩阵的求导运算”。但这里貌似,就只是对公式上的求导。而非具体值。
为了验证结果,将x和y的一些数据进行改变,再次验证。
x = torch.tensor([[1., 2.], [3., 4.]], requires_grad=True)
y = torch.tensor([[5., 4.], [12., 23.]], requires_grad=True)
print(x.requires_grad)
print(y.requires_grad)
print(x)
print(y)
z = x + y
print(z)
#z = z.mean()
print(z)
z.backward()
print(x.grad)
print(y.grad)
输出结果。
True
True
tensor([[1., 2.],
[3., 4.]], requires_grad=True)
tensor([[ 5., 4.],
[12., 23.]], requires_grad=True)
tensor([[ 6., 6.],
[15., 27.]], grad_fn=<AddBackward0>)
tensor([[ 6., 6.],
[15., 27.]], grad_fn=<AddBackward0>)
tensor([[1., 1.],
[1., 1.]])
tensor([[1., 1.],
[1., 1.]])
3°当输出是多维张量。
x = torch.ones(2, 2, requires_grad=True)
y = torch.ones(2, 2, requires_grad=True)
z = 2 * x + 3 * y
print(z)
z.backward(torch.ones_like(z))
print(x.grad)
print(y.grad)
当输出z是一个标量时,不需要在backward()中指定任何参数,但如果z是一个多维张量,则需要在backward()中参数,来匹配相应的尺寸。
4°禁止自动求导。使用with torch.no_grad():指令。
print(x.requires_grad)
print((2 * x).requires_grad)
with torch.no_grad():
print((2 * x).requires_grad)
四、参考文献
torch.ones_like函数和torch.zero_like函数
requires_grad,grad_fn,grad的含义及使用