日期:2020.10.30
主题:PyTorch入门
内容:
-
根据PyTorch官方教程文档,学习PyTorch中所有神经网络的核心——Autograd 包的基础操作(主要与张量相关)。
-
根据自己的理解和试验,为代码添加少量注解。
具体代码如下 ↓
from __future__ import print_function
import torch
"""
{Autograd:自动求导}
PyTorch中,所有神经网络的核心是 autograd 包,其为张量上的所有操作提供了自动求导机制。
它是一个在运行时定义(define-by-run)的框架。
这意味着反向传播是根据代码如何运行来决定的,并且每次迭代可以是不同的。
"""
"""
【张量】
1.Tensor 是 autograd 包的核心类,其有 .grad 属性。
若它的属性 .requires_grad 为 True,那么它将会追踪对于该张量的所有操作。
当完成计算后可以通过调用 .backward(),来自动计算所有的梯度,自动累加到.grad属性.
若要阻止跟踪历史,可以调用 .detach() 方法
该方法将张量与计算历史分离,并阻止它未来的计算记录被跟踪。
2.Function 是 autograd 包的另一个重要的类
Tensor 和 Function 互相连接生成了一个无圈图(acyclic graph),它编码了完整的计算历史。
每个张量都有一个 .grad_fn 属性
该属性引用了创建 Tensor 自身的Function(默认为None)
若需要计算导数,可以在 Tensor 上调用 .backward()。
如果 Tensor 是一个标量(即它包含一个元素的数据),则不需要为 backward() 指定任何参数;
但是如果它有更多的元素,则需要指定一个 gradient 参数,该参数是形状匹配的张量。
"""
# 创建一个张量并设置requires_grad=True用来追踪其计算历史
x = torch.ones(2, 2, requires_grad=True)
print(x)
print("x.grad_fn = ", x.grad_fn) # x不是通过运算方法来创建,故其grad_fn = none
# 对这个张量做一次加法运算
y = x + 2
print('\n', y)
print("y.grad_fn = \n", y.grad_fn) # y由加法创建,所以它的grad_fn = AddBackward0
# 同理,对张量做一次Hadamard积
z = x * y
print('\n', z)
print("z.grad_fn = \n", z.grad_fn) #y由Hadamard积产生,所以它的grad_fn = MulBackward0
# 同理,对张量做一次矩阵乘法
z = x @ y
print('\n', z)
print("z.grad_fn = \n", z.grad_fn) #y由Hadamard积产生,所以它的grad_fn = MmBackward
print('-'*40, '\n')
# 对张量求均值
z = torch.tensor([[1., 0., 2.],
[0., 1., 0.],
[1., 0., 2.]])
print(z)
out = z.mean() # 对 m*n 个数求均值,返回一个实数 0.7778
print("\nz.mean() = ", out)
out = z.mean(0) # 压缩行,对各列求均值,返回 1* n 矩阵
print("\nz.mean(0) =\n", out)
out = z.mean(1) # 压缩列,对各行求均值,返回 m *1 矩阵
print("\nz.mean(1) =\n", out)
print('-'*40, '\n')
# .requires_grad_(...) 原地改变了现有张量的 requires_grad 标志(默认为 False)。
a = torch.randn(2, 2)
a = ((a * 3) / (a - 1))
print("a.requires_grad = ", a.requires_grad) # False
a.requires_grad_(True) # 开始追踪a的计算历史
print("a.requires_grad = ", a.requires_grad) # True
b = (a * a).sum() # b继承a的 requires_grad 属性,其计算历史被追踪
print("b.grad_fn = \n", b.grad_fn) # SumBackward0
print('-'*40, '\n')
"""
【梯度】
通常来说,torch.autograd 是计算雅可比向量积的一个“引擎”。
"""
y = x + 2
z = y * y * 3
out = z.mean()
print(out, '\n') # tensor(27., grad_fn=<MeanBackward0>)
# 调用 .backward(),进行反向传播(自动计算所有的梯度,并累加到 .grad 属性)。
out.backward() # out.backward() == out.backward(torch.tensor(1.))(因为 out 是标量)
print(x.grad) # 输出导数 d(out)/dx
# 先将整个过程“平铺陈述”,详细到“祖先”,再进行梯度计算
# 数学上,若有向量值函数y = f(x),则y相对于x的梯度是一个雅可比矩阵
print('-'*40, '\n')
# 雅可比向量积的例子
x = torch.ones(3, requires_grad=True)
y = x * 2
t = 0
print(x, '\n')
while y.data.norm() < 10: # y.data.norm()指的是y的范数
y = y * 2
t += 1
print(y, t, '\n')
# 在这种情况下,y 不再是标量。
# torch.autograd 不能直接计算完整的雅可比矩阵,
# 但若只想要雅可比向量积,只需将这个向量作为参数传给 backward
v = torch.tensor([0.1, 1.0, 0.0001], dtype=torch.float) # v可视为每个out分量对an求导时的权重
y.backward(v) # 相当于对张量做一次Hadamard积
print(x.grad)
print('-'*40, '\n')
# 可以将代码块包装在 with torch.no_grad(): 中,来阻止跟踪。
x.requires_grad = False # 先将x的跟踪关闭
print("(x ** 2).requires_grad = ", (x ** 2).requires_grad) # False
with torch.no_grad():
x.requires_grad = True # 在封装代码中尝试打开x的跟踪
print("(x ** 2).requires_grad = ", (x ** 2).requires_grad) # False
print("(x ** 2).requires_grad = ", (x ** 2).requires_grad, '\n') # True
# 可见在包装中的代码依然有效,包装只是暂时“搁置”了对x的跟踪请求(“按下不表”)