我正在观看 Andrej Karpathy 的视频“神经网络和反向传播的详细介绍:构建 micrograd”,并且在 “手动反向传播示例 #1:简单表达式”章节中他使用他的演示库构建了一个示例表达式然后手动逐步通过该表达式进行反向传播。他在视频中构建的“micrograd”库将表达式存储为图形,同时也是一个自动微分引擎,并存储图形中每个节点的中间梯度,以便可以轻松地应用链式法则。
我认为拥有他的示例的 SymPy 版本会很有趣,不仅可以看到每个步骤的数字梯度,还可以看到图表每个节点的符号导数,并且首先我只想做基础知识SymPy,但我不确定如何使用 SymPy.diff() 获得某些中间表达式的导数
这是代码:
(a, b, c, d, e, f, L) = sp.symbols('a b c d e f L')
e = a * b
d = e + c
L = d * f
sublist = {a: 2.0, b:-3.0, c:10.0, f:-2.0}
dLda = sp.diff(L, a)
dLdb = sp.diff(L, b)
dLdf = sp.diff(L, f)
dLdc = sp.diff(L, c)
dddc = sp.diff(d, c)
print(L)
print(L.subs(sublist))
print(dLdc)
print(dLdf)
print(dddc)
#dLdc = dLdd * dddc
dLdd = sp.diff(L, d) # should get 'f'
我得到:
f*(a*b + c)
-8.00000000000000
f
a*b + c
1
ValueError Traceback (most recent call last)
Cell In[48], line 20
18 print(dddc)
19 #dLdc = dLdd * dddc
---> 20 dLdd = sp.diff(L, d) # should print 'f'
<...>
ValueError:
Can't calculate derivative wrt a*b + c.
我知道通常这是没有意义的,因为像 d 这样的中间“节点”并不是在实际网络中对值进行任何调整的地方(即,如果这是一个实际的神经网络,我只会“轻推” ' a、b、f 和 c),但是如果您逐步遍历每个节点来跟踪到目前为止的导数,我认为尝试将 dLdd 作为一个东西并不是不合理的,而且最好是SymPy 可以做到这一点。再次强调,我并不打算尝试使用 SymPy 来实际执行这些计算,它只是为了说明幕后发生的事情。
我尝试使用
with sp.evaluate(False):
以几种不同的方式运行它,但我没有任何运气,但也许我错过了一些东西,并且有某种方法可以实际保留 'd' 和 'e' 而无需 SymPy 简化他们走了。
这看起来像一个 SymPy 陷阱 - 当你写这个时,
d
不再是一个简单的Symbol
,并且名称被赋予了e
和c
的表达式(你会得到同样的问题)如果您尝试使用e
)
d = e + c # d Symbol lost and name re-used
L = d * f
而是使用
Eq()
或减去它们并给新结果起一个不同的名称
e_ = a * b
d_ = e_ + c
L = d_ * f
sublist = {a: 2.0, b:-3.0, c:10.0, f:-2.0}
dLda = sp.diff(L, a)
dLdb = sp.diff(L, b)
dLdf = sp.diff(L, f)
dLdc = sp.diff(L, c)
dddc = sp.diff(d_, c)