pytorch如何计算导数_Pytorch的自动求导机制与使用方法(一)
本文以線性模型為例,講解線性模型的求解的pytorch梯度實(shí)現(xiàn)方法.
要注意幾個(gè)問(wèn)題:在PyTorch 0.4.0版本之后,Variable類已經(jīng)被禁用了,所有的torch.Tensor與torch.autograd.Variable的功能可以通過(guò)torch.Tensor方法實(shí)現(xiàn).
在pytorch里面,默認(rèn)只能是標(biāo)量對(duì)標(biāo)量,或者標(biāo)量對(duì)向量/矩陣求導(dǎo)!
要想使x支持求導(dǎo),必須讓x為浮點(diǎn)類型.
在目前的深度學(xué)習(xí)框架(PyTorch,Tensorflow,MXnet)中,自動(dòng)求導(dǎo)功能是最核心也是最基礎(chǔ)的功能.構(gòu)建與訓(xùn)練深度學(xué)習(xí)的基本流程是:根據(jù)網(wǎng)絡(luò)結(jié)構(gòu)逐步搭建計(jì)算圖,然后求得損失函數(shù),之后根據(jù)計(jì)算圖來(lái)計(jì)算導(dǎo)數(shù),最后利用梯度下降方法更新參數(shù).
首先根據(jù)一個(gè)例子,
,當(dāng)
時(shí),它的導(dǎo)數(shù)為12,這樣通過(guò)一個(gè)代碼來(lái)實(shí)現(xiàn).
import torch
import torch.autograd
x = torch.tensor([2.0], requires_grad=True)
print("x = ",x)
print("x.requires_grad = ", x.requires_grad)
y = x ** 3
print("y = ",y)
print("y.requires_grad = ", y.grad_fn)
y.backward() #反向傳播,求解導(dǎo)數(shù)
print("x.grad = ", x.grad)
輸出結(jié)果為:
x = tensor([2.], requires_grad=True)
x.requires_grad = True
y = tensor([8.], grad_fn=)
y.requires_grad =
x.grad = tensor([12.])
這里要注意:需要反向傳播的數(shù)值要為浮點(diǎn)型,不可以為整數(shù).
1.torch.Tensor
PyTorch中數(shù)據(jù)以張量(n維數(shù)組)的形式流動(dòng)torch.Tensor可以用來(lái)創(chuàng)建張量.當(dāng)Tensor的屬性中requires_grad=True時(shí),則系統(tǒng)會(huì)開(kāi)始跟蹤針對(duì)此Tensor的所有操作.其中每個(gè)操作都會(huì)有此操作想對(duì)于輸入的梯度,則整個(gè)操作完成的輸出張量相對(duì)于輸入張量的梯度就是中間過(guò)程中所有張量的鏈?zhǔn)椒▌t.例如,
為輸入張量,
,
,則:
其中
為葉節(jié)點(diǎn),
為根節(jié)點(diǎn),
并不會(huì)被收集.
每個(gè)張量都有一個(gè)grad_fn屬性用于保存張量的操作,如果這個(gè)張量為用戶自己創(chuàng)建的,則grad_fn為None.
從圖中可以看出,一個(gè)Tensor中:data中保存著所存有的數(shù)據(jù)
grad中保存這梯度
requires_grad表示是否開(kāi)始追蹤所有的操作歷史
想計(jì)算導(dǎo)數(shù)時(shí),調(diào)用Tensor.backward()
在調(diào)用backward()時(shí),只有當(dāng)requires_grad和is_leaf同時(shí)為真時(shí),才會(huì)計(jì)算節(jié)點(diǎn)的梯度值.
2.Pytorch到底在計(jì)算什么?(雅克比矩陣和向量)
autograd類的原理其實(shí)就是一個(gè)雅克比矩陣向量積計(jì)算引擎.
設(shè)函數(shù)
為從n維度映射到m維度的一個(gè)函數(shù),它的輸入為向量
,輸出向量
為:
則雅可比矩陣是一個(gè)m*n的矩陣:
由于矩陣描述了向量空間中的運(yùn)動(dòng)-變換,而雅可比矩陣看作是將點(diǎn)
轉(zhuǎn)化到點(diǎn)
.
設(shè)一個(gè)pytorch張量為
,通過(guò)函數(shù)f(X)得到一個(gè)向量
:
.這個(gè)過(guò)程相當(dāng)于
的過(guò)程.然后設(shè)向量
通過(guò)函數(shù)映射到一個(gè)標(biāo)量
.這時(shí)求解
相對(duì)與
的梯度
很容易,
的公式如下:
根據(jù)導(dǎo)數(shù)的鏈?zhǔn)椒▌t,張量
相對(duì)于標(biāo)量
的梯度為:
向量
到標(biāo)量
類似于MSE函數(shù)將minibatch平均為一個(gè)平均loss上.
當(dāng)
不為標(biāo)量時(shí),可以通過(guò)輸入一個(gè)外部的梯度來(lái)獲得其梯度.
3.例程
這一部分會(huì)詳細(xì)說(shuō)明一些細(xì)節(jié)性的問(wèn)題,在最后會(huì)有線性模型的例程.
在上邊說(shuō)過(guò)葉子節(jié)點(diǎn)(is_leaf),如果有多個(gè)葉子節(jié)點(diǎn),這些節(jié)點(diǎn)的is_leaf都是False,則整體才是不可求導(dǎo)的,例程如下:
import torch
import torch.autograd
x = torch.tensor(3.0, requires_grad=True)
y = torch.tensor(4.0, requires_grad=False)
z = torch.pow(x,2) + torch.pow(y,3)
print(x.requires_grad)
print(y.requires_grad)
print(z.requires_grad)
z.backward()
print(x.grad)
print(y.grad)
上邊例程中,關(guān)閉了y的梯度追蹤,但是x的梯度追蹤還開(kāi)著,所以z是可以求導(dǎo)(print(z.requires_grad)為True).
當(dāng)x也關(guān)閉了梯度追蹤,那么z也無(wú)法求導(dǎo)(print(z.requires_grad)為False).
當(dāng)計(jì)算梯度之后,y是不可求導(dǎo)的,這時(shí)候y.grad為None.
可以通過(guò)x.requires_grad_(True/False)修改葉子節(jié)點(diǎn)的可導(dǎo)與不可導(dǎo).
Tensor.backward()方法
.backward()默認(rèn)計(jì)算對(duì)計(jì)算圖葉子節(jié)點(diǎn)的導(dǎo)數(shù),中間過(guò)程的導(dǎo)數(shù)是不計(jì)算的.例程如下:
x = torch.tensor(3.0, requires_grad=True)
y = 2*x
z = y**2
f = z+2
f.backward()
print(x.grad)
print(y.grad)
print(z.grad)
結(jié)果如下:
tensor(24.)
None
None
上邊的例子大多都是標(biāo)量對(duì)標(biāo)量的求導(dǎo)例程.
標(biāo)量對(duì)向量求導(dǎo):
下面來(lái)說(shuō)明一個(gè)線性模型的雛形版本,輸入為向量,輸出為標(biāo)量.設(shè)一個(gè)輸入為
,權(quán)重向量為
,則
.
偏導(dǎo)數(shù)為:
例程:
x = torch.tensor([1.0,2.0,3.0], requires_grad=True)
w = torch.tensor([4.0,5.0,6.0], requires_grad=True)
b = 10
y = torch.dot(x,w)+b
y.backward()
print(x.grad)
標(biāo)量對(duì)矩陣求導(dǎo):
設(shè)輸入為一個(gè)矩陣(2*3)
第一次操作:
第二次操作,Y上每個(gè)元素平方:
第三次操作,Z上所有元素平均值:
偏導(dǎo)數(shù)為:
import torch
import torch.autograd
x = torch.tensor([[1.0,2.0,3.0],[4.0,5.0,6.0]], requires_grad=True)
y = x+1
z = y**2
f = torch.mean(z)
f.backward()
print(x.grad)
向量/矩陣對(duì)向量/矩陣求導(dǎo)
backward()默認(rèn)輸出為標(biāo)量,這是因?yàn)槲覀冊(cè)谏疃葘W(xué)習(xí)中最后的loss為一個(gè)標(biāo)量,這是常用的方式.
當(dāng)輸出為向量或矩陣時(shí),需要通過(guò)地一個(gè)參數(shù)gradient來(lái)實(shí)現(xiàn).gradient參數(shù)的維度與最終的輸出要保持一致,gradient中每個(gè)元素表示為對(duì)應(yīng)的最終輸出中相同位置元素所對(duì)應(yīng)的權(quán)重.
例程:
import torch
import torch.autograd
x = torch.tensor([[1.0,2.0,3.0],[4.0,5.0,6.0]], requires_grad=True)
y = x**2 + x
gradient1 = torch.tensor([[1.,1.,1.],[1.,1.,1.]])
gradient2 = torch.tensor([[1.,0.1,0.01],[1.,1.,1.]])
y.backward(gradient1)
print(x.grad)
x.grad.zero_()
y = x**2 + x
y.backward(gradient2)
print(x.grad)
輸出為:
tensor([[ 3., 5., 7.],
[ 9., 11., 13.]])
tensor([[ 3.0000, 0.5000, 0.0700],
[ 9.0000, 11.0000, 13.0000]])
線性模型的求解過(guò)程
import numpy as np
import torch.autograd
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
#生成實(shí)驗(yàn)數(shù)據(jù)
def gendata():
gen_x = torch.randn(1000,2)
gen_w = torch.FloatTensor([5, 10]).view(2,1)
gen_b = 2.0
y_true = torch.mm(gen_x, gen_w) + gen_b
noise = torch.zeros(1000,1).normal_(std=0.01)
y_lable = y_true + noise
return gen_x,y_true,y_lable
#測(cè)試生成數(shù)據(jù)的有效性
def plot2d(x_in):
fig1 = plt.figure(1)
x_in = x_in.numpy()
plt.scatter(x_in[:,0], x_in[:,1])
plt.show()
#測(cè)試生成數(shù)據(jù)的有效性
def plot3d(x_in, y_in):
fig2 = plt.figure(2)
x_in = x_in.numpy()
y_in = y_in.numpy()
fig = plt.figure(2)
ax = fig.add_subplot(111, projection='3d')
ax.scatter(x_in[:,0], x_in[:,1], y_in)
plt.show()
def weight_init(layer_size):
weight = torch.randn(layer_size,1 , requires_grad = True)
bias = torch.randn(1,1, requires_grad = True)
return weight, bias
def loss_mse(y_l, y_pred):
return (y_l - y_pred) ** 2/2
x, y_t, y_l = gendata()
print(y_l.size())
# print(x.size())
# plot2d(x)
# plot3d(x,y_t)
weight, bias = weight_init(2)
print("w :", weight)
print("b :", bias)
learning_rate = 0.03
mini_batch = 1000
for i in range(500):
print(i)
y_pred = torch.matmul(x, weight) + bias
loss = loss_mse(y_l, y_pred)
loss.sum().backward()
weight.data = weight.data - learning_rate/mini_batch*weight.grad.data
bias.data = bias.data - learning_rate/mini_batch*bias.grad.data
weight.grad.zero_()## 必要清零
bias.grad.zero_()## 必要清零
print("w up:", weight)
print("b up:", bias)
with torch.no_grad():
y_pred = torch.matmul(x, weight) + bias
train_loss = loss_mse(y_l, y_pred)
print("epoch", i, train_loss.mean().item())
在這段代碼中要注意,如果將
weight.data = weight.data - learning_rate/mini_batch*weight.grad.data
更換為
weight = weight - learning_rate/mini_batch*weight.grad.data
會(huì)報(bào)錯(cuò),因?yàn)閣eight表示了一個(gè)新的對(duì)象,他的名字還是weight,變?yōu)榱酥虚g節(jié)點(diǎn),默認(rèn)情況下是無(wú)法獲取其梯度的,可以采用weight.retain_grad()來(lái)處理.
參考:
總結(jié)
以上是生活随笔為你收集整理的pytorch如何计算导数_Pytorch的自动求导机制与使用方法(一)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: LOL S8最新卡莎套路 符文点法&am
- 下一篇: 微信视频怎么下载?如何下载微信中的视频?