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 的数据:
对应代码:
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)
- 定义变量(
tf.Variable)→ 可训练参数 - 定义占位符(
tf.placeholder)→ 输入数据 - 构建计算图(用 TF 操作定义损失函数)
- 选择优化器(
.minimize(loss)) - 启动 Session,初始化变量
- 循环执行:
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 等 |
| Session | TF 1.x 中执行计算的上下文环境 |