MACHINE LEARNING · TENSORFLOW · Google 的深度学习框架

TensorFlow:从Keras 一行到生产部署

这页带你把 TensorFlow 2.x 装起来、跑起来:两行 Keras 建模型,一个闭环走完 compile → fit → evaluate → predict,最后用一个 Fashion-MNIST 的 CNN 实战把 EarlyStopping 和 .keras 保存串起来。

zsh — ~/ml-playground
$ pip install tensorflow
Successfully installed keras-3.15.1 tensorflow-2.21.0 …
$ python
>>> import tensorflow as tf
>>> model = tf.keras.Sequential([tf.keras.layers.Dense(64, activation="relu"), tf.keras.layers.Dense(1)])
>>> model.compile("adam", "mse"); model.fit(x, y, epochs=5)
Epoch 5/5 - loss: 0.0231(示意输出) 两行建模型,一行开训
本页目录

01

Google 的深度学习框架:Keras 3 时代

TensorFlow 是 Google 开源的深度学习框架,覆盖「定义模型 → 训练 → 保存 → 部署」整条链路。2.x 默认以 eager 执行(写一行、跑一行,跟普通 Python 一样好调试);从 2.16 起,默认内置的高层 API 就是 Keras 3——同一套 Keras 代码,后端可以换成 TensorFlow、JAX 或 PyTorch。

它解决的问题很直接:你不想手写梯度下降和矩阵求导,只想把精力放在模型结构和数据上。用 model.fit() 十行以内就能完成一次完整训练;等模型练好了,.keras 保存、model.export() 导出、TF Serving 上线,路径都是官方铺好的。

适合谁:要在生产环境部署模型(TF Serving / LiteRT 移动端)的团队、想用最省事 API 入门深度学习的初学者。如果你的方向是研究、追新论文,PyTorch 生态更主流——两套都值得摸一遍,心智模型是相通的。

一句话版本史:1.x 的静态图让无数人吃过「先定义再 run」的苦,2.0 起全面拥抱 eager 执行;2.16 把 Keras 3 推上默认位,打通多后端。现在装 2.x,你拿到的就是「默认即时执行 + Keras 3」的组合。

生态位怎么记:训练用 TensorFlow / Keras;部署分两条线——服务器侧 TF Serving 吃 SavedModel,手机与嵌入式端走 LiteRT(原 TF Lite),浏览器里另有 TensorFlow.js。本页聚焦训练主线,部署概念在 s5、s7 里点到为止。

最新稳定版
2.21.0(2026-11 核对)
内置 Keras
Keras 3(2.16 起默认)
开发方
Google,Apache-2.0 开源
语言
Python(核心为 C++)
Python 要求
≥ 3.10(2.21 移除了 3.9)
执行方式
默认 eager,可切换图模式
GPU 支持
Linux + NVIDIA CUDA(pip 装 [and-cuda])
macOS GPU 构建
无——Apple Silicon 只能 CPU 跑
Keras 3 的另一层意义:把 import tensorflow as tf 换成 import keras,再用 KERAS_BACKEND=jax(或 torch)切后端,同一份模型代码可以在三个框架之间迁移——写法见第 04 节。对初学者的好处是:学 Keras 这一门 API,背后三个引擎都能用。

02

安装:一条 pip 走完,GPU 另说

TensorFlow 2.x 只有一个包名 tensorflow。装完验证 tf.__version__ 有输出就说明环境没问题;要不要 GPU,看你的系统和硬件。

Terminal
# 先升级 pip(TF 对 pip 版本有要求,macOS 要求 pip > 20.3)
pip install --upgrade pip

# 三个平台通用;macOS 直接装,不需要任何 -macos 后缀包
pip install tensorflow

# 验证
python -c "import tensorflow as tf; print(tf.__version__)"
2.16 起 tensorflow-macos 这个包已经不需要了,Apple Silicon 上装 tensorflow 本体即可。首次 import tensorflow 会打印一屏日志,属正常现象。
GPU 心理预期先摆正:macOS 没有 GPU 版 TensorFlow,Apple Silicon 只能 CPU 训练(小模型完全够用);真正的 GPU 加速在 Linux + NVIDIA 上。Windows 用户要么装 Linux/WSL2,要么就用 CPU 把本页所有示例跑完——本页示例数据量都很小,CPU 没问题。

03

五分钟上手:8 行代码拟一条直线

不碰图像、不碰 GPU,先用最小例子走完「建模型 → compile → fit → predict」闭环:让一个神经元自己学出 y = 2x + 1 里的系数。跑通它,你就跑通了 TensorFlow 90% 的日常形态。

fit_line.py
# python fit_line.py —— CPU 上几秒跑完
import numpy as np
import tensorflow as tf

x = np.linspace(-1, 1, 200).astype("float32")
y = 2 * x + 1 + 0.05 * np.random.randn(200).astype("float32")   # 带噪声的观测

model = tf.keras.Sequential([tf.keras.layers.Dense(1)])            # 一行建模型
model.compile(optimizer="sgd", loss="mse")                        # 三要素配齐
model.fit(x, y, epochs=50, verbose=0)                              # 静默训练
print(model.predict(tf.constant([0.5]), verbose=0))                # 约 [[2.0]](示意输出)
  • 01

    准备数据

    numpy 数组直接喂给 Keras,它会自己转成 Tensor。注意 dtype 用 float32——TF 的默认计算精度就是它,float64 会报 dtype 不匹配。

  • 02

    一行建模型

    Sequential 按顺序堆层。Dense(1) 只有一个神经元:权重 w、偏置 b,正是要学的 2 和 1。

  • 03

    compile 定三要素

    优化器怎么走(sgd)、损失怎么算(mse)、要盯什么指标(这次回归任务不需要 metrics)。编译只是配置,不碰数据。

  • 04

    fit 开训、predict 验收

    fit 里 epochs 是遍历全数据的次数,verbose=0 静音进度条。训完拿 0.5 去问模型,输出接近 [[2.0]](y = 2×0.5 + 1)就说明学对了。

想看每轮 loss 就把 verbose 设为 1。第一次 import tensorflow 打的 CUDA 相关 WARNING 不影响 CPU 训练,可以无视;报 dtype 错误大概率是喂了 float64 的 numpy 数据,转成 float32 即可。

想亲眼验证学到了什么:训练完调 model.get_weights(),返回数组里 w ≈ 2、b ≈ 1——一个神经元学一条直线,参数刚好看得清清楚楚。

04

核心概念:Tensor、Tape、tf.data 与建模三式

Tensor 与 Variable:数据和参数

Tensor(张量)是多维数组的统称,是 TF 里一切数据的容器;它是不可变的,算完产生新张量。Variable 是可训练的张量——模型的权重和偏置都是 Variable,训练器的本质就是每步沿着梯度改它。

python
>>> t = tf.constant([[1.0, 2.0], [3.0, 4.0]])   # 不可变张量
>>> v = tf.Variable([[1.0, 2.0]])              # 可训练变量:模型参数就是它
>>> t.shape, t.dtype
(TensorShape([2, 2]), tf.float32)
>>> t + 10                                      # 运算产生新张量,t 本身不变

tf.GradientTape:自动求导

eager 模式下,你在 GradientTape 上下文里做的每一步运算都被录在「磁带」上;出上下文后调 gradient(目标, 变量),它就沿磁带反推导数。手写训练循环全靠它(第 06 节速查表里给了一整段模板)。

python
>>> x = tf.Variable(3.0)
>>> with tf.GradientTape() as tape:
...     y = x ** 2 + 2 * x                       # y = x² + 2x
>>> dy_dx = tape.gradient(y, x)                 # 导数 2x + 2 在 x=3 处
>>> dy_dx.numpy()
8.0

tf.data:把数据变成流水线

数据一多就不能整块塞进内存,tf.data.Dataset 负责把数据变成「边流边喂」的管道:打乱、组批、预处理、预取各一步,链式写完。CPU 准备下一批和 GPU 训练上一批是并行的——prefetch + AUTOTUNE 是官方给的标准姿势,加上后训练速度常有可观提升。

python
ds = tf.data.Dataset.from_tensor_slices((x_train, y_train))   # 切成一条条样本
ds = ds.shuffle(10000).batch(128).prefetch(tf.data.AUTOTUNE)  # 打乱 → 组批 → 预取
model.fit(ds, epochs=5)                                       # Dataset 直接当 fit 输入

训练闭环与自定义训练循环

Keras 把标准闭环封装成四步:compile 配置 → fit 训练 → evaluate 评估 → predict 推理,90% 的任务到这就够了。但当损失函数复杂到 fit 表达不了(GAN 的对抗损失、多任务联合优化)时,就要用 GradientTape 亲手写循环——这也是理解「训练到底在做什么」的最佳练习。

custom_loop.py
# 自定义训练循环:结构永远不变——前向、求梯度、更新
optimizer = tf.keras.optimizers.Adam(1e-3)
ds = tf.data.Dataset.from_tensor_slices((x_train, y_train)).batch(64)

for epoch in range(10):
    for xb, yb in ds:
        with tf.GradientTape() as tape:
            logits = model(xb, training=True)                # 1 前向
            loss = tf.keras.losses.sparse_categorical_crossentropy(yb, logits)
        grads = tape.gradient(loss, model.trainable_variables)           # 2 求梯度
        optimizer.apply_gradients(zip(grads, model.trainable_variables)) # 3 更新
    print(f"epoch {epoch} done")

# fit 自带的进度条、回调、验证集都要自己补;先跑通 fit,再按需下探

Keras 建模三式:Sequential / Functional / Subclassing

Sequential 适合「一层接一层」的直筒模型(本页 s3、s6 都在用);Functional 用 Input + 函数式调用拼图,适合多输入多输出、有分支的结构,是工程里最常用的;Subclassing 继承 tf.keras.Model 自己写 call,自由度最高、样板代码也最多。

subclassed_model.py
class MyModel(tf.keras.Model):            # 子类化:自由度最高的一式
    def __init__(self):
        super().__init__()
        self.dense = tf.keras.layers.Dense(10)

    def call(self, inputs):               # 定义数据怎么流过模型
        return self.dense(inputs)

model = MyModel()
model.build(tf.TensorShape([None, 4]))    # 子类式要先 build 或跑一次才有形状

选型口诀:直筒用 Sequential,有分叉用 Functional,搞研究要极尽灵活才上 Subclassing。三种方式都接同一套 compile / fit。

05

API 速查:常用的就这几张表

下面这块表格能搜。忘了某个层、某个损失函数叫什么,直接搜 conv、损失、早停、batch 之类的词。

API作用
tf.keras.Sequential([...])按顺序堆层的最简模型容器
tf.keras.Input(shape=...)声明输入形状,Functional 建模的入口
tf.keras.Model(inputs, outputs)函数式建模,多输入多输出就用它
tf.keras.layers.Dense(64)全连接层;activation="relu" 常配
tf.keras.layers.Conv2D(32, 3)二维卷积层,图像任务主力
tf.keras.layers.MaxPooling2D()最大池化,卷积后降尺寸
tf.keras.layers.Flatten()摊平成一维,卷积接全连接的桥
tf.keras.layers.Dropout(0.5)训练期随机置零一部分激活,抗过拟合
tf.keras.layers.BatchNormalization()批归一化,深网络稳定训练
tf.keras.layers.Embedding(vocab, 16)ID 序列转稠密向量,文本任务入口层
tf.keras.layers.LSTM(64)循环层,序列任务经典选择
model.summary()打印每层形状与参数量,建模完先看它

06

实战:Fashion-MNIST 的 CNN 训练全程

Fashion-MNIST 是 MNIST 的「换装版」:同样 6 万训练 + 1 万测试、28×28 灰度图、10 类,但换成 T 恤、裤子、鞋、包等服饰,比手写数字难一点,更适合当教学基准。目标:一个几十 KB 参数的小 CNN,CPU 上几分钟内把测试集准确率练到 0.90 左右。

第 1 步:数据加载与预处理

fashion_mnist.py · 数据
import tensorflow as tf

# 1) 内置数据集:60000 训练 + 10000 测试,28x28 灰度图,10 类
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.fashion_mnist.load_data()

# 2) 归一化到 0~1:像素 0~255 直接喂会让 loss 又大又不稳
x_train = x_train / 255.0
x_test = x_test / 255.0

# 3) 加通道维:(60000, 28, 28) -> (60000, 28, 28, 1),Conv2D 要求 4 维输入
x_train = x_train[..., tf.newaxis]
x_test = x_test[..., tf.newaxis]

print(x_train.shape, y_train.shape)   # (60000, 28, 28, 1) (60000,)

第 2 步:用 Functional 方式搭 CNN

fashion_mnist.py · 模型
# 两轮「卷积 + 池化」提特征,摊平后接全连接出 10 类
inputs = tf.keras.Input(shape=(28, 28, 1))
x = tf.keras.layers.Conv2D(32, 3, activation="relu")(inputs)
x = tf.keras.layers.MaxPooling2D()(x)
x = tf.keras.layers.Conv2D(64, 3, activation="relu")(x)
x = tf.keras.layers.MaxPooling2D()(x)
x = tf.keras.layers.Flatten()(x)
x = tf.keras.layers.Dropout(0.4)(x)                       # 小数据集防过拟合
outputs = tf.keras.layers.Dense(10, activation="softmax")(x)
model = tf.keras.Model(inputs, outputs)
model.summary()                                           # 先看一眼结构与参数量

别被 Conv2D(32, 3) 两个数字迷惑:32 是输出通道数,3 是卷积核边长。卷积核在全图上共享,参数量远小于同规模全连接层——这正是 CNN 适合图像的根本原因。

第 3 步:compile + 回调 + fit

fashion_mnist.py · 训练
# 标签是 0~9 整数,用 sparse 前缀的交叉熵
model.compile(
    optimizer=tf.keras.optimizers.Adam(1e-3),
    loss="sparse_categorical_crossentropy",
    metrics=["accuracy"],
)

callbacks = [
    tf.keras.callbacks.EarlyStopping(monitor="val_accuracy", patience=3,
                                     restore_best_weights=True),   # 不再变好就停
    tf.keras.callbacks.ModelCheckpoint("best_model.keras", monitor="val_accuracy",
                                       save_best_only=True),       # 只存最优
]

history = model.fit(
    x_train, y_train,
    epochs=30,                # 上限开大,早停会自己收手
    batch_size=128,
    validation_split=0.1,     # 留 10% 训练数据当验证集
    callbacks=callbacks,
)

fit 返回的 history 是个字典,history.history["val_accuracy"] 就是每轮验证准确率——画曲线就用它。训练尾段常见现象:train accuracy 还在涨、val 停滞甚至回落,EarlyStopping 就是在这个拐点上踩刹车。

第 4 步:evaluate、predict 与保存

fashion_mnist.py · 评估与保存
# 测试集评估(fit 从没见过这 10000 张)
test_loss, test_acc = model.evaluate(x_test, y_test, verbose=0)
print(f"测试集准确率:{test_acc:.3f}")        # 0.90 左右(示意输出)

# 单张预测:predict 返回 10 个概率,取最大者当类别
import numpy as np
probs = model.predict(x_test[:3], verbose=0)
print(probs.argmax(axis=1))                  # [9 2 1](示意输出)

# 全量保存为 .keras:结构 + 权重 + 优化器状态一起带走
model.save("fashion_mnist_cnn.keras")
restored = tf.keras.models.load_model("fashion_mnist_cnn.keras")

第 5 步:用 TensorBoard 看训练曲线

fashion_mnist.py · 日志
# 在第 3 步的 callbacks 列表里追加一项:
callbacks.append(tf.keras.callbacks.TensorBoard(log_dir="logs"))

# 训练开跑后,另开一个终端启动看板(浏览器开 http://localhost:6006):
# tensorboard --logdir logs —— loss / accuracy 曲线实时刷新

调参时盯着 val 曲线比盯终端数字直观得多——训练损失继续降、验证曲线抬头的那一眼,就是过拟合的现场。

以上准确率与预测数字均为示意输出:CNN 初始化带随机性,同一份代码不同机器的结果会小幅波动。想部署成服务,把 model.save 换成 model.export("serving") 导出 SavedModel,交给 TF Serving 加载即可。

07

避坑:版本、形状、速度与保存

坑一:版本混装,Keras 2 / 3 断代

最常见的环境病:pip install tensorflow 装的是 Keras 3,网上老教程却在用 Keras 2 API,跑起来满屏 AttributeError。2.16 是分水岭:之前 tf.keras 是 Keras 2,之后是 Keras 3。老代码别硬改,用官方兼容层。

Terminal
pip uninstall tensorflow-macos     # 2.16 起不再需要,留着会互相打架
pip install --upgrade --force-reinstall tensorflow
pip install tf_keras               # Keras 2 兼容包
export TF_USE_LEGACY_KERAS=1       # 让 tf.keras 指回 Keras 2 行为
判断方法:出问题时先跑 python -c "import keras; print(keras.__version__)"。版本 3.x 开头就按 Keras 3 语法排查;另外确认同一个虚拟环境里只装了一份 tensorflow——混装 tensorflow、tensorflow-macos、tensorflow-gpu 是历史遗留重灾区。

坑二:形状不对、没归一化,loss 不收敛

图像进 Conv2D 必须是 4 维 (批, 高, 宽, 通道),灰度图的通道维要自己补;像素 0~255 直接喂网络,梯度会被大数值搅得来回震荡。这两件事没做,症状都是「几十个 epoch 了 loss 还在 2 以上」。

python
# 两个修正都齐了再进模型
x = x.astype("float32") / 255.0     # 归一化
x = x[..., tf.newaxis]              # (N, 28, 28) -> (N, 28, 28, 1)
# 标签是整数就配 sparse_categorical_crossentropy;
# 报 sparse/one-hot 相关错误,八成是损失函数跟标签格式配错了对

坑三:训练慢——CPU 全在等数据,或 GPU 没用上

慢有三个惯犯:没建 tf.data 管道(每步都在现场做预处理)、batch 太小(GPU 吃不饱)、GPU 根本没被认到。排查顺序也是这三步。

python · GPU 排查
import tensorflow as tf
print(tf.config.list_physical_devices("GPU"))
# 空列表 = GPU 没生效。Linux:确认装了 tensorflow[and-cuda]、驱动正常;
# WSL2:2.21.0 有 GPU 识别问题(见 GitHub issue #113541),可先用 2.20.0
内存爆了:OOM 多半是 batch_size 太大或数据集整个读进内存。前者调小 batch_size,后者把加载换成 tf.data 的逐条读取 + cache()/prefetch(),别一股脑 np.load 全量进 RAM。

坑四:保存了模型,换环境加载报错

两个高频报错都有明确解法:加载 .keras 报 unknown layer——自定义层/子类模型保存时要能被找到(自定义层实现 get_config,加载前先 import 定义它的模块);分不清两种保存——.keras 是给「继续训练 / Python 里复用」的全量格式,model.export() 导出的是纯推理 SavedModel,只喂给 TF Serving 这类部署工具。

python · 两条路线别混
# 路线 A:继续训练 / 自己加载推理
model.save("model.keras")
m2 = tf.keras.models.load_model("model.keras")

# 路线 B:上 TF Serving / 服务器部署(只含推理图)
model.export("serving/1")
# Serving 端按目录号加载:serving/1, serving/2 … 模型版本管理天然成型

08

进阶:官方文档与部署路线

本页只覆盖训练主线,下面这些是官方入口,按需取用:

  • TensorFlow 安装指南 —— 各平台、GPU、虚拟环境的权威说法,版本冲突先查这里。
  • 官方教程库 —— 图像分类、NLP、时序、GAN 等成套的 Colab 教程,比任何二手教程都新。
  • Keras 3 开发者文档 —— 多后端说明、层与回调的完整 API、迁移指南都在这里。
  • GitHub 仓库 —— Release Notes 追版本变化(如 2.21 的 CUDA 变更)。
  • TF Serving —— 把 SavedModel 变成高吞吐推理服务的主流方案。
  • LiteRT(原 TF Lite) —— 手机、嵌入式端部署;2.20 起 TFLite 已整体迁到 LiteRT 项目,新代码别再从 tf.lite 起手。
学完下一步去哪:① 拿同一个 Fashion-MNIST 去 PyTorch 教程 写一遍训练循环,对比两套框架的心智模型,理解会深一层;② 需要传统机器学习(表格数据)时回到 Scikit-learn 教程;③ 想动手部署,TF Serving + Docker 是最小可行路径。

09

收尾:照着勾一遍

0 / 5

已复制