编程框架机理
- TensorFlow设计原则
- TensorFlow计算图机制
-
- 自动求导计算图
-
- 常用的求导方法
-
- 手动求解法
- 数值求导法
- 符号求导法
- 自动求导法
- 对比四种求导方法
- 检查点机制
- TensorFlow 中的控制流
-
- 5个基本控制流算子
- 编译控制流结构
- 计算图的执行模式
- 本地执行计算图
-
- 计算图剪枝
- 计算图分配
- 计算图优化
- 计算图切割和设备通信
- 分布式执行计算图
-
- 分布式通信
- 容错机制
TensorFlow设计原则
TensorFlow设计原则如下:,,
-
高性能
-
TensorFlow在计算中,底层硬件架构在设计过程中得到了充分的优化.
-
对生成的计算图,TensorFlow为了提高计算图的运行效率,供了一系列优化操作.
-
TensorFlow调度器可以根据网络结构特点,并发运行没有数据依赖的节点.
例如,以下示例:
import tensorflow as tf a = tf.constant(1.0) b = tf.constant(2.0) c = tf.sin(a) d = tf.cos(b) e = tf.add(c, d) with tf.Session() as sess: sess.run(e)
上面例子中
c
和d
没有依赖关系,可以并发执行. -
-
易开发
-
TensorFlow对于各种现有的深度学习算法,提取了大的共性操作,并将其包装成算子.
-
用户使用TensorFlow在开发算法时,可以直接调用这些算子,方便实现算法.
-
-
可移植
- TensorFlow各种异构系统可以工作.
- 在不同的设备上实现每个算子(如矩阵乘法)的不同底层.
- 通过上述机制,统一的用户程序可以在不同的硬件平台上执行.
TensorFlow计算图机制
计算图的自动求导
-
在深度学习中,通常采用梯度下降法更新模型参数.
-
梯度计算直观,但手动计算复杂模型梯度非常困难
-
目前,大多数深度学习框架提供自动梯度计算功能
-
用户只需描述前向计算的过程,编程框架自动推导反向计算图,完成导数计算
常用的求导方法
常用的求导方法包括: ,,,.
手动求解法
即传统的反向传播算法: 用链式法则手动解决梯度公式,替换值,获得最终梯度值.
缺点:
- 对于大规模的深度学习算法,手动用链式法则进行梯度计算并转换成计算机程序非常困难.
- 需要手动编写梯度解码代码.
- 每次修改算法模型时,都修改相应的梯度求解算法.
数值求导法
解释使用导数的原始定义 f ′ ( x ) = lim h → 0 f ( x h ) ? f ( x ) h f'(x) = \lim_{h \rarr 0} \frac{f(x h) - f(x)}{h} f′(x)=h→0limhf(x+h)−f(x) 优点:
- 易操作
- 可对用户隐藏求解过程
缺点:
- 计算量大,求解速度慢
- 可能引起舍入误差和截断误差
符号求导法
利用求导规则来对表达式进行自动操作,从而获得导数.
常见求导规则: d d x ( f ( x ) + g ( x ) ) = d d x f ( x ) + d d x g ( x ) d d x ( f ( x ) g ( x ) ) = ( d d x f ( x ) ) g ( x ) + f ( x ) ( d d x g ( x ) ) d d x f ( x ) g ( x ) = f ′ ( x ) g ( x ) − f ( x ) g ′ ( x ) g ( x ) 2 \frac{d}{dx}(f(x)+g(x)) = \frac{d}{dx}f(x) + \frac{d}{dx}g(x) \\ \frac{d}{dx}(f(x) \, g(x)) = (\frac{d}{dx}f(x))g(x) + f(x)(\frac{d}{dx}g(x)) \\ \frac{d}{dx} \frac{f(x)}{g(x)} = \frac{f'(x)g(x) - f(x)g'(x)}{g(x)^2} dxd(f(x)+g(x))=dxdf(x)+dxdg(x)dxd(f(x)g(x))=(dxdf(x))g(x)+f(x)(dxdg(x))dxdg(x)f(x)=g(x)2f′(x)g(x)−f(x)g′(x)
缺点: 表达式膨胀问题
自动求导法
-
上述求导方法的对比:
- 数值求导法: 一开始直接代入数值近似求解
- 符号求导法: 直接对代数表达式求解,最后才代入问题数字
- 自动求导法: 介于数值求导和符号求导的方法
-
计算图结构天然适用于自动求导:
计算图将多输入的复杂计算表达成了由多个基本二元计算组成的有向图,并保留了所有中间变量,有助于程序自动利用链式法则进行求导.
-
优点:
- 灵活,可以完全向用户隐藏求导过程.
- 只对基本函数运用符号求导法,因此可以灵活结合编程语言的循环结构、条件结构等.
-
下面例子展示TensorFlow中注册Sin(x)函数反向求导方法:
@ops.RegisterGradient("Sin") def _SinGrad(op, grad): """Returns grad * cos(x) """ x = op.inputs[0] with ops.control_dependencies([grad]): x = math_ops.conj(x) return grad * math_ops.cos(x)
-
TensorFlow会自动生成对应的反向计算节点,并将其加入到计算图中.
v1 = tf.Variable(0.0, name="v1") v2 = tf.Variable(0.0, name="v2") loss = tf.add(tf.sin(v1), v2) sgd = tf.train.GradientDescentOptimizer(0.01) grads_and_vars = sgd.compute_gradient(loss)
-
计算过程: 分两步执行
-
原始函数建立计算图,数据正向传播,计算出中间节点 x i x_i xi,并记录计算图中的节点依赖关系.
-
反向遍历计算图,计算输出对于每个节点的导数
x i ˉ = ∂ y j ∂ x i \bar{x_i} = \frac{\partial{y_j}}{\partial{x_i}} xiˉ=∂xi∂yj
对于前向计算中一个数据( x i x_i xi)连接多个输出数据( y j y_j yj, y k y_k yk)的情况,自动求导中,将这些输出数据相对于该数据的导数累加. x i ˉ = y j ˉ ∂ y j ∂ x i + y k ˉ ∂ y k ∂ x i \bar{x_i} = \bar{y_j} \frac{\partial{y_j}}{\partial{x_i}} + \bar{y_k} \frac{\partial{y_k}}{\partial{x_i}} xiˉ=yjˉ∂xi∂yj+ykˉ∂xi∂yk
-
示例: 对于函数 f ( x 1 , x 2 ) = ( e x 1 + x 2 ) ( x 2 + 1 ) f(x_1,x_2) = (e^{x_1}+x_2)(x_2+1) f(x1,x2)=(ex1+x2)(x2+1)
前向计算 反向计算 计算过程 示意图
-
四种求导方法的对比
方法 | 对图的遍历次数 | 精度 | 备注 |
---|---|---|---|
手动求解法 | N A N_A NA | 高 | 实现复杂 |
数值求导法 | n i + 1 n_i+1 ni+1 | 低 | 计算量大,速度慢 |
符号求导法 | N A N_A NA 标签: kyk连接器 |