當前主流的深度學習框架,除了能夠便捷高效的搭建機器學習的模型之外,其自動並行和自動微分等功能還為其他領域的科學計算帶來了模式的變革。本文我們將探索如何用mindspore去實現乙個多維的自動微分,並且得到該多元函式的雅可比矩陣。
首先我們給定乙個比較簡單的z關於自變數x的函式形式(其中y和i是一些引數):
\[z_(x)=y_ix_j
\]比如我們考慮乙個3*3的z,我們最終需要計算的是這樣乙個雅可比矩陣:
\[j_z(x)=
\left[
\begin
\frac & \frac & \frac\\
\frac & \frac & \frac\\
\frac & \frac & \frac
\end
\right]
\]假如我們給定一些簡單的初始值:
\[x=[1,2,3]\\
y=[1,3,2]
\]那麼理論上我們應該得到的結果是:
\[j_z(x)=\left[
\begin
1 & 0 & 0\\
0 & 0 & 3\\
0 & 2 & 0\\
\end
\right]
\]接下來我們看看如何在mindspore的自動微分框架下實現這一功能。
from mindspore import nn, tensor, ops
from mindspore.ops.functional import grad
import numpy as np
from mindspore import numpy as msnp
class net(nn.cell):
def __init__(self, y, index):
super(net, self).__init__()
self.y = y
self.index = index
self.norm = nn.norm(-1)
def construct(self, x):
return self.y[self.index]*x
x = tensor(np.array([1,2,3]).astype(np.float32))
y = tensor(np.array([[1],[2],[3]]).astype(np.float32))
index = tensor(np.array([0,2,1]).astype(np.int32))
shape = (y.shape[0], x.shape[0])
output = grad(net(y,index))(x)
print(output)
# [6. 6. 6.]
在這個案例中,我們得到的結果,首先維度就不對,我們理想中的雅可比矩陣應該是3*3大小的,可見mindspore中自動微分的邏輯是把其中的乙個維度進行了加和,類似於這樣的形式:
\[\left[
\frac+\frac+\frac, \frac+\frac+\frac, \frac+\frac+\frac
\right]
\]所以為了得到我們的結果,需要對輸入的x進行擴維。
在mindspore中提供了broadcastto
這樣的介面,可以自動的在擴充套件維度填充待擴充套件張量的元素,我們需要把x的最外層維度擴充套件到與引數y一致,在這個案例中就是3*3的維度,具體**實現如下所示:
from mindspore import nn, tensor, ops
from mindspore.ops.functional import grad
import numpy as np
from mindspore import numpy as msnp
class net(nn.cell):
def __init__(self, y, index):
super(net, self).__init__()
self.y = y
self.index = index
self.norm = nn.norm(-1)
def construct(self, x):
return self.y[self.index]*x
x = tensor(np.array([1,2,3]).astype(np.float32))
y = tensor(np.array([[1],[2],[3]]).astype(np.float32))
index = tensor(np.array([0,2,1]).astype(np.int32))
shape = (y.shape[0], x.shape[0])
output = grad(net(y,index))(ops.broadcastto(shape)(x))
print(output)
'''[[1. 1. 1.]
[3. 3. 3.]
[2. 2. 2.]]
'''
從這個輸出結果中我們發現,雖然維度上是被擴充套件成功了,但是那些本該為0的位置卻出現了非0元素,這說明在自動微分計算的過程中,我們輸入的引數y也被自動的broadcast了,而實際上正確的計算過程中是不能使用broadcast的。
上乙個章節中說道,如果利用tensor本身的自動broadcast會導致輸入引數被擴維,會得到乙個錯誤的微分結果。因此這裡我們手動對輸入引數進行正確的擴維,這個過程是新增乙個mask矩陣,用於標記每乙個引數所對應的位置。這裡我們假設輸入乙個這樣的mask矩陣:
\[i=\left[
\begin
1 & 0 & 0\\
0 & 0 & 1\\
0 & 1 & 0
\end
\right]
\]
from mindspore import nn, tensor, ops
from mindspore.ops.functional import grad
import numpy as np
from mindspore import numpy as msnp
class net(nn.cell):
def __init__(self, y, index, size):
super(net, self).__init__()
self.y = y
self.index = index
self.norm = nn.norm(-1)
self.mask = msnp.zeros((y.shape[0],size))
self.mask[msnp.arange(self.index.shape[0]),self.index] = 1
def construct(self, x):
return self.mask*self.y[self.index]*x
x = tensor(np.array([1,2,3]).astype(np.float32))
y = tensor(np.array([[1],[2],[3]]).astype(np.float32))
index = tensor(np.array([0,2,1]).astype(np.int32))
shape = (y.shape[0], x.shape[0])
output = grad(net(y,index,x.shape[0]))(ops.broadcastto(shape)(x))
print(output)
'''[[1. 0. 0.]
[0. 0. 3.]
[0. 2. 0.]]
'''
這裡我們看到得到的結果就是正確的了。當然,需要說明的是,雖然這個案例只是非常簡單的內容,但是這裡給出的如何去計算多維函式的自動微分的方法,同樣也適用於一些更加複雜的網路和函式。
在本文中通過乙個實際函式案例的多次嘗試,給出了得到預期結果的一種解決方案。雖然mindspore框架本身提供了jvp和vjp等功能,但是實際上和grad沒有太大的區別,只是用tuple的形式增加了輸入的乙個維度。如果可以使用純tensor的輸入,用這種mask加上grad或者gradoperation的方案會更加簡單一些。同時我也嘗試過使用hypermap(類似於jax中的vmap)來解決這個問題,只需要寫好一條對z求導的函式形式,就可以自動對這個求導過程進行擴維,兩者的結果是一致的。但是mindspore的hypermap在graph模式下相容效果不是很好,建議非必要不嘗試。
本文首發鏈結為:
作者id:dechinphy
更多原著文章請參考:
多元函式微分學
無窮小 當x 0時,a x 趨向於0,則說明a x 是無窮小。無窮下一般用a表示,高階無窮小用o表示。微分 0y ax o 0x 其中o 0x 這個符號,說明o是0x的高階無窮小。在極限的運算過程中,f x 在x0處的極限為a,則f x a 多元函式的概念 z f x,y 極限 從各個方向向 x0,...
中多元線性方程組 一元微分與多元微分
存在唯一性是要在前面進行判定的,要求是 在內點某一區域上連續 f x0,y0 0 存在連續偏導數fy 這個應該初步保證了隱函式存在,且唯一 fy 0 與上一條一起,保證了隱函式存在,可導 總之連續偏導數存在基本都能滿足,不用死摳條件,這條件已經強烈到範圍很狹小而必定收斂。可以在框圖箭頭上寫偏導符號,...
Autograd 自動微分
1 深度學習的演算法本質上是通過反向傳播求導數,pytorch的autograd模組實現了此功能 在tensor上的所有操作,autograd都能為他們自動提供微分,避免手動計算導數的複雜過程。2 autograd.variable是autograd中的核心類,它簡單的封裝了tensor,並支援幾乎...