```python
>>> y_pred=tf.convert_to_tensor([4.,3.])
>>> with tf.GradientTape() as g:
... g.watch(y_pred)
... with tf.GradientTape() as gg:
... gg.watch(y_pred)
... # 计算L2范数
... mo_pred=tf.reduce_sum(y_pred**2)**0.5
... # 计算L2范数一阶导数
... dmo = gg.gradient(mo_pred, y_pred)
...
>>> # 计算L2范数二阶导数
>>> ddmo = g.gradient(dmo, y_pred)
```
#L2范数一阶导数=Y/||Y||,没问题
dmoL2范数二阶导数=?,这里实际计算的就是(Y/||Y||)的导数,tape计算出的结果好像有点问题, 经手工算,这个导数应该是(||Y||^2-Y^2)/||Y||^3,其中^n表示n次幂运算, 后续贴出手算过程,而且这个Y/||Y||应该是单调增函数,怎么经过tape计算出的导数有负数呢?
ddmo
还有Y/||Y||应该是单调增函数,举例假设Y是二维向量[y1,y2], 设y1固定为3的情况下,||Y||曲线应该是y2/((3^2+y^2)^0.5),这个函数在单个维度上明显是一个单调增函数呀,求偏导不应出现负数啊
这种情况可能是因为在计算梯度时,没有考虑到分母中的L2范数对分子的导数的影响。
在使用GradientTape计算(Y/||Y||)的导数时,需要考虑分母中的L2范数对分子的导数的影响,这可以通过链式法则来实现。具体来说,假设Y是一个向量,L2范数为norm(Y),则(Y/||Y||)的导数可以表示为:
d(Y/||Y||)/dY = (1/||Y|| - Y * (Y dot Y)^(-3/2)) * I
其中,* 表示点积,^ 表示幂运算,I 表示单位矩阵。
在使用GradientTape计算导数时,可以将上述公式直接应用于代码中,如下所示:
with tf.GradientTape() as tape:
y_norm = tf.norm(y)
y_normalized = y / y_norm
loss = tf.reduce_sum(tf.square(y_normalized))
# 计算梯度
grads = tape.gradient(loss, [y])
grad_normalized = (1 / y_norm - y * tf.pow(y_norm, -3)) * tf.eye(y.shape[0])
grads_normalized = tf.matmul(grad_normalized, grads[0])
这样计算出来的梯度应该就会和手工计算的结果一致了。