【pytorch 3.2 autograd】autograd总结

笔记链接:Autograd链接
以下为笔记内容
3.2 Autograd
torch.autograd是为了方便用户计算,专门开发的一套自动求导引擎,能够根据输入与前向传播过程自动构建计算图,并执行反向传播.
3.2.1 Variable
from future import print_function
import torch as t
from torch.autograd import Variable as V
“”"--------------------"""

从tensor中创建Variable, 需要指定求导

a = V(t.ones(3, 4), requires_grad =True)
a
“”"--------------------"""
b = V(t.zeros(3,4))
b
“”"--------------------"""

函数的使用与Tensor一致

c = a + b

c = a.add(b)
c
“”"--------------------"""
d = c.sum()
d.backward() # 反向传播
“”"--------------------"""

注意二者的区别

前者在取data后变为tensor,从tensor计算sum得到float

后者计算sum后还是Variable

c.data.sum(), c.sum()
“”"--------------------"""
a.grad
“”"--------------------"""

此处虽然没有指定C需要求导,但是C依赖于A,而A需要求导

因此c的requires_grad属性会自动设置为true

a.requires_grad, b.requires_grad, c.requires_grad,d.requires_grad
“”"--------------------"""

由用户创建的variable属于叶子节点,对应的grad_fn为none

a.is_leaf,b.is_leaf,c.is_leaf,d.is_leaf
“”"--------------------"""
#c.grad 是none,c不是叶子节点,它的梯度用来计算A的梯度
# 虽然c.requeires_grad = True ,但其梯度计算之后就会立刻被释放
c.grad is None
手动推导和Atograd的区别
from future import print_function
import torch as t
from torch.autograd import Variable as V

def f(x):
“”“计算y”""
y = x ** 2 * t.exp(x)
return y

def gradf(x):
“手动求导”
dx = x * 2 t.exp(x) + x**2t.exp(x)
return dx
“”"--------------------"""
x = V(t.randn(3,4),requires_grad = True)
y = f(x)
y
“”"--------------------"""
y.backward(t.ones(y.size())) # grad_variable形状与Y一致
x.grad
“”"--------------------"""

autograd 计算的公式与手动计算的结果一样

gradf(x)
autograd的细节:
from future import print_function
import torch as t
from torch.autograd import Variable as V

x = V(t.ones(1))
b = V(t.rand(1),requires_grad =True)
w = V(t.rand(1),requires_grad =True)
y = w * x # 等价于 y = w.mul(x)
z = y + b # 等价于 z = y.add(b)
“”"--------------------"""
x.requires_grad, b.requires_grad,w.requires_grad
“”"--------------------"""

虽然未指定y.requires_grad 为True,但由于y依赖于需要求导的w

故而y.requires_grad 为True

y.requires_grad
“”"--------------------"""
x.is_leaf,w.is_leaf,b.is_leaf,y.is_leaf,z.is_leaf
“”"--------------------"""

next_functions保存grad_fn的输入,是一个tuple,tuple的元素也是Function

第一个是y,它是乘法(mul)的输出,所以对应的反向传播函数y.grad_fn是MulBackward

第二个是b,它是叶子节点,由用户创建,grad_fn为None,但是有

z.grad_fn.next_functions
“”"--------------------"""

variable的grad_fn对应着和图中的function相对应

z.grad_fn.next_functions[0][0] == y.grad_fn
“”"--------------------"""

叶子节点的grad_fn是None

w.grad_fn,x.grad_fn
计算w的梯度的时候,需要用到x的数值(${\partial y\over \partial w} = x $),这些数值在前向过程中会保存成buffer,在计算完梯度之后会自动清空。为了能够多次反向传播需要指定retain_graph来保留这些buffer。
from future import print_function
import torch as t
from torch.autograd import Variable as V

使用retain_graph来保存buffer

z.backward(retain_graph=True)
w.grad
“”"--------------------"""

多次反向传播,梯度累加,这也就是w中AccumulateGrad标识的含义

z.backward()
w.grad
PyTorch使用的是动态图,它的计算图在每次前向传播时都是从头开始构建,所以它能够使用Python控制语句(如for、if等)根据需求创建计算图。这点在自然语言处理领域中很有用,它意味着你不需要事先构建所有可能用到的图的路径,图在运行时才构建。
from future import print_function
import torch as t
from torch.autograd import Variable as V

def abs(x):
if x.data[0] > 0:return x
else:return -x

x = t.ones(1, requires_grad =True)
y = abs(x)
y.backward()
x.grad
“”"--------------------"""
x = -1 * t.ones(1)
x = x.requires_grad_()
y = abs(x)
y.backward()
x.grad
“”"--------------------"""
def f(x):
result = 1
for ii in x:
if ii.item()>0: result=ii*result
return result
x = t.arange(-2.0,4.0,requires_grad=True)

y = f(x) # y = x[3]*x[4]*x[5]
y.backward()
x.grad
“”"--------------------"""
有些时候我们可能不希望autograd对tensor求导。认为求导需要缓存许多中间结构,增加额外的内存/显存开销,那么我们可以关闭自动求导。对于不需要反向传播的情景(如inference,即测试推理时),关闭自动求导可实现一定程度的速度提升,并节省约一半显存,因其不需要分配空间计算梯度。
x = t.ones(1, requires_grad=True)
w = t.rand(1, requires_grad=True)
y = x * w

y依赖于w,而w.requires_grad = True

x.requires_grad, w.requires_grad, y.requires_grad
“”"--------------------"""
with t.no_grad():
x = t.ones(1)
w = t.rand(1, requires_grad = True)
y = x * w

y依赖于w和x,虽然w.requires_grad = True,但是y的requires_grad依旧为False

x.requires_grad, w.requires_grad, y.requires_grad
“”"--------------------"""

等价于t.no_grad()

t.set_grad_enabled(False)
x = t.ones(1)
w = t.rand(1, requires_grad = True)
y = x * w

y依赖于w和x,虽然w.requires_grad = True,但是y的requires_grad依旧为False

x.requires_grad, w.requires_grad, y.requires_grad

在反向传播过程中非叶子节点的导数计算完之后即被清空。若想查看这些变量的梯度,有两种方法:
使用autograd.grad函数
使用hook
autograd.grad和hook方法都是很强大的工具,更详细的用法参考官方api文档,这里举例说明基础的使用。推荐使用hook方法,但是在实际使用中应尽量避免修改grad的值。
x = t.ones(3, requires_grad=True)
w = t.rand(3, requires_grad=True)
y = x * w

y依赖于w,而w.requires_grad = True

z = y.sum()
x.requires_grad, w.requires_grad, y.requires_grad
“”"--------------------"""

非叶子节点grad计算完之后自动清空,y.grad是None

z.backward()
(x.grad, w.grad, y.grad)
“”"--------------------"""

第一种方法:使用grad获取中间变量的梯度

x = t.ones(3, requires_grad=True)
w = t.rand(3, requires_grad=True)
y = x * w
z = y.sum()

z对y的梯度,隐式调用backward()

t.autograd.grad(z, y)
“”"--------------------"""

第二种方法:使用hook

hook是一个函数,输入是梯度,不应该有返回值

def variable_hook(grad):
print(‘y的梯度:’,grad)

x = t.ones(3, requires_grad=True)
w = t.rand(3, requires_grad=True)
y = x * w

注册hook

hook_handle = y.register_hook(variable_hook)
z = y.sum()
z.backward()

除非你每次都要用hook,否则用完之后记得移除hook

hook_handle.remove()
“”"--------------------"""

variable中grad属性和backward函数grad_variables参数的含义
x = t.arange(0,3, requires_grad=True)
y = x2 + x*2
z = y.sum()
z.backward() # 从z开始反向传播
x.grad
“”"--------------------"""
x = t.arange(0,3, requires_grad=True)
y = x
2 + x*2
z = y.sum()
y_gradient = t.Tensor([1,1,1]) # dz/dy
y.backward(y_gradient) #从y开始反向传播
x.grad
用Variable实现线性回归
import torch as t
%matplotlib inline
from matplotlib import pyplot as plt
from IPython import display
import numpy as np
“”"--------------------"""

设置随机数种子,为了在不同人电脑上运行时下面的输出一致

t.manual_seed(1000)
def get_fake_data(batch_size=8):
‘’’ 产生随机数据:y = x*2 + 3,加上了一些噪声’’’
x = t.rand(batch_size,1) * 5
y = x * 2 + 3 + t.randn(batch_size, 1)
return x, y
“”"--------------------"""

来看看产生x-y分布是什么样的

x, y = get_fake_data()
plt.scatter(x.squeeze().numpy(), y.squeeze().numpy())
“”"--------------------"""

随机初始化参数

w = t.rand(1,1, requires_grad=True)
b = t.zeros(1,1, requires_grad=True)
losses = np.zeros(500)

lr =0.005 # 学习率

for ii in range(500):
x, y = get_fake_data(batch_size=32)

# forward:计算loss
y_pred = x.mm(w) + b.expand_as(y)
loss = 0.5 * (y_pred - y) ** 2
loss = loss.sum()
losses[ii] = loss.item()

# backward:手动计算梯度
loss.backward()

# 更新参数
w.data.sub_(lr * w.grad.data)
b.data.sub_(lr * b.grad.data)

# 梯度清零
w.grad.data.zero_()
b.grad.data.zero_()

if ii%50 ==0:
    # 画图
    display.clear_output(wait=True)
    x = t.arange(0, 6).view(-1, 1)
	x = t.tensor(x, dtype=t.float32)  # 要注意格式
    y = x.mm(w.data) + b.data.expand_as(x)
    plt.plot(x.numpy(), y.numpy()) # predicted
    
    x2, y2 = get_fake_data(batch_size=20) 
    plt.scatter(x2.numpy(), y2.numpy()) # true data
    
    plt.xlim(0,5)
    plt.ylim(0,13)   
    plt.show()
    plt.pause(0.5)

print(w.item(), b.item())
“”"--------------------"""
plt.plot(losses)
plt.ylim(5,50)

猜你喜欢

转载自blog.csdn.net/Leomn_J/article/details/113122576