TensorFlow中的一个重要ops---MatMul的实现(二)

来源:互联网 发布:微博上最恶心的公知 编辑:程序博客网 时间:2024/05/06 12:42

上面一篇文章我们主要介绍了MatMul这个ops的正向计算的实现,我们知道模型的最后一步是计算优化模型的参数,而一般采用的方法是梯度下降法,所以每个ops节点不仅要实现一个正向的计算节点,还要实现一个反向的梯度计算节点。

关于反向计算的结点官网有如下一段介绍:


Implement the gradient in Python

Given a graph of ops, TensorFlow uses automatic differentiation (backpropagation) to add new ops representing gradients with respect to the existing ops (seeGradient Computation). To make automatic differentiation work for new ops, you must register a gradient function which computes gradients with respect to the ops' inputs given gradients with respect to the ops' outputs.Mathematically, if an op computes y = f(x) the registered gradient op converts gradients ∂L/∂y of losswith respect toy into gradients ∂L/∂x with respect to x via the chain rule:


给定一个 Ops 组成的图, TensorFlow 使用自动微分 (反向传播) 来添加新的 Ops 以表示梯度运算, 同时不影响已有的 Ops (参见梯度运算). 为了使自动微分能够与新的 Ops 协同工作, 必须注册一个梯度函数, 从 Ops 的输入计算梯度, 并返回代表梯度值的输出.数学上, 如果一个 Ops 计算 y = f(x) , 注册的梯度 Ops 通过以下链式法则, 将 ∂L/∂y的梯度运算转化为  ∂L/∂x的梯度运算.


总结起来就是一个图无非是如下结构的递归堆叠:

L=g(f(x))

因为TensorFlow的图有个特点上个节点的输出是下个节点的输入,这个不正满足了链式法则的要求。对于上式,假设y=f(x),我们要求的是 相对于 的梯度(求出梯度就可以利用梯度下降法计算下一步的变量值了),也就是∂L/∂x的值,只需要先分别求出第一个ops节点的梯度∂y/∂x,和第二个ops节点的梯度 ∂L/∂y,依据链式法则自然有:


加上一点想象力,假设L=g(y)也是一个符合函数,那不就是可以扩展成有很多节点的复杂的图了。

接下来我们看下MatMul梯度节点的实现:代码在:python/ops/math_grad.py文件里面是一个Python的实现,

@ops.RegisterGradient("MatMul")def _MatMulGrad(op, grad):  """Gradient for MatMul."""  t_a = op.get_attr("transpose_a")  t_b = op.get_attr("transpose_b")  a = math_ops.conj(op.inputs[0])  b = math_ops.conj(op.inputs[1])  if not t_a and not t_b:    grad_a = math_ops.matmul(grad, b, transpose_b=True)    grad_b = math_ops.matmul(a, grad, transpose_a=True)  elif not t_a and t_b:    grad_a = math_ops.matmul(grad, b)    grad_b = math_ops.matmul(grad, a, transpose_a=True)  elif t_a and not t_b:    grad_a = math_ops.matmul(b, grad, transpose_b=True)    grad_b = math_ops.matmul(a, grad)  elif t_a and t_b:    grad_a = math_ops.matmul(b, grad, transpose_a=True, transpose_b=True)    grad_b = math_ops.matmul(grad, a, transpose_a=True, transpose_b=True)  return grad_a, grad_b


我们首先看

@ops.RegisterGradient("MatMul")

这个是表示这个申明的是一个梯度

然后我们看到,返回值有两个:grad_a,grad_b,为什么呢,因为MatMul(A ,B)这个ops节点函数的参数有两个,A和B,他也不知道那个是变量那个是定值,所以就对两个变量都求了梯度,然后可以选择利用哪个。

现在那问题就转化为对于函数 y=A*B,分别求y相对于A矩阵和B矩阵的导数,

矩阵求导有如下公式:

Y = A * X --> DY/DX = A'Y = X * A --> DY/DX = AY = A' * X * B --> DY/DX = A * B'Y = A' * X' * B --> DY/DX = B * A'

更多关于矩阵求导可以参考下文:

http://blog.csdn.net/wbgxx333/article/details/22992977

那现在再来看上面的求导结果就非常清楚了,而且应该说还是比较简单的


0 0