文章

35 TensorFlow 基础与自动微分机制

35 TensorFlow 基础与自动微分机制

一、核心目标

  通过一个简单的二次损失函数优化问题,演示 TensorFlow 程序的基本结构,并揭示其如何自动完成:

  • 前向计算(定义损失函数)
  • 反向传播(自动求导)
  • 参数更新(优化器)

  从而为训练复杂神经网络打下基础。


二、示例问题:最小化一个二次损失函数

  给定损失函数:

\[J(w) = w^2 - 10w + 25 = (w - 5)^2\]

  显然,当 $w = 5$ 时,$J(w)$ 取得最小值 0。

💡 关键思想:即使我们不知道解析解,也可以用 TensorFlow 自动通过梯度下降找到最优 $w$。


三、TensorFlow 程序基本结构(TF 1.x 风格)

⚠️ 注意:本课程使用的是 TensorFlow 1.x 的静态图(Session + Placeholder)模式,与 TF 2.x 的 Eager Execution 不同,但原理相通。

1. 导入库

1
2
import numpy as np
import tensorflow as tf

2. 定义可训练参数(变量)

1
w = tf.Variable(0.0, dtype=tf.float32)
  • tf.Variable 表示需要被优化的参数(如神经网络权重)。
  • 初始值设为 0。

3. 定义损失函数(前向计算)

  两种写法等价:

  写法一(显式调用 TF 操作)

1
cost = tf.add(tf.add(w**2, tf.multiply(-10.0, w)), 25.0)

  写法二(运算符重载,更简洁)

1
cost = w**2 - 10.0 * w + 25.0

✅ TensorFlow 对 +, -, *, ** 等运算符进行了重载,使代码更接近数学表达。

4. 定义优化器与训练操作

1
train = tf.train.GradientDescentOptimizer(0.01).minimize(cost)
  • 学习率 $\alpha = 0.01$
  • minimize(cost) 自动:

    • cost 关于所有 tf.Variable 求梯度
    • 执行参数更新:$w := w - \alpha \cdot \frac{\partial J}{\partial w}$

5. 初始化与执行(Session 机制)

1
2
3
4
5
6
7
8
9
10
11
12
13
init = tf.global_variables_initializer()
with tf.Session() as session:  # 推荐使用 with 结构,自动管理资源
    session.run(init)
    print("初始 w =", session.run(w))  # 输出: 0.0

    # 执行一次梯度下降
    session.run(train)
    print("一步后 w =", session.run(w))  # 输出: 0.1

    # 执行 1000 次迭代
    for _ in range(1000):
        session.run(train)
    print("1000 步后 w =", session.run(w))  # 输出 ≈ 4.99999

🔁 关键点session.run(train) 才真正触发一次优化步骤。


四、引入训练数据:使用 tf.placeholder

  现实中的损失函数依赖于训练数据(如 $x, y$)。TensorFlow 使用 placeholder 占位符来表示“稍后输入的数据”。

1. 定义占位符

1
x = tf.placeholder(tf.float32, [3, 1])  # 形状为 (3,1) 的输入数据

2. 将损失函数参数化

  将原损失函数的系数(1, -10, 25)替换为来自 x 的数据:

\[J(w; x) = x[0][0] \cdot w^2 + x[1][0] \cdot w + x[2][0]\]

  对应代码:

1
cost = x[0][0] * w**2 + x[1][0] * w + x[2][0]

3. 准备实际数据并“喂入”(feed)

1
2
3
4
5
coefficients = np.array([[1.], [-10.], [25.]])  # 对应 (w-5)^2
# 或改为 [[1.], [-20.], [100.]] 对应 (w-10)^2

# 在 run 时通过 feed_dict 传入
session.run(train, feed_dict={x: coefficients})

🧠 核心机制feed_dict 允许在每次运行时动态传入不同的 mini-batch 数据,这是训练神经网络的关键。


五、TensorFlow 的核心优势:自动微分与计算图

1. 计算图(Computation Graph)

  • 当你写 cost = w**2 - 10*w + 25 时,TensorFlow 并不立即计算数值,而是构建一个符号计算图
  • 图中节点表示操作(如平方、乘法、加法),边表示数据流。

2. 自动微分(Automatic Differentiation)

  • TensorFlow 内置了所有基本操作(add, multiply, pow 等)的反向传播规则(梯度函数)
  • 调用 minimize() 时,系统自动:

    • cost 节点反向遍历计算图
    • 应用链式法则计算 $\frac{\partial J}{\partial w}$
    • 更新 w

你只需定义前向传播,反向传播全自动完成!

3. 优化器灵活切换

  只需修改一行代码即可更换优化算法:

1
2
3
4
5
# 梯度下降
train = tf.train.GradientDescentOptimizer(0.01).minimize(cost)

# Adam 优化器(通常更快更稳)
train = tf.train.AdamOptimizer(0.01).minimize(cost)

六、编程习惯与最佳实践

写法说明
with tf.Session() as sess:推荐:自动关闭 Session,避免资源泄漏
tf.global_variables_initializer()必须显式初始化所有 Variable
使用 feed_dict 传入数据适用于 placeholder,支持 mini-batch

七、总结:TensorFlow 编程范式(TF 1.x)

  1. 定义变量tf.Variable)→ 可训练参数
  2. 定义占位符tf.placeholder)→ 输入数据
  3. 构建计算图(用 TF 操作定义损失函数)
  4. 选择优化器.minimize(loss)
  5. 启动 Session,初始化变量
  6. 循环执行session.run(train_op, feed_dict={...})

这一范式可无缝扩展到深层神经网络:只需将 cost 替换为复杂的网络输出 + 损失函数(如交叉熵),其余结构几乎不变。


八、延伸思考(现代 TF 2.x 对比)

  虽然本课程使用 TF 1.x,但 TF 2.x 默认启用 Eager Execution,无需 Session 和 Placeholder,代码更像普通 Python:

1
2
3
4
5
6
7
8
9
10
import tensorflow as tf

w = tf.Variable(0.0)
optimizer = tf.keras.optimizers.SGD(learning_rate=0.01)

for _ in range(1000):
    with tf.GradientTape() as tape:
        cost = w**2 - 10*w + 25
    grads = tape.gradient(cost, [w])
    optimizer.apply_gradients(zip(grads, [w]))

  但自动微分的核心思想完全一致:你定义前向,框架处理反向。


✅ 学习要点回顾

概念说明
tf.Variable表示模型参数,会被优化器更新
tf.placeholder表示外部输入数据(TF 1.x)
feed_dict向 placeholder 传入实际数据
计算图符号化表示计算流程,支持自动求导
自动微分框架自动计算梯度,无需手动推导
优化器一行代码切换 GD / Adam / RMSProp 等
SessionTF 1.x 中执行计算的上下文环境
本文由作者按照 CC BY 4.0 进行授权