pytorch如何计算导数_Pytorch的自动求导机制与使用方法(一)
本文以線性模型為例,講解線性模型的求解的pytorch梯度實現(xiàn)方法.
要注意幾個問題:在PyTorch 0.4.0版本之后,Variable類已經(jīng)被禁用了,所有的torch.Tensor與torch.autograd.Variable的功能可以通過torch.Tensor方法實現(xiàn).
在pytorch里面,默認只能是標(biāo)量對標(biāo)量,或者標(biāo)量對向量/矩陣求導(dǎo)!
要想使x支持求導(dǎo),必須讓x為浮點類型.
在目前的深度學(xué)習(xí)框架(PyTorch,Tensorflow,MXnet)中,自動求導(dǎo)功能是最核心也是最基礎(chǔ)的功能.構(gòu)建與訓(xùn)練深度學(xué)習(xí)的基本流程是:根據(jù)網(wǎng)絡(luò)結(jié)構(gòu)逐步搭建計算圖,然后求得損失函數(shù),之后根據(jù)計算圖來計算導(dǎo)數(shù),最后利用梯度下降方法更新參數(shù).
首先根據(jù)一個例子,
,當(dāng)
時,它的導(dǎo)數(shù)為12,這樣通過一個代碼來實現(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ù)值要為浮點型,不可以為整數(shù).
1.torch.Tensor
PyTorch中數(shù)據(jù)以張量(n維數(shù)組)的形式流動torch.Tensor可以用來創(chuàng)建張量.當(dāng)Tensor的屬性中requires_grad=True時,則系統(tǒng)會開始跟蹤針對此Tensor的所有操作.其中每個操作都會有此操作想對于輸入的梯度,則整個操作完成的輸出張量相對于輸入張量的梯度就是中間過程中所有張量的鏈式法則.例如,
為輸入張量,
,
,則:
其中
為葉節(jié)點,
為根節(jié)點,
并不會被收集.
每個張量都有一個grad_fn屬性用于保存張量的操作,如果這個張量為用戶自己創(chuàng)建的,則grad_fn為None.
從圖中可以看出,一個Tensor中:data中保存著所存有的數(shù)據(jù)
grad中保存這梯度
requires_grad表示是否開始追蹤所有的操作歷史
想計算導(dǎo)數(shù)時,調(diào)用Tensor.backward()
在調(diào)用backward()時,只有當(dāng)requires_grad和is_leaf同時為真時,才會計算節(jié)點的梯度值.
2.Pytorch到底在計算什么?(雅克比矩陣和向量)
autograd類的原理其實就是一個雅克比矩陣向量積計算引擎.
設(shè)函數(shù)
為從n維度映射到m維度的一個函數(shù),它的輸入為向量
,輸出向量
為:
則雅可比矩陣是一個m*n的矩陣:
由于矩陣描述了向量空間中的運動-變換,而雅可比矩陣看作是將點
轉(zhuǎn)化到點
.
設(shè)一個pytorch張量為
,通過函數(shù)f(X)得到一個向量
:
.這個過程相當(dāng)于
的過程.然后設(shè)向量
通過函數(shù)映射到一個標(biāo)量
.這時求解
相對與
的梯度
很容易,
的公式如下:
根據(jù)導(dǎo)數(shù)的鏈式法則,張量
相對于標(biāo)量
的梯度為:
向量
到標(biāo)量
類似于MSE函數(shù)將minibatch平均為一個平均loss上.
當(dāng)
不為標(biāo)量時,可以通過輸入一個外部的梯度來獲得其梯度.
3.例程
這一部分會詳細說明一些細節(jié)性的問題,在最后會有線性模型的例程.
在上邊說過葉子節(jié)點(is_leaf),如果有多個葉子節(jié)點,這些節(jié)點的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的梯度追蹤還開著,所以z是可以求導(dǎo)(print(z.requires_grad)為True).
當(dāng)x也關(guān)閉了梯度追蹤,那么z也無法求導(dǎo)(print(z.requires_grad)為False).
當(dāng)計算梯度之后,y是不可求導(dǎo)的,這時候y.grad為None.
可以通過x.requires_grad_(True/False)修改葉子節(jié)點的可導(dǎo)與不可導(dǎo).
Tensor.backward()方法
.backward()默認計算對計算圖葉子節(jié)點的導(dǎo)數(shù),中間過程的導(dǎo)數(shù)是不計算的.例程如下:
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)量對標(biāo)量的求導(dǎo)例程.
標(biāo)量對向量求導(dǎo):
下面來說明一個線性模型的雛形版本,輸入為向量,輸出為標(biāo)量.設(shè)一個輸入為
,權(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)量對矩陣求導(dǎo):
設(shè)輸入為一個矩陣(2*3)
第一次操作:
第二次操作,Y上每個元素平方:
第三次操作,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)
向量/矩陣對向量/矩陣求導(dǎo)
backward()默認輸出為標(biāo)量,這是因為我們在深度學(xué)習(xí)中最后的loss為一個標(biāo)量,這是常用的方式.
當(dāng)輸出為向量或矩陣時,需要通過地一個參數(shù)gradient來實現(xiàn).gradient參數(shù)的維度與最終的輸出要保持一致,gradient中每個元素表示為對應(yīng)的最終輸出中相同位置元素所對應(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]])
線性模型的求解過程
import numpy as np
import torch.autograd
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
#生成實驗數(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
#測試生成數(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()
#測試生成數(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
會報錯,因為weight表示了一個新的對象,他的名字還是weight,變?yōu)榱酥虚g節(jié)點,默認情況下是無法獲取其梯度的,可以采用weight.retain_grad()來處理.
參考:
總結(jié)
以上是生活随笔為你收集整理的pytorch如何计算导数_Pytorch的自动求导机制与使用方法(一)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: LOL S8最新卡莎套路 符文点法&am
- 下一篇: 凝血酶分子机器人_了不得!这个机器人可以