1. 拿到第三版,我第一反应是重新验证老知识
干了这么多年深度学习,我其实很少从头到尾啃一本书。多数时候是翻文档、查教程、抄代码。但最近拿到《Python 深度学习》第三版之后,我破例从头开始过了一遍,第一部分的章节尤其让我感慨。它不是简单地把旧内容更新一下版本号,而是把整个知识体系按当前的技术现状重新组织了。作为给新人打基础的编排,这套内容其实比很多视频课都严谨。
这本书的第一部分,核心关键词就是"深度学习基础"和"Keras入门"。先讲清楚深度学习到底在解决什么问题,再把神经网络的前向传播、梯度、反向传播这些数学机制拆开讲明白,最后落到工程实践上,用Keras搭建和训练模型。它不是一个纯理论书,也不是一个纯代码教程,而是把"为什么"和"怎么做"很自然地串联起来。对新入门的同学来说,这可能是最低成本的正确学习路径;对有经验的从业者来说,这部分内容也是很好的知识校准机会,能把你以前模糊或者理解偏差的点重新梳顺。
1.1 第三版相比前版,为什么值得重新读
要说第三版最大的变化,我第一个想到的是它站在了Keras 3的语境下。前两个版本写的是Keras作为TensorFlow内置模块的用法,模型构建、数据流水线、配置流程都和今天实际工程里的做法不完全一致。第三版直接切换到Keras的独立多后端模式,同一个模型代码可以根据你的需求跑在不同框架上,这种变化对实际项目的意义非常大。
我简单做过一个对比,列在这边:
| 维度 | 旧版典型写法 | 第三版通用做法 |
|---|---|---|
| 框架绑定 | 主要绑定单一后端 | 支持多种后端,代码解耦 |
| 模型构建 | Sequential为主,API说明零散 | Functional API和子类化有系统讲解 |
| 数据准备 | 手动转换数组 | 更多使用数据集管道方案 |
| 训练流程 | 静态地fit模型 | 重视回调、自定义循环、分布式配置 |
| 前沿内容 | 简单提及生成模型 | 有专门的现代生成技术扩展 |
当然,这些变化不是让你把旧知识全扔掉。反而让我觉得,第三版把以前埋在各种文档角落里的最佳实践,终于用一个正式的教材框架说清楚了。你会发现很多你以为的"野路子"方法,其实是官方推荐的常规解法。
1.2 第一部分的知识地图与读者画像
第一部分的内容在整本书里的分量很大,也可以说是最值得精读的地方。它基本按照"认识问题—理解原理—动手实践—建立方法论"的顺序推进。前面先把深度学习是什么、能做什么、边界在哪说清楚,然后快速进入神经网络内部的数学机制,让你了解张量、梯度、优化器这些概念在训练里各自扮演什么角色。接着进入Keras的实际操作,从最简单的全连接网络开始,逐步过渡到卷积网络以及图像数据的处理方式。最后回到机器学习通用问题,比如数据划分、过拟合、超参数调整,这部分是很多自学的人最容易忽略的。
刚入门的朋友可以先只看第一部分,不用急着翻后面的图像生成、大语言模型等内容。把这里面的概念吃透,再去看前沿章节会有一种"原来如此"的顺畅感。而做传统开发想转AI方向的同学,这本书也是最合适的过渡材料,它没有一上来就堆复杂公式,而是把数学原理用代码体现出来了。
2. 环境搭建与工具链选择,这是第一道敲门砖
深度学习环境的安装,对很多人来说比写模型代码还折磨。第三版第一部分对这块的讲解很务实,我按它的组织方式重新在干净环境里做了一遍,已经绕开了不少坑。我强烈建议你照着它的思路来,不要自己乱搭配版本号,否则后续跑代码时,报错会消耗掉你大量时间。
2.1 一个可复现的安装方案
在Python版本上,建议使用当前主流但不过分超前的版本,比如3.10或3.11。太新的Python版本有时会遇到个别底层依赖还没适配的情况,虽然书中并没有特别强调这件事,但在实际操作时这是第一道坎。Keras本身是纯Python包,但它依赖的一些科学计算库需要跟Python解释器版本匹配。
我的建议是新建一个虚拟环境,避免和其他项目互相污染。命令行里执行下面的操作即可:
python -m venv dlbook_env source dlbook_env/bin/activate # Windows下用 dlbook_env\Scripts\activate pip install --upgrade pip pip install "keras>=3.0" ipython matplotlib这里没有把某个具体后端一起写进去,是因为第三版的核心特色就是你可以灵活选择后端。你可以先装好Keras,再安装你偏好的后端框架之一来跑书里的例子。
# 选择其中之一,后续可以通过环境变量切换 pip install "tensorflow" # 经典路线,资料最多 pip install "torch" # 研究圈常用 pip install "jax[cpu]" # 新锐路线,偏科学计算我自己的电脑是CPU环境,所以选了纯CPU版本的配合,实测下来书里第一部分的所有训练代码,都能在合理的时间内跑完。如果你有独立显卡,装GPU版本能让后面的图像分类例子明显提速,但CPU环境也完全不影响学习效果,只是把喝杯咖啡的时间拉长了而已。
2.2 Keras多后端模式的配置心得
一开始看到"多后端"这个概念时,我有点担心会不会增加学习成本,实际操作之后发现反而更简单了。Keras 3把模型层、损失函数、优化器这些抽象成统一API,底层计算再交给具体的计算框架去执行。对我这种常年切换项目的人来说,最大的好处是:换项目时不用重学一套建模语法。
切换后端可以用环境变量来实现,在运行Python之前设置即可:
export KERAS_BACKEND=torch也可以在Python代码里动态指定:
import os os.environ["KERAS_BACKEND"] = "torch" import keras需要重点提醒的是,这个环境变量必须在导入Keras之前设置,否则不会生效。我见过不少人在导入之后才发现切不过去,然后浪费了大量时间查原因。如果你同时安装了多个计算框架,建议给每个项目写一个固定的切换脚本,或者用配置文件管理好后端的选择,这样在不同电脑上复现实验结果会省事很多。
3. 张量、梯度与层:从第一段代码理解深度学习的核心逻辑
书的第一部分没有急着让你写网络,而是先用一个非常简单的模型,把神经网络运行的完整流程铺开。我觉得这种安排的妙处在于,你对后续每一个概念都会有一个具体的参照物。我自己再次读到这里时,对"张量"这个词的体会比几年前清晰了很多。
张量其实就是多维数组的通用说法。一维的叫向量,二维的叫矩阵,三维以上的统称为张量。深度学习中所有的数据,不管是图像、文本、还是表格数据,最终都会变成数字组成的张量。我发现很多人问题出在"张量"这个术语本身听起来神秘,但它本质上和你平时用NumPy处理数组时面对的东西没有区别,只是多了一些额外规则:所有元素类型要一致,形状要能被上层计算逻辑理解。
3.1 数据从哪里来,怎么预处理
书中第一部分用到的数据集都不大,最常见的是手写数字识别和图像分类数据。它们可以从Keras自带的数据集接口直接加载,不需要自己爬数据:
from keras.datasets import mnist (train_images, train_labels), (test_images, test_labels) = mnist.load_data()加载出来的数据形状是(样本数, 高, 宽),比如60000张28x28的图片,对应形状就是(60000, 28, 28)。对初学者来说,打印输出数据的形状是理解神经网络输入维度最快的办法:
print(train_images.shape) # (60000, 28, 28) print(train_images.dtype) # uint8预处理时最关键的一步是把整数像素值转换成浮点数,然后缩放到0到1之间。这个步骤看似简单,但极其重要,因为神经网络对输入的数值范围很敏感。梯度更新的时候,如果输入数值范围过大或过小,都会让训练不稳定。
train_images = train_images.astype("float32") / 255 test_images = test_images.astype("float32") / 255标签部分,如果做分类任务,通常要把整数标签转成分类编码的格式,也就是独热编码。你可以手动实现,也可以用Keras自带的工具:
from keras.utils import to_categorical train_labels = to_categorical(train_labels) test_labels = to_categorical(test_labels)转成独热编码之后,每个标签变成了一个和类别数量等长的向量,对应类别的位置是1,其余是0。这能让损失函数在计算预测概率和真实概率的距离时更直接。
3.2 模型构建:认识Sequential之外的更多选择
书的这部分内容里,从最简单的Sequential模型开始,然后马上就引入了Functional API。我见过太多只停留在Sequential的人,遇到多输入多输出模型时整个懵了。其实两者并不存在谁替代谁的关系,Sequential适合纯顺序堆叠的线性结构,Functional则允许你定义分支、合并更复杂的连接关系。
我这里用一个简单分类模型来展示这两者的差异:
from keras import layers, models # Sequential风格 model = models.Sequential([ layers.Dense(64, activation="relu"), layers.Dense(10, activation="softmax") ]) # Functional风格 inputs = keras.Input(shape=(28, 28)) x = layers.Flatten()(inputs) x = layers.Dense(64, activation="relu")(x) outputs = layers.Dense(10, activation="softmax")(x) model = keras.Model(inputs=inputs, outputs=outputs)Functional看起来多写几行,但它非常清晰地告诉了你每一层张量的流向:从输入开始,经过展平成向量,再经过全连接层变换,最后输出每个类别的概率。这个"数据流动"的可视化思维,对调试复杂模型和论文复现都有巨大帮助。
层(Layer)是模型的基本构件。Dense层做的就是矩阵乘法加偏置再加激活函数,用数学公式表达就是输出等于激活(w*x+b)。书里对这种基础操作的拆解不厌其烦,我看了发现还能想起来更多细节。理解层内部发生什么,其实至关重要,因为你自己写自定义层或修改现有层结构时,靠的完全不是记忆,而是这套扎实的基本功。
4. 一次图像分类任务的完整实战记录
光看书不动手,永远学不会深度学习。我跟着第一部分自己搭了一个图像分类Demo,从数据生成到训练评估走了一遍完整流程。这个例子不复杂,但它把前面提到的张量、层、模型、编译这些概念全部串起来了。我强烈建议你在自己电脑上重新打一遍代码,而不是直接复制粘贴,因为手动敲代码的过程会让你对每个函数的参数有真实记忆。
4.1 数据集构建与增强策略
我这里没有用MNIST,而是从一个简单的模拟图像数据集开始,这样可以更聚焦在流程本身。我们用Keras提供的一个小型图像数据集来演示,它自带训练集和验证集目录结构,非常贴近真实项目里的数据组织形式。
from keras.utils import image_dataset_from_directory train_ds = image_dataset_from_directory( "data/train", validation_split=0.2, subset="training", seed=42, image_size=(180, 180), batch_size=32 ) val_ds = image_dataset_from_directory( "data/train", validation_split=0.2, subset="validation", seed=42, image_size=(180, 180), batch_size=32 )这里batch_size=32是我深思熟虑的选择,而不是随手写的。深度学习训练时,每次更新梯度用的不是全部数据,而是一个小批量的平均梯度。批量太大会让训练变慢且容易陷入局部极值,批量太小则噪声太大,收敛不稳定。32在绝大多数CPU和GPU环境里都是甜点值。
数据增强是我在跑这个Demo时比较喜欢的环节。它通过对原始图片做随机旋转、翻转、缩放,变相扩大了训练集规模,也因此能有效缓解过拟合。Keras里用一行代码就能完成:
data_augmentation = keras.Sequential([ layers.RandomFlip("horizontal"), layers.RandomRotation(0.1), layers.RandomZoom(0.1), ])需要留意的是,增强只应该应用在训练集上,验证集和测试集要保留原始数据,否则你评估出的模型效果是虚高的。很多初学者把增强也加到了验证集上,最后模型在验证集上的分数反而更差,还会让人误以为是模型出问题了。
4.2 模型构建与训练监控
图像分类任务里,卷积神经网络是标配。我搭了一个小规模的卷积网络,遵循的是"卷积提取特征、池化压缩尺寸、全连接输出分类"的基本设计。代码结构很经典:
from keras import layers, models model = models.Sequential([ data_augmentation, layers.Rescaling(1./255), layers.Conv2D(32, (3, 3), activation="relu"), layers.MaxPooling2D((2, 2)), layers.Conv2D(64, (3, 3), activation="relu"), layers.MaxPooling2D((2, 2)), layers.Conv2D(128, (3, 3), activation="relu"), layers.Flatten(), layers.Dense(128, activation="relu"), layers.Dropout(0.5), layers.Dense(num_classes, activation="softmax") ]) model.compile( optimizer="adam", loss="categorical_crossentropy", metrics=["accuracy"] )编译这一步很多人以为只是把配置填好,实际它决定了训练的优化策略。我选Adam作为优化器,它自带自适应学习率,对新手非常友好,不需要手动调太多参数就能稳定收敛。损失函数选择了分类交叉熵,这是多分类问题的标准选择,它的特点和这个任务贴合得很紧密:每个样本的预测分布要和真实分布尽可能接近。
训练时一定要加回调。回调是最容易被忽视但特别重要的功能,它能监控训练过程,并在预设条件下自动优化训练行为。我最常用的两个回调是模型检查点和提前停止:
from keras.callbacks import ModelCheckpoint, EarlyStopping callbacks = [ ModelCheckpoint("best_model.keras", save_best_only=True), EarlyStopping(patience=10, restore_best_weights=True) ] history = model.fit( train_ds, validation_data=val_ds, epochs=50, callbacks=callbacks )提前停止的作用是,当验证集指标连续多轮不再变好时,自动结束训练,避免浪费时间,也不会因为过多训练产生过拟合。我在实际项目中几乎每次都靠它保住最优模型。save_best_only参数保证只有验证指标最好的那一轮才会存盘,这个细节在长训练里特别值钱。
4.3 训练过程的解读思路
训练跑完后,你会得到一个history对象。很多人只盯着最终的准确率,这是一个容易错过的角度。我更推荐你画一张训练曲线图,把训练集和验证集的损失随轮次的变化一起显示出来。这也是我这次实操中最大的收获。
import matplotlib.pyplot as plt acc = history.history["accuracy"] val_acc = history.history["val_accuracy"] loss = history.history["loss"] val_loss = history.history["val_loss"] epochs = range(1, len(acc) + 1) plt.plot(epochs, loss, "bo", label="Training loss") plt.plot(epochs, val_loss, "b", label="Validation loss") plt.title("Training and validation loss") plt.legend() plt.show()从损失曲线上你能很直观地看出模型是不是过拟合了。如果训练损失持续下降,但验证损失在某轮开始反弹,这个点就是过拟合开始的信号。这时候配上早停回调,模型就能停在最佳点附近。我这次跑的过程中,验证损失从第30轮左右开始波动,EarlyStopping在第42轮刚好触发,保存下来的模型在验证集上准确率大约在0.87左右,效果已经相当不错。
5. 实操过程中踩过的坑与排查思路
这部分是我最愿意分享的内容,因为官方文档里找不到。环境问题的报错往往千奇百怪,但80%的原因其实集中在几个点上。我按自己的踩坑顺序整理了一份清单,照着排查能省下不少时间。
5.1 环境与依赖类问题
这本书的新手最容易遇到的报错,基本有几个固定场景。我把常见现象、原因和解决思路整理成表格,方便你对照排查:
| 常见报错现象 | 可能原因 | 解决思路 |
|---|---|---|
| 导入Keras时报错找不到模块 | 虚拟环境未激活或包没装对 | 确认当前解释器在虚拟环境内,检查pip list |
| NumPy版本刚更新完就崩 | 底层科学计算库版本不匹配 | 固定到当前后端已验证的NumPy版本区间 |
| 训练时提示显存不足 | batch_size过大或同时开多个进程 | 调小batch_size,或限制显存增长 |
| 切换后端不生效 | 环境变量设置晚于Keras导入 | 在进程启动时就要设置好 |
| 加载数据集时网络错误 | 数据集需要远程下载,本地网络受限 | 预先手动下载并放到本地缓存目录 |
CPU环境下训练特别容易遇到一个隐性坑:数据预处理如果做在了GPU计算图里,每个epoch都会重新跑预处理,纯CPU下这会让单轮时间变成几倍。最简单的规避办法是先把增强后的数据通过预处理存成新数据集,或者干脆减小epoch数目观察效果。我测试时发现,Preprocessing放在模型内部虽方便,但CPU训练的实际耗时上升很明显,后面我把增强改成部分离线预处理,速度才恢复正常。
5.2 训练不收敛与过拟合的处理
模型训练loss不下降,或者验证集指标奇差无比,这类问题在我帮别人排查时见得最多。我总结出了三个最常见的元凶:
- 数据没有归一化。神经网络对输入的数值范围非常敏感,原始像素值的0到255会直接破坏梯度更新的稳定性。
- 标签处理错误。比如分类任务没用独热编码,或者标签从0开始计数。如果类别数是5,标签的取值范围却是1到5,训练会始终不收敛。
- 学习率设置不合适。Adam虽然自适应,但过大会震荡,过小则收敛极慢。建议从1e-3起步,观察曲线再微调。
对于过拟合,除了提前停止和Dropout之外,我还习惯用收集更多数据之外的另一个思路:简化模型结构。很多人以为模型越复杂效果越好,但数据量不够时,复杂模型恰恰会记住噪声而不是学出规律。我在Demo里做了个对比实验,三层卷积的效果明显好于五层卷积,就是数据规模有限时复杂度定律的直观体现。
5.3 模型保存与复现细节
第三版里模型保存的推荐格式是.keras文件。一个文件里包含了模型结构、权重、优化器状态以及编译配置,加载的时候也极其干净:
model = models.load_model("best_model.keras")模型的保存路径,我建议不要用中文路径,也尽量别放到带有特殊符号的目录下,Windows尤其容易出现编码问题。这个看似无关紧要的细节,在跨平台复现时会浪费大量时间。
如果你的实验要保证可复现,还要记得固定随机种子。深度学习训练过程有大量随机性,数据打乱、权重初始化和数据增强都会引入随机因素。在训练脚本开头设置随机种子,能让每次运行结果基本一致,这对论文复现和团队协作都是必要的。
6. 学完第一部分,我的一些个人体会
第一部分读到这里,其实已经覆盖了完整的"理论-实践-问题排查"闭环。如果你耐着性子把这个Demo完整跑通了,后面的卷积变体、目标检测或者Transformer类模型,本质上都是在这个框架里面做文章。你真正需要做的,是让自己对这些基础操作产生肌肉记忆。
我自己的一个小技巧是,每学完一章就写一个"一句话总结",把当前章节最核心的概念用自己的语言压缩出来。比如学完反向传播,我会写"训练就是不断用链式法则求每一层权重对整体误差的贡献,并沿着减少误差的方向更新权重"。这种主动输出比单纯划线阅读有效得多。
另外我还建议你尽早养成看官方API文档的习惯。书里讲到某个损失函数或回调时,顺手打开官方文档看一眼完整的参数列表,往往能发现书里没有展开的高级用法。教材给你方向和框架,文档给你细节和边界,两者搭配才是完整的学习方式。
这一部分内容消化好了,像后面会出现的注意力机制、生成模型、模型微调等主题,对你来说就不再是高不可攀的新大陆,而是你已经熟悉的神经网络玩具上的新积木。继续保持动手练习的节奏,一边跑代码一边改参数,比什么都重要。