10  深度学习

10.1 导读

深度学习用多层神经网络学习复杂的非线性表示。本章介绍多层感知机、卷积网络与循环网络的基本结构,并讨论它们在文本、图像和时间序列任务中的适用条件。金融数据通常样本有限、噪声强且容易发生分布变化,因此复杂网络必须与简单基线在同一时间切分上比较;本章不预设深度模型一定占优。

10.2 学习目标

完成本章后,学生应能达到以下标准:

  1. 对给定的一样本 MLP,写出各张量形状并手算前向值、二元交叉熵、四个梯度与一次更新,数值误差不超过 \(10^{-3}\)。
  2. 用“连接范围、参数共享、状态传递”区分 MLP、CNN 与 RNN/LSTM,并能纠正“卷积天然平移不变”的误解。
  3. 在时间有序数据上建立训练—验证—测试三段式比较,所有预处理仅拟合开发期,测试集只评价一次。
  4. 用训练—验证差距、基线比较与学习曲线诊断欠拟合、过拟合和优化失败,不把损失下降等同于样本外有效。
  5. 针对一个陌生任务说明网络、损失、正则化与基线选择,并给出“不应使用深度学习”的证据边界。
先修自检

不运行代码,回答:若 \(z=wx+b\)、\(\hat y=\sigma(z)\)、\(L=-[y\log\hat y+(1-y)\log(1-\hat y)]\),当 \(x=2,y=1,w=0,b=0\) 时,\(\hat y\) 与 \(\partial L/\partial w\) 是多少?答案为 \(0.5\) 与 \((0.5-1)\times2=-1\);若未答对,可先复习 式 10.4、链式法则和第 3 章逻辑回归。

10.3 深度学习概述

深度学习是机器学习的一个分支,它使用多层神经网络从数据与任务损失中共同估计中间表示。与完全依赖预先固定特征的流程相比,深度网络可以减少部分手工组合工作;但输入定义、时点对齐、业务约束与稳健特征仍需研究者设计,“表示学习”不等于完全不需要特征工程。

短检索检查:为什么“网络学习中间表示”不能改写成“不需要特征工程”?答题须同时指出:原始输入与信息时点仍由研究者定义,任务损失只会奖励与当前目标相关的表示,样本外稳健性仍需基线和预先固定数据验证。

深度学习已用于金融经济及其他领域,但是否优于简单模型取决于数据量、信噪比和验证设计:

  • 量化投资:资产价格预测、因子挖掘、算法交易策略等
  • 金融风控:信用评分、欺诈检测、反洗钱监控等
  • 自然语言处理:财经新闻情感分析、公告信息提取、智能客服等
  • 时间序列预测:股价走势、波动率预测、宏观经济指标预测等
  • 推荐系统:基金产品推荐、个性化理财等

10.4 单层神经网络

我们从单隐藏层前馈网络开始。严格地说,经典感知机是带阈值判别规则的线性分类器;含可微激活函数与连续输出的网络不应一概称为感知机。令输入 \(x\in\mathbb R^p\),隐藏层有 \(K\) 个单元,输出层有 \(M\) 个单元。第 \(m\) 个输出先形成 logit(或回归线性预测量)

\[ z_m=\beta_{m0}+\sum_{k=1}^{K}\beta_{mk}A_k,\qquad m=1,\ldots,M, \tag{10.1}\]

再由与任务匹配的输出映射 \(q\) 得到

\[ \widehat y=q(z). \tag{10.2}\]

其中

\[ A_k=h_k(x)=g\left(w_{k0}+\sum_{j=1}^{p}w_{kj}x_j\right),\qquad k=1,\ldots,K. \tag{10.3}\]

因此 \(W=(w_{kj})\in\mathbb R^{K\times p}\),输出权重 \(B=(\beta_{mk})\in\mathbb R^{M\times K}\),且 \(A\in\mathbb R^K\)、\(z,\widehat y\in\mathbb R^M\)。标量回归取 \(M=1,q(z)=z\);二分类取 \(M=1,q(z)=\sigma(z)\);\(M\) 类互斥分类取 \(q(z)_m=\exp(z_m)/\sum_{r=1}^{M}\exp(z_r)\)。正文统一用 \(y\) 表示观测响应、\(\widehat y\) 表示预测;\(K\) 始终只表示隐藏单元数,\(M\) 始终只表示输出维数或类别数。

这里 \(g(\cdot)\) 是隐藏层激活函数,例如 sigmoid 或 ReLU(修正线性单元);它与任务特定的输出映射 \(q\) 不必相同。

10.4.1 激活函数

激活函数引入非线性使神经网络能够学习复杂的模式。常见的激活函数包括

  1. Sigmoid 函数: \[ g(z) = \frac{1}{1 + e^{-z}} \tag{10.4}\]

    值域为开区间 \((0,1)\),常用于二元分类输出层。

  2. tanh 函数: \[ g(z) = \tanh(z) = \frac{e^z - e^{-z}}{e^z + e^{-z}} \]

    值域为开区间 \((-1,1)\),并以 0 为中心。

  3. ReLU(修正线性单元): \[ g(z) = \max(0, z) \tag{10.5}\]

    对正输入输出原值,对非正输入输出 0。其正半轴导数为 1,可缓解饱和激活造成的梯度衰减,但不能保证消除梯度问题。

  4. Softmax 函数(用于 \(M\) 类互斥分类):

    \[ \operatorname{softmax}(z)_m=\frac{e^{z_m}}{\sum_{r=1}^{M}e^{z_r}},\qquad m=1,\ldots,M. \tag{10.6}\]

    每个输出位于 \((0,1)\),且 \(\sum_{m=1}^{M}\widehat y_m=1\)。数值实现通常先减去 \(\max_r z_r\),避免指数溢出。

提示:为什么需要非线性激活函数

如果所有隐藏层都使用恒等映射,无论网络有多少层,它都等价于单层仿射模型,因为仿射映射的复合仍是仿射映射。非线性激活扩大了可表示函数族,但“能逼近”必须说明函数类别、定义域、误差度量和网络宽度。

具体来说,若 \(g(a)=a\)(恒等函数),则标量输出的线性预测量满足

\[ z(x)=\beta_0+\sum_{k=1}^{K}\beta_k\left(w_{k0}+\sum_{j=1}^{p}w_{kj}x_j\right)=\widetilde\beta_0+\sum_{j=1}^{p}\widetilde\beta_jx_j. \]

这只是另一个线性回归模型。

补充说明:通用逼近定理的精确边界

令目标函数 \(f\) 是单位立方体 \([0,1]^p\) 上的连续实值函数,隐藏层使用连续 sigmoid 型激活。对任意 \(\varepsilon>0\),存在有限宽度 \(K\) 以及一组参数,使单隐藏层网络 \(F_K\) 满足 \(\sup_{x\in[0,1]^p}|F_K(x)-f(x)|<\varepsilon\) (Cybenko 1989年)。结论可经连续坐标变换推广到紧致长方体,但它不覆盖任意不连续目标,也不说明所需宽度很小、梯度法能找到参数、有限样本泛化良好或深层网络必然优于浅层网络。

短检索检查:闭书回答通用逼近结论中的四个限定——目标函数类别、输入定义域、误差度量、宽度量词。标准答案为“连续函数、紧致域(原定理单位立方体)、一致范数、存在有限但可随 \(\varepsilon\) 增大的宽度”;漏掉任一项,就不能写“网络可逼近任意函数”。

10.4.2 多层神经网络

单层神经网络的表达能力有限。通过堆叠多个隐藏层我们可以构建深度神经网络,能够学习更复杂的函数。

一个具有 \(L\) 个隐藏层、各层宽度分别为 \(K_1,\ldots,K_L\) 的网络可以表示为

\[ z_m=\beta_{m0}+\sum_{k=1}^{K_L}\beta_{mk}A^{(L)}_k,\qquad \widehat y=q(z),\qquad m=1,\ldots,M. \]

其中 \(K_0=p\),并且

\[ A^{(l)}_k=g\left(w^{(l)}_{k0}+\sum_{j=1}^{K_{l-1}}w^{(l)}_{kj}A^{(l-1)}_j\right),\quad k=1,\ldots,K_l,\quad l=1,\ldots,L. \tag{10.7}\]

以及 \(A^{(0)}_j=x_j\)。于是 \(W^{(l)}\in\mathbb R^{K_l\times K_{l-1}}\),输出权重 \(B\in\mathbb R^{M\times K_L}\);层号、单元号与输出号不再共用同一个上界。

每个隐藏层都对前一层表示作参数化变换;在合适数据、损失和优化下,较后层可能形成更贴近任务的组合特征,但“越深必然越抽象”不是定理,应通过探针、消融或样本外表现检验。

10.4.3 最小 MLP:前向、损失、梯度与一次更新

进入 CNN 与 RNN 前,先固定 MLP 的训练闭环:前向传播得到 \(\hat y\),损失函数 \(L(y,\hat y)\) 衡量任务误差,反向传播用链式法则计算梯度,优化器再更新参数。对权重 \(w\) 的一次梯度下降更新为

\[ w^{(t+1)}=w^{(t)}-\eta\frac{\partial L}{\partial w}. \tag{10.8}\]

CNN、RNN 和 LSTM 改变的是连接结构与参数共享方式,不改变“前向—损失—反向—更新”的基本逻辑。下面用一个样本、两个输入、一个 sigmoid 隐单元和一个 sigmoid 输出单元完成所有步骤。

10.4.3.1 手算 worked example

给定 \(x=(1,2)^\top,y=1\),隐层参数 \(w=(0.2,-0.1)^\top,b=0.1\),输出参数 \(v=0.4,c=-0.2\),学习率 \(\eta=0.1\)。隐层预激活记为 \(s\),输出 logit 记为 \(z\);形状依次为 \(x,w\in\mathbb R^2\),\(s,h,z,\widehat y\in\mathbb R\)。

第一步:前向传播。

\[ s=w^\top x+b=0.2-0.2+0.1=0.1, \qquad h=\sigma(s)=0.524979. \]

\[ z=vh+c=0.4(0.524979)-0.2=0.009992, \qquad \widehat y=\sigma(z)=0.502498. \]

第二步:损失。 对 \(y=1\),二元交叉熵为

\[ L=-\log(\hat y)=0.688164. \]

第三步:反向传播。 sigmoid 与交叉熵合并后,输出 logit 的误差是 \(\delta_z=\partial L/\partial z=\widehat y-y=-0.497502\)。因此

\[ \frac{\partial L}{\partial v}=\delta_z h=-0.261178, \qquad \frac{\partial L}{\partial c}=\delta_z=-0.497502. \]

隐层误差为

\[ \delta_s=\delta_z v h(1-h)=-0.049626, \]

所以

\[ \nabla_wL=\delta_sx=(-0.049626,-0.099252)^\top, \qquad \frac{\partial L}{\partial b}=\delta_s. \]

第四步:一次更新。 使用 式 10.8 得到

\[ w^+=(0.204963,-0.090075)^\top,\quad b^+=0.104963, \quad v^+=0.426118,\quad c^+=-0.150250. \]

因为标签为 1,本次更新应提高预测概率;把更新后参数重新前向计算,得到 \(\hat y^+\approx0.5191>0.5025\),这是最低限度的方向健全性检查。

列表 10.1: 最小 MLP 的一次前向、反向与更新
import numpy as np  # 使用向量运算逐项复核手算结果
input_features = np.array([1.0, 2.0])  # 固定两个输入以便人工复算
hidden_weights = np.array([0.2, -0.1])  # 固定隐层初始权重
hidden_bias, output_weight, output_bias = 0.1, 0.4, -0.2  # 固定三个标量参数
target_value, learning_rate = 1.0, 0.1  # 固定标签与一步学习率
sigmoid = lambda value: 1.0 / (1.0 + np.exp(-value))  # 定义本例所需激活函数
hidden_value = sigmoid(hidden_weights @ input_features + hidden_bias)  # 完成隐层前向
predicted_probability = sigmoid(output_weight * hidden_value + output_bias)  # 完成输出前向
cross_entropy = -np.log(predicted_probability)  # 计算正类交叉熵
output_delta = predicted_probability - target_value  # 计算输出 logit 梯度
hidden_delta = output_delta * output_weight * hidden_value * (1 - hidden_value)  # 链式回传隐层
hidden_weights -= learning_rate * hidden_delta * input_features  # 更新两个隐层权重
hidden_bias -= learning_rate * hidden_delta  # 更新隐层偏置
output_weight -= learning_rate * output_delta * hidden_value  # 更新输出权重
output_bias -= learning_rate * output_delta  # 更新输出偏置
updated_probability = sigmoid(output_weight * sigmoid(hidden_weights @ input_features + hidden_bias) + output_bias)  # 复核方向
assert updated_probability > predicted_probability  # 正类样本的一步更新必须提高预测概率
print({'bce': cross_entropy, 'grad_w': hidden_delta * input_features, 'grad_b': hidden_delta, 'grad_v': output_delta * hidden_value, 'grad_c': output_delta, 'probability_before': predicted_probability, 'probability_after': updated_probability})  # 把损失、四组梯度与更新方向放在同一复核输出中
{'bce': 0.6881638222083897, 'grad_w': array([-0.04962604, -0.09925208]), 'grad_b': -0.04962604167707618, 'grad_v': -0.2611782492944771, 'grad_c': -0.49750210203324385, 'probability_before': 0.5024978979667561, 'probability_after': 0.5191443062393047}

运行 列表 10.1 后,bce 和 grad_w/grad_b/grad_v/grad_c 应分别与前三步的手算损失和四组梯度一致。probability_before 与 probability_after 直接检查一步更新的方向:对当前正类样本,后者应大于前者。该代码块只依赖 numpy,可单独运行和复算。

10.4.3.2 CNN 前必须掌握的基本训练逻辑

  1. 先定义 estimand 与基线:分类至少与训练期多数类或逻辑回归比较;回归至少与训练均值或 last-value 比较。
  2. 再锁定数据边界:训练集拟合权重与标准化器,验证集选结构、学习率和停止轮次,测试集只在全部选择预先固定后使用一次。
  3. 用小批量反复更新:一个 epoch 是训练样本的一次遍历;mini-batch 梯度是总体梯度的有噪声近似。
  4. 同时看优化与泛化:训练损失不降提示学习率、缩放或梯度问题;训练降而验证升提示过拟合;两者都高提示欠拟合。
  5. 记录失败模式:随机种子、参数量、各集合 \(n\)、类别比例、停止轮次和基线差异必须随结果交付。

只有能复算 小节 10.4.3.1 并解释上述五点后,CNN 的局部连接、RNN 的状态传递才是“结构扩展”,而非一组孤立 API。

10.5 共享训练框架:反向传播、优化与泛化诊断

在训练任何完整 CNN 或 RNN 之前,先掌握所有神经网络共用的闭环:前向传播产生预测,损失函数把预测与标签比较,反向传播计算梯度,优化器更新参数,而验证集决定结构、正则化强度与停止轮次。反向传播把多变量链式法则组织为高效的梯度计算 (Rumelhart 等 1986年);它不保证非凸优化得到全局最优,也不保证样本外性能。

数学推导:反向传播算法的链式法则核心

沿用 式 10.7 的约定:\(L\) 只表示隐藏层数,\(A^{(0)}=x\);对 \(l=1,\ldots,L\),前向传播计算 \(Z^{(l)}=W^{(l)}A^{(l-1)}+b^{(l)}\) 与 \(A^{(l)}=g(Z^{(l)})\)。输出层另记为 \(z=BA^{(L)}+\beta_0\)、\(\widehat y=q(z)\),其中 \(B\in\mathbb R^{M\times K_L}\)。

先定义输出 logit 误差 \(\delta^{\mathrm{out}}=\nabla_zJ\),再定义隐藏层局部误差 \(\delta^{(l)}=\partial J/\partial Z^{(l)}\)。若 \(Dq(z)\) 是 \(q\) 的 Jacobian,则

\[ \delta^{\mathrm{out}}=[Dq(z)]^\top\nabla_{\widehat y}J. \tag{10.9}\]

对 sigmoid 二分类与 BCE,或 softmax 互斥多分类与 one-hot 交叉熵,融合后都有 \(\delta^{\mathrm{out}}=\widehat y-y\);这个简式依赖指定的“输出映射—损失”配对。随后从输出向输入递推:

\[ \delta^{(L)}=(B^\top\delta^{\mathrm{out}})\odot g'(Z^{(L)}), \]

\[ \delta^{(l)}=((W^{(l+1)})^\top\delta^{(l+1)})\odot g'(Z^{(l)}),\qquad l=L-1,\ldots,1. \tag{10.10}\]

最后把局部误差与进入该层的激活相乘:

\[ \nabla_BJ=\delta^{\mathrm{out}}(A^{(L)})^\top,\quad \nabla_{\beta_0}J=\delta^{\mathrm{out}},\quad \nabla_{W^{(l)}}J=\delta^{(l)}(A^{(l-1)})^\top,\quad \nabla_{b^{(l)}}J=\delta^{(l)}. \tag{10.11}\]

CNN 改变了参数出现的位置:同一卷积核在多个局部感受野复用,因此该核的梯度是所有应用位置贡献之和。RNN 则在时间步之间复用状态更新参数;其展开后的 Jacobian 连乘将在 小节 10.8 说明。两者仍遵守同一条链式法则。

10.5.1 梯度消失和爆炸

多层 Jacobian 连乘的奇异值可能快速趋近于零或增大,使早期层几乎得不到学习信号,或使更新幅度失控。ReLU、适当初始化、归一化与残差连接可能改善优化条件;梯度裁剪可限制爆炸更新。它们都不是“梯度一定稳定”的保证,必须同时检查损失是否有限、梯度范数与验证轨迹。

10.5.2 优化算法

SGD 用小批量梯度近似总体梯度;Momentum 累积方向以减弱局部震荡;RMSprop 按近期平方梯度调整各坐标步长;Adam 结合一阶矩与二阶矩估计。优化器名称不能替代学习率选择:学习率过大常使损失震荡或发散,过小则可能在有限预算内几乎不动。候选优化器、初始学习率、批量大小和衰减规则必须在测试前声明,并用同一训练—验证键比较。

10.5.3 正则化与早停

深度网络参数多,训练误差继续下降时仍可能扩大泛化差距。常用控制包括 \(\ell_1/\ell_2\) 权重惩罚、训练时随机屏蔽激活的 Dropout、符合数据语义的数据增强,以及限制宽度和深度。早停也是正则化:在预设最大轮数内只根据验证损失选择停止点,并恢复验证损失最低轮次的权重。耐心值、最小改善量与最大轮数必须事前固定;测试集不能触发停止,也不能在观察测试结果后改变这些规则。

10.5.4 学习曲线诊断

每次完整训练至少保存逐轮训练损失、验证损失、停止轮次和最佳轮次,并与简单基线同表报告。训练与验证损失都高,提示表示能力不足、特征/标签问题或优化失败;训练损失下降而验证损失持续上升,提示过拟合;两条曲线都剧烈震荡,优先检查学习率、尺度和批量。仅凭训练损失下降不能宣称预测有效;单次曲线也不能区分随机初始化波动与稳定结论,必要时应报告重复运行的离散度。

10.6 卷积神经网络(CNN)

卷积神经网络是专门为处理网格结构数据的神经网络。CNN 的核心思想是局部连接与权重共享。卷积层对平移是等变的:输入平移会使特征图相应平移;分类层、池化与数据增强可以提高近似平移不变性,但不应把两者混为一谈。

10.6.1 CNN的架构

一个典型的CNN由以下几种层组成:

  1. 卷积层: 使用卷积核滤波器从输入中提取特征
  2. 池化层: 降低特征图的维度,减少计算量并控制过拟合
  3. 全连接层: 在最后进行分类或回归

图 10.1 显示了一个用于图像分类的CNN架构。

图像包含大量具有空间结构的像素;若直接使用全连接网络,参数量会随输入维数快速增长。卷积神经网络(CNN)通过局部连接和参数共享利用这种空间结构,因此常用于计算机视觉任务。 下面的架构草图用 Python 渲染展示了一个经典的 CNN 骨架。你可以看到,信息在网络中不再是平铺直叙地流动,而是经过了一层层类似“滤镜”的物理挤压。 左侧蓝色的原始图像输入后,首先迎来了红色的卷积层。在这里,无数个小巧的“卷积核”像手电筒的光斑一样在图像上滑动扫描,局部连接与参数共享显著压缩了参数量,并使边缘、纹理等特征对平移保持等变性:输入平移时,特征图也相应平移。近似平移不变性还需要池化、全局聚合或数据增强等环节配合。 紧接着的绿色池化层用局部最大值(Max Pooling)降低空间分辨率,从而减少计算量,并使后续表示对小幅位置变化不那么敏感。 多次卷积和池化后,二维特征图被展平为一维向量,再由全连接层整合信息,最后通过 Softmax 输出分类概率。在金融场景中,这种结构只有在图像表示确实对应业务对象、标签可靠且未来期评价通过时,才可能用于图表或视觉材料分类。

import numpy as np  # 表示网络层尺寸并核对图形坐标
import matplotlib.pyplot as plt  # 呈现CNN层级尺寸与前向连接关系

# 定义CNN各层的参数:名称、类型、尺寸及其他超参数
layers = [  # CNN层参数列表
    {'name': '输入层', 'type': 'input', 'size': 32, 'channels': 3},  # 输入层:32×32像素RGB图像
    {'name': '卷积层1', 'type': 'conv', 'size': 32, 'filters': 6, 'kernel': 3},  # 第一卷积层:6个3×3滤波器
    {'name': '池化层1', 'type': 'pool', 'size': 16},  # 第一池化层:特征图降至16×16
    {'name': '卷积层2', 'type': 'conv', 'size': 16, 'filters': 12, 'kernel': 3},  # 第二卷积层:12个3×3滤波器
    {'name': '池化层2', 'type': 'pool', 'size': 8},  # 第二池化层:特征图降至8×8
    {'name': '展平层', 'type': 'flatten', 'size': 8},  # 展平层:将2D特征图转为1D向量
    {'name': '全连接层', 'type': 'fc', 'size': 64},  # 全连接层:64个神经元
    {'name': '输出层', 'type': 'output', 'size': 10}  # 输出层:10个类别(0-9)
]  # CNN层超参数定义完成

以下代码定义了一个辅助函数,用于在给定的坐标轴上逐层绘制CNN网络架构示意图。

def draw_input_layer(ax, layer, y):  # 绘制输入张量的尺寸与通道信息
    rect = plt.Rectangle((8, y - 0.5), 4, 1, facecolor='#3498db', edgecolor='black', linewidth=2)  # 用蓝色框区分原始输入
    ax.add_patch(rect)  # 把输入张量绘制为架构图的蓝色起点
    ax.text(10, y, f'输入\n{layer["size"]}×{layer["size"]}×{layer["channels"]}',  # 标明空间尺寸与通道数
            ha='center', va='center', fontsize=10, color='white', weight='bold')  # 将说明置于输入框中心
def draw_conv_layer(ax, layer, y):  # 绘制卷积层的多滤波器结构
    for filter_index in range(layer['filters']):  # 用叠放小矩形表示多个共享卷积核的输出通道
        rect = plt.Rectangle((7 + filter_index * 0.5, y - 0.3), 0.8, 0.6,  # 横向错开各通道增强层次感
                             facecolor='#e74c3c', edgecolor='black', linewidth=1)  # 用红色叠片表示卷积输出通道
        ax.add_patch(rect)  # 将当前卷积通道加入架构坐标轴
    ax.text(14, y, f'卷积\n{layer["filters"]}个滤波器', ha='left', va='center', fontsize=9)  # 报告该层滤波器数量
def draw_pool_layer(ax, layer, y):  # 绘制池化后的空间尺寸
    rect = plt.Rectangle((9, y - 0.3), 2, 0.6, facecolor='#2ecc71', edgecolor='black', linewidth=2)  # 用绿色框标识下采样
    ax.add_patch(rect)  # 用绿色框表示空间下采样阶段
    ax.text(14, y, f'池化\n{layer["size"]}×{layer["size"]}', ha='left', va='center', fontsize=9)  # 标明下采样后的空间尺寸
def draw_flatten_layer(ax, layer, y):  # 绘制从特征图到一维向量的转换
    rect = plt.Rectangle((7, y - 0.15), 6, 0.3, facecolor='#9b59b6', edgecolor='black', linewidth=2)  # 用紫色细框表示只改变形状
    ax.add_patch(rect)  # 用细长框表示从特征图到向量的形状变换
    ax.text(14, y, f'展平\n{layer["size"]}×{layer["size"]}×12', ha='left', va='center', fontsize=9)  # 标明展平前的张量尺寸
def draw_dense_layer(ax, layer, y):  # 绘制整合全部展平特征的全连接层
    rect = plt.Rectangle((7.5, y - 0.5), 5, 1, facecolor='#f39c12', edgecolor='black', linewidth=2)  # 用橙色框区分密集连接
    ax.add_patch(rect)  # 用橙色框表示全局特征整合阶段
    ax.text(10, y, f'全连接\n{layer["size"]}个单元', ha='center', va='center',  # 标明隐层宽度
            fontsize=10, color='white', weight='bold')  # 将层说明置于框内
    for node_index in range(5):  # 用五个示意节点代表而非逐一绘制全部单元
        ax.add_patch(plt.Circle((8 + node_index, y), 0.15, facecolor='white', edgecolor='black'))  # 叠加可读的节点符号
def draw_output_layer(ax, layer, y):  # 绘制互斥分类的Softmax输出层
    rect = plt.Rectangle((8, y - 0.4), 4, 0.8, facecolor='#1abc9c', edgecolor='black', linewidth=2)  # 用青色框标识最终概率向量
    ax.add_patch(rect)  # 用绿色框表示互斥类别概率输出
    ax.text(10, y, f'Softmax\n{layer["size"]}个类别', ha='center', va='center',  # 标明输出概率维数
            fontsize=10, color='white', weight='bold')  # 将类别说明置于输出框中心
layer_drawers = {  # 集中注册层类型以便按架构声明自动绘图
    'input': draw_input_layer, 'conv': draw_conv_layer, 'pool': draw_pool_layer,  # 映射输入、卷积与池化的绘制逻辑
    'flatten': draw_flatten_layer, 'fc': draw_dense_layer, 'output': draw_output_layer,  # 映射向量化、全连接与输出逻辑
}  # 完成层类型到绘图函数的分派表

def draw_cnn_architecture(ax, layers):  # 按声明顺序组合各层的架构草图
    y_position = len(layers) - 1  # 从顶部为第一层预留纵向位置
    for layer_index, layer in enumerate(layers):  # 逐层调用对应的专用绘图函数
        layer_drawers[layer['type']](ax, layer, y_position - 2 * layer_index)  # 以固定间距绘制当前层
        if layer_index > 0:  # 除输入层外均连接到上一层
            current_y = y_position - 2 * layer_index  # 计算当前层的纵坐标
            ax.add_line(plt.Line2D([10, 10], [current_y + 0.5, current_y + 1.5], color='black', linewidth=2, alpha=0.5))  # 显示前向信息流
    return y_position  # 返回纵向上界供外层设置坐标范围

以下代码调用上述函数创建并显示完整的CNN架构可视化图。

plt.rcParams['font.sans-serif'] = ['Source Han Serif SC']  # 使用系统已安装的思源黑体显示中文
plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题
fig, ax = plt.subplots(figsize=(14, 10))  # 为纵向展示全部CNN层预留空间
y_pos = draw_cnn_architecture(ax, layers)  # 调用辅助函数绘制CNN各层架构
ax.set_xlim(0, 20)  # 为层标签和参数说明保留右侧空白
ax.set_ylim(-1, y_pos + 1)  # 确保输入到输出的全部层都在视野内
ax.set_aspect('equal')  # 避免架构节点因纵横缩放而失真
ax.axis('off')  # 去除无统计含义的坐标刻度以突出层级关系
ax.set_title('卷积神经网络(CNN)架构', fontsize=16, fontname='Source Han Serif SC', pad=20)  # 概括图中局部连接与逐层压缩主题
plt.tight_layout()  # 避免层标签与架构节点在成图时重叠
plt.show()  # 输出用于对照局部连接与层级尺寸的架构图
从左至右的层级框图依次显示彩色输入、两组卷积与池化、展平、全连接和十类输出,并标注各层尺寸。
图 10.1: 用于图像分类的卷积神经网络架构示例。该网络包含两个卷积层,每个后面跟着一个最大池化层,最后是全连接层和输出层。

10.6.2 卷积操作

卷积层使用一组可学习的滤波器(也称为卷积核)从输入中提取特征。对于一个二维输入I\(和一个H \times W\)的滤波器\(K\),卷积操作定义为

\[ (I * K)_{i,j} = \sum_{m=0}^{H-1} \sum_{n=0}^{W-1} I_{i+m, j+n} K_{m, n} \tag{10.12}\]

深度学习库通常实现互相关:滤波器不翻转;工程文献习惯把该运算称为卷积。“valid”描述是否补零及输出尺寸,不是互相关的同义词。

10.6.3 池化操作

池化层用于降低特征图的维度最常见的池化操作是最大池化:

\[ \text{MaxPool}(I)_{i,j} = \max_{(m,n) \in R_{i,j}} I_{m,n} \tag{10.13}\]

其中 \(R_{i,j}\) 是 \((i,j)\) 位置附近的局部区域,通常是 \(2 \times 2\) 窗口。最大池化降低维度,并可提高对小幅平移的稳定性,但不保证对任意平移严格不变。

10.6.4 案例:手写数字识别(MNIST)

我们使用MNIST手写数字数据集来演示CNN的应用。MNIST 数据集包含 60,000 个训练样本和 10,000 个测试样本,每个样本是 \(28 \times 28\) 的灰度图像。

下面用 TensorFlow/Keras 在 MNIST 上构造一个紧凑 CNN。卷积、池化和全连接层的作用由结构与运行日志解释;ReLU 只能缓解部分优化问题,不能保证消除梯度消失。训练设定明确的最大轮数,并只用训练期内部的验证损失触发早停;恢复验证损失最低轮次的权重后,才访问测试集一次。这一演示不预先保证准确率,也不构成 CNN 普遍优于其他方法的证据。

import os  # 在导入TensorFlow前约束设备探测并控制底层日志
import numpy as np  # 组织MNIST图像张量与训练历史数值
import matplotlib.pyplot as plt  # 对照CNN训练期和验证期曲线
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'  # 屏蔽 TensorFlow 的 INFO/WARNING 级别底层日志
os.environ['TF_ENABLE_ONEDNN_OPTS'] = '0'  # 关闭 oneDNN 提示日志,减少非关键告警输出
os.environ['CUDA_VISIBLE_DEVICES'] = '-1'  # 显式禁用 GPU 探测,避免 CUDA 初始化失败日志
stderr_backup_fd = os.dup(2)  # 备份当前标准错误文件描述符,便于导入 TensorFlow 后恢复
with open(os.devnull, 'w') as null_stderr:  # 打开空设备文件,用于临时吞掉 TensorFlow 的底层启动日志
    os.dup2(null_stderr.fileno(), 2)  # 将进程级标准错误重定向到空设备,覆盖 C/C++ 层日志输出
    try:  # 尝试在静默状态下导入 TensorFlow 相关模块
        from tensorflow import keras  # 读取MNIST并调用统一训练接口
        from tensorflow.keras import layers, models  # 声明卷积、池化和输出层
    finally:  # 无论导入是否成功,都恢复原始标准错误输出
        os.dup2(stderr_backup_fd, 2)  # 恢复原始标准错误文件描述符,避免影响后续代码块输出
        os.close(stderr_backup_fd)  # 关闭备份文件描述符,避免文件描述符泄漏
列表 10.2: MNIST 预处理与 CNN 结构
# 读取标准基准及其官方划分,使训练选择与最终评价样本保持分离
mnist = keras.datasets.mnist  # 指向下文使用的手写数字分类基准
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()  # 保留官方训练集与锁定测试集边界

# 为卷积层补充通道维,并用事前固定的像素上界统一输入尺度
train_images = train_images.reshape((60000, 28, 28, 1)).astype('float32') / 255  # 把训练像素映射到[0,1]以稳定梯度尺度
test_images = test_images.reshape((10000, 28, 28, 1)).astype('float32') / 255  # 对锁定测试图像应用同一确定性映射

# 用局部特征提取、空间降采样和十类概率输出组成紧凑候选架构
model = models.Sequential([  # 固定层序与容量,避免测试后再改变候选架构
    layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)),  # 从灰度图学习第一层局部边缘表示
    layers.MaxPooling2D((2, 2)),  # 压缩空间分辨率并限制后续参数量
    layers.Conv2D(64, (3, 3), activation='relu'),  # 在降采样表示上组合更高阶局部模式
    layers.MaxPooling2D((2, 2)),  # 再次压缩空间表示以控制计算预算
    layers.Conv2D(64, (3, 3), activation='relu'),  # 扩展最终卷积表示的感受野
    layers.Flatten(),  # 把空间特征映射到分类头所需的向量表示
    layers.Dense(64, activation='relu'),  # 汇总局部特征以形成十类判别表示
    layers.Dense(10, activation='softmax')  # 输出总和为1的十类预测概率
])  # 锁定进入验证早停阶段的候选网络

# 以整数标签的负对数似然训练概率模型,并同时记录离散分类正确率
model.compile(optimizer='adam',  # 用自适应梯度更新该固定架构的参数
              loss='sparse_categorical_crossentropy',  # 使训练目标对错误的高置信概率施加更大惩罚
              metrics=['accuracy'])  # 与概率损失并列报告阈值化后的分类结果
/home/ubuntu/miniconda3/envs/peter/lib/python3.10/site-packages/keras/src/layers/convolutional/base_conv.py:113: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.
  super().__init__(activity_regularizer=activity_regularizer, **kwargs)
# 在明确上限内仅依据训练期验证损失锁定停止轮次
cnn_max_epochs = 20  # 给早停留出观察验证损失反弹的有限训练预算
cnn_early_stopping = keras.callbacks.EarlyStopping(monitor='val_loss', patience=2, restore_best_weights=True)  # 验证损失连续两轮不改善即恢复最佳权重
print('开始训练CNN模型...')  # 标记验证驱动训练开始,避免误读后续测试输出
history = model.fit(train_images, train_labels, epochs=cnn_max_epochs,  # 在事前固定的最大轮数内拟合
                    batch_size=64, validation_split=0.2, callbacks=[cnn_early_stopping],  # 留出训练期内部20%并应用早停
                    verbose=1)  # 保留逐轮训练与验证指标供诊断
cnn_best_validation_epoch = int(np.argmin(history.history['val_loss']) + 1)  # 按最小验证损失确定已锁定轮次
print(f'验证损失最佳轮次(已恢复该轮权重): {cnn_best_validation_epoch}/{cnn_max_epochs}')  # 明示测试前已完成轮次选择
开始训练CNN模型...

Epoch 1/20


  1/750 ━━━━━━━━━━━━━━━━━━━━ 11:05 888ms/step - accuracy: 0.1094 - loss: 2.3204

  6/750 ━━━━━━━━━━━━━━━━━━━━ 8s 11ms/step - accuracy: 0.2064 - loss: 2.2887    

 11/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.2569 - loss: 2.2568

 16/750 ━━━━━━━━━━━━━━━━━━━━ 8s 11ms/step - accuracy: 0.2890 - loss: 2.2143

 21/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.3169 - loss: 2.1616

 27/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.3491 - loss: 2.0839

 33/750 ━━━━━━━━━━━━━━━━━━━━ 7s 10ms/step - accuracy: 0.3795 - loss: 2.0007

 38/750 ━━━━━━━━━━━━━━━━━━━━ 7s 10ms/step - accuracy: 0.4027 - loss: 1.9322

 44/750 ━━━━━━━━━━━━━━━━━━━━ 7s 10ms/step - accuracy: 0.4277 - loss: 1.8552

 50/750 ━━━━━━━━━━━━━━━━━━━━ 7s 10ms/step - accuracy: 0.4500 - loss: 1.7841

 56/750 ━━━━━━━━━━━━━━━━━━━━ 7s 10ms/step - accuracy: 0.4703 - loss: 1.7187

 62/750 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.4885 - loss: 1.6597

 68/750 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.5053 - loss: 1.6056

 74/750 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.5205 - loss: 1.5563

 80/750 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.5346 - loss: 1.5107

 86/750 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.5477 - loss: 1.4683

 92/750 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.5598 - loss: 1.4290

 98/750 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.5711 - loss: 1.3925

104/750 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.5815 - loss: 1.3587

110/750 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.5913 - loss: 1.3270

116/750 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.6004 - loss: 1.2973

122/750 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.6090 - loss: 1.2692

128/750 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.6172 - loss: 1.2428

134/750 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.6249 - loss: 1.2177

140/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.6322 - loss: 1.1940

146/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.6391 - loss: 1.1715

152/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.6458 - loss: 1.1501

158/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.6521 - loss: 1.1297

164/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.6581 - loss: 1.1102

170/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.6639 - loss: 1.0917

176/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.6694 - loss: 1.0740

182/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.6747 - loss: 1.0570

188/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.6797 - loss: 1.0407

194/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.6846 - loss: 1.0250

200/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.6893 - loss: 1.0099

206/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.6938 - loss: 0.9954

212/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.6982 - loss: 0.9814

218/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.7024 - loss: 0.9679

224/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.7064 - loss: 0.9549

230/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.7103 - loss: 0.9423

236/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.7140 - loss: 0.9302

242/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.7176 - loss: 0.9184

248/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.7211 - loss: 0.9071

254/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.7245 - loss: 0.8961

260/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.7278 - loss: 0.8855

266/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.7310 - loss: 0.8751

272/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.7341 - loss: 0.8652

278/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.7370 - loss: 0.8555

284/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.7399 - loss: 0.8461

290/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.7427 - loss: 0.8370

296/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.7455 - loss: 0.8281

302/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.7481 - loss: 0.8195

308/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.7507 - loss: 0.8111

314/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.7532 - loss: 0.8030

320/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.7557 - loss: 0.7951

326/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.7581 - loss: 0.7873

332/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.7604 - loss: 0.7798

338/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.7627 - loss: 0.7725

344/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.7649 - loss: 0.7653

350/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.7670 - loss: 0.7584

356/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.7691 - loss: 0.7515

362/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.7712 - loss: 0.7449

368/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.7732 - loss: 0.7384

374/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.7752 - loss: 0.7320

380/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.7771 - loss: 0.7258

386/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.7790 - loss: 0.7198

392/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.7808 - loss: 0.7138

398/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.7826 - loss: 0.7080

404/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.7844 - loss: 0.7024

410/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.7861 - loss: 0.6968

416/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.7878 - loss: 0.6914

422/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.7894 - loss: 0.6860

428/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.7910 - loss: 0.6808

433/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.7924 - loss: 0.6765

439/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.7939 - loss: 0.6715

445/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.7954 - loss: 0.6666

451/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.7969 - loss: 0.6617

457/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.7984 - loss: 0.6570

463/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.7998 - loss: 0.6523

469/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.8012 - loss: 0.6478

474/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.8024 - loss: 0.6440

479/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.8035 - loss: 0.6404

484/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.8046 - loss: 0.6367

490/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.8060 - loss: 0.6324

496/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.8073 - loss: 0.6282

502/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.8085 - loss: 0.6241

508/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.8098 - loss: 0.6200

514/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.8110 - loss: 0.6160

520/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.8122 - loss: 0.6121

526/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.8134 - loss: 0.6083

532/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.8146 - loss: 0.6045

538/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.8157 - loss: 0.6007

544/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.8169 - loss: 0.5970

550/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.8180 - loss: 0.5934

556/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.8191 - loss: 0.5899

562/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.8201 - loss: 0.5864

568/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.8212 - loss: 0.5829

574/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.8222 - loss: 0.5795

580/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.8233 - loss: 0.5762

586/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.8243 - loss: 0.5729

592/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.8253 - loss: 0.5696

598/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.8263 - loss: 0.5665

604/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.8272 - loss: 0.5633

610/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.8282 - loss: 0.5602

616/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.8291 - loss: 0.5572

622/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.8300 - loss: 0.5542

628/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.8309 - loss: 0.5512

634/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.8318 - loss: 0.5483

640/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.8327 - loss: 0.5454

646/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.8336 - loss: 0.5425

652/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.8345 - loss: 0.5397

658/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.8353 - loss: 0.5370

664/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.8362 - loss: 0.5342

669/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.8368 - loss: 0.5320

675/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.8377 - loss: 0.5293

681/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.8385 - loss: 0.5267

687/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.8393 - loss: 0.5241

693/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.8401 - loss: 0.5215

699/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.8408 - loss: 0.5190

705/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.8416 - loss: 0.5165

711/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.8424 - loss: 0.5140

717/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.8431 - loss: 0.5116

723/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.8438 - loss: 0.5092

729/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.8446 - loss: 0.5068

735/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.8453 - loss: 0.5045

741/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.8460 - loss: 0.5022

747/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.8467 - loss: 0.4999

750/750 ━━━━━━━━━━━━━━━━━━━━ 9s 11ms/step - accuracy: 0.9338 - loss: 0.2157 - val_accuracy: 0.9803 - val_loss: 0.0657

Epoch 2/20


  1/750 ━━━━━━━━━━━━━━━━━━━━ 19s 26ms/step - accuracy: 0.9531 - loss: 0.0958

  6/750 ━━━━━━━━━━━━━━━━━━━━ 7s 10ms/step - accuracy: 0.9768 - loss: 0.0589 

 12/750 ━━━━━━━━━━━━━━━━━━━━ 7s 10ms/step - accuracy: 0.9765 - loss: 0.0590

 18/750 ━━━━━━━━━━━━━━━━━━━━ 7s 10ms/step - accuracy: 0.9772 - loss: 0.0582

 23/750 ━━━━━━━━━━━━━━━━━━━━ 7s 10ms/step - accuracy: 0.9779 - loss: 0.0575

 29/750 ━━━━━━━━━━━━━━━━━━━━ 7s 10ms/step - accuracy: 0.9782 - loss: 0.0583

 35/750 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.9780 - loss: 0.0612

 41/750 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.9776 - loss: 0.0634

 47/750 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.9774 - loss: 0.0650

 53/750 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.9771 - loss: 0.0664

 58/750 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.9770 - loss: 0.0675

 64/750 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.9768 - loss: 0.0685

 70/750 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.9767 - loss: 0.0692

 76/750 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.9767 - loss: 0.0695

 82/750 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.9767 - loss: 0.0697

 88/750 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.9768 - loss: 0.0698

 94/750 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.9768 - loss: 0.0698

100/750 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.9769 - loss: 0.0697

106/750 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.9770 - loss: 0.0695

112/750 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.9772 - loss: 0.0692

118/750 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.9773 - loss: 0.0690

124/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.9774 - loss: 0.0688

130/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.9775 - loss: 0.0686

136/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.9777 - loss: 0.0684

142/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.9777 - loss: 0.0682

148/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.9778 - loss: 0.0680

154/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.9779 - loss: 0.0679

160/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.9780 - loss: 0.0678

166/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.9780 - loss: 0.0676

171/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.9781 - loss: 0.0675

177/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.9781 - loss: 0.0674

183/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.9782 - loss: 0.0674

189/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.9782 - loss: 0.0673

195/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.9783 - loss: 0.0672

201/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.9784 - loss: 0.0671

207/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.9784 - loss: 0.0670

213/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.9785 - loss: 0.0669

219/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.9785 - loss: 0.0668

225/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9785 - loss: 0.0667

231/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9786 - loss: 0.0666

237/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9786 - loss: 0.0665

243/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9787 - loss: 0.0664

249/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9787 - loss: 0.0663

255/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9788 - loss: 0.0661

261/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9788 - loss: 0.0660

267/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9789 - loss: 0.0658

273/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9789 - loss: 0.0657 

279/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9790 - loss: 0.0656

285/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9790 - loss: 0.0655

291/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9790 - loss: 0.0653

297/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9791 - loss: 0.0652

303/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9791 - loss: 0.0651

309/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9792 - loss: 0.0650

315/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9792 - loss: 0.0649

321/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9793 - loss: 0.0648

327/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9793 - loss: 0.0647

333/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9793 - loss: 0.0646

339/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9794 - loss: 0.0644

345/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9794 - loss: 0.0643

351/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9795 - loss: 0.0643

357/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9795 - loss: 0.0642

363/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9795 - loss: 0.0641

369/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9796 - loss: 0.0640

375/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9796 - loss: 0.0639

381/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9796 - loss: 0.0639

387/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9797 - loss: 0.0638

393/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9797 - loss: 0.0637

399/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9797 - loss: 0.0636

405/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9798 - loss: 0.0636

410/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9798 - loss: 0.0635

416/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9798 - loss: 0.0635

422/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9798 - loss: 0.0634

428/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9798 - loss: 0.0634

434/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9799 - loss: 0.0633

440/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9799 - loss: 0.0632

445/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9799 - loss: 0.0632

451/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9799 - loss: 0.0631

457/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9800 - loss: 0.0631

463/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9800 - loss: 0.0630

469/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9800 - loss: 0.0629

475/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9800 - loss: 0.0629

481/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9801 - loss: 0.0628

487/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9801 - loss: 0.0628

493/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9801 - loss: 0.0627

499/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9801 - loss: 0.0627

505/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9801 - loss: 0.0626

511/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9802 - loss: 0.0626

517/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9802 - loss: 0.0625

523/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9802 - loss: 0.0625

529/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9802 - loss: 0.0624

535/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9802 - loss: 0.0624

541/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9803 - loss: 0.0623

547/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9803 - loss: 0.0623

553/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9803 - loss: 0.0623

559/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9803 - loss: 0.0622

565/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9803 - loss: 0.0622

571/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9804 - loss: 0.0622

577/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9804 - loss: 0.0621

583/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9804 - loss: 0.0621

589/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9804 - loss: 0.0621

595/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9804 - loss: 0.0620

601/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9804 - loss: 0.0620

607/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9804 - loss: 0.0619

613/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9805 - loss: 0.0619

619/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9805 - loss: 0.0619

625/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9805 - loss: 0.0618

631/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9805 - loss: 0.0618

637/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9805 - loss: 0.0618

643/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9805 - loss: 0.0618

649/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9805 - loss: 0.0617

655/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9805 - loss: 0.0617

661/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9806 - loss: 0.0617

667/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9806 - loss: 0.0617

673/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9806 - loss: 0.0616

679/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9806 - loss: 0.0616

685/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9806 - loss: 0.0616

691/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9806 - loss: 0.0616

697/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9806 - loss: 0.0616

703/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9806 - loss: 0.0615

708/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9806 - loss: 0.0615

713/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9806 - loss: 0.0615

719/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9806 - loss: 0.0615

725/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9806 - loss: 0.0615

731/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9806 - loss: 0.0615

737/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9807 - loss: 0.0615

743/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9807 - loss: 0.0614

749/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9807 - loss: 0.0614

750/750 ━━━━━━━━━━━━━━━━━━━━ 8s 10ms/step - accuracy: 0.9818 - loss: 0.0589 - val_accuracy: 0.9858 - val_loss: 0.0491

Epoch 3/20


  1/750 ━━━━━━━━━━━━━━━━━━━━ 16s 22ms/step - accuracy: 0.9844 - loss: 0.0418

  7/750 ━━━━━━━━━━━━━━━━━━━━ 7s 9ms/step - accuracy: 0.9854 - loss: 0.0427  

 13/750 ━━━━━━━━━━━━━━━━━━━━ 7s 10ms/step - accuracy: 0.9866 - loss: 0.0377

 19/750 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.9862 - loss: 0.0386 

 25/750 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.9859 - loss: 0.0395

 31/750 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.9859 - loss: 0.0397

 37/750 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.9860 - loss: 0.0401

 43/750 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.9861 - loss: 0.0403

 49/750 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.9862 - loss: 0.0403

 55/750 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.9861 - loss: 0.0407

 61/750 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.9861 - loss: 0.0410

 67/750 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.9860 - loss: 0.0413

 73/750 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.9860 - loss: 0.0415

 79/750 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.9861 - loss: 0.0415

 85/750 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.9861 - loss: 0.0415

 91/750 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.9862 - loss: 0.0414

 97/750 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.9863 - loss: 0.0412

103/750 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.9863 - loss: 0.0411

109/750 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.9864 - loss: 0.0410

115/750 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.9864 - loss: 0.0411

121/750 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.9864 - loss: 0.0411

127/750 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.9864 - loss: 0.0411

133/750 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.9864 - loss: 0.0411

139/750 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.9865 - loss: 0.0411

145/750 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.9865 - loss: 0.0412

151/750 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.9865 - loss: 0.0412

157/750 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.9865 - loss: 0.0412

163/750 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.9865 - loss: 0.0412

169/750 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.9865 - loss: 0.0411

175/750 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.9865 - loss: 0.0412

181/750 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.9865 - loss: 0.0412

187/750 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.9866 - loss: 0.0412

193/750 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.9866 - loss: 0.0412

199/750 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.9866 - loss: 0.0412

205/750 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.9866 - loss: 0.0412

211/750 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.9866 - loss: 0.0412

217/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9866 - loss: 0.0412

223/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9866 - loss: 0.0412

229/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9866 - loss: 0.0412

235/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9867 - loss: 0.0412

241/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9867 - loss: 0.0412

246/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9867 - loss: 0.0412

252/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9867 - loss: 0.0412

258/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9867 - loss: 0.0412

264/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9867 - loss: 0.0412

270/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9867 - loss: 0.0412

276/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9867 - loss: 0.0412

282/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9867 - loss: 0.0412

288/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9867 - loss: 0.0412

294/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9867 - loss: 0.0412

300/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9867 - loss: 0.0413

305/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9867 - loss: 0.0413

311/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9867 - loss: 0.0413

317/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9867 - loss: 0.0414

323/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9867 - loss: 0.0414

329/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9867 - loss: 0.0415

335/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9867 - loss: 0.0415

341/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9867 - loss: 0.0415

347/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9867 - loss: 0.0416

353/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9867 - loss: 0.0416

359/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9867 - loss: 0.0416

365/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9867 - loss: 0.0417

371/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9867 - loss: 0.0417

377/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9867 - loss: 0.0417

383/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9867 - loss: 0.0418

389/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9867 - loss: 0.0418

395/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9867 - loss: 0.0418

401/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9867 - loss: 0.0418

407/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9867 - loss: 0.0418

413/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9867 - loss: 0.0418

419/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9867 - loss: 0.0419

424/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9867 - loss: 0.0419

429/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9867 - loss: 0.0419

435/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9867 - loss: 0.0419

441/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9867 - loss: 0.0419

447/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9867 - loss: 0.0419

453/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9867 - loss: 0.0420

459/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9867 - loss: 0.0420

465/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9867 - loss: 0.0420

471/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9867 - loss: 0.0420

477/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9867 - loss: 0.0420

482/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9867 - loss: 0.0420

486/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.9867 - loss: 0.0420

492/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.9867 - loss: 0.0420

498/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.9867 - loss: 0.0420

504/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.9867 - loss: 0.0420

510/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9867 - loss: 0.0420 

516/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9868 - loss: 0.0420

522/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9868 - loss: 0.0420

528/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9868 - loss: 0.0420

534/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9868 - loss: 0.0420

540/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9868 - loss: 0.0420

546/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9868 - loss: 0.0420

552/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9868 - loss: 0.0420

558/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9868 - loss: 0.0420

564/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9868 - loss: 0.0420

570/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9868 - loss: 0.0420

576/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9868 - loss: 0.0420

582/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9868 - loss: 0.0419

588/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9868 - loss: 0.0419

594/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9868 - loss: 0.0419

600/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9868 - loss: 0.0419

606/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9868 - loss: 0.0419

612/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9868 - loss: 0.0419

618/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9868 - loss: 0.0419

624/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9868 - loss: 0.0419

630/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9869 - loss: 0.0419

636/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9869 - loss: 0.0418

642/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9869 - loss: 0.0418

648/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9869 - loss: 0.0418

654/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9869 - loss: 0.0418

660/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9869 - loss: 0.0418

666/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9869 - loss: 0.0418

672/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9869 - loss: 0.0418

678/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9869 - loss: 0.0417

684/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9869 - loss: 0.0417

690/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9869 - loss: 0.0417

696/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9869 - loss: 0.0417

702/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9869 - loss: 0.0417

708/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9869 - loss: 0.0417

714/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9869 - loss: 0.0416

720/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9869 - loss: 0.0416

726/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9870 - loss: 0.0416

732/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9870 - loss: 0.0416

738/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9870 - loss: 0.0416

744/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9870 - loss: 0.0416

750/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9870 - loss: 0.0416

750/750 ━━━━━━━━━━━━━━━━━━━━ 8s 10ms/step - accuracy: 0.9877 - loss: 0.0396 - val_accuracy: 0.9858 - val_loss: 0.0480

Epoch 4/20


  1/750 ━━━━━━━━━━━━━━━━━━━━ 16s 22ms/step - accuracy: 1.0000 - loss: 0.0050

  7/750 ━━━━━━━━━━━━━━━━━━━━ 7s 10ms/step - accuracy: 0.9976 - loss: 0.0131 

 13/750 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.9949 - loss: 0.0170 

 19/750 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.9939 - loss: 0.0195

 25/750 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.9933 - loss: 0.0210

 31/750 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.9929 - loss: 0.0222

 37/750 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.9926 - loss: 0.0229

 43/750 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.9925 - loss: 0.0232

 49/750 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.9924 - loss: 0.0235

 55/750 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.9924 - loss: 0.0237

 61/750 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.9924 - loss: 0.0239

 67/750 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.9923 - loss: 0.0241

 73/750 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.9923 - loss: 0.0245

 79/750 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.9922 - loss: 0.0248

 85/750 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.9921 - loss: 0.0251

 91/750 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.9921 - loss: 0.0254

 97/750 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.9920 - loss: 0.0257

103/750 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.9920 - loss: 0.0259

109/750 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.9919 - loss: 0.0261

115/750 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.9919 - loss: 0.0262

121/750 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.9919 - loss: 0.0263

127/750 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.9919 - loss: 0.0264

133/750 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.9919 - loss: 0.0265

139/750 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.9918 - loss: 0.0266

144/750 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.9918 - loss: 0.0267

150/750 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.9918 - loss: 0.0268

156/750 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.9918 - loss: 0.0269

162/750 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.9918 - loss: 0.0269

168/750 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.9918 - loss: 0.0270

174/750 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.9918 - loss: 0.0271

180/750 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.9917 - loss: 0.0272

186/750 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.9917 - loss: 0.0273

192/750 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.9917 - loss: 0.0273

198/750 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.9917 - loss: 0.0274

204/750 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.9917 - loss: 0.0275

210/750 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.9916 - loss: 0.0276

216/750 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.9916 - loss: 0.0277

222/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9916 - loss: 0.0278

228/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9916 - loss: 0.0278

234/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9916 - loss: 0.0279

240/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9915 - loss: 0.0280

246/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9915 - loss: 0.0281

252/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9915 - loss: 0.0281

258/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9915 - loss: 0.0282

264/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9915 - loss: 0.0282

270/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9914 - loss: 0.0283

276/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9914 - loss: 0.0283

282/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9914 - loss: 0.0284

287/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9914 - loss: 0.0284

293/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9914 - loss: 0.0285

299/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9914 - loss: 0.0285

305/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9913 - loss: 0.0285

311/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9913 - loss: 0.0286

317/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9913 - loss: 0.0286

323/750 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9913 - loss: 0.0287

329/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9913 - loss: 0.0287

335/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9913 - loss: 0.0287

341/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9913 - loss: 0.0288

347/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9912 - loss: 0.0288

353/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9912 - loss: 0.0288

359/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9912 - loss: 0.0289

365/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9912 - loss: 0.0289

371/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9912 - loss: 0.0289

377/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9912 - loss: 0.0290

383/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9911 - loss: 0.0290

389/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9911 - loss: 0.0290

395/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9911 - loss: 0.0291

401/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9911 - loss: 0.0291

407/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9911 - loss: 0.0292

413/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9910 - loss: 0.0292

419/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9910 - loss: 0.0293

425/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9910 - loss: 0.0293

431/750 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9910 - loss: 0.0293

437/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9910 - loss: 0.0294

443/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9910 - loss: 0.0294

449/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9910 - loss: 0.0294

455/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9910 - loss: 0.0295

461/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9909 - loss: 0.0295

467/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9909 - loss: 0.0295

473/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9909 - loss: 0.0295

479/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9909 - loss: 0.0295

485/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9909 - loss: 0.0296

491/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9909 - loss: 0.0296

496/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9909 - loss: 0.0296

501/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9909 - loss: 0.0296

507/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9909 - loss: 0.0296

513/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9909 - loss: 0.0297

519/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9909 - loss: 0.0297

525/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9909 - loss: 0.0297

531/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9909 - loss: 0.0297

537/750 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.9909 - loss: 0.0297

543/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9909 - loss: 0.0297

548/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9908 - loss: 0.0298

553/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9908 - loss: 0.0298

559/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9908 - loss: 0.0298

565/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9908 - loss: 0.0298

571/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9908 - loss: 0.0298

577/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9908 - loss: 0.0299

583/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9908 - loss: 0.0299

589/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9908 - loss: 0.0299

595/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9908 - loss: 0.0299

601/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9908 - loss: 0.0299

607/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9908 - loss: 0.0300

613/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9908 - loss: 0.0300

619/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9908 - loss: 0.0300

625/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9908 - loss: 0.0300

631/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9907 - loss: 0.0301

637/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9907 - loss: 0.0301

643/750 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.9907 - loss: 0.0301

649/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9907 - loss: 0.0301

655/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9907 - loss: 0.0301

661/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9907 - loss: 0.0302

667/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9907 - loss: 0.0302

673/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9907 - loss: 0.0302

679/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9907 - loss: 0.0302

684/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9907 - loss: 0.0302

690/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9907 - loss: 0.0303

696/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9906 - loss: 0.0303

702/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9906 - loss: 0.0303

708/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9906 - loss: 0.0303

714/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9906 - loss: 0.0304

720/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9906 - loss: 0.0304

726/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9906 - loss: 0.0304

732/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9906 - loss: 0.0304

738/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9906 - loss: 0.0304

744/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9906 - loss: 0.0304

750/750 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step - accuracy: 0.9906 - loss: 0.0305

750/750 ━━━━━━━━━━━━━━━━━━━━ 8s 10ms/step - accuracy: 0.9897 - loss: 0.0326 - val_accuracy: 0.9881 - val_loss: 0.0434

Epoch 5/20


  1/750 ━━━━━━━━━━━━━━━━━━━━ 16s 22ms/step - accuracy: 0.9844 - loss: 0.0305

  7/750 ━━━━━━━━━━━━━━━━━━━━ 7s 10ms/step - accuracy: 0.9942 - loss: 0.0211 

 13/750 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.9944 - loss: 0.0217 

 19/750 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.9942 - loss: 0.0223

 24/750 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.9940 - loss: 0.0224

 29/750 ━━━━━━━━━━━━━━━━━━━━ 7s 10ms/step - accuracy: 0.9939 - loss: 0.0224

 34/750 ━━━━━━━━━━━━━━━━━━━━ 7s 10ms/step - accuracy: 0.9937 - loss: 0.0225

 40/750 ━━━━━━━━━━━━━━━━━━━━ 7s 10ms/step - accuracy: 0.9936 - loss: 0.0223

 46/750 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.9936 - loss: 0.0221

 52/750 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.9935 - loss: 0.0218

 58/750 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.9935 - loss: 0.0216

 64/750 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.9936 - loss: 0.0214

 70/750 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.9936 - loss: 0.0212

 76/750 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.9936 - loss: 0.0211

 82/750 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.9937 - loss: 0.0208

 88/750 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.9937 - loss: 0.0206

 94/750 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.9938 - loss: 0.0204

100/750 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.9939 - loss: 0.0202

106/750 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.9939 - loss: 0.0200

112/750 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.9940 - loss: 0.0198

118/750 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.9940 - loss: 0.0197

124/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.9941 - loss: 0.0195

130/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.9941 - loss: 0.0194

136/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.9941 - loss: 0.0193

142/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.9941 - loss: 0.0192

148/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.9941 - loss: 0.0191

154/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.9942 - loss: 0.0191

160/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.9942 - loss: 0.0190

166/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.9942 - loss: 0.0190

172/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.9942 - loss: 0.0190

178/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.9942 - loss: 0.0190

184/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.9942 - loss: 0.0190

190/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.9942 - loss: 0.0189

196/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.9942 - loss: 0.0189

202/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.9942 - loss: 0.0189

207/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.9942 - loss: 0.0189

212/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.9942 - loss: 0.0189

218/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.9942 - loss: 0.0189

223/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.9942 - loss: 0.0189

228/750 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.9941 - loss: 0.0189

233/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9941 - loss: 0.0189

238/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9941 - loss: 0.0190

243/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9941 - loss: 0.0190

248/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9941 - loss: 0.0190

253/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9941 - loss: 0.0190

258/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9941 - loss: 0.0190

263/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9941 - loss: 0.0191

268/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9941 - loss: 0.0191

273/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9941 - loss: 0.0191

278/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9941 - loss: 0.0191

283/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9940 - loss: 0.0191

288/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9940 - loss: 0.0192

293/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9940 - loss: 0.0192

298/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9940 - loss: 0.0192

303/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9940 - loss: 0.0192

308/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9940 - loss: 0.0193

314/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9940 - loss: 0.0193

320/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9940 - loss: 0.0193

326/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9939 - loss: 0.0193

332/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9939 - loss: 0.0193

338/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9939 - loss: 0.0194

344/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9939 - loss: 0.0194

350/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9939 - loss: 0.0194

356/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9939 - loss: 0.0194

362/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9939 - loss: 0.0195

368/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9939 - loss: 0.0195

374/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9939 - loss: 0.0195

380/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9939 - loss: 0.0195

385/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9938 - loss: 0.0196

390/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9938 - loss: 0.0196

396/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9938 - loss: 0.0196

402/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9938 - loss: 0.0196

408/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9938 - loss: 0.0196

414/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9938 - loss: 0.0197

420/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9938 - loss: 0.0197

426/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9938 - loss: 0.0197

432/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9938 - loss: 0.0197

438/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9938 - loss: 0.0197

444/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.9938 - loss: 0.0198

450/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.9938 - loss: 0.0198

456/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.9938 - loss: 0.0198

461/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.9938 - loss: 0.0198

466/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.9938 - loss: 0.0198

471/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.9938 - loss: 0.0198

476/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.9937 - loss: 0.0199

481/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.9937 - loss: 0.0199

486/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.9937 - loss: 0.0199

492/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.9937 - loss: 0.0199

497/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.9937 - loss: 0.0199

503/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.9937 - loss: 0.0200

508/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.9937 - loss: 0.0200

513/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.9937 - loss: 0.0200

518/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.9937 - loss: 0.0200

523/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.9937 - loss: 0.0200

528/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.9937 - loss: 0.0201

533/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.9937 - loss: 0.0201

538/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.9937 - loss: 0.0201

543/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.9937 - loss: 0.0201

549/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.9937 - loss: 0.0202

554/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.9936 - loss: 0.0202

559/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.9936 - loss: 0.0202

564/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.9936 - loss: 0.0202

569/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.9936 - loss: 0.0203

574/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.9936 - loss: 0.0203

579/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.9936 - loss: 0.0203

584/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.9936 - loss: 0.0203

589/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.9936 - loss: 0.0203

593/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.9936 - loss: 0.0204

595/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.9936 - loss: 0.0204

599/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.9936 - loss: 0.0204

603/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.9936 - loss: 0.0204

608/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.9936 - loss: 0.0204

613/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.9936 - loss: 0.0204

618/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.9936 - loss: 0.0204

623/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.9936 - loss: 0.0205

628/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.9936 - loss: 0.0205

633/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.9936 - loss: 0.0205

638/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.9935 - loss: 0.0205

643/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.9935 - loss: 0.0205

648/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.9935 - loss: 0.0206

653/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.9935 - loss: 0.0206

658/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.9935 - loss: 0.0206

663/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.9935 - loss: 0.0206

669/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.9935 - loss: 0.0206

674/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.9935 - loss: 0.0206

679/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.9935 - loss: 0.0207

684/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.9935 - loss: 0.0207

690/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.9935 - loss: 0.0207

696/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.9935 - loss: 0.0207

702/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.9935 - loss: 0.0207

708/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.9935 - loss: 0.0208

714/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.9935 - loss: 0.0208

720/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.9935 - loss: 0.0208

726/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.9934 - loss: 0.0208

732/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.9934 - loss: 0.0208

738/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.9934 - loss: 0.0209

743/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.9934 - loss: 0.0209

749/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.9934 - loss: 0.0209

750/750 ━━━━━━━━━━━━━━━━━━━━ 8s 11ms/step - accuracy: 0.9925 - loss: 0.0237 - val_accuracy: 0.9881 - val_loss: 0.0462

Epoch 6/20


  1/750 ━━━━━━━━━━━━━━━━━━━━ 17s 24ms/step - accuracy: 0.9844 - loss: 0.0534

  6/750 ━━━━━━━━━━━━━━━━━━━━ 7s 10ms/step - accuracy: 0.9887 - loss: 0.0324 

 11/750 ━━━━━━━━━━━━━━━━━━━━ 7s 10ms/step - accuracy: 0.9907 - loss: 0.0271

 16/750 ━━━━━━━━━━━━━━━━━━━━ 7s 10ms/step - accuracy: 0.9916 - loss: 0.0248

 21/750 ━━━━━━━━━━━━━━━━━━━━ 7s 10ms/step - accuracy: 0.9923 - loss: 0.0238

 26/750 ━━━━━━━━━━━━━━━━━━━━ 7s 10ms/step - accuracy: 0.9926 - loss: 0.0234

 31/750 ━━━━━━━━━━━━━━━━━━━━ 7s 10ms/step - accuracy: 0.9928 - loss: 0.0230

 36/750 ━━━━━━━━━━━━━━━━━━━━ 7s 10ms/step - accuracy: 0.9930 - loss: 0.0225

 41/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9931 - loss: 0.0221

 46/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9932 - loss: 0.0221

 51/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9932 - loss: 0.0220

 56/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9932 - loss: 0.0218

 61/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9933 - loss: 0.0218

 66/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9933 - loss: 0.0217

 71/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9933 - loss: 0.0216

 76/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9934 - loss: 0.0215

 81/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9934 - loss: 0.0213

 86/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9935 - loss: 0.0212

 91/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9935 - loss: 0.0211

 96/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.9935 - loss: 0.0210

101/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.9936 - loss: 0.0209

106/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.9936 - loss: 0.0207

111/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.9936 - loss: 0.0206

116/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.9937 - loss: 0.0205

121/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.9937 - loss: 0.0205

126/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.9937 - loss: 0.0204

128/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.9937 - loss: 0.0204

133/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.9937 - loss: 0.0204

138/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.9938 - loss: 0.0203

143/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.9938 - loss: 0.0203

148/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.9938 - loss: 0.0203

153/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.9938 - loss: 0.0203

158/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.9938 - loss: 0.0203

163/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.9938 - loss: 0.0203

168/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.9938 - loss: 0.0202

174/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.9938 - loss: 0.0202

179/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.9938 - loss: 0.0202

184/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.9938 - loss: 0.0201

189/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.9938 - loss: 0.0201

194/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.9938 - loss: 0.0201

199/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.9938 - loss: 0.0200

205/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.9939 - loss: 0.0200

210/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.9939 - loss: 0.0200

215/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.9939 - loss: 0.0199

221/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.9939 - loss: 0.0199

227/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.9939 - loss: 0.0198

233/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.9939 - loss: 0.0198

239/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.9939 - loss: 0.0197

245/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.9940 - loss: 0.0197

251/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.9940 - loss: 0.0197

257/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.9940 - loss: 0.0196

263/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.9940 - loss: 0.0196

269/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.9940 - loss: 0.0196

275/750 ━━━━━━━━━━━━━━━━━━━━ 4s 11ms/step - accuracy: 0.9940 - loss: 0.0196

281/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9940 - loss: 0.0195

287/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9941 - loss: 0.0195

293/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9941 - loss: 0.0195

298/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9941 - loss: 0.0195

303/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9941 - loss: 0.0195

309/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9941 - loss: 0.0195

315/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9941 - loss: 0.0195

321/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9941 - loss: 0.0195

327/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9941 - loss: 0.0195

333/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9941 - loss: 0.0195

339/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9941 - loss: 0.0195

345/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9941 - loss: 0.0195

351/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9941 - loss: 0.0195

357/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9941 - loss: 0.0195

363/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9941 - loss: 0.0195

369/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9941 - loss: 0.0195

375/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9941 - loss: 0.0195

381/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9941 - loss: 0.0195

387/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9941 - loss: 0.0195

393/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9941 - loss: 0.0195

399/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9941 - loss: 0.0196

405/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9941 - loss: 0.0196

411/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9941 - loss: 0.0196

417/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9941 - loss: 0.0196

423/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9941 - loss: 0.0196

429/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9941 - loss: 0.0196

435/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9941 - loss: 0.0196

441/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9941 - loss: 0.0196

447/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9941 - loss: 0.0196

453/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.9941 - loss: 0.0196

459/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.9941 - loss: 0.0196

465/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.9941 - loss: 0.0197

471/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.9940 - loss: 0.0197

477/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.9940 - loss: 0.0197

483/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.9940 - loss: 0.0197

489/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.9940 - loss: 0.0197

495/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.9940 - loss: 0.0197

501/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.9940 - loss: 0.0197

507/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.9940 - loss: 0.0197

513/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.9940 - loss: 0.0197

519/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.9940 - loss: 0.0197

525/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.9940 - loss: 0.0197

531/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.9940 - loss: 0.0197

537/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.9940 - loss: 0.0197

543/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.9940 - loss: 0.0197

549/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.9940 - loss: 0.0197

555/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.9940 - loss: 0.0197

561/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.9940 - loss: 0.0197

566/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.9940 - loss: 0.0197

572/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.9940 - loss: 0.0197

578/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.9940 - loss: 0.0197

584/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.9940 - loss: 0.0197

590/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.9940 - loss: 0.0197

596/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.9940 - loss: 0.0197

602/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.9940 - loss: 0.0197

608/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.9940 - loss: 0.0197

614/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.9940 - loss: 0.0197

620/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.9940 - loss: 0.0198

626/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.9939 - loss: 0.0198

632/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.9939 - loss: 0.0198

638/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.9939 - loss: 0.0198

644/750 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.9939 - loss: 0.0198

650/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.9939 - loss: 0.0198

656/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.9939 - loss: 0.0198

662/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.9939 - loss: 0.0198

668/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.9939 - loss: 0.0198

674/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.9939 - loss: 0.0198

680/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.9939 - loss: 0.0198

686/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.9939 - loss: 0.0198

692/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.9939 - loss: 0.0198

698/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.9939 - loss: 0.0198

704/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.9939 - loss: 0.0198

710/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.9939 - loss: 0.0198

716/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.9939 - loss: 0.0198

722/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.9939 - loss: 0.0198

728/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.9939 - loss: 0.0198

734/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.9939 - loss: 0.0198

740/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.9939 - loss: 0.0198

746/750 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step - accuracy: 0.9939 - loss: 0.0198

750/750 ━━━━━━━━━━━━━━━━━━━━ 8s 11ms/step - accuracy: 0.9935 - loss: 0.0200 - val_accuracy: 0.9859 - val_loss: 0.0560

验证损失最佳轮次(已恢复该轮权重): 4/20

训练日志用于比较训练与验证曲线,具体数值以当前执行输出为准。二十轮是明确的教学计算上限,不是推荐训练长度;早停只读取训练期内部保留的验证损失,并恢复其中最低轮次的权重。测试集仅在模型、轮数和评价口径全部锁定后评价一次。

CNN 模型训练完成后,先用 列表 10.3 留存一次测试访问,再由 图 10.2 展示训练期与验证期曲线;二者分别承担数值核对与图形诊断,不能共用表格标签。

列表 10.3: MNIST 锁定测试集的一次评价记录
test_loss, test_acc = model.evaluate(test_images, test_labels, verbose=0)  # 用已恢复的最佳验证权重进行唯一一次测试评价
print(f'\n测试集准确率: {test_acc:.4f}')  # 报告一次性测试分类正确比例
print(f'测试集损失: {test_loss:.4f}')  # 同时记录与训练目标一致的测试损失

测试集准确率: 0.9888
测试集损失: 0.0355
plt.figure(figsize=(12, 4))  # 并排展示准确率与损失以核对训练—验证差距

# 左图检验分类正确率的训练—验证差距
plt.subplot(1, 2, 1)  # 将正确率轨迹置于左侧以便与损失诊断并读
plt.plot(history.history['accuracy'], label='训练集准确率')  # 显示拟合样本分类正确率随轮次变化
plt.plot(history.history['val_accuracy'], label='验证集准确率')  # 对照独立验证正确率识别泛化差距
plt.xlabel('Epoch', fontsize=12)  # 明示横轴为早停前实际完成的训练轮次
plt.ylabel('准确率', fontsize=12)  # 明示纵轴为样本分类正确比例
plt.title('模型准确率', fontsize=14, fontname='Source Han Serif SC')  # 提示左图用于比较拟合与验证分类表现
plt.legend(fontsize=10)  # 区分训练与验证证据
plt.grid(True, alpha=0.3)  # 便于按轮次比较两条准确率轨迹

# 右图检查早停依据的交叉熵损失
plt.subplot(1, 2, 2)  # 将选轮依据置于右侧与正确率诊断对照
plt.plot(history.history['loss'], label='训练集损失')  # 显示优化目标在拟合样本上的变化
plt.plot(history.history['val_loss'], label='验证集损失')  # 检查验证损失是否先于训练损失反弹
plt.xlabel('Epoch', fontsize=12)  # 明示横轴为早停前实际完成的训练轮次
plt.ylabel('损失', fontsize=12)  # 明示纵轴为用于优化与早停的交叉熵
plt.title('模型损失', fontsize=14, fontname='Source Han Serif SC')  # 提示右图承担验证选轮诊断
plt.legend(fontsize=10)  # 区分拟合与验证损失
plt.grid(True, alpha=0.3)  # 便于读取各轮损失差距

plt.tight_layout()  # 防止两项训练诊断的轴标签相互遮挡
plt.show()  # 输出共同训练历史的两项诊断
左图为训练与验证准确率随轮次变化,右图为训练与验证损失随轮次变化;两图不包含测试集选模轨迹。
图 10.2: MNIST CNN 的训练期与验证期准确率、损失曲线

测试准确率与损失由 列表 10.3 现场报告。阅读 图 10.2 时应同时关注训练—验证差距、随机初始化和数据集难度;本例只说明该架构在 MNIST 上的一个可复现实验流程,不把一次运行的高分外推到更复杂图像任务。

补充说明:在深度学习里,准确率曲线和损失曲线分别告诉你什么

阅读训练图时,不能把“准确率高”直接等同于概率预测已经可靠。准确率曲线和损失曲线传递的信息并不相同。

  • 准确率曲线回答的是:在当前阈值和当前任务定义下,模型判对了多少样本;
  • 损失曲线回答的是:模型分配给真实类别的概率有多大,以及错误类别上的高置信概率受到多大惩罚。

例如,对同一个分类正确的观测,一个模型给真实类别分配 0.99 概率,另一个分配 0.55 概率;两者对该观测的准确率贡献相同,但前者的交叉熵更低。这个单点比较不能证明前者整体更稳定或校准更好:若 0.99 概率给了错误类别,交叉熵会施加更大惩罚。概率可靠性还需要结合验证损失与校准证据判断。

因此,在深度学习训练中更完整的阅读顺序是:

  1. 先看训练损失是否稳定下降,判断优化是否正常进行;
  2. 再看验证损失是否同步下降,判断是否开始过拟合;
  3. 最后看准确率是否达到业务可接受水平。

对 CNN 来说,训练曲线还可帮助区分拟合不足与过拟合。如果训练准确率和验证准确率都低,可能需要检查优化、表示能力或数据质量;如果训练准确率很高而验证准确率明显较低,则提供了过拟合的诊断线索,但仍应结合损失曲线与重复运行判断。

计算预算与模型选择

深度学习模型尤其是 CNN 可能需要较多训练时间和内存。GPU 可以缩短计算时间,却不会改变训练—验证—测试的证据边界。网络深度、滤波器数、正则化、数据增强与最大训练轮数都是候选设计:应在训练期内部用同一验证规则比较,而不是无条件增加容量。本例最多训练二十轮,并在验证损失不再改善时按事前规则早停和恢复最佳权重;模型与停止轮次锁定后,测试集只评价一次。二十轮只是教材复算的计算上限,不能据此推荐其他任务的训练长度。

10.7 循环神经网络(RNN)

循环神经网络(RNN)是专门用于处理序列数据的神经网络,如时间序列、文本、语音等。与CNN处理空间结构不同,RNN处理时间序列结构。

10.7.1 RNN的基本结构

在一个简单的 RNN 中,输入序列记为 \(x_1,\ldots,x_T\),其中 \(x_t\in\mathbb R^p\);隐藏状态统一记为 \(h_t\in\mathbb R^K\)。第 \(k\) 个隐藏分量为

\[ h_{tk}=g\left(b_k+\sum_{j=1}^{p}W_{kj}x_{tj}+\sum_{s=1}^{K}U_{ks}h_{t-1,s}\right),\qquad k=1,\ldots,K. \tag{10.14}\]

若每个时点都需要一个标量预测,则

\[ \widehat y_t=\beta_0+\sum_{k=1}^{K}\beta_kh_{tk}. \tag{10.15}\]

对只使用末时点输出的标量回归,观测响应为 \(y\),平方损失为

\[ \mathcal L(y,\widehat y_T)=(y-\widehat y_T)^2. \tag{10.16}\]

这里 \(W\in\mathbb R^{K\times p}\)、\(U\in\mathbb R^{K\times K}\)、\(\beta\in\mathbb R^K\);\(T\) 是序列长度,\(K\) 是隐藏宽度,二者不可混用。

图 10.3 显示了RNN的结构。

当输入具有明确顺序,例如文本中的词、语音帧或逐日价格时,普通前馈网络不会显式保存相邻时点之间的状态。循环神经网络(RNN)通过隐藏状态递推表示这种序列依赖。 在 图 10.3 的左图中,自循环方框表示同一状态更新规则在各时间步复用;右图把它按时间展开。每个 \(x_t\) 与上一隐藏状态 \(h_{t-1}\) 共同决定当前状态 \(h_t\)。 按时间轴展开后,隐藏状态在相邻时点之间递推,把过去的信息与当前观测共同用于输出。这种状态传递为建模序列依赖提供了结构基础,但并不保证模型能保留任意长的上下文。

下面把同一个 RNN 分别画成紧凑表示和按时间展开的表示,以便对照递推方程中的输入、隐藏状态与输出。

展开制图实现
import numpy as np  # 提供RNN结构图所需的规则坐标
import matplotlib.pyplot as plt  # 绘制紧凑与时间展开两种RNN表示

# 并排对照同一递归计算图的紧凑表示与时间展开表示
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 8))  # 为四个时间步的展开连线预留横向空间

# 左图:紧凑表示 — 将RNN表示为单个循环模块
ax1.set_xlim(0, 10)  # 容纳输入、循环模块和最终输出
ax1.set_ylim(0, 10)  # 容纳四个纵向输入时点
ax1.axis('off')  # 去除无统计含义的刻度以突出单步递归关系

# 绘制输入序列(4个输入节点X1-X4)
for i in range(4):  # 遍历4个输入时间步
    circle = plt.Circle((1, 8 - i * 2), 0.4, facecolor='#3498db',  # 创建蓝色输入节点圆圈
                       edgecolor='black', linewidth=2)  # 用黑色边框保持输入节点可辨
    ax1.add_patch(circle)  # 将输入节点添加到左图
    ax1.text(1, 8 - i * 2, f'$X_{i+1}$', ha='center', va='center',  # 标注输入节点名称
            fontsize=11, fontweight='bold')  # 将时点编号置于输入节点中心
    if i < 3:  # 在非最后一个节点之间绘制箭头
        ax1.arrow(1.5, 8 - i * 2, 0, -1.2, head_width=0.15,  # 绘制向下的箭头连接
                 head_length=0.2, fc='black', ec='black')  # 用向下箭头表示序列先后

# 绘制RNN循环层(红色矩形模块)
rect = plt.Rectangle((4, 6), 2, 3, facecolor='#e74c3c',  # 创建红色RNN层矩形
                     edgecolor='black', linewidth=2)  # 用黑色边框突出循环模块边界
ax1.add_patch(rect)  # 将RNN层矩形添加到左图
ax1.text(5, 7.5, 'RNN', ha='center', va='center', fontsize=12,  # 标注RNN层名称
        color='white', weight='bold')  # 在红色模块中心标记共享RNN状态更新

# 绘制输入节点到RNN层的连接线
for i in range(4):  # 每个输入节点都连接到RNN层
    ax1.plot([1.5, 4], [8 - i * 2, 7.5 - i * 0.5], 'k-',  # 绘制连接线
             linewidth=1.5, alpha=0.5)  # 用半透明连线避免遮挡节点

# 绘制输出节点(绿色圆圈)
circle = plt.Circle((9, 5), 0.4, facecolor='#2ecc71',  # 创建绿色输出节点圆圈
                   edgecolor='black', linewidth=2)  # 用边框区分预测节点与背景
ax1.add_patch(circle)  # 将输出节点添加到左图
ax1.text(9, 5, '$\\hat y_T$', ha='center', va='center', fontsize=11, fontweight='bold')  # 标注末时点预测而非观测响应
ax1.plot([6, 8.6], [7.5, 5], 'k-', linewidth=1.5, alpha=0.5)  # RNN层到输出节点的连接线

ax1.set_title('紧凑表示', fontsize=14, fontname='Source Han Serif SC')  # 标识未展开的单步递归依赖

# 右图:展开形式 — 展示各时间步的详细连接
ax2.set_xlim(0, 16)  # 容纳四个横向展开的时间步
ax2.set_ylim(0, 10)  # 对齐输入、隐藏状态与输出三层节点
ax2.axis('off')  # 去除无统计含义的刻度以突出跨时点状态传递

# 绘制展开的RNN:4个时间步的完整结构
for i in range(4):  # 遍历4个时间步
    # 绘制输入节点(蓝色圆圈)
    circle = plt.Circle((1 + i * 3.5, 8), 0.4, facecolor='#3498db',  # 创建蓝色输入节点
                       edgecolor='black', linewidth=2)  # 统一展开图输入节点样式
    ax2.add_patch(circle)  # 将输入节点添加到右图
    ax2.text(1 + i * 3.5, 8, f'$X_{i+1}$', ha='center', va='center',  # 标注输入节点名称
            fontsize=11, fontweight='bold')  # 在节点中心标明对应输入时点

    # 绘制隐藏层(红色矩形)
    rect = plt.Rectangle((3 + i * 3.5, 5), 2, 3, facecolor='#e74c3c',  # 创建红色隐藏层矩形
                         edgecolor='black', linewidth=2)  # 用边框区分各时点隐藏状态
    ax2.add_patch(rect)  # 将隐藏层矩形添加到右图
    ax2.text(4 + i * 3.5, 6.5, f'$h_{i+1}$', ha='center', va='center',  # 标注统一记号下的隐藏状态
            fontsize=12, color='white', weight='bold')  # 在红色框内标明隐藏状态下标

    # 绘制输出节点(绿色小圆圈)
    circle = plt.Circle((7 + i * 3.5, 8), 0.3, facecolor='#2ecc71',  # 创建绿色输出节点
                       edgecolor='black', linewidth=2)  # 保持各时点输出节点样式一致
    ax2.add_patch(circle)  # 将输出节点添加到右图
    ax2.text(7 + i * 3.5, 8, f'$O_{i+1}$', ha='center', va='center',  # 标注输出节点名称
            fontsize=10, fontweight='bold')  # 在输出节点中心标明时点下标

    # 绘制时间步之间的连接线
    if i < 3:  # 非最后一个时间步
        ax2.plot([1.5 + i * 3.5, 3 + i * 3.5], [8, 7], 'k-',  # 输入到隐藏层的连接
                 linewidth=1.5, alpha=0.5)  # 用细线表示当前输入进入隐藏状态
        ax2.plot([5 + i * 3.5, 6.7 + i * 3.5], [6.5, 8], 'k-',  # 隐藏层到输出的连接
                 linewidth=1.5, alpha=0.5)  # 用细线表示隐藏状态生成当期输出
        ax2.plot([5 + i * 3.5, 3 + (i+1) * 3.5], [6, 7], 'r-',  # 隐藏层之间的递归连接(红色)
                 linewidth=2)  # 用较粗红线突出跨时点递归依赖

    # 最后一个时间步的最终输出连接
    if i == 3:  # 仅在最后一个时间步绘制
        ax2.plot([7 + i * 3.5, 13.5], [8, 5], 'k-',  # 连接到最终输出节点
                 linewidth=2)  # 用粗线连接末时点表示与最终预测
        circle = plt.Circle((14, 5), 0.4, facecolor='#f39c12',  # 创建黄色最终输出节点
                           edgecolor='black', linewidth=2)  # 用橙色节点区分最终预测
        ax2.add_patch(circle)  # 将最终输出节点添加到右图
        ax2.text(14, 5, '$\\hat y_T$', ha='center', va='center',  # 标注最终预测而非观测响应
                fontsize=12, fontweight='bold')  # 在末节点标记最终预测而非观测值

ax2.set_title('展开形式', fontsize=14, fontname='Source Han Serif SC')  # 标识跨时点共享参数与状态传递

plt.suptitle('循环神经网络(RNN)结构', fontsize=16, fontname='Source Han Serif SC', y=0.95)  # 概括紧凑表示与时间展开的对应关系
plt.tight_layout()  # 防止节点说明与两种结构视图相互遮挡
plt.show()  # 输出递归方程与展开连线的视觉对照
左图以自循环方框表示 RNN,右图将四个时间步横向展开,箭头连接相邻隐藏状态及各步输入输出。
图 10.3: 循环神经网络(RNN)的结构示意图。左侧是紧凑表示,右侧是展开形式。网络按顺序处理输入序列,每个时间步的隐藏层接收前一步的激活并产生当前激活。

阅读 图 10.3 时,先沿右图由左向右追踪红色隐藏状态:每个 \(h_t\) 同时接收当前输入 \(x_t\) 与上一状态 \(h_{t-1}\),因此参数在时间步之间共享。图中的连线只说明递推依赖,不表示早期信息必然被完整保留;是否能学到长程关系仍取决于优化、序列长度与数据证据。

10.7.2 长短期记忆网络(LSTM)

标准 RNN 在长序列上容易发生梯度消失或爆炸,使网络难以学习长期依赖。长短期记忆网络(LSTM)引入门控与加法细胞状态通路来改善误差传递 (Hochreiter 和 Schmidhuber 1997年);这只能缓解困难,并不保证任意长期依赖一定可学、梯度一定稳定或样本外预测一定改善。

令输入为 \(x_t\in\mathbb R^p\),隐藏状态为 \(h_t\in\mathbb R^K\),细胞状态为 \(c_t\in\mathbb R^K\)。LSTM 的三个门、候选状态与更新方程为

\[ f_t=\sigma(W_fx_t+U_fh_{t-1}+b_f),\qquad i_t=\sigma(W_ix_t+U_ih_{t-1}+b_i), \tag{10.17}\]

\[ \widetilde c_t=\tanh(W_cx_t+U_ch_{t-1}+b_c),\qquad c_t=f_t\odot c_{t-1}+i_t\odot\widetilde c_t, \tag{10.18}\]

\[ o_t=\sigma(W_ox_t+U_oh_{t-1}+b_o),\qquad h_t=o_t\odot\tanh(c_t). \tag{10.19}\]

其中 \(W_\bullet\in\mathbb R^{K\times p}\)、\(U_\bullet\in\mathbb R^{K\times K}\)、\(b_\bullet\in\mathbb R^K\),\(\odot\) 表示逐元素乘法。三个门的作用分别是

  1. 遗忘门:控制上一细胞状态沿直接通路保留多少。
  2. 输入门:控制候选信息向当前细胞状态写入多少。
  3. 输出门:控制当前细胞状态向隐藏状态暴露多少。

提示:为什么 LSTM 只能说“缓解”长期依赖困难?

由 式 10.18 可见,在固定门值并暂不计经 \(h_{t-1}\) 返回各门的其他路径时,细胞状态的直接 Jacobian 为

\[ \left.\frac{\partial c_t}{\partial c_{t-1}}\right|_{\mathrm{direct}}=\operatorname{diag}(f_t), \qquad \left.\frac{\partial c_t}{\partial c_s}\right|_{\mathrm{direct}}=\prod_{k=s+1}^{t}\operatorname{diag}(f_k). \tag{10.20}\]

因此,只有相关遗忘门分量在多个时点都接近 1 时,这条直接通路才近似保留梯度;它并不产生普遍恒定的误差流。完整 Jacobian 还包含隐藏状态影响各门的递归路径。门饱和、遗忘门持续小于 1、优化不良、序列过长或训练信号不足时,梯度仍可能衰减或爆炸,模型也可能失败。门控机制允许网络

  1. 遗忘: 通过遗忘门 \(f_t\) 决定保留多少旧信息
  2. 更新: 通过输入门 \(i_t\) 决定添加多少新信息
  3. 输出: 通过输出门 \(o_t\) 决定输出多少信息

这种设计使 LSTM 能选择性保留或遗忘信息,因此比普通 RNN 更有机会学习较长依赖;它是结构偏好,不是性能保证。文本、语音或金融时间序列中仍须用预先固定验证集和同键简单基线检验增量证据。

10.8 RNN 的时间反向传播

BPTT(Backpropagation Through Time)把 小节 10.7.1 的递归计算图按时间展开。对简单 RNN \(h_t=\phi(Wx_t+Uh_{t-1}+b)\),令 \(D_t=\operatorname{diag}\{\phi'(Wx_t+Uh_{t-1}+b)\}\),则相邻状态的 Jacobian 为

\[ J_t=\frac{\partial h_t}{\partial h_{t-1}}=D_tU. \tag{10.21}\]

从较晚时点 \(t\) 回传到较早时点 \(s\) 的梯度包含时变乘积 \(J_{s+1}^{\mathsf T}\cdots J_t^{\mathsf T}\),而不是单独重复乘同一个 \(U\)。乘积的奇异值可能衰减或增大;只考察 \(U\) 的谱半径,是线性激活或导数近似恒定时的简化直觉。LSTM 通过 式 10.20 的直接细胞状态通路缓解这一困难,但遗忘门乘积不保证梯度恒定。实际训练仍须执行 小节 10.5.4 的共同诊断。

10.9 文档分类应用

本节说明财经文本分类对数据和验证设计的要求;当前仓库没有可确认许可与标签口径的本地中文文本快照,因此不报告文本模型性能。

10.9.1 词嵌入

在自然语言处理中我们需要将文本转换为数值表示。简单的方法是词袋模型(Bag of Words),但更好的方法是使用词嵌入(Word Embeddings)。

词嵌入将每个词表示为一个低维实数向量使得语义相似的词在嵌入空间中距离更近。常见的预训练词嵌入包括Word2Vec和GloVe。

10.9.2 财经文本分类的学习边界

文本分类可以用于上市公司公告风险提示、财经新闻主题识别和客户反馈归类。词嵌入把离散词项映射为连续向量,神经网络再从词序列中学习组合表示。但只有随机词索引和随机标签的数据不能展示语义学习,也不能支持任何商业结论,因此本节不再把这种张量练习称为情感分析案例。

一个合格的财经文本案例必须说明文本来源、标签形成过程、时间切分、类别不平衡和错误成本,并与词频逻辑回归等简单基线比较。当前仓库没有可确认许可和标签口径的本地中文文本快照,本节仅解释方法与数据要求;可运行的核心应用转到下一节真实 A 股时间序列。取得合规文本数据后,应先锁定训练期词表,再在未来文本上评价,避免把后期词汇和标签规则泄漏到训练阶段。

10.10 时间序列预测应用

RNN/LSTM 可以作为时间序列预测的候选结构。下面使用海康威视历史收盘价检验 LSTM 能否改善下一交易日价格预测;响应始终记为 \(y\),预测记为 \(\widehat y\),结论由同一时间切分上的简单基线决定。

10.10.1 金融时间序列示例

下面把序列模型放入噪声较强、分布可能变化的金融时间序列任务,检验带记忆结构能否在相同时间切分上改善海康威视下一交易日价格预测。 代码第 3 步的 create_sequences 把一维价格序列转换为形状为 (样本数, 60, 1) 的三维张量:每个样本包含连续 60 个交易日,目标是紧随其后的第 61 日价格。滑动窗口只是一种输入组织方式,不保证这 60 日包含稳定的预测信息。 本例把 LSTM 作为一种候选序列模型,而不预设它是默认优选方案。堆叠层允许模型表达较长依赖,Dropout(0.2) 只是训练期正则化机制;两者都不保证样本外改进。最终判断必须来自同一锁定测试期上相对于 last-value 和线性基线的损失。

表 10.1: LSTM 时间序列样本准备核对
import numpy as np  # 构造时序窗口并处理模型输出数组
import pandas as pd  # 整理后复权行情与预测日期
import matplotlib.pyplot as plt  # 绘制锁定测试期预测与训练损失
from sklearn.preprocessing import MinMaxScaler  # 仅用训练期价格估计缩放参数
from tensorflow import keras  # 提供序列模型训练所需的Keras接口
from tensorflow.keras import layers, models  # 声明LSTM、Dropout与全连接层

# 1. 加载海康威视股价数据
import os  # 将在线教材的固定数据根同步给本章后续独立代码块
from pathlib import Path  # 使用跨平台路径对象解析显式数据根
BOOK_DATA_DIR = Path('/home/ubuntu/r2_data_mount/data').resolve()  # 明文定义在线教材的BOOK_DATA_DIR绝对路径
DATA_DIR = BOOK_DATA_DIR  # 保留本章后续代码使用的数据根名称
os.environ['BOOK_DATA_DIR'] = str(BOOK_DATA_DIR)  # 为本章后续习题入口登记同一路径
assert DATA_DIR.is_dir(), f'BOOK_DATA_DIR 不存在或不是目录: {DATA_DIR}'  # 在读取前验证数据根
path = DATA_DIR / 'stock' / 'stock_price_post_adjusted.h5'  # 构建后复权股价数据文件路径
assert path.is_file(), f'缺少后复权行情文件: {path};请检查 BOOK_DATA_DIR'  # 空目录时指出确切所需文件
haikang_company_id = '002415.XSHE'  # 固定教学案例公司,避免按结果事后选择证券
stock_price_history = pd.read_hdf(path, where=f'order_book_id={haikang_company_id!r}', columns=['close']).reset_index()  # 在存储层只读取海康威视收盘价以限制内存占用
assert {'order_book_id', 'date', 'close'}.issubset(stock_price_history.columns), '选择性读取结果缺少公司、日期或收盘价字段'  # 核验时序任务的数据身份
assert set(stock_price_history['order_book_id'].dropna().unique()) == {'002415.XSHE'}  # 阻止查询条件失效后混入其他公司
stock_price_history['date'] = pd.to_datetime(stock_price_history['date'], errors='coerce')  # 统一交易日为可排序日期
stock_price_history['close'] = pd.to_numeric(stock_price_history['close'], errors='coerce')  # 统一价格为可检验数值
assert stock_price_history['date'].notna().all()  # 阻止无效日期进入时序窗口
assert np.isfinite(stock_price_history['close']).all() and stock_price_history['close'].gt(0).all()  # 要求价格有限且为正

选择性读取通过字段、公司、日期和价格检查后,再固定时间顺序与训练—验证—测试边界。

haikang_data = stock_price_history.sort_values('date').reset_index(drop=True)  # 形成唯一公司的有序价格路径

closing_prices = haikang_data['close'].values.reshape(-1, 1)  # 提取收盘价并转为二维数组

# 2. 先固定拟合、验证和测试的原始时间边界,再仅用拟合期估计缩放参数
raw_split_index = int(len(closing_prices) * 0.8)  # 最后20%原始观测作为锁定测试期
raw_validation_index = int(raw_split_index * 0.9)  # 将测试前时期的最后10%留作按时间排序的验证期
scaler = MinMaxScaler(feature_range=(0, 1))  # 用统一尺度训练序列模型并比较平方误差
scaler.fit(closing_prices[:raw_validation_index])  # 缩放参数不读取验证期与锁定测试期价格
scaled_closing_prices = scaler.transform(closing_prices)  # 用拟合期参数转换验证期和测试期而不重估

接下来定义序列数据构造函数,将一维时间序列切割为固定窗口长度的样本对,并按目标日期划分拟合集、验证集与锁定测试集。

# 3. 准备序列数据:用滑动窗口法将时间序列转为监督学习格式
def create_sequences(data, length_of_sequence):  # 把滞后价格映射为下一交易日监督学习样本
    """将时间序列转换为固定窗口特征与下一期目标。"""  # 明确函数输出的监督学习语义
    sequence_features, sequence_targets = [], []  # 分别累积历史窗口与其下一时点价格目标
    for i in range(len(data) - length_of_sequence):  # 滑动窗口遍历
        sequence_features.append(data[i:i + length_of_sequence])  # 截取窗口内的数据作为特征
        sequence_targets.append(data[i + length_of_sequence])  # 窗口后一天的值作为目标
    return np.array(sequence_features), np.array(sequence_targets)  # 转为numpy数组返回

seq_length = 60  # 使用过去60个交易日预测下一天
sequence_features, sequence_targets = create_sequences(scaled_closing_prices, seq_length)  # 构造序列数据

# 4. 依据目标日期顺序划分拟合、验证和测试窗口
validation_start = raw_validation_index - seq_length  # 使验证目标从原始验证边界开始
test_start = raw_split_index - seq_length  # 使测试目标从原始测试边界开始
train_sequences = sequence_features[:validation_start]  # 拟合窗口严格早于验证目标期
validation_sequences = sequence_features[validation_start:test_start]  # 验证窗口保持在测试边界之前
test_sequences = sequence_features[test_start:]  # 锁定最终20%目标日期供一次评价
train_targets = sequence_targets[:validation_start]  # 拟合响应与拟合窗口一一对应
validation_targets = sequence_targets[validation_start:test_start]  # 验证响应仅用于早停选轮
test_targets = sequence_targets[test_start:]  # 测试响应不进入训练与选轮
assert min(len(train_targets), len(validation_targets), len(test_targets)) > 0  # 防止短样本产生空分区并破坏时序评价

下面把双层 LSTM 作为候选模型:门控与细胞状态为较长依赖提供结构通路,但是否改善预测仍由验证早停和同测试期基线决定。

# 5. 构建LSTM模型(双层LSTM + Dropout正则化)
model = models.Sequential([  # 按序列表示与回归输出顺序组合LSTM候选
    layers.LSTM(50, return_sequences=True, input_shape=(seq_length, 1)),  # 第一层LSTM:50个单元,返回完整序列
    layers.Dropout(0.2),  # Dropout层:随机丢弃20%的神经元防止过拟合
    layers.LSTM(50, return_sequences=False),  # 第二层LSTM:50个单元,只返回最后时间步
    layers.Dropout(0.2),  # Dropout层:再次随机丢弃20%
    layers.Dense(25),  # 全连接层:25个神经元
    layers.Dense(1)  # 输出层:预测1个值(下一天收盘价)
])  # 完成下一交易日价格的序列回归网络

model.compile(optimizer='adam', loss='mean_squared_error')  # 编译模型:Adam优化器 + MSE损失

# 在明确上限内仅依据测试前的时序验证损失锁定停止轮次
lstm_max_epochs = 20  # 限定课堂计算预算并为早停提供有限搜索范围
lstm_early_stopping = keras.callbacks.EarlyStopping(monitor='val_loss', patience=3, restore_best_weights=True)  # 验证损失连续三轮不改善即恢复最佳权重
print('开始训练LSTM模型...')  # 标记按时间验证的训练阶段开始
history = model.fit(train_sequences, train_targets, epochs=lstm_max_epochs,  # 只在拟合期窗口更新参数
                    batch_size=64, validation_data=(validation_sequences, validation_targets),  # 以紧邻拟合期的未来尾段选轮
                    callbacks=[lstm_early_stopping], shuffle=False, verbose=1)  # 禁止打乱序列并执行验证早停
lstm_best_validation_epoch = int(np.argmin(history.history['val_loss']) + 1)  # 按最小验证损失锁定停止轮次
print(f'验证损失最佳轮次(已恢复该轮权重): {lstm_best_validation_epoch}/{lstm_max_epochs}')  # 在测试访问前报告选择结果
开始训练LSTM模型...
Epoch 1/20
/home/ubuntu/miniconda3/envs/peter/lib/python3.10/site-packages/keras/src/layers/rnn/rnn.py:199: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.
  super().__init__(**kwargs)
 1/42 ━━━━━━━━━━━━━━━━━━━━ 59s 1s/step - loss: 2.0094e-04

 3/42 ━━━━━━━━━━━━━━━━━━━━ 1s 26ms/step - loss: 1.7062e-04

 5/42 ━━━━━━━━━━━━━━━━━━━━ 0s 26ms/step - loss: 1.5703e-04

 7/42 ━━━━━━━━━━━━━━━━━━━━ 0s 26ms/step - loss: 1.4828e-04

10/42 ━━━━━━━━━━━━━━━━━━━━ 0s 26ms/step - loss: 1.6977e-04

12/42 ━━━━━━━━━━━━━━━━━━━━ 0s 26ms/step - loss: 1.8775e-04

15/42 ━━━━━━━━━━━━━━━━━━━━ 0s 26ms/step - loss: 2.2731e-04

18/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 2.8734e-04

20/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 3.5963e-04

23/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 4.4171e-04

26/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 5.0079e-04

29/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 5.6949e-04

31/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 6.3638e-04

34/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 7.3679e-04

36/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 8.1907e-04

39/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 9.5339e-04

41/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0011    

42/42 ━━━━━━━━━━━━━━━━━━━━ 3s 31ms/step - loss: 0.0036 - val_loss: 0.0254

Epoch 2/20


 1/42 ━━━━━━━━━━━━━━━━━━━━ 1s 37ms/step - loss: 0.0586

 4/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0524

 6/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0479

 8/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0437

11/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0383

14/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0342

16/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0319

18/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0301

21/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0280

24/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0262

26/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0253

28/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0245

30/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0239

32/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0234

34/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0229

36/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0224

38/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0220

40/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0216

42/42 ━━━━━━━━━━━━━━━━━━━━ 1s 27ms/step - loss: 0.0166 - val_loss: 0.0025

Epoch 3/20


 1/42 ━━━━━━━━━━━━━━━━━━━━ 1s 37ms/step - loss: 0.0226

 3/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0235

 6/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0233

 8/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0224

10/42 ━━━━━━━━━━━━━━━━━━━━ 0s 26ms/step - loss: 0.0211

12/42 ━━━━━━━━━━━━━━━━━━━━ 0s 26ms/step - loss: 0.0198

15/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0182

17/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0173

20/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0163

22/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0157

25/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0149

28/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0143

30/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0140

33/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0136

35/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0135

38/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0133

41/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0131

42/42 ━━━━━━━━━━━━━━━━━━━━ 1s 27ms/step - loss: 0.0115 - val_loss: 0.0026

Epoch 4/20


 1/42 ━━━━━━━━━━━━━━━━━━━━ 1s 37ms/step - loss: 7.6926e-04

 3/42 ━━━━━━━━━━━━━━━━━━━━ 1s 26ms/step - loss: 5.5451e-04

 6/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 4.3859e-04

 9/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 4.1707e-04

12/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 4.1031e-04

14/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 4.0469e-04

16/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 3.9948e-04

19/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 3.9406e-04

21/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 3.9212e-04

23/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 3.8985e-04

25/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 3.8894e-04

27/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 4.0714e-04

29/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 4.6234e-04

32/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 5.5667e-04

35/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 6.3867e-04

38/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 7.1794e-04

41/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 8.1032e-04

42/42 ━━━━━━━━━━━━━━━━━━━━ 1s 27ms/step - loss: 0.0023 - val_loss: 0.0017

Epoch 5/20


 1/42 ━━━━━━━━━━━━━━━━━━━━ 1s 38ms/step - loss: 0.0040

 3/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0039

 5/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0036

 8/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0032

10/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0030

13/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0027

16/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0024

18/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0023

21/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0021

24/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0020

26/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0019

28/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0018

30/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0018

33/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0018

36/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0018

38/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0018

41/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0018

42/42 ━━━━━━━━━━━━━━━━━━━━ 1s 27ms/step - loss: 0.0026 - val_loss: 0.0031

Epoch 6/20


 1/42 ━━━━━━━━━━━━━━━━━━━━ 1s 38ms/step - loss: 0.0010

 3/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 8.3142e-04

 5/42 ━━━━━━━━━━━━━━━━━━━━ 0s 26ms/step - loss: 7.2911e-04

 8/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 6.2305e-04

11/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 5.7111e-04

13/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 5.7536e-04

16/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 5.8926e-04

19/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 6.0512e-04

22/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 6.3116e-04

24/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 6.4041e-04

27/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 6.6123e-04

29/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 7.1175e-04

32/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 7.9434e-04

35/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 8.9332e-04

37/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 9.7220e-04

39/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0011    

42/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0012

42/42 ━━━━━━━━━━━━━━━━━━━━ 1s 27ms/step - loss: 0.0031 - val_loss: 0.0027

Epoch 7/20


 1/42 ━━━━━━━━━━━━━━━━━━━━ 1s 40ms/step - loss: 0.0064

 3/42 ━━━━━━━━━━━━━━━━━━━━ 1s 26ms/step - loss: 0.0054

 5/42 ━━━━━━━━━━━━━━━━━━━━ 0s 26ms/step - loss: 0.0047

 7/42 ━━━━━━━━━━━━━━━━━━━━ 0s 26ms/step - loss: 0.0041

 9/42 ━━━━━━━━━━━━━━━━━━━━ 0s 26ms/step - loss: 0.0036

11/42 ━━━━━━━━━━━━━━━━━━━━ 0s 27ms/step - loss: 0.0033

13/42 ━━━━━━━━━━━━━━━━━━━━ 0s 26ms/step - loss: 0.0031

15/42 ━━━━━━━━━━━━━━━━━━━━ 0s 26ms/step - loss: 0.0029

17/42 ━━━━━━━━━━━━━━━━━━━━ 0s 26ms/step - loss: 0.0028

19/42 ━━━━━━━━━━━━━━━━━━━━ 0s 27ms/step - loss: 0.0026

21/42 ━━━━━━━━━━━━━━━━━━━━ 0s 27ms/step - loss: 0.0025

23/42 ━━━━━━━━━━━━━━━━━━━━ 0s 28ms/step - loss: 0.0024

25/42 ━━━━━━━━━━━━━━━━━━━━ 0s 28ms/step - loss: 0.0023

27/42 ━━━━━━━━━━━━━━━━━━━━ 0s 28ms/step - loss: 0.0023

29/42 ━━━━━━━━━━━━━━━━━━━━ 0s 27ms/step - loss: 0.0022

31/42 ━━━━━━━━━━━━━━━━━━━━ 0s 27ms/step - loss: 0.0021

33/42 ━━━━━━━━━━━━━━━━━━━━ 0s 27ms/step - loss: 0.0021

35/42 ━━━━━━━━━━━━━━━━━━━━ 0s 27ms/step - loss: 0.0021

37/42 ━━━━━━━━━━━━━━━━━━━━ 0s 27ms/step - loss: 0.0020

40/42 ━━━━━━━━━━━━━━━━━━━━ 0s 27ms/step - loss: 0.0020

42/42 ━━━━━━━━━━━━━━━━━━━━ 1s 29ms/step - loss: 0.0019 - val_loss: 0.0014

Epoch 8/20


 1/42 ━━━━━━━━━━━━━━━━━━━━ 1s 37ms/step - loss: 2.5186e-04

 3/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 2.2323e-04

 5/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 2.1470e-04

 8/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 2.0406e-04

11/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 1.9433e-04

14/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 1.8940e-04

17/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 1.8554e-04

19/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 1.8819e-04

21/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 1.9363e-04

24/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 1.9893e-04

26/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 2.0185e-04

29/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 2.2261e-04

31/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 2.4571e-04

34/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 2.8271e-04

36/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 3.0871e-04

38/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 3.3619e-04

40/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 3.7006e-04

42/42 ━━━━━━━━━━━━━━━━━━━━ 1s 27ms/step - loss: 0.0014 - val_loss: 0.0014

Epoch 9/20


 1/42 ━━━━━━━━━━━━━━━━━━━━ 1s 42ms/step - loss: 0.0015

 3/42 ━━━━━━━━━━━━━━━━━━━━ 1s 26ms/step - loss: 0.0014

 5/42 ━━━━━━━━━━━━━━━━━━━━ 0s 27ms/step - loss: 0.0013

 7/42 ━━━━━━━━━━━━━━━━━━━━ 0s 27ms/step - loss: 0.0011

 9/42 ━━━━━━━━━━━━━━━━━━━━ 0s 26ms/step - loss: 0.0010

11/42 ━━━━━━━━━━━━━━━━━━━━ 0s 27ms/step - loss: 9.5087e-04

13/42 ━━━━━━━━━━━━━━━━━━━━ 0s 27ms/step - loss: 9.0067e-04

15/42 ━━━━━━━━━━━━━━━━━━━━ 0s 26ms/step - loss: 8.6658e-04

17/42 ━━━━━━━━━━━━━━━━━━━━ 0s 26ms/step - loss: 8.3800e-04

19/42 ━━━━━━━━━━━━━━━━━━━━ 0s 26ms/step - loss: 8.1616e-04

21/42 ━━━━━━━━━━━━━━━━━━━━ 0s 27ms/step - loss: 7.9825e-04

23/42 ━━━━━━━━━━━━━━━━━━━━ 0s 27ms/step - loss: 7.7956e-04

25/42 ━━━━━━━━━━━━━━━━━━━━ 0s 27ms/step - loss: 7.6067e-04

27/42 ━━━━━━━━━━━━━━━━━━━━ 0s 27ms/step - loss: 7.4562e-04

29/42 ━━━━━━━━━━━━━━━━━━━━ 0s 27ms/step - loss: 7.3993e-04

31/42 ━━━━━━━━━━━━━━━━━━━━ 0s 27ms/step - loss: 7.4014e-04

33/42 ━━━━━━━━━━━━━━━━━━━━ 0s 27ms/step - loss: 7.4296e-04

35/42 ━━━━━━━━━━━━━━━━━━━━ 0s 27ms/step - loss: 7.4844e-04

37/42 ━━━━━━━━━━━━━━━━━━━━ 0s 27ms/step - loss: 7.5703e-04

39/42 ━━━━━━━━━━━━━━━━━━━━ 0s 27ms/step - loss: 7.6913e-04

40/42 ━━━━━━━━━━━━━━━━━━━━ 0s 28ms/step - loss: 7.7892e-04

42/42 ━━━━━━━━━━━━━━━━━━━━ 0s 28ms/step - loss: 8.0670e-04

42/42 ━━━━━━━━━━━━━━━━━━━━ 1s 30ms/step - loss: 0.0014 - val_loss: 0.0019

Epoch 10/20


 1/42 ━━━━━━━━━━━━━━━━━━━━ 1s 41ms/step - loss: 0.0020

 3/42 ━━━━━━━━━━━━━━━━━━━━ 1s 28ms/step - loss: 0.0017

 5/42 ━━━━━━━━━━━━━━━━━━━━ 1s 28ms/step - loss: 0.0015

 7/42 ━━━━━━━━━━━━━━━━━━━━ 0s 28ms/step - loss: 0.0013

 9/42 ━━━━━━━━━━━━━━━━━━━━ 0s 27ms/step - loss: 0.0012

11/42 ━━━━━━━━━━━━━━━━━━━━ 0s 27ms/step - loss: 0.0011

13/42 ━━━━━━━━━━━━━━━━━━━━ 0s 27ms/step - loss: 0.0010

15/42 ━━━━━━━━━━━━━━━━━━━━ 0s 27ms/step - loss: 9.9196e-04

17/42 ━━━━━━━━━━━━━━━━━━━━ 0s 27ms/step - loss: 9.5232e-04

19/42 ━━━━━━━━━━━━━━━━━━━━ 0s 27ms/step - loss: 9.1930e-04

21/42 ━━━━━━━━━━━━━━━━━━━━ 0s 27ms/step - loss: 8.9050e-04

24/42 ━━━━━━━━━━━━━━━━━━━━ 0s 27ms/step - loss: 8.4905e-04

26/42 ━━━━━━━━━━━━━━━━━━━━ 0s 26ms/step - loss: 8.2408e-04

28/42 ━━━━━━━━━━━━━━━━━━━━ 0s 26ms/step - loss: 8.0602e-04

31/42 ━━━━━━━━━━━━━━━━━━━━ 0s 26ms/step - loss: 7.9399e-04

33/42 ━━━━━━━━━━━━━━━━━━━━ 0s 26ms/step - loss: 7.9002e-04

36/42 ━━━━━━━━━━━━━━━━━━━━ 0s 26ms/step - loss: 7.8901e-04

39/42 ━━━━━━━━━━━━━━━━━━━━ 0s 26ms/step - loss: 7.9389e-04

41/42 ━━━━━━━━━━━━━━━━━━━━ 0s 26ms/step - loss: 8.0801e-04

42/42 ━━━━━━━━━━━━━━━━━━━━ 1s 28ms/step - loss: 0.0013 - val_loss: 9.1130e-04

Epoch 11/20


 1/42 ━━━━━━━━━━━━━━━━━━━━ 1s 36ms/step - loss: 0.0012

 3/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.0011

 5/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 9.3712e-04

 7/42 ━━━━━━━━━━━━━━━━━━━━ 0s 26ms/step - loss: 8.3741e-04

10/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 7.2921e-04

12/42 ━━━━━━━━━━━━━━━━━━━━ 0s 26ms/step - loss: 6.8042e-04

15/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 6.3093e-04

18/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 5.9394e-04

20/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 5.7467e-04

22/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 5.5656e-04

25/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 5.3102e-04

27/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 5.1833e-04

30/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 5.1438e-04

33/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 5.1900e-04

36/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 5.2689e-04

38/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 5.3442e-04

40/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 5.4813e-04

42/42 ━━━━━━━━━━━━━━━━━━━━ 1s 27ms/step - loss: 0.0011 - val_loss: 0.0014

Epoch 12/20


 1/42 ━━━━━━━━━━━━━━━━━━━━ 1s 39ms/step - loss: 3.2673e-04

 4/42 ━━━━━━━━━━━━━━━━━━━━ 0s 24ms/step - loss: 2.9223e-04

 6/42 ━━━━━━━━━━━━━━━━━━━━ 0s 24ms/step - loss: 2.8194e-04

 9/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 2.6713e-04

11/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 2.5916e-04

14/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 2.5424e-04

16/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 2.5129e-04

19/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 2.4870e-04

22/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 2.4829e-04

24/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 2.4659e-04

26/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 2.4486e-04

28/42 ━━━━━━━━━━━━━━━━━━━━ 0s 26ms/step - loss: 2.4869e-04

30/42 ━━━━━━━━━━━━━━━━━━━━ 0s 26ms/step - loss: 2.5948e-04

33/42 ━━━━━━━━━━━━━━━━━━━━ 0s 26ms/step - loss: 2.8083e-04

36/42 ━━━━━━━━━━━━━━━━━━━━ 0s 26ms/step - loss: 3.0679e-04

39/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 3.3769e-04

41/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 3.6813e-04

42/42 ━━━━━━━━━━━━━━━━━━━━ 1s 27ms/step - loss: 0.0011 - val_loss: 0.0020

Epoch 13/20


 1/42 ━━━━━━━━━━━━━━━━━━━━ 1s 38ms/step - loss: 7.8601e-04

 3/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 7.1294e-04

 5/42 ━━━━━━━━━━━━━━━━━━━━ 0s 26ms/step - loss: 6.5190e-04

 7/42 ━━━━━━━━━━━━━━━━━━━━ 0s 26ms/step - loss: 5.9315e-04

10/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 5.3456e-04

13/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 5.0930e-04

16/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 4.9628e-04

19/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 4.8385e-04

22/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 4.7389e-04

25/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 4.6147e-04

28/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 4.5715e-04

31/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 4.6831e-04

33/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 4.7852e-04

35/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 4.9148e-04

38/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 5.1493e-04

41/42 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 5.5065e-04

42/42 ━━━━━━━━━━━━━━━━━━━━ 1s 27ms/step - loss: 0.0013 - val_loss: 0.0014

验证损失最佳轮次(已恢复该轮权重): 10/20

训练与验证损失应从本次 history 对象读取。二十轮只是教学计算上限,不是推荐训练长度;代码已在测试前时期内部按时间保留验证尾段,并按事前早停规则恢复验证损失最低轮次的权重。两条曲线共同下降只说明优化在当前切分上正常进行;是否具有增量预测能力,仍要在选择完成后仅一次检查锁定测试期相对于 last-value 和线性基线的损失。

模型训练完成后,使用测试集进行预测并将归一化的价格还原为原始价格,最后以图表形式展示预测效果。

列表 10.4: LSTM、最近值与线性基线的锁定测试比较准备
predicted_targets = model.predict(test_sequences, verbose=0)  # 用已恢复的最佳验证权重进行唯一一次测试预测
from sklearn.linear_model import LinearRegression  # 构造同一滞后窗口上的简单线性基线
from sklearn.metrics import mean_squared_error  # 用共同尺度比较三种候选的平方误差
naive_targets = test_sequences[:, -1, 0].reshape(-1, 1)  # 以窗口末值作为最近值基线
linear_baseline = LinearRegression()  # 声明不含非线性隐藏层的参照模型
linear_baseline.fit(train_sequences.reshape(len(train_sequences), -1), train_targets)  # 只用训练窗口估计线性关系
linear_targets = linear_baseline.predict(test_sequences.reshape(len(test_sequences), -1))  # 在同一测试键生成线性预测
baseline_comparison = pd.DataFrame({'model': ['last_value', 'linear', 'lstm'], 'test_mse_scaled': [mean_squared_error(test_targets, naive_targets), mean_squared_error(test_targets, linear_targets), mean_squared_error(test_targets, predicted_targets)]})  # 汇总共同测试期误差
print(baseline_comparison)  # 输出复杂模型是否提供增量的直接证据
rescaled_predicted_targets = scaler.inverse_transform(predicted_targets)  # 将LSTM预测还原到价格尺度供作图
rescaled_test_targets = scaler.inverse_transform(test_targets)  # 将共同测试目标还原到价格尺度
        model  test_mse_scaled
0  last_value         0.000093
1      linear         0.000102
2        lstm         0.000466
# 8. 绘制预测结果对比图
plt.figure(figsize=(14, 6))  # 为锁定测试期真实值与预测值提供共同时间轴

# 为了图表清晰,只展示最近365天(约一年)的数据
plot_len = 365  # 展示天数
if len(rescaled_test_targets) > plot_len:  # 如果测试集超过365天
    plot_test_targets = rescaled_test_targets[-plot_len:]  # 取最后365天的真实值
    plot_predicted_targets = rescaled_predicted_targets[-plot_len:]  # 取最后365天的预测值
else:  # 测试集不足365天则全部展示
    plot_test_targets = rescaled_test_targets  # 使用全部真实值
    plot_predicted_targets = rescaled_predicted_targets  # 使用全部预测值

plt.plot(plot_test_targets, label='真实股价', linewidth=2)  # 显示锁定测试期实际价格路径
plt.plot(plot_predicted_targets, label='预测股价', linewidth=2, linestyle='--')  # 用虚线对照LSTM测试预测
plt.title('海康威视股价预测 (LSTM)', fontsize=14)  # 标明图中两条路径属于锁定测试时期
plt.xlabel('测试期交易时点', fontsize=12)  # 将横轴限定为按时间排序的锁定测试观测
plt.ylabel('后复权收盘价', fontsize=12)  # 将纵轴还原为可解释的价格尺度
plt.legend()  # 区分真实价格路径与一次性测试预测路径
plt.grid(True, alpha=0.3)  # 便于读取两条路径在各时点的偏差
plt.show()  # 输出锁定测试期的价格尺度比较

# 9. 训练过程损失曲线可视化
plt.figure(figsize=(12, 4))  # 单独展示LSTM训练过程的优化诊断
plt.plot(history.history['loss'], label='训练集Loss')  # 跟踪拟合样本损失随轮次下降情况
plt.plot(history.history['val_loss'], label='验证集Loss')  # 检查时序验证损失是否出现反弹
plt.title('模型训练损失')  # 标明曲线记录拟合期优化目标而非测试误差
plt.legend()  # 区分参数拟合轨迹与早停选轮依据
plt.show()  # 输出用于判断过拟合起点的损失对照
上图叠加锁定测试期真实价格实线与 LSTM 预测虚线,下图显示训练与验证损失随轮次变化。
(a) LSTM模型对海康威视股价预测结果。虚线为模型预测值,实线为真实股价。
上图叠加锁定测试期真实价格实线与 LSTM 预测虚线,下图显示训练与验证损失随轮次变化。
(b)
图 10.4

图 10.4 的曲线视觉贴合不能证明预测能力,因为价格水平高度持久,简单的 last-value 基线也会呈现相似走势。应以同一锁定测试期的 baseline_comparison 为准;若 LSTM 未稳定优于朴素与线性基线,则报告“没有观察到增量预测证据”。训练损失下降只证明优化过程降低了训练目标,不替代样本外比较。

滞后与突变误差只能依据本次锁定测试输出作条件式诊断:若预测曲线在转折后才移动,且转折附近误差相对平稳区间明显增大,才报告当前模型存在滞后或未捕捉突变;若 last-value 与线性基线呈现相同现象,则不能把它归因于 LSTM 结构。无论图形是否贴合,都应以同一测试期的误差表为主要证据。这个仅使用历史价格的案例也不构成投资建议;宏观、政策、情绪和交易成本等信息均未进入当前模型。

10.11 时间序列模型比较活动

本活动使用正文的海康威视时间序列案例比较三类预测:上一期价格朴素基线、线性滞后模型和小型 LSTM。三者在相同锁定测试日期评价;线性模型与 LSTM 使用相同拟合窗口,缩放器也只在该拟合期估计。LSTM 结构事前固定,停止轮次只由其后的时序验证期锁定,测试期不参与任何选择。

10.11.1 数据、模型与评价

先画出价格与收益率,说明价格水平的持久性为何会让“曲线看起来贴合”产生误导。随后以同一测试期的 MAE 或 MSE 比较三个模型,并报告训练时间。若 LSTM 没有稳定优于朴素和线性基线,应如实报告没有增量预测证据。

10.11.2 决策解释

提交内容是一张模型比较表、一张真实值与预测值图,以及不超过 300 字的解释。解释必须区分预测关联与投资建议,并讨论样本量、结构突变、交易成本和价格尺度对结论的限制。

10.12 小结

本章介绍了深度学习的基础概念和应用。与固定线性形式相比,神经网络的主要特点包括:

  1. 联合表示学习:可从任务损失中学习部分中间表示,但仍需要输入、时点与业务约束设计
  2. 端到端学习: 从原始数据直接学习到最终输出
  3. 表达能力:在 小节 10.4.1 的通用逼近条件下,有限宽度单隐层网络对紧致域上的连续函数可以达到任意给定的一致误差;这是存在性结论,不保证优化成功或样本外泛化
  4. 条件性可扩展: 更多数据和计算可以支持更大模型与更充分的搜索,但性能是否提升仍取决于数据质量、分布漂移、结构偏好、优化和预先固定评价设计

深度学习的挑战

  1. 数据需求: 通常需要大量标注数据
  2. 计算资源: 训练深度模型需要大量计算资源
  3. 可解释性: 深度学习模型的内部表示通常难以直接解释
  4. 超参数调优: 有许多超参数需要调整
  5. 过拟合风险: 深度模型容易过拟合训练数据

何时使用深度学习:

  • 有大量数据可用
  • 问题具有复杂的模式
  • 传统方法性能瓶颈
  • 有足够的计算资源

Python深度学习库

  • TensorFlow/Keras: 提供张量运算、自动微分和高层神经网络训练接口
  • PyTorch: 提供动态图、自动微分以及研究与部署工具
  • Scikit-learn: 提供MLPClassifier等简单的神经网络实现

10.13 理论来源与前沿

深度学习的发展既依赖算法与理论突破(反向传播、非凸优化、正则化与初始化),也依赖数据与算力的工程进步(GPU、分布式训练)。从统计学习视角看,深度网络是一个高度灵活的函数族,其泛化性能受到隐式正则化、网络结构偏置与优化路径的共同影响。

近年来的前沿趋势包括:

  1. 大模型与迁移学习:预训练-微调范式降低下游任务的数据需求,在文本、视觉与多模态中尤为突出。
  2. 对齐与安全:关注模型输出可靠性、偏见控制与对抗鲁棒性,适配高风险部署。
  3. 可解释性与监控:在金融与医疗等领域,需要可解释方法与上线后漂移监控体系。

10.14 练习

10.14.1 概念题

  1. [核心|难度:1|分值:4|任务:独立] 解释深度神经网络中的“表示学习(representation learning)”是什么意思。它与手工特征工程的关系是什么?

  2. [核心|难度:2|分值:5|任务:独立] 按“连接范围、参数共享、跨时点状态”三列比较 MLP、CNN、RNN/LSTM;再分别指出图像平移分类与月度设备故障序列更匹配哪一种结构偏好,以及这种偏好为什么不保证样本外优胜。

  3. [核心|难度:2|分值:5|任务:独立] Dropout 与\(\ell_2\) 正则化的作用相同吗?它们分别更擅长缓解什么类型的过拟合?

  4. [拓展|难度:2|分值:4|任务:独立] Batch Normalization(或 LayerNorm)在训练中通常带来哪些好处?它是否一定提升泛化?

  5. [核心|难度:2|分值:4|任务:独立] 早停(early stopping)为什么可以被视为一种正则化?

10.14.2 应用题

  1. [核心|难度:3|分值:16|任务:独立] 紧凑时序基线实验:使用海康威视日线数据,以交易日 \(t\) 收盘后可得的最近 5 个日收益率预测 \(t+1\) 日收益率是否为正,比较:

    • 逻辑回归概率分类基线
    • 多层感知机(MLP)

要求:按日期顺序采用 60%/20%/20% 的训练—验证—测试切分;只用验证期选择网络,最后报告 LR 与 MLP 的测试 AUC、Brier 分数及开发期—测试期 AUC 差距,并给出网络宽度、深度与正则化强度的依据。核心提交物固定为三项可观察证据:一张三段日期与样本数表、一张 MLP 候选验证表、一张锁定后的 LR/MLP 比较表;缺少任一项或用测试集选择结构,均不能判定本题达标。

  1. [拓展|难度:3|分值:12|任务:独立] 设计一个“特征归一化+ 学习率策略”的消融实验:保持模型结构不变,只改变

    • 是否标准化输入
    • 学习率(常数学习率 vs 余弦衰减)

四格候选只比较验证期收敛速度与最佳验证损失;按事前规则锁定唯一配置后,用训练期与验证期重拟合,并只评价测试集一次。

  1. [核心|难度:2|分值:8|任务:独立] 如果你的样本量不大(典型的结构化表格金融数据),说明你会如何优先选择模型与训练策略,使其更稳健(例如更小网络、强正则、交叉验证、集成等)。

10.14.3 理论题

  1. [核心|难度:2|分值:8|任务:独立] 对二分类的逻辑回归/神经网络输出层,设\(p=\sigma(z)\),交叉熵损失为\(\ell(y,p)=-[y\log p+(1-y)\log(1-p)]\)。推导\(\frac{\partial \ell}{\partial z}\) 的简洁形式,并解释它为何有利于数值稳定训练。本题是目标 1 的必修梯度桥梁;练习 11 仍是完整前向—反向—更新的终端核心证据。

  2. [拓展|难度:3|分值:10|任务:独立] 以两层网络(输入-隐层-输出)为例,写出反向传播计算梯度的链式法则结构,说明梯度如何从输出层逐层传回。本题推广矩阵形式;核心晋级仍由练习 11 的完整数值证据判定。

  3. [核心|难度:3|分值:12|任务:独立] 对一个样本的两输入—单隐单元—单输出网络,给定 \(x=(2,-1)^\top,y=0\),\(w=(0.1,0.3)^\top,b=-0.2,v=-0.4,c=0.15\),两层均用 sigmoid,损失为二元交叉熵,学习率 \(\eta=0.05\)。依次计算:(a) 隐层预激活 \(s=w^\top x+b\)、\(h=\sigma(s)\)、输出 logit \(z=vh+c\)、\(\widehat y=\sigma(z)\) 与 BCE;(b) 四组梯度 \(\nabla_wL,\partial L/\partial b,\partial L/\partial v,\partial L/\partial c\);(c) 一次梯度下降后的 \(w^+,b^+,v^+,c^+\) 与 \(\widehat y^+\)。每个标量及每个向量分量允许 \(10^{-3}\) 绝对误差,并用“\(y=0\) 时更新后概率应下降”做方向检查。

10.15 练习参考解答

展开完整解答、评分点与常见失败模式

评分以题面分值为准;手算允许 \(10^{-3}\) 绝对误差,随机训练结果不设固定数值容差,但必须保持同一切分、种子和预算。应用题若缺基线、训练—测试差距或测试集被用于选模,各扣 25%;把训练损失下降解释为预测有效,结论项不得分。

10.15.1 概念题参考解答

  1. 表示学习:模型自动从原始输入中学习到对任务有用的中间表示(特征),而不是完全依赖手工设计的因子/规则。对结构化金融数据而言,手工特征仍重要,深度模型更多用于自动组合与非线性拟合。

  2. 架构比较:MLP 通常全连接、不同位置不共享参数、没有跨时点状态;CNN 只连接局部感受野并在位置间共享卷积核,本身提供平移等变结构;RNN/LSTM 在时间步间共享转移参数并递推隐藏状态。图像任务可优先检验 CNN,月度序列可优先检验 RNN/LSTM,但最终选择仍取决于预先固定验证集与同键简单基线,结构偏好不等于泛化保证。

  3. Dropout vs \(\ell_2\):二者都能抑制过拟合,但机制不同:\(\ell_2\) 直接惩罚权重大小、偏向更平滑的函数;Dropout 训练时随机屏蔽神经元,近似对大量子网络做模型平均,更能缓解共适应(co-adaptation)。

  4. 归一化的好处与限制:常见好处是加速收敛、改善条件数、提高训练稳定性并允许更大学习率;但并不保证一定提升泛化,且对小批量、分布漂移等场景需要谨慎。

  5. 早停视为正则化:在优化过程中,模型从简单到复杂逐步拟合数据;过长训练会把噪声也拟合进去。早停相当于限制了有效复杂度(类似控制参数范数或隐式正则)。

10.15.2 应用题参考解答

  1. 逻辑回归 vs MLP(核心时序实验):先用逻辑回归作为概率分类基线,再在事先限定的小型 MLP 结构中用时序验证期选择。为避免内置随机验证打乱时序边界,下文候选与最终 MLP 均设 early_stopping=False,并固定 \(\ell_2\) 惩罚后比较隐层结构。表 10.2、表 10.3 与 表 10.4 构成完整可观察证据链。

10.15.2.1 第 6—7 题共享数据准备

第 6、7 题使用同一组按日期划分的数据。先运行本小节的数据加载与 lst-ex6-features-split,再分别完成模型比较和消融实验。

补充说明:为什么这里比较 MLP 时更适合报告 AUC

当任务是预测“明天涨还是跌”这类二分类问题时,很多神经网络教材会先让学生看准确率;但在结构化金融数据中,AUC 往往更能反映模型是否真的学到了可排序的信号。

原因在于,MLP 和逻辑回归都先输出一个概率分数。若只看准确率,你等于默认把阈值固定在0.5;可这个阈值未必是业务上最优的。AUC 则不依赖某一个固定阈值,而是评价:模型能否把更可能上涨的样本整体排在更前面。

这对于比较线性模型和 MLP 尤其重要:

  • 如果 MLP 的 AUC 明显高于逻辑回归,说明它确实从非线性结构中提取到了额外排序信息;
  • 如果两者 AUC 差不多,甚至 MLP 更差,那就说明更复杂的网络结构并没有带来稳定收益,反而可能只是增加了训练不确定性。

换句话说,AUC 更像是在比较“谁更会排队”,而准确率更像是在比较“在某一条门槛线上谁判得更对”。做模型研究时先看前者,做业务执行时再结合阈值去看后者,通常更稳妥。

import pandas as pd  # 整理公司交易日特征与时间切分
import numpy as np  # 构造固定滞后矩阵和索引边界
from sklearn.preprocessing import StandardScaler  # 在管道内用拟合期估计特征尺度
from sklearn.pipeline import Pipeline  # 绑定预处理和分类器避免信息泄漏
from sklearn.linear_model import LogisticRegression  # 提供简单概率分类基线
from sklearn.neural_network import MLPClassifier  # 提供有限网格内的非线性候选
from sklearn.metrics import roc_auc_score, brier_score_loss  # 同时评价排序与概率质量

# 1. 加载数据
import os  # 读取跨平台数据根环境变量
from pathlib import Path  # 使用跨平台路径对象解析显式数据根
book_data_dir_value = os.environ.get('BOOK_DATA_DIR')  # 安全读取习题独立入口的数据根
assert book_data_dir_value, '请先设置 BOOK_DATA_DIR,使其指向包含 stock/ 子目录的数据根'  # 缺失时说明修复方法
DATA_DIR = Path(book_data_dir_value).expanduser().resolve()  # 将数据根解析为绝对路径
assert DATA_DIR.is_dir(), f'BOOK_DATA_DIR 不存在或不是目录: {DATA_DIR}'  # 在读取前检查目录
path = DATA_DIR / 'stock' / 'stock_price_post_adjusted.h5'  # 构建后复权股价文件路径
assert path.is_file(), f'缺少后复权行情文件: {path};请检查 BOOK_DATA_DIR'  # 空目录时指出习题所需的确切文件
exercise_company_id = '002415.XSHE'  # 固定习题公司以保持时间样本身份可复核
stock_price_history = pd.read_hdf(path, where=f'order_book_id={exercise_company_id!r}', columns=['close']).reset_index()  # 在存储层只读取习题所需公司与收盘价
assert {'order_book_id', 'date', 'close'}.issubset(stock_price_history.columns), '选择性读取结果缺少公司、日期或收盘价字段'  # 核验习题数据身份
assert set(stock_price_history['order_book_id'].dropna().unique()) == {'002415.XSHE'}  # 阻止查询条件失效后混入其他公司
stock_price_history['date'] = pd.to_datetime(stock_price_history['date'], errors='coerce')  # 统一交易日为可排序日期
stock_price_history['close'] = pd.to_numeric(stock_price_history['close'], errors='coerce')  # 统一收盘价为可检验数值
assert stock_price_history['date'].notna().all()  # 阻止无效日期进入滞后构造
assert np.isfinite(stock_price_history['close']).all() and stock_price_history['close'].gt(0).all()  # 要求价格有限且为正
stock_price_history = stock_price_history.sort_values('date').reset_index(drop=True)  # 固定唯一公司内的时间顺序
列表 10.5: 习题6:滞后特征与时间三分
# 2. 用交易日t及此前四日收益率预测t+1日方向
stock_price_history['Ret_0'] = stock_price_history['close'].pct_change()  # 计算交易日t的收盘收益率
for lag in range(1, 5):  # 构造t-1至t-4日收益率
    stock_price_history[f'Ret_{lag}'] = stock_price_history['Ret_0'].shift(lag)  # 保证特征在t日收盘后可得
stock_price_history['next_day_return'] = stock_price_history['Ret_0'].shift(-1)  # 将下一交易日收益率对齐到t日
stock_price_history['y'] = (stock_price_history['next_day_return'] > 0).astype(int)  # 定义t+1日上涨标签
stock_price_history = stock_price_history.dropna().iloc[-2000:].copy()  # 删除窗口缺失并保留最近2000个完整样本
feature_columns = [f'Ret_{lag}' for lag in range(5)]  # 固定五个收盘后可得特征
lag_features_matrix = stock_price_history[feature_columns].to_numpy()  # 提取特征矩阵
next_day_direction = stock_price_history['y'].to_numpy()  # 提取下一交易日方向标签

train_end_index = int(len(stock_price_history) * 0.6)  # 最早60%用于候选模型拟合
validation_end_index = int(len(stock_price_history) * 0.8)  # 中间20%仅用于选择MLP超参数
stock_train_features = lag_features_matrix[:train_end_index]  # 取得最早六成特征用于候选拟合
stock_validation_features = lag_features_matrix[train_end_index:validation_end_index]  # 取得中间两成特征用于结构选择
stock_test_features = lag_features_matrix[validation_end_index:]  # 封存最后两成特征直到最终评价
stock_train_labels = next_day_direction[:train_end_index]  # 对齐训练期下一日方向标签
stock_validation_labels = next_day_direction[train_end_index:validation_end_index]  # 对齐验证期标签
stock_test_labels = next_day_direction[validation_end_index:]  # 对齐一次性测试标签
assert tuple(map(len, [stock_train_labels, stock_validation_labels, stock_test_labels])) == (1200, 400, 400)  # 核对当前真实样本的三段规模
列表 10.6: 习题6:三段时间身份审计
time_split_records = []  # 保存三段时间身份供表格审计
for split_name, split_slice in {  # 逐段输出时间身份而不读取未来指标选边界
    'train': slice(0, train_end_index),  # 映射最早六成训练行
    'validation': slice(train_end_index, validation_end_index),  # 映射中间两成验证行
    'test': slice(validation_end_index, None),  # 映射最后两成封存测试行
}.items():  # 对固定三段执行同一日期核对
    split_dates = stock_price_history['date'].iloc[split_slice]  # 提取当前分段的公司交易日
    time_split_records.append({'split': split_name, 'start_date': split_dates.min().date(), 'end_date': split_dates.max().date(), 'n': len(split_dates)})  # 留存边界与样本量
time_split_table = pd.DataFrame(time_split_records)  # 把三段日期身份整理为可观察表格
time_split_table  # 输出边界与分母供测试封存审计
表 10.2: 习题6:按时间锁定的训练、验证与测试身份
split start_date end_date n
0 train 2017-10-10 2022-09-09 1200
1 validation 2022-09-13 2024-05-13 400
2 test 2024-05-14 2025-12-30 400
列表 10.7: 习题6:只用训练与验证期选择 MLP 结构
# 3. 构建Logistic回归管道(标准化 + 分类器)
logistic_regression_pipeline = Pipeline([
        ('scaler', StandardScaler()),  # 标准化预处理
        ('clf', LogisticRegression(max_iter=2000))  # Logistic回归分类器
])  # 完成带训练期标准化的逻辑回归基线
列表 10.8: 习题6:验证期 MLP 结构网格
# 4. 仅用按时间排在训练集之后的验证集选择MLP复杂度。
# MLPClassifier的内置early_stopping会随机抽取验证样本,因此在时序任务中显式关闭。
candidate_hidden_layers = [(16,), (32, 16), (64, 32)]  # 预先限定三种小型网络结构控制搜索预算
validation_architecture_records = []  # 保存每种结构的验证排序与收敛证据
for hidden_layers in candidate_hidden_layers:  # 仅遍历事先声明的宽度与深度
    candidate_pipeline = Pipeline([  # 将折内标准化和当前MLP绑定
        ('scaler', StandardScaler()),  # 只用训练段估计特征尺度
        ('clf', MLPClassifier(  # 声明当前隐藏层结构的概率分类器
            hidden_layer_sizes=hidden_layers,  # 设置候选网络宽度与深度
            alpha=1e-4,  # 固定L2惩罚避免额外调参维度
            learning_rate_init=1e-3,  # 固定初始学习率保证公平比较
            max_iter=2000,  # 给当前真实样本足够的确定性收敛预算
            early_stopping=False,  # 避免内部随机验证破坏时间顺序
            random_state=0,  # 固定权重初始化使候选比较可复现
        )),  # 完成当前MLP配置
    ])  # 完成候选训练管道
    candidate_pipeline.fit(stock_train_features, stock_train_labels)  # 仅在最早训练段拟合候选
    validation_probability = candidate_pipeline.predict_proba(stock_validation_features)[:, 1]  # 生成中间期正类概率
    candidate_classifier = candidate_pipeline.named_steps['clf']  # 读取当前候选的实际优化状态
    candidate_iterations = int(candidate_classifier.n_iter_)  # 记录达到停止条件所用迭代数
    candidate_converged = candidate_iterations < candidate_classifier.max_iter  # 把耗尽上限视为未确认收敛
    candidate_validation_auc = roc_auc_score(stock_validation_labels, validation_probability)  # 使用同一验证标签评价排序
    validation_architecture_records.append({'hidden_layers': hidden_layers, 'validation_auc': candidate_validation_auc, 'n_iter': candidate_iterations, 'converged': candidate_converged})  # 同表保存性能与收敛状态

任何耗尽迭代上限的候选都先报告为未收敛并停止选择,不能把其验证 AUC 与已收敛候选静默排序。

validation_architecture_table = pd.DataFrame(validation_architecture_records)  # 形成三个候选的共同验证证据表
assert np.isfinite(validation_architecture_table['validation_auc']).all()  # 阻止非有限验证指标进入排序
assert validation_architecture_table['converged'].all(), '存在未收敛MLP候选;应增加训练预算或调整优化器后重新验证'  # 未收敛时不得选择结构
selected_architecture_index = validation_architecture_table['validation_auc'].idxmax()  # 只在全部收敛后按验证AUROC锁定结构
selected_hidden_layers = validation_architecture_table.loc[selected_architecture_index, 'hidden_layers']  # 保存测试前选定的宽度与深度
validation_architecture_table.assign(selected=lambda frame: frame.index == selected_architecture_index)  # 展示每个候选的迭代数、收敛状态与选择标记
表 10.3: 习题6:三个MLP候选的验证AUC与收敛状态
hidden_layers validation_auc n_iter converged selected
0 (16,) 0.479450 114 True False
1 (32, 16) 0.561799 1102 True True
2 (64, 32) 0.552697 780 True False
列表 10.9: 习题6:测试前锁定的MLP结构与开发期数据
# 5. 选择完成后,用训练+验证时段重拟合;测试集只在最后评估一次。
development_features = lag_features_matrix[:validation_end_index]  # 合并训练与验证特征供锁定模型重拟合
development_labels = next_day_direction[:validation_end_index]  # 对齐训练加验证期的重拟合标签
neural_network_pipeline = Pipeline([  # 用锁定结构声明最终MLP管道
    ('scaler', StandardScaler()),  # 在完整开发期重新估计尺度
    ('clf', MLPClassifier(  # 构造测试前已锁定的分类器
        hidden_layer_sizes=selected_hidden_layers,  # 使用验证期选定的隐藏层结构
        alpha=1e-4,  # 保持候选比较时的惩罚强度
        learning_rate_init=1e-3,  # 保持候选比较时的学习率
        max_iter=2000,  # 保持候选选择时已验证的收敛预算
        early_stopping=False,  # 不让内部随机切分改变时序边界
        random_state=0,  # 固定初始化保证结果可复算
    )),  # 完成锁定MLP配置
])  # 完成最终神经网络管道
列表 10.10: 习题6:锁定结构后的基线、测试 AUC 与过拟合差距
locked_model_records = []  # 保存简单基线与锁定MLP的共同评价字段
for name, model in [('LR', logistic_regression_pipeline), ('MLP', neural_network_pipeline)]:  # 在同一键上比较简单与复杂模型
    model.fit(development_features, development_labels)  # 用训练加验证期重拟合锁定模型
    fitted_classifier = model.named_steps['clf']  # 读取当前最终模型的实际优化状态
    fitted_iterations = int(np.max(np.atleast_1d(fitted_classifier.n_iter_)))  # 统一提取逻辑回归与MLP的迭代数
    fitted_converged = fitted_iterations < fitted_classifier.max_iter  # 把耗尽上限登记为未确认收敛
    assert name != 'MLP' or fitted_converged, '最终MLP未收敛,不得报告测试比较'  # 保护锁定测试解释免受优化失败干扰
    development_probabilities = model.predict_proba(development_features)[:, 1]  # 取得开发期概率用于过拟合差距
    predicted_probabilities = model.predict_proba(stock_test_features)[:, 1]  # 仅此处生成封存测试概率
    development_auc = roc_auc_score(development_labels, development_probabilities)  # 计算样本内开发期排序
    test_auc = roc_auc_score(stock_test_labels, predicted_probabilities)  # 计算一次性未来期排序
    test_brier = brier_score_loss(stock_test_labels, predicted_probabilities)  # 计算未来期概率平方误差
    locked_model_records.append({'model': name, 'n_iter': fitted_iterations, 'converged': fitted_converged, 'development_auc': development_auc, 'test_auc': test_auc, 'test_brier': test_brier, 'auc_gap': development_auc - test_auc})  # 同表留存收敛、增量与泛化差距
locked_model_comparison = pd.DataFrame(locked_model_records)  # 形成简单基线与复杂模型的同键结果表
locked_model_comparison  # 输出测试前锁定模型的可评分比较证据
表 10.4: 习题6:锁定后的逻辑回归与MLP时间外比较
model n_iter converged development_auc test_auc test_brier auc_gap
0 LR 2 True 0.525001 0.502913 0.250520 0.022089
1 MLP 1007 True 0.837496 0.447086 0.307254 0.390409

题 6 的三项核心产物分别是 表 10.2、表 10.3 与 表 10.4。当前真实样本应形成 1200/400/400 三段,且三个候选均产生有限验证 AUC 并在 2000 次预算内收敛;任一候选未收敛时,本次结构排序无效。AUC 越大表示排序越好,Brier 分数越小表示概率误差越小;若 MLP 没有同时给出有意义的增量,则结论应是“复杂模型没有稳定增量证据”。

  1. 归一化与学习率策略的完整消融:

本题沿用 小节 10.15.2.1 产生的训练、验证和测试数组。

下例保持 Dense(16)-Dense(1)、30 轮上限、批量 32、固定样本顺序和初始权重不变,形成“原尺度/训练期标准化 × 常数/余弦衰减”四格。每个候选在建模前重置同一种子,使比较不被初始化或批次顺序差异混杂。选择与收敛比较只看验证期;测试期仍只作最终报告。

列表 10.11: 习题7:固定网络并切换尺度与学习率策略
import tensorflow as tf  # 使用同一深度学习框架完成四格消融
from sklearn.preprocessing import StandardScaler  # 只用训练期拟合标准化器
ablation_initialization_seed = 20260812  # 为四格候选预先固定共同初始化
ablation_epochs = 30  # 为四格候选预先固定共同训练上限
ablation_batch_size = 32  # 为四格候选预先固定共同批量大小
ablation_shuffle = False  # 固定样本顺序以隔离尺度和学习率策略差异
training_scaler = StandardScaler().fit(stock_train_features)  # 严格只拟合最早训练段
scaled_train = training_scaler.transform(stock_train_features)  # 转换训练输入
scaled_validation = training_scaler.transform(stock_validation_features)  # 用预先固定尺度转换验证输入
def make_ablation_learning_rate(schedule_name, decay_steps):  # 用共同训练比例定义候选学习率路径
    return 1e-3 if schedule_name == 'constant' else tf.keras.optimizers.schedules.CosineDecay(1e-3, decay_steps, alpha=0.1)  # 只切换常数或余弦路径
def evaluate_epoch_boundary_rates(learning_rate, epoch_boundaries, steps_per_epoch):  # 在整轮边界核对归一化学习率时钟
    return np.asarray([float(learning_rate) if np.isscalar(learning_rate) else float(learning_rate(int(epoch * steps_per_epoch)).numpy()) for epoch in epoch_boundaries])  # 把每个边界学习率转为可比较数值
def build_ablation_model(schedule_name, decay_steps):  # 构造结构完全相同的候选网络
    learning_rate = make_ablation_learning_rate(schedule_name, decay_steps)  # 按共同训练比例取得学习率路径
    ablation_model = tf.keras.Sequential([tf.keras.layers.Input((stock_train_features.shape[1],)), tf.keras.layers.Dense(16, activation='relu'), tf.keras.layers.Dense(1, activation='sigmoid')])  # 固定宽度深度
    ablation_model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate), loss='binary_crossentropy', metrics=[tf.keras.metrics.AUC(name='auc')])  # 固定优化器与指标
    return ablation_model  # 返回待训练候选模型
列表 10.12: 习题7:四格消融的训练—验证选择证据
ablation_records = []  # 保存四个候选的可比证据
ablation_initial_weights = []  # 保存各候选训练前权重以核对公平起点
for should_scale in [False, True]:  # 切换是否使用训练期标准化
    candidate_train = scaled_train if should_scale else stock_train_features  # 选择训练输入尺度
    candidate_validation = scaled_validation if should_scale else stock_validation_features  # 保持验证尺度一致
    for schedule_name in ['constant', 'cosine']:  # 切换常数与余弦衰减
        candidate_steps_per_epoch = int(np.ceil(len(candidate_train) / ablation_batch_size))  # 按共同批量计算候选每轮步数
        candidate_decay_steps = ablation_epochs * candidate_steps_per_epoch  # 让余弦时钟覆盖完整30轮候选预算
        tf.keras.utils.set_random_seed(ablation_initialization_seed)  # 在每个候选建模前恢复相同随机起点
        candidate_model = build_ablation_model(schedule_name, candidate_decay_steps)  # 建立同结构同归一化时钟候选网络
        ablation_initial_weights.append(np.concatenate([weight.numpy().ravel() for weight in candidate_model.weights]))  # 展平初始权重供四格一致性检查
        history = candidate_model.fit(candidate_train, stock_train_labels, validation_data=(candidate_validation, stock_validation_labels), epochs=ablation_epochs, batch_size=ablation_batch_size, shuffle=ablation_shuffle, verbose=0)  # 按共同契约只用训练与验证
        validation_loss_history = np.asarray(history.history['val_loss'], dtype=float)  # 统一为数值轨迹以检查最佳轮次
        best_validation_epoch = int(np.argmin(validation_loss_history) + 1)  # 记录最小验证损失所在轮次
        best_validation_loss = float(validation_loss_history[best_validation_epoch - 1])  # 读取入选所依据的最小损失
        convergence_epoch = next(i + 1 for i, value in enumerate(validation_loss_history) if value <= 1.05 * best_validation_loss)  # 定义进入最佳值 5% 的速度
        ablation_records.append({'standardized': should_scale, 'schedule': schedule_name, 'steps_per_epoch': candidate_steps_per_epoch, 'convergence_epoch': convergence_epoch, 'best_validation_epoch': best_validation_epoch, 'best_validation_loss': best_validation_loss})  # 留存开发期时钟、收敛与损失字段
initialization_max_difference = max(float(np.max(np.abs(weights - ablation_initial_weights[0]))) for weights in ablation_initial_weights)  # 量化四格候选训练前权重的最大差异
assert initialization_max_difference == 0.0  # 要求每格从完全相同的初始权重开始
ablation_validation_table = pd.DataFrame(ablation_records)  # 保留预先声明的四格顺序和全部验证字段
assert np.isfinite(ablation_validation_table[['convergence_epoch', 'best_validation_epoch', 'best_validation_loss']]).all().all()  # 阻止非有限验证证据进入选择
selected_ablation = ablation_validation_table.loc[ablation_validation_table['best_validation_loss'].idxmin()].to_dict()  # 只按最小验证损失预先固定一个配置
print(ablation_validation_table)  # 输出四格验证证据而不接触测试集
   standardized  schedule  steps_per_epoch  convergence_epoch  \
0         False  constant               38                  1   
1         False    cosine               38                  1   
2          True  constant               38                  1   
3          True    cosine               38                  1   

   best_validation_epoch  best_validation_loss  
0                      7              0.693272  
1                     11              0.693269  
2                     19              0.701090  
3                     30              0.701203  

四行表中,convergence_epoch 表示首次进入最佳损失 5% 范围的轮次,best_validation_epoch 是验证损失真正取最小值的轮次;二者分别衡量收敛速度与重拟合长度。只按 best_validation_loss 选定配置后,用训练期与验证期合并数据重拟合,再评价测试集。

列表 10.13: 习题7:入选配置的开发期重拟合与测试评价
selected_ablation['refit_epochs'] = int(selected_ablation['best_validation_epoch'])  # 用最佳验证轮次预先固定重拟合长度
selected_ablation['architecture'] = 'Dense(16)-Dense(1)'  # 登记保持不变的网络结构
development_features = np.vstack([stock_train_features, stock_validation_features])  # 可选地合并训练与验证输入重拟合
development_labels = np.concatenate([stock_train_labels, stock_validation_labels])  # 合并对应开发期标签
development_scaler = StandardScaler().fit(development_features) if selected_ablation['standardized'] else None  # 仅在所选配置需要时重拟合尺度
refit_features = development_scaler.transform(development_features) if development_scaler else development_features  # 按预先固定配置转换开发期
development_steps_per_epoch = int(np.ceil(len(refit_features) / ablation_batch_size))  # 按共同批量计算开发期每轮步数
refit_decay_steps = ablation_epochs * development_steps_per_epoch  # 保持与候选相同的30轮归一化余弦时钟
selected_candidate_decay_steps = ablation_epochs * int(selected_ablation['steps_per_epoch'])  # 恢复入选候选的完整衰减时钟
epoch_boundaries = np.arange(selected_ablation['refit_epochs'] + 1)  # 覆盖第0轮到重拟合结束的全部边界
candidate_boundary_rates = evaluate_epoch_boundary_rates(make_ablation_learning_rate(selected_ablation['schedule'], selected_candidate_decay_steps), epoch_boundaries, int(selected_ablation['steps_per_epoch']))  # 计算入选候选的边界学习率
refit_boundary_rates = evaluate_epoch_boundary_rates(make_ablation_learning_rate(selected_ablation['schedule'], refit_decay_steps), epoch_boundaries, development_steps_per_epoch)  # 计算重拟合的同轮边界学习率
assert np.allclose(candidate_boundary_rates, refit_boundary_rates)  # 要求样本量变化不改变每轮归一化学习率
tf.keras.utils.set_random_seed(ablation_initialization_seed)  # 使最终重拟合从预先约定的初始化开始
refit_model = build_ablation_model(selected_ablation['schedule'], refit_decay_steps)  # 只重建唯一入选模型并保持归一化时钟
refit_model.fit(refit_features, development_labels, epochs=selected_ablation['refit_epochs'], batch_size=ablation_batch_size, shuffle=ablation_shuffle, verbose=0)  # 按共同批量与顺序重拟合且不使用测试信息
test_features = development_scaler.transform(stock_test_features) if development_scaler else stock_test_features  # 按入选预处理转换测试输入
selected_test_metrics = refit_model.evaluate(test_features, stock_test_labels, verbose=0, return_dict=True)  # 评价入选配置
print({'selected_config': selected_ablation, 'test_metrics': selected_test_metrics})  # 输出配置与测试指标
{'selected_config': {'standardized': False, 'schedule': 'cosine', 'steps_per_epoch': 38, 'convergence_epoch': 1, 'best_validation_epoch': 11, 'best_validation_loss': 0.6932693719863892, 'refit_epochs': 11, 'architecture': 'Dense(16)-Dense(1)'}, 'test_metrics': {'auc': 0.5148752927780151, 'loss': 0.6935133337974548}}

相同配置重复时指标可能有随机波动,因此不预写固定优胜者。四格实验必须共用初始权重、30 轮上限、批量大小和固定样本顺序,并完整报告收敛轮次、最佳轮次和最佳验证损失;若损失非有限、初始权重不同、任一集合单类或四格训练契约不同,该次消融无效。重拟合轮数必须来自入选候选的 best_validation_epoch;即使开发期合并后每轮步数改变,余弦学习率也应在相同 epoch 边界保持一致。结论只能依据验证期选择和最终一次测试结果条件式表述。

  1. 小样本表格数据的稳健策略:优先考虑更简单的模型(线性/树模型/MLP),严格时间切分,强正则化与早停,必要时做模型集成,并把重心放在特征质量与数据泄露控制。

10.15.3 理论题参考解答(练习 9 为核心梯度桥梁,练习 10 为拓展)

  1. 交叉熵对 logit 的梯度:\(p=\sigma(z)=\frac{1}{1+e^{-z}}\),有

\[ \frac{\partial \ell}{\partial z}=\frac{\partial \ell}{\partial p}\cdot\frac{\partial p}{\partial z}= \Big(-\frac{y}{p}+\frac{1-y}{1-p}\Big)\cdot p(1-p)=p-y. \]

得到简洁形式\(\partial\ell/\partial z = p-y\),数值稳定且便于实现(也是很多框架将 sigmoid 与BCE 合并实现的原因)。

  1. 两层网络的链式结构:设

\[ h=\phi(W_1x+b_1),\quad z=W_2h+b_2,\quad \hat y=\psi(z), \]

损失为\(\ell(y,\hat y)\)。反向传播按链式法则:先算输出层误差项\(\delta_2=\partial\ell/\partial z\),再传回隐层 \(\delta_1=(W_2^\top\delta_2)\odot \phi'(W_1x+b_1)\),从而得到

\[ \nabla_{W_2}\ell=\delta_2 h^\top,\;\nabla_{b_2}\ell=\delta_2,\;\nabla_{W_1}\ell=\delta_1 x^\top,\;\nabla_{b_1}\ell=\delta_1. \]

这说明梯度从输出层逐层乘上权重转置与激活导数向前传播。

  1. 一个新初值下的一步完整更新与评分证据

前向传播先给出

\[ s=0.1(2)+0.3(-1)-0.2=-0.3,\qquad h=\sigma(s)=0.425557, \]

\[ z=-0.4(0.425557)+0.15=-0.020223,\qquad \widehat y=\sigma(z)=0.494944. \]

由于 \(y=0\),BCE 为

\[ L=-\log(1-\widehat y)=0.683087. \]

输出误差是 \(\delta_z=\widehat y-y=0.494944\),故四组梯度为

\[ \frac{\partial L}{\partial v}=\delta_zh=0.210627,\qquad \frac{\partial L}{\partial c}=\delta_z=0.494944, \]

\[ \delta_s=\delta_zvh(1-h)=-0.048397, \]

\[ \nabla_wL=\delta_sx=(-0.096795,0.048397)^\top,\qquad \frac{\partial L}{\partial b}=\delta_s=-0.048397. \]

代入 \(\theta^+=\theta-0.05\nabla_\theta L\) 得

\[ w^+=(0.104840,0.297580)^\top,\quad b^+=-0.197580, \]

\[ v^+=-0.410531,\quad c^+=0.125253,\quad \widehat y^+=0.487275. \]

完整计分证据为:前向四量 \(s,h,z,\widehat y\) 共 2 分;BCE 1 分;四组梯度各 1 分,共 4 分;四组更新各 1 分,共 4 分;方向检查 1 分。任一向量分量也按 \(10^{-3}\) 容差单独判断;只写公式不代入数值时,对应数值项不得分。这里 \(\widehat y^+<\widehat y\) 与负类标签方向一致。

10.16 章末回顾

本章从多层感知机的前向传播与反向传播出发,比较了 CNN 的局部连接和参数共享、RNN/LSTM 的状态递推,以及深度模型常用的正则化和优化方法。复杂结构扩大了可表达的函数集合,也提高了数据量、调参和诊断要求;因此模型选择必须以同一切分下的简单基线为参照。

学习完成后,读者应能独立回答三组问题:第一,二元交叉熵对 logit 的梯度为何是 \(\hat p-y\);第二,标准化器、早停判据和最终测试集分别能使用哪些数据;第三,当 MLP 没有胜过同切分的逻辑回归时,怎样写出不过度外推的结论。若仍有困难,可回看 小节 10.4.3.1、小节 10.15.2.1 和 小节 10.11。

面对新的供应链延迟或客户流失序列,应先定义预测时点、标签和朴素基线,再决定使用 MLP、CNN 还是 RNN。样本很小、验证结果不稳定或简单基线表现相当时,优先选择更简单的模型。下一章把“事件是否发生”扩展为带删失的“何时发生”。

Cybenko, George. 1989年. 《Approximation by Superpositions of a Sigmoidal Function》. Mathematics of Control, Signals, and Systems 2 (4): 303~14. https://doi.org/10.1007/BF02551274.
Hochreiter, Sepp, 和 Jürgen Schmidhuber. 1997年. 《Long Short-Term Memory》. Neural Computation 9 (8): 1735~80. https://doi.org/10.1162/neco.1997.9.8.1735.
Rumelhart, David E., Geoffrey E. Hinton, 和 Ronald J. Williams. 1986年. 《Learning Representations by Back-Propagating Errors》. Nature 323: 533~36. https://doi.org/10.1038/323533a0.