autograd库测试笔记-(一个基于Numpy的自动求导库)
導(dǎo)入 autograd 庫,同時(shí)導(dǎo)入這個(gè)庫里的numpy(應(yīng)該是作者自己把numpy放入了這個(gè)庫的命名空間里面)以及逐項(xiàng)求導(dǎo)elementwise_grad。
from autograd import grad import autograd.numpy as np from autograd import elementwise_grad接下來定義第一個(gè)函數(shù),這個(gè)函數(shù)非常簡(jiǎn)單,其實(shí)就是一個(gè)線性變換:
yn×l=Xn×dwd×l\mathbf{y}_{n\times l} = \mathbf{X}_{n\times d} \mathbf{ w} _{d\times l} yn×l?=Xn×d?wd×l?
這個(gè)變換也常用于神經(jīng)網(wǎng)絡(luò)的輸入層。
def lin_func(x,w):return np.dot(x,w)x = np.array([[1,2,3],[4,5,6]]) w = np.array([1,1,1]).astype(float)print(lin_func(x,w)) [ 6. 15.]這里也要注意一個(gè)問題:autograd并不能支持numpy的所有操作。比如上面的線性變換其實(shí)也可以用 x.dot(w) 來實(shí)現(xiàn),但這種方式下就不能再用autograd自動(dòng)求導(dǎo)了。因此在使用時(shí)也要注意看看文檔。
接下來寫出這個(gè)函數(shù)的導(dǎo)數(shù) ?y?X\frac{\partial \mathbf{y}}{\partial \mathbf{X}}?X?y?。
用法很簡(jiǎn)單,直接將函數(shù)名作為"參數(shù)"傳入 elementwise_grad 即可,得到的仍然是一個(gè)callable 的類型,這里命名為 lin_grads:
lin_grads = elementwise_grad(lin_func)dx = lin_grads(x,w)print(dx)print(dx.shape) [[1 1 1][1 1 1]] (2, 3)這里其實(shí)存在一個(gè)問題,如果我們用標(biāo)量求導(dǎo)來類推,那么此時(shí)導(dǎo)數(shù)應(yīng)該是:
?y?X=wT\frac{\partial \mathbf{y}}{\partial \mathbf{X}} =\mathbf{w}^T ?X?y?=wT
而剛剛我們?cè)O(shè)好的 w\mathbf{w}w 是 3×13 \times 13×1 維,那么上面的導(dǎo)數(shù)就應(yīng)該是 1×31 \times 31×3維,而此時(shí)得到的結(jié)果卻是2×32 \times 32×3 維。這一點(diǎn)和我們直觀的認(rèn)識(shí)是不一樣的。因?yàn)樵?strong>矩陣對(duì)矩陣求導(dǎo)時(shí),許多在標(biāo)量求導(dǎo)時(shí)的結(jié)論是不存在的。 而如果要統(tǒng)一這些運(yùn)算,則需要考慮 Kronecker 乘積。這個(gè)問題就相對(duì)復(fù)雜了,有空可以去看看這個(gè):Kronecker Products and Matrix Calculus in System Theory
這里最簡(jiǎn)單的解釋是:剛剛的導(dǎo)數(shù),對(duì)于 X\mathbf{X}X 的每一行其實(shí)都是 wT\mathbf{w}^TwT,而此時(shí) X\mathbf{X}X 有2行,因此結(jié)果出現(xiàn)了2列。
緊接著另外一個(gè)問題是:這個(gè)庫求導(dǎo)時(shí),只是對(duì)elementwise_grad 中的第一個(gè)參數(shù)求導(dǎo),下面這個(gè)例子演示了 ?y?w\frac{\partial \mathbf{y}}{\partial \mathbf{w}}?w?y? 的計(jì)算結(jié)果:
def lin_wx(w,x):return np.dot(x,w)print(lin_wx(w,x)) [ 6. 15.] lin_grad_wx = elementwise_grad(lin_wx)dw = lin_grad_wx(w,x)print(dw) print(dw.shape) [5. 7. 9.] (3,)這里其實(shí)又出現(xiàn)了一個(gè)“反常”的現(xiàn)象:同樣地,如果按照標(biāo)量求導(dǎo)的思路來看,此時(shí)的導(dǎo)數(shù)應(yīng)該是:
?y?w=XT\frac{\partial \mathbf{y}}{\partial \mathbf{w}} =\mathbf{X}^T ?w?y?=XT
而事實(shí)卻是:
?y?w=∑i=1nXi,:T\frac{\partial \mathbf{y}}{\partial \mathbf{w}} = \sum_{i=1}^{n} \mathbf{X}_{i,:}^T ?w?y?=i=1∑n?Xi,:T?
這里Xi,:\mathbf{X}_{i,:}Xi,:? 是 X\mathbf{X}X 的第 iii 行。 如果要想徹底弄明白這個(gè)問題還是建議去看這個(gè):Kronecker Products and Matrix Calculus in System Theory。
當(dāng)然,通常而言,我們只需要考慮 loss 對(duì)某個(gè)參數(shù)的導(dǎo)數(shù)就可以了,這個(gè)庫也做到了這一點(diǎn):即對(duì)某個(gè)標(biāo)量函數(shù),其實(shí)是完全可以得到與某參數(shù)的shape一模一樣的導(dǎo)數(shù),這樣在調(diào)用梯度做訓(xùn)練時(shí)也就比較簡(jiǎn)單了。
以下是官方的一個(gè)完整的logistic regression的例子。
from builtins import range import autograd.numpy as np from autograd import grad from autograd.test_util import check_gradsdef sigmoid(x):return 0.5*(np.tanh(x) + 1)def logistic_predictions(weights, inputs):# Outputs probability of a label being true according to logistic model.return sigmoid(np.dot(inputs, weights))def training_loss(weights):# Training loss is the negative log-likelihood of the training labels.preds = logistic_predictions(weights, inputs)label_probabilities = preds * targets + (1 - preds) * (1 - targets)return -np.sum(np.log(label_probabilities))# Build a toy dataset. inputs = np.array([[0.52, 1.12, 0.77],[0.88, -1.08, 0.15],[0.52, 0.06, -1.30],[0.74, -2.49, 1.39]]) targets = np.array([True, True, False, True])# Build a function that returns gradients of training loss using autograd. training_gradient_fun = grad(training_loss)# Check the gradients numerically, just to be safe. weights = np.array([0.0, 0.0, 0.0]) check_grads(training_loss, modes=['rev'])(weights)# Optimize weights using gradient descent. print("Initial loss:", training_loss(weights)) for i in range(100):weights -= training_gradient_fun(weights) * 0.01print("Trained loss:", training_loss(weights)) Initial loss: 2.772588722239781 Trained loss: 0.38900754315581143總結(jié)
以上是生活随笔為你收集整理的autograd库测试笔记-(一个基于Numpy的自动求导库)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 用Python实现Gauss-Jorda
- 下一篇: 区间数计算之Python实现