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