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层超参数定义完成10 深度学习
10.1 导读
深度学习用多层神经网络学习复杂的非线性表示。本章介绍多层感知机、卷积网络与循环网络的基本结构,并讨论它们在文本、图像和时间序列任务中的适用条件。金融数据通常样本有限、噪声强且容易发生分布变化,因此复杂网络必须与简单基线在同一时间切分上比较;本章不预设深度模型一定占优。
10.2 学习目标
完成本章后,学生应能达到以下标准:
- 对给定的一样本 MLP,写出各张量形状并手算前向值、二元交叉熵、四个梯度与一次更新,数值误差不超过 \(10^{-3}\)。
- 用“连接范围、参数共享、状态传递”区分 MLP、CNN 与 RNN/LSTM,并能纠正“卷积天然平移不变”的误解。
- 在时间有序数据上建立训练—验证—测试三段式比较,所有预处理仅拟合开发期,测试集只评价一次。
- 用训练—验证差距、基线比较与学习曲线诊断欠拟合、过拟合和优化失败,不把损失下降等同于样本外有效。
- 针对一个陌生任务说明网络、损失、正则化与基线选择,并给出“不应使用深度学习”的证据边界。
先修自检
不运行代码,回答:若 \(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 激活函数
激活函数引入非线性使神经网络能够学习复杂的模式。常见的激活函数包括
Sigmoid 函数: \[ g(z) = \frac{1}{1 + e^{-z}} \tag{10.4}\]
值域为开区间 \((0,1)\),常用于二元分类输出层。
tanh 函数: \[ g(z) = \tanh(z) = \frac{e^z - e^{-z}}{e^z + e^{-z}} \]
值域为开区间 \((-1,1)\),并以 0 为中心。
ReLU(修正线性单元): \[ g(z) = \max(0, z) \tag{10.5}\]
对正输入输出原值,对非正输入输出 0。其正半轴导数为 1,可缓解饱和激活造成的梯度衰减,但不能保证消除梯度问题。
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\),这是最低限度的方向健全性检查。
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 前必须掌握的基本训练逻辑
- 先定义 estimand 与基线:分类至少与训练期多数类或逻辑回归比较;回归至少与训练均值或 last-value 比较。
- 再锁定数据边界:训练集拟合权重与标准化器,验证集选结构、学习率和停止轮次,测试集只在全部选择预先固定后使用一次。
- 用小批量反复更新:一个 epoch 是训练样本的一次遍历;mini-batch 梯度是总体梯度的有噪声近似。
- 同时看优化与泛化:训练损失不降提示学习率、缩放或梯度问题;训练降而验证升提示过拟合;两者都高提示欠拟合。
- 记录失败模式:随机种子、参数量、各集合 \(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)}), \]
最后把局部误差与进入该层的激活相乘:
\[ \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由以下几种层组成:
- 卷积层: 使用卷积核滤波器从输入中提取特征
- 池化层: 降低特征图的维度,减少计算量并控制过拟合
- 全连接层: 在最后进行分类或回归
图 10.1 显示了一个用于图像分类的CNN架构。
图像包含大量具有空间结构的像素;若直接使用全连接网络,参数量会随输入维数快速增长。卷积神经网络(CNN)通过局部连接和参数共享利用这种空间结构,因此常用于计算机视觉任务。 下面的架构草图用 Python 渲染展示了一个经典的 CNN 骨架。你可以看到,信息在网络中不再是平铺直叙地流动,而是经过了一层层类似“滤镜”的物理挤压。 左侧蓝色的原始图像输入后,首先迎来了红色的卷积层。在这里,无数个小巧的“卷积核”像手电筒的光斑一样在图像上滑动扫描,局部连接与参数共享显著压缩了参数量,并使边缘、纹理等特征对平移保持等变性:输入平移时,特征图也相应平移。近似平移不变性还需要池化、全局聚合或数据增强等环节配合。 紧接着的绿色池化层用局部最大值(Max Pooling)降低空间分辨率,从而减少计算量,并使后续表示对小幅位置变化不那么敏感。 多次卷积和池化后,二维特征图被展平为一维向量,再由全连接层整合信息,最后通过 Softmax 输出分类概率。在金融场景中,这种结构只有在图像表示确实对应业务对象、标签可靠且未来期评价通过时,才可能用于图表或视觉材料分类。
以下代码定义了一个辅助函数,用于在给定的坐标轴上逐层绘制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.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) # 关闭备份文件描述符,避免文件描述符泄漏# 读取标准基准及其官方划分,使训练选择与最终评价样本保持分离
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 ━━━━━━━━━━━━━━━━━━━━ 12:21 990ms/step - accuracy: 0.2031 - loss: 2.2921 6/750 ━━━━━━━━━━━━━━━━━━━━ 8s 11ms/step - accuracy: 0.2155 - loss: 2.2701 11/750 ━━━━━━━━━━━━━━━━━━━━ 8s 11ms/step - accuracy: 0.2359 - loss: 2.2357 16/750 ━━━━━━━━━━━━━━━━━━━━ 8s 11ms/step - accuracy: 0.2737 - loss: 2.1840 21/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.3056 - loss: 2.1260 26/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.3328 - loss: 2.0633 31/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.3581 - loss: 1.9979 36/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.3827 - loss: 1.9314 41/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.4060 - loss: 1.8668 46/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.4272 - loss: 1.8064 51/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.4459 - loss: 1.7518 56/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.4628 - loss: 1.7016 61/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.4785 - loss: 1.6547 66/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.4930 - loss: 1.6110 71/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.5066 - loss: 1.5703 76/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.5193 - loss: 1.5318 81/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.5312 - loss: 1.4953 86/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.5425 - loss: 1.4610 91/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.5530 - loss: 1.4285 96/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.5629 - loss: 1.3981 101/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.5721 - loss: 1.3694 106/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.5809 - loss: 1.3421 111/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.5891 - loss: 1.3163 116/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.5970 - loss: 1.2917 121/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.6045 - loss: 1.2682 126/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.6117 - loss: 1.2459 131/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.6185 - loss: 1.2246 136/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.6249 - loss: 1.2042 141/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.6311 - loss: 1.1847 146/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.6370 - loss: 1.1661 151/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.6426 - loss: 1.1482 156/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.6480 - loss: 1.1311 161/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.6533 - loss: 1.1145 166/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.6583 - loss: 1.0986 171/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.6632 - loss: 1.0832 176/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.6678 - loss: 1.0684 180/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.6715 - loss: 1.0569 184/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.6750 - loss: 1.0457 189/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.6793 - loss: 1.0321 194/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.6835 - loss: 1.0190 199/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.6874 - loss: 1.0063 204/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.6913 - loss: 0.9940 209/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.6950 - loss: 0.9821 213/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.6980 - loss: 0.9729 218/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.7015 - loss: 0.9616 223/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.7049 - loss: 0.9507 228/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.7083 - loss: 0.9401 233/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.7115 - loss: 0.9298 238/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.7147 - loss: 0.9198 243/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.7178 - loss: 0.9100 248/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.7207 - loss: 0.9005 253/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.7237 - loss: 0.8913 258/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.7265 - loss: 0.8822 262/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.7287 - loss: 0.8752 267/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.7314 - loss: 0.8666 272/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.7340 - loss: 0.8583 277/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.7366 - loss: 0.8501 282/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.7391 - loss: 0.8421 287/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.7415 - loss: 0.8344 292/750 ━━━━━━━━━━━━━━━━━━━━ 4s 11ms/step - accuracy: 0.7439 - loss: 0.8268 297/750 ━━━━━━━━━━━━━━━━━━━━ 4s 11ms/step - accuracy: 0.7462 - loss: 0.8193 302/750 ━━━━━━━━━━━━━━━━━━━━ 4s 11ms/step - accuracy: 0.7485 - loss: 0.8121 307/750 ━━━━━━━━━━━━━━━━━━━━ 4s 11ms/step - accuracy: 0.7507 - loss: 0.8051 311/750 ━━━━━━━━━━━━━━━━━━━━ 4s 11ms/step - accuracy: 0.7525 - loss: 0.7995 316/750 ━━━━━━━━━━━━━━━━━━━━ 4s 11ms/step - accuracy: 0.7546 - loss: 0.7927 321/750 ━━━━━━━━━━━━━━━━━━━━ 4s 11ms/step - accuracy: 0.7567 - loss: 0.7861 326/750 ━━━━━━━━━━━━━━━━━━━━ 4s 11ms/step - accuracy: 0.7587 - loss: 0.7796 331/750 ━━━━━━━━━━━━━━━━━━━━ 4s 11ms/step - accuracy: 0.7607 - loss: 0.7733 336/750 ━━━━━━━━━━━━━━━━━━━━ 4s 11ms/step - accuracy: 0.7627 - loss: 0.7671 341/750 ━━━━━━━━━━━━━━━━━━━━ 4s 11ms/step - accuracy: 0.7646 - loss: 0.7610 346/750 ━━━━━━━━━━━━━━━━━━━━ 4s 11ms/step - accuracy: 0.7664 - loss: 0.7550 351/750 ━━━━━━━━━━━━━━━━━━━━ 4s 11ms/step - accuracy: 0.7683 - loss: 0.7492 356/750 ━━━━━━━━━━━━━━━━━━━━ 4s 11ms/step - accuracy: 0.7700 - loss: 0.7435 361/750 ━━━━━━━━━━━━━━━━━━━━ 4s 11ms/step - accuracy: 0.7718 - loss: 0.7379 366/750 ━━━━━━━━━━━━━━━━━━━━ 4s 11ms/step - accuracy: 0.7735 - loss: 0.7324 371/750 ━━━━━━━━━━━━━━━━━━━━ 4s 11ms/step - accuracy: 0.7752 - loss: 0.7270 376/750 ━━━━━━━━━━━━━━━━━━━━ 4s 11ms/step - accuracy: 0.7768 - loss: 0.7218 381/750 ━━━━━━━━━━━━━━━━━━━━ 4s 11ms/step - accuracy: 0.7785 - loss: 0.7166 386/750 ━━━━━━━━━━━━━━━━━━━━ 3s 11ms/step - accuracy: 0.7801 - loss: 0.7115 391/750 ━━━━━━━━━━━━━━━━━━━━ 3s 11ms/step - accuracy: 0.7816 - loss: 0.7065 396/750 ━━━━━━━━━━━━━━━━━━━━ 3s 11ms/step - accuracy: 0.7831 - loss: 0.7016 400/750 ━━━━━━━━━━━━━━━━━━━━ 3s 11ms/step - accuracy: 0.7844 - loss: 0.6977 405/750 ━━━━━━━━━━━━━━━━━━━━ 3s 11ms/step - accuracy: 0.7858 - loss: 0.6930 410/750 ━━━━━━━━━━━━━━━━━━━━ 3s 11ms/step - accuracy: 0.7873 - loss: 0.6883 415/750 ━━━━━━━━━━━━━━━━━━━━ 3s 11ms/step - accuracy: 0.7887 - loss: 0.6837 420/750 ━━━━━━━━━━━━━━━━━━━━ 3s 11ms/step - accuracy: 0.7901 - loss: 0.6792 425/750 ━━━━━━━━━━━━━━━━━━━━ 3s 11ms/step - accuracy: 0.7915 - loss: 0.6748 430/750 ━━━━━━━━━━━━━━━━━━━━ 3s 11ms/step - accuracy: 0.7929 - loss: 0.6704 435/750 ━━━━━━━━━━━━━━━━━━━━ 3s 11ms/step - accuracy: 0.7942 - loss: 0.6662 440/750 ━━━━━━━━━━━━━━━━━━━━ 3s 11ms/step - accuracy: 0.7955 - loss: 0.6620 445/750 ━━━━━━━━━━━━━━━━━━━━ 3s 11ms/step - accuracy: 0.7968 - loss: 0.6578 450/750 ━━━━━━━━━━━━━━━━━━━━ 3s 11ms/step - accuracy: 0.7981 - loss: 0.6538 455/750 ━━━━━━━━━━━━━━━━━━━━ 3s 11ms/step - accuracy: 0.7994 - loss: 0.6498 461/750 ━━━━━━━━━━━━━━━━━━━━ 3s 11ms/step - accuracy: 0.8008 - loss: 0.6450 466/750 ━━━━━━━━━━━━━━━━━━━━ 3s 11ms/step - accuracy: 0.8020 - loss: 0.6412 471/750 ━━━━━━━━━━━━━━━━━━━━ 3s 11ms/step - accuracy: 0.8032 - loss: 0.6374 476/750 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.8044 - loss: 0.6336 481/750 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.8055 - loss: 0.6300 486/750 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.8067 - loss: 0.6263 491/750 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.8078 - loss: 0.6228 496/750 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.8089 - loss: 0.6193 501/750 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.8100 - loss: 0.6158 506/750 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.8110 - loss: 0.6124 511/750 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.8121 - loss: 0.6090 516/750 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.8131 - loss: 0.6057 521/750 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.8141 - loss: 0.6024 526/750 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.8151 - loss: 0.5992 531/750 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.8161 - loss: 0.5960 536/750 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.8171 - loss: 0.5929 541/750 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.8181 - loss: 0.5898 546/750 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.8190 - loss: 0.5868 551/750 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.8199 - loss: 0.5838 556/750 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.8209 - loss: 0.5808 561/750 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.8218 - loss: 0.5779 566/750 ━━━━━━━━━━━━━━━━━━━━ 1s 11ms/step - accuracy: 0.8227 - loss: 0.5750 571/750 ━━━━━━━━━━━━━━━━━━━━ 1s 11ms/step - accuracy: 0.8236 - loss: 0.5721 576/750 ━━━━━━━━━━━━━━━━━━━━ 1s 11ms/step - accuracy: 0.8244 - loss: 0.5693 581/750 ━━━━━━━━━━━━━━━━━━━━ 1s 11ms/step - accuracy: 0.8253 - loss: 0.5665 586/750 ━━━━━━━━━━━━━━━━━━━━ 1s 11ms/step - accuracy: 0.8261 - loss: 0.5638 591/750 ━━━━━━━━━━━━━━━━━━━━ 1s 11ms/step - accuracy: 0.8270 - loss: 0.5611 596/750 ━━━━━━━━━━━━━━━━━━━━ 1s 11ms/step - accuracy: 0.8278 - loss: 0.5585 601/750 ━━━━━━━━━━━━━━━━━━━━ 1s 11ms/step - accuracy: 0.8286 - loss: 0.5558 606/750 ━━━━━━━━━━━━━━━━━━━━ 1s 11ms/step - accuracy: 0.8294 - loss: 0.5532 611/750 ━━━━━━━━━━━━━━━━━━━━ 1s 11ms/step - accuracy: 0.8302 - loss: 0.5507 616/750 ━━━━━━━━━━━━━━━━━━━━ 1s 11ms/step - accuracy: 0.8310 - loss: 0.5482 621/750 ━━━━━━━━━━━━━━━━━━━━ 1s 11ms/step - accuracy: 0.8318 - loss: 0.5457 626/750 ━━━━━━━━━━━━━━━━━━━━ 1s 11ms/step - accuracy: 0.8326 - loss: 0.5432 631/750 ━━━━━━━━━━━━━━━━━━━━ 1s 11ms/step - accuracy: 0.8333 - loss: 0.5407 636/750 ━━━━━━━━━━━━━━━━━━━━ 1s 11ms/step - accuracy: 0.8341 - loss: 0.5383 641/750 ━━━━━━━━━━━━━━━━━━━━ 1s 11ms/step - accuracy: 0.8348 - loss: 0.5360 646/750 ━━━━━━━━━━━━━━━━━━━━ 1s 11ms/step - accuracy: 0.8355 - loss: 0.5336 651/750 ━━━━━━━━━━━━━━━━━━━━ 1s 11ms/step - accuracy: 0.8363 - loss: 0.5313 656/750 ━━━━━━━━━━━━━━━━━━━━ 1s 11ms/step - accuracy: 0.8370 - loss: 0.5290 661/750 ━━━━━━━━━━━━━━━━━━━━ 0s 11ms/step - accuracy: 0.8377 - loss: 0.5267 666/750 ━━━━━━━━━━━━━━━━━━━━ 0s 11ms/step - accuracy: 0.8384 - loss: 0.5244 671/750 ━━━━━━━━━━━━━━━━━━━━ 0s 11ms/step - accuracy: 0.8391 - loss: 0.5222 676/750 ━━━━━━━━━━━━━━━━━━━━ 0s 11ms/step - accuracy: 0.8398 - loss: 0.5200 681/750 ━━━━━━━━━━━━━━━━━━━━ 0s 11ms/step - accuracy: 0.8404 - loss: 0.5178 686/750 ━━━━━━━━━━━━━━━━━━━━ 0s 11ms/step - accuracy: 0.8411 - loss: 0.5157 691/750 ━━━━━━━━━━━━━━━━━━━━ 0s 11ms/step - accuracy: 0.8418 - loss: 0.5135 696/750 ━━━━━━━━━━━━━━━━━━━━ 0s 11ms/step - accuracy: 0.8424 - loss: 0.5114 701/750 ━━━━━━━━━━━━━━━━━━━━ 0s 11ms/step - accuracy: 0.8431 - loss: 0.5093 706/750 ━━━━━━━━━━━━━━━━━━━━ 0s 11ms/step - accuracy: 0.8437 - loss: 0.5073 711/750 ━━━━━━━━━━━━━━━━━━━━ 0s 11ms/step - accuracy: 0.8444 - loss: 0.5052 716/750 ━━━━━━━━━━━━━━━━━━━━ 0s 11ms/step - accuracy: 0.8450 - loss: 0.5032 721/750 ━━━━━━━━━━━━━━━━━━━━ 0s 11ms/step - accuracy: 0.8456 - loss: 0.5012 726/750 ━━━━━━━━━━━━━━━━━━━━ 0s 11ms/step - accuracy: 0.8462 - loss: 0.4992 731/750 ━━━━━━━━━━━━━━━━━━━━ 0s 11ms/step - accuracy: 0.8468 - loss: 0.4973 736/750 ━━━━━━━━━━━━━━━━━━━━ 0s 11ms/step - accuracy: 0.8474 - loss: 0.4953 741/750 ━━━━━━━━━━━━━━━━━━━━ 0s 11ms/step - accuracy: 0.8480 - loss: 0.4934 746/750 ━━━━━━━━━━━━━━━━━━━━ 0s 11ms/step - accuracy: 0.8486 - loss: 0.4915 750/750 ━━━━━━━━━━━━━━━━━━━━ 10s 12ms/step - accuracy: 0.9362 - loss: 0.2088 - val_accuracy: 0.9793 - val_loss: 0.0667 Epoch 2/20 1/750 ━━━━━━━━━━━━━━━━━━━━ 19s 26ms/step - accuracy: 1.0000 - loss: 0.0102 6/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9925 - loss: 0.0248 11/750 ━━━━━━━━━━━━━━━━━━━━ 8s 11ms/step - accuracy: 0.9900 - loss: 0.0323 16/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9890 - loss: 0.0368 21/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9883 - loss: 0.0395 26/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9879 - loss: 0.0410 31/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9878 - loss: 0.0422 36/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9875 - loss: 0.0435 41/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9870 - loss: 0.0448 46/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9865 - loss: 0.0462 51/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9860 - loss: 0.0475 56/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9855 - loss: 0.0488 61/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9850 - loss: 0.0501 66/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9845 - loss: 0.0514 71/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9840 - loss: 0.0525 76/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9836 - loss: 0.0535 81/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9832 - loss: 0.0543 86/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9830 - loss: 0.0550 90/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9828 - loss: 0.0554 95/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9826 - loss: 0.0559 100/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.9825 - loss: 0.0563 105/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.9824 - loss: 0.0566 110/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.9823 - loss: 0.0569 115/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.9822 - loss: 0.0572 120/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.9821 - loss: 0.0574 125/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.9820 - loss: 0.0576 130/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.9820 - loss: 0.0577 135/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.9819 - loss: 0.0578 140/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.9819 - loss: 0.0578 145/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.9819 - loss: 0.0579 150/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.9819 - loss: 0.0579 155/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.9819 - loss: 0.0579 160/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.9819 - loss: 0.0579 165/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.9819 - loss: 0.0578 170/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.9819 - loss: 0.0578 175/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.9819 - loss: 0.0578 180/750 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.9819 - loss: 0.0578 186/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.9819 - loss: 0.0577 191/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.9819 - loss: 0.0577 196/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.9819 - loss: 0.0577 201/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.9819 - loss: 0.0577 206/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.9819 - loss: 0.0576 212/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.9819 - loss: 0.0576 217/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.9819 - loss: 0.0576 222/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.9819 - loss: 0.0576 227/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.9819 - loss: 0.0576 232/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.9819 - loss: 0.0576 237/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.9819 - loss: 0.0575 242/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.9819 - loss: 0.0575 247/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.9819 - loss: 0.0575 252/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.9819 - loss: 0.0575 257/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.9819 - loss: 0.0575 262/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.9818 - loss: 0.0575 267/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.9818 - loss: 0.0574 272/750 ━━━━━━━━━━━━━━━━━━━━ 5s 11ms/step - accuracy: 0.9818 - loss: 0.0574 277/750 ━━━━━━━━━━━━━━━━━━━━ 4s 11ms/step - accuracy: 0.9818 - loss: 0.0574 282/750 ━━━━━━━━━━━━━━━━━━━━ 4s 11ms/step - accuracy: 0.9818 - loss: 0.0574 287/750 ━━━━━━━━━━━━━━━━━━━━ 4s 11ms/step - accuracy: 0.9818 - loss: 0.0574 292/750 ━━━━━━━━━━━━━━━━━━━━ 4s 11ms/step - accuracy: 0.9818 - loss: 0.0574 297/750 ━━━━━━━━━━━━━━━━━━━━ 4s 11ms/step - accuracy: 0.9818 - loss: 0.0574 302/750 ━━━━━━━━━━━━━━━━━━━━ 4s 11ms/step - accuracy: 0.9817 - loss: 0.0574 307/750 ━━━━━━━━━━━━━━━━━━━━ 4s 11ms/step - accuracy: 0.9817 - loss: 0.0574 312/750 ━━━━━━━━━━━━━━━━━━━━ 4s 11ms/step - accuracy: 0.9817 - loss: 0.0574 318/750 ━━━━━━━━━━━━━━━━━━━━ 4s 11ms/step - accuracy: 0.9817 - loss: 0.0574 324/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9817 - loss: 0.0574 329/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9817 - loss: 0.0574 334/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9817 - loss: 0.0574 339/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9817 - loss: 0.0574 344/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9817 - loss: 0.0574 350/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9817 - loss: 0.0575 356/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9817 - loss: 0.0575 361/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9816 - loss: 0.0575 366/750 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9816 - loss: 0.0575 371/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9816 - loss: 0.0575 376/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9816 - loss: 0.0575 381/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9816 - loss: 0.0575 386/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9816 - loss: 0.0575 391/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9816 - loss: 0.0575 396/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9816 - loss: 0.0575 401/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9816 - loss: 0.0575 406/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9816 - loss: 0.0575 411/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9815 - loss: 0.0575 416/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9815 - loss: 0.0575 421/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9815 - loss: 0.0576 425/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9815 - loss: 0.0576 430/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9815 - loss: 0.0576 436/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9815 - loss: 0.0576 441/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9815 - loss: 0.0576 446/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9815 - loss: 0.0576 451/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9815 - loss: 0.0576 456/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9814 - loss: 0.0576 461/750 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.9814 - loss: 0.0576 466/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.9814 - loss: 0.0577 471/750 ━━━━━━━━━━━━━━━━━━━━ 2s 10ms/step - accuracy: 0.9814 - loss: 0.0577 475/750 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.9814 - loss: 0.0577 480/750 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.9814 - loss: 0.0577 485/750 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.9814 - loss: 0.0577 490/750 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.9814 - loss: 0.0577 495/750 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.9814 - loss: 0.0577 500/750 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.9814 - loss: 0.0577 505/750 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.9814 - loss: 0.0577 510/750 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.9814 - loss: 0.0577 515/750 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.9814 - loss: 0.0577 520/750 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.9814 - loss: 0.0577 524/750 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.9814 - loss: 0.0577 528/750 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.9814 - loss: 0.0577 532/750 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.9813 - loss: 0.0578 537/750 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.9813 - loss: 0.0578 542/750 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.9813 - loss: 0.0578 547/750 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.9813 - loss: 0.0578 552/750 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.9813 - loss: 0.0578 556/750 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.9813 - loss: 0.0578 560/750 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.9813 - loss: 0.0578 564/750 ━━━━━━━━━━━━━━━━━━━━ 1s 11ms/step - accuracy: 0.9813 - loss: 0.0577 569/750 ━━━━━━━━━━━━━━━━━━━━ 1s 11ms/step - accuracy: 0.9813 - loss: 0.0577 574/750 ━━━━━━━━━━━━━━━━━━━━ 1s 11ms/step - accuracy: 0.9813 - loss: 0.0577 579/750 ━━━━━━━━━━━━━━━━━━━━ 1s 11ms/step - accuracy: 0.9813 - loss: 0.0577 584/750 ━━━━━━━━━━━━━━━━━━━━ 1s 11ms/step - accuracy: 0.9813 - loss: 0.0577 589/750 ━━━━━━━━━━━━━━━━━━━━ 1s 11ms/step - accuracy: 0.9813 - loss: 0.0577 594/750 ━━━━━━━━━━━━━━━━━━━━ 1s 11ms/step - accuracy: 0.9813 - loss: 0.0577 599/750 ━━━━━━━━━━━━━━━━━━━━ 1s 11ms/step - accuracy: 0.9813 - loss: 0.0577 604/750 ━━━━━━━━━━━━━━━━━━━━ 1s 11ms/step - accuracy: 0.9813 - loss: 0.0577 609/750 ━━━━━━━━━━━━━━━━━━━━ 1s 11ms/step - accuracy: 0.9813 - loss: 0.0577 614/750 ━━━━━━━━━━━━━━━━━━━━ 1s 11ms/step - accuracy: 0.9813 - loss: 0.0577 619/750 ━━━━━━━━━━━━━━━━━━━━ 1s 11ms/step - accuracy: 0.9813 - loss: 0.0577 624/750 ━━━━━━━━━━━━━━━━━━━━ 1s 11ms/step - accuracy: 0.9813 - loss: 0.0577 629/750 ━━━━━━━━━━━━━━━━━━━━ 1s 11ms/step - accuracy: 0.9813 - loss: 0.0577 634/750 ━━━━━━━━━━━━━━━━━━━━ 1s 11ms/step - accuracy: 0.9813 - loss: 0.0577 639/750 ━━━━━━━━━━━━━━━━━━━━ 1s 11ms/step - accuracy: 0.9813 - loss: 0.0577 644/750 ━━━━━━━━━━━━━━━━━━━━ 1s 11ms/step - accuracy: 0.9813 - loss: 0.0577 649/750 ━━━━━━━━━━━━━━━━━━━━ 1s 11ms/step - accuracy: 0.9813 - loss: 0.0577 654/750 ━━━━━━━━━━━━━━━━━━━━ 1s 11ms/step - accuracy: 0.9813 - loss: 0.0577 659/750 ━━━━━━━━━━━━━━━━━━━━ 0s 11ms/step - accuracy: 0.9813 - loss: 0.0577 663/750 ━━━━━━━━━━━━━━━━━━━━ 0s 11ms/step - accuracy: 0.9813 - loss: 0.0577 668/750 ━━━━━━━━━━━━━━━━━━━━ 0s 11ms/step - accuracy: 0.9813 - loss: 0.0577 674/750 ━━━━━━━━━━━━━━━━━━━━ 0s 11ms/step - accuracy: 0.9813 - loss: 0.0576 679/750 ━━━━━━━━━━━━━━━━━━━━ 0s 11ms/step - accuracy: 0.9814 - loss: 0.0576 684/750 ━━━━━━━━━━━━━━━━━━━━ 0s 11ms/step - accuracy: 0.9814 - loss: 0.0576 689/750 ━━━━━━━━━━━━━━━━━━━━ 0s 11ms/step - accuracy: 0.9814 - loss: 0.0576 694/750 ━━━━━━━━━━━━━━━━━━━━ 0s 11ms/step - accuracy: 0.9814 - loss: 0.0576 699/750 ━━━━━━━━━━━━━━━━━━━━ 0s 11ms/step - accuracy: 0.9814 - loss: 0.0576 704/750 ━━━━━━━━━━━━━━━━━━━━ 0s 11ms/step - accuracy: 0.9814 - loss: 0.0576 709/750 ━━━━━━━━━━━━━━━━━━━━ 0s 11ms/step - accuracy: 0.9814 - loss: 0.0576 714/750 ━━━━━━━━━━━━━━━━━━━━ 0s 11ms/step - accuracy: 0.9814 - loss: 0.0576 719/750 ━━━━━━━━━━━━━━━━━━━━ 0s 11ms/step - accuracy: 0.9814 - loss: 0.0576 725/750 ━━━━━━━━━━━━━━━━━━━━ 0s 11ms/step - accuracy: 0.9814 - loss: 0.0576 730/750 ━━━━━━━━━━━━━━━━━━━━ 0s 11ms/step - accuracy: 0.9814 - loss: 0.0575 736/750 ━━━━━━━━━━━━━━━━━━━━ 0s 11ms/step - accuracy: 0.9814 - loss: 0.0575 741/750 ━━━━━━━━━━━━━━━━━━━━ 0s 11ms/step - accuracy: 0.9814 - loss: 0.0575 746/750 ━━━━━━━━━━━━━━━━━━━━ 0s 11ms/step - accuracy: 0.9814 - loss: 0.0575 750/750 ━━━━━━━━━━━━━━━━━━━━ 9s 12ms/step - accuracy: 0.9822 - loss: 0.0553 - val_accuracy: 0.9858 - val_loss: 0.0495 Epoch 3/20 1/750 ━━━━━━━━━━━━━━━━━━━━ 21s 29ms/step - accuracy: 0.9844 - loss: 0.0214 6/750 ━━━━━━━━━━━━━━━━━━━━ 8s 12ms/step - accuracy: 0.9882 - loss: 0.0342 11/750 ━━━━━━━━━━━━━━━━━━━━ 8s 12ms/step - accuracy: 0.9903 - loss: 0.0327 16/750 ━━━━━━━━━━━━━━━━━━━━ 8s 11ms/step - accuracy: 0.9913 - loss: 0.0314 21/750 ━━━━━━━━━━━━━━━━━━━━ 8s 11ms/step - accuracy: 0.9913 - loss: 0.0321 26/750 ━━━━━━━━━━━━━━━━━━━━ 8s 11ms/step - accuracy: 0.9912 - loss: 0.0329 31/750 ━━━━━━━━━━━━━━━━━━━━ 8s 11ms/step - accuracy: 0.9909 - loss: 0.0337 36/750 ━━━━━━━━━━━━━━━━━━━━ 8s 11ms/step - accuracy: 0.9907 - loss: 0.0344 41/750 ━━━━━━━━━━━━━━━━━━━━ 8s 11ms/step - accuracy: 0.9906 - loss: 0.0349 46/750 ━━━━━━━━━━━━━━━━━━━━ 8s 12ms/step - accuracy: 0.9904 - loss: 0.0352 51/750 ━━━━━━━━━━━━━━━━━━━━ 8s 12ms/step - accuracy: 0.9903 - loss: 0.0356 56/750 ━━━━━━━━━━━━━━━━━━━━ 7s 12ms/step - accuracy: 0.9900 - loss: 0.0360 61/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9898 - loss: 0.0364 66/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9895 - loss: 0.0367 71/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9894 - loss: 0.0368 76/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9892 - loss: 0.0369 81/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9891 - loss: 0.0370 86/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9890 - loss: 0.0371 91/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9890 - loss: 0.0371 96/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9889 - loss: 0.0371 101/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9888 - loss: 0.0372 106/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9888 - loss: 0.0372 111/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9887 - loss: 0.0373 116/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9886 - loss: 0.0373 121/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9886 - loss: 0.0373 126/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9886 - loss: 0.0373 131/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9886 - loss: 0.0373 136/750 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9885 - loss: 0.0373 140/750 ━━━━━━━━━━━━━━━━━━━━ 7s 12ms/step - accuracy: 0.9885 - loss: 0.0373 145/750 ━━━━━━━━━━━━━━━━━━━━ 7s 12ms/step - accuracy: 0.9885 - loss: 0.0374 150/750 ━━━━━━━━━━━━━━━━━━━━ 6s 12ms/step - accuracy: 0.9885 - loss: 0.0374 155/750 ━━━━━━━━━━━━━━━━━━━━ 6s 12ms/step - accuracy: 0.9884 - loss: 0.0374 160/750 ━━━━━━━━━━━━━━━━━━━━ 6s 12ms/step - accuracy: 0.9884 - loss: 0.0374 165/750 ━━━━━━━━━━━━━━━━━━━━ 6s 12ms/step - accuracy: 0.9884 - loss: 0.0374 170/750 ━━━━━━━━━━━━━━━━━━━━ 6s 12ms/step - accuracy: 0.9884 - loss: 0.0374 175/750 ━━━━━━━━━━━━━━━━━━━━ 6s 12ms/step - accuracy: 0.9884 - loss: 0.0374 179/750 ━━━━━━━━━━━━━━━━━━━━ 6s 12ms/step - accuracy: 0.9884 - loss: 0.0374 184/750 ━━━━━━━━━━━━━━━━━━━━ 6s 12ms/step - accuracy: 0.9884 - loss: 0.0373 189/750 ━━━━━━━━━━━━━━━━━━━━ 6s 12ms/step - accuracy: 0.9884 - loss: 0.0373 194/750 ━━━━━━━━━━━━━━━━━━━━ 6s 12ms/step - accuracy: 0.9884 - loss: 0.0373 199/750 ━━━━━━━━━━━━━━━━━━━━ 6s 12ms/step - accuracy: 0.9884 - loss: 0.0372 204/750 ━━━━━━━━━━━━━━━━━━━━ 6s 12ms/step - accuracy: 0.9884 - loss: 0.0372 208/750 ━━━━━━━━━━━━━━━━━━━━ 6s 12ms/step - accuracy: 0.9884 - loss: 0.0371 212/750 ━━━━━━━━━━━━━━━━━━━━ 6s 12ms/step - accuracy: 0.9884 - loss: 0.0371 216/750 ━━━━━━━━━━━━━━━━━━━━ 6s 12ms/step - accuracy: 0.9884 - loss: 0.0371 220/750 ━━━━━━━━━━━━━━━━━━━━ 6s 12ms/step - accuracy: 0.9884 - loss: 0.0371 224/750 ━━━━━━━━━━━━━━━━━━━━ 6s 12ms/step - accuracy: 0.9884 - loss: 0.0370 228/750 ━━━━━━━━━━━━━━━━━━━━ 6s 12ms/step - accuracy: 0.9884 - loss: 0.0370 231/750 ━━━━━━━━━━━━━━━━━━━━ 6s 12ms/step - accuracy: 0.9884 - loss: 0.0370 235/750 ━━━━━━━━━━━━━━━━━━━━ 6s 12ms/step - accuracy: 0.9884 - loss: 0.0370 239/750 ━━━━━━━━━━━━━━━━━━━━ 6s 12ms/step - accuracy: 0.9884 - loss: 0.0369 244/750 ━━━━━━━━━━━━━━━━━━━━ 6s 12ms/step - accuracy: 0.9884 - loss: 0.0369 249/750 ━━━━━━━━━━━━━━━━━━━━ 6s 12ms/step - accuracy: 0.9884 - loss: 0.0369 254/750 ━━━━━━━━━━━━━━━━━━━━ 6s 12ms/step - accuracy: 0.9885 - loss: 0.0369 259/750 ━━━━━━━━━━━━━━━━━━━━ 5s 12ms/step - accuracy: 0.9885 - loss: 0.0368 263/750 ━━━━━━━━━━━━━━━━━━━━ 5s 12ms/step - accuracy: 0.9885 - loss: 0.0368 267/750 ━━━━━━━━━━━━━━━━━━━━ 5s 12ms/step - accuracy: 0.9885 - loss: 0.0368 272/750 ━━━━━━━━━━━━━━━━━━━━ 5s 12ms/step - accuracy: 0.9885 - loss: 0.0368 277/750 ━━━━━━━━━━━━━━━━━━━━ 5s 12ms/step - accuracy: 0.9885 - loss: 0.0368 282/750 ━━━━━━━━━━━━━━━━━━━━ 5s 12ms/step - accuracy: 0.9885 - loss: 0.0368 287/750 ━━━━━━━━━━━━━━━━━━━━ 5s 12ms/step - accuracy: 0.9885 - loss: 0.0368 292/750 ━━━━━━━━━━━━━━━━━━━━ 5s 12ms/step - accuracy: 0.9885 - loss: 0.0368 297/750 ━━━━━━━━━━━━━━━━━━━━ 5s 12ms/step - accuracy: 0.9885 - loss: 0.0368 302/750 ━━━━━━━━━━━━━━━━━━━━ 5s 12ms/step - accuracy: 0.9885 - loss: 0.0367 307/750 ━━━━━━━━━━━━━━━━━━━━ 5s 12ms/step - accuracy: 0.9886 - loss: 0.0368 312/750 ━━━━━━━━━━━━━━━━━━━━ 5s 12ms/step - accuracy: 0.9885 - loss: 0.0368 317/750 ━━━━━━━━━━━━━━━━━━━━ 5s 12ms/step - accuracy: 0.9885 - loss: 0.0368 322/750 ━━━━━━━━━━━━━━━━━━━━ 5s 12ms/step - accuracy: 0.9885 - loss: 0.0368 327/750 ━━━━━━━━━━━━━━━━━━━━ 5s 12ms/step - accuracy: 0.9885 - loss: 0.0368 332/750 ━━━━━━━━━━━━━━━━━━━━ 5s 12ms/step - accuracy: 0.9885 - loss: 0.0368 337/750 ━━━━━━━━━━━━━━━━━━━━ 4s 12ms/step - accuracy: 0.9885 - loss: 0.0368 342/750 ━━━━━━━━━━━━━━━━━━━━ 4s 12ms/step - accuracy: 0.9885 - loss: 0.0369 347/750 ━━━━━━━━━━━━━━━━━━━━ 4s 12ms/step - accuracy: 0.9885 - loss: 0.0369 350/750 ━━━━━━━━━━━━━━━━━━━━ 4s 12ms/step - accuracy: 0.9885 - loss: 0.0369 355/750 ━━━━━━━━━━━━━━━━━━━━ 4s 12ms/step - accuracy: 0.9885 - loss: 0.0369 360/750 ━━━━━━━━━━━━━━━━━━━━ 4s 12ms/step - accuracy: 0.9885 - loss: 0.0369 365/750 ━━━━━━━━━━━━━━━━━━━━ 4s 12ms/step - accuracy: 0.9885 - loss: 0.0370 370/750 ━━━━━━━━━━━━━━━━━━━━ 4s 12ms/step - accuracy: 0.9885 - loss: 0.0370 375/750 ━━━━━━━━━━━━━━━━━━━━ 4s 12ms/step - accuracy: 0.9885 - loss: 0.0370 380/750 ━━━━━━━━━━━━━━━━━━━━ 4s 12ms/step - accuracy: 0.9885 - loss: 0.0370 385/750 ━━━━━━━━━━━━━━━━━━━━ 4s 12ms/step - accuracy: 0.9885 - loss: 0.0371 390/750 ━━━━━━━━━━━━━━━━━━━━ 4s 12ms/step - accuracy: 0.9885 - loss: 0.0371 394/750 ━━━━━━━━━━━━━━━━━━━━ 4s 12ms/step - accuracy: 0.9885 - loss: 0.0371 399/750 ━━━━━━━━━━━━━━━━━━━━ 4s 12ms/step - accuracy: 0.9885 - loss: 0.0372 404/750 ━━━━━━━━━━━━━━━━━━━━ 4s 12ms/step - accuracy: 0.9885 - loss: 0.0372 409/750 ━━━━━━━━━━━━━━━━━━━━ 4s 12ms/step - accuracy: 0.9884 - loss: 0.0372 414/750 ━━━━━━━━━━━━━━━━━━━━ 4s 12ms/step - accuracy: 0.9884 - loss: 0.0373 419/750 ━━━━━━━━━━━━━━━━━━━━ 3s 12ms/step - accuracy: 0.9884 - loss: 0.0373 424/750 ━━━━━━━━━━━━━━━━━━━━ 3s 12ms/step - accuracy: 0.9884 - loss: 0.0373 429/750 ━━━━━━━━━━━━━━━━━━━━ 3s 12ms/step - accuracy: 0.9884 - loss: 0.0374 433/750 ━━━━━━━━━━━━━━━━━━━━ 3s 12ms/step - accuracy: 0.9884 - loss: 0.0374 437/750 ━━━━━━━━━━━━━━━━━━━━ 3s 12ms/step - accuracy: 0.9884 - loss: 0.0374 442/750 ━━━━━━━━━━━━━━━━━━━━ 3s 12ms/step - accuracy: 0.9884 - loss: 0.0375 447/750 ━━━━━━━━━━━━━━━━━━━━ 3s 12ms/step - accuracy: 0.9884 - loss: 0.0375 452/750 ━━━━━━━━━━━━━━━━━━━━ 3s 12ms/step - accuracy: 0.9884 - loss: 0.0375 457/750 ━━━━━━━━━━━━━━━━━━━━ 3s 12ms/step - accuracy: 0.9883 - loss: 0.0376 462/750 ━━━━━━━━━━━━━━━━━━━━ 3s 12ms/step - accuracy: 0.9883 - loss: 0.0376 467/750 ━━━━━━━━━━━━━━━━━━━━ 3s 12ms/step - accuracy: 0.9883 - loss: 0.0376 471/750 ━━━━━━━━━━━━━━━━━━━━ 3s 12ms/step - accuracy: 0.9883 - loss: 0.0376 475/750 ━━━━━━━━━━━━━━━━━━━━ 3s 12ms/step - accuracy: 0.9883 - loss: 0.0377 480/750 ━━━━━━━━━━━━━━━━━━━━ 3s 12ms/step - accuracy: 0.9883 - loss: 0.0377 485/750 ━━━━━━━━━━━━━━━━━━━━ 3s 12ms/step - accuracy: 0.9883 - loss: 0.0377 490/750 ━━━━━━━━━━━━━━━━━━━━ 3s 12ms/step - accuracy: 0.9883 - loss: 0.0377 495/750 ━━━━━━━━━━━━━━━━━━━━ 3s 12ms/step - accuracy: 0.9883 - loss: 0.0378 500/750 ━━━━━━━━━━━━━━━━━━━━ 2s 12ms/step - accuracy: 0.9883 - loss: 0.0378 505/750 ━━━━━━━━━━━━━━━━━━━━ 2s 12ms/step - accuracy: 0.9883 - loss: 0.0378 510/750 ━━━━━━━━━━━━━━━━━━━━ 2s 12ms/step - accuracy: 0.9883 - loss: 0.0378 515/750 ━━━━━━━━━━━━━━━━━━━━ 2s 12ms/step - accuracy: 0.9882 - loss: 0.0379 520/750 ━━━━━━━━━━━━━━━━━━━━ 2s 12ms/step - accuracy: 0.9882 - loss: 0.0379 525/750 ━━━━━━━━━━━━━━━━━━━━ 2s 12ms/step - accuracy: 0.9882 - loss: 0.0379 530/750 ━━━━━━━━━━━━━━━━━━━━ 2s 12ms/step - accuracy: 0.9882 - loss: 0.0379 535/750 ━━━━━━━━━━━━━━━━━━━━ 2s 12ms/step - accuracy: 0.9882 - loss: 0.0379 540/750 ━━━━━━━━━━━━━━━━━━━━ 2s 12ms/step - accuracy: 0.9882 - loss: 0.0380 545/750 ━━━━━━━━━━━━━━━━━━━━ 2s 12ms/step - accuracy: 0.9882 - loss: 0.0380 550/750 ━━━━━━━━━━━━━━━━━━━━ 2s 12ms/step - accuracy: 0.9882 - loss: 0.0380 555/750 ━━━━━━━━━━━━━━━━━━━━ 2s 12ms/step - accuracy: 0.9882 - loss: 0.0380 559/750 ━━━━━━━━━━━━━━━━━━━━ 2s 12ms/step - accuracy: 0.9882 - loss: 0.0380 563/750 ━━━━━━━━━━━━━━━━━━━━ 2s 12ms/step - accuracy: 0.9882 - loss: 0.0381 567/750 ━━━━━━━━━━━━━━━━━━━━ 2s 12ms/step - accuracy: 0.9882 - loss: 0.0381 571/750 ━━━━━━━━━━━━━━━━━━━━ 2s 12ms/step - accuracy: 0.9882 - loss: 0.0381 576/750 ━━━━━━━━━━━━━━━━━━━━ 2s 12ms/step - accuracy: 0.9882 - loss: 0.0381 581/750 ━━━━━━━━━━━━━━━━━━━━ 2s 12ms/step - accuracy: 0.9882 - loss: 0.0381 586/750 ━━━━━━━━━━━━━━━━━━━━ 1s 12ms/step - accuracy: 0.9882 - loss: 0.0381 590/750 ━━━━━━━━━━━━━━━━━━━━ 1s 12ms/step - accuracy: 0.9882 - loss: 0.0381 595/750 ━━━━━━━━━━━━━━━━━━━━ 1s 12ms/step - accuracy: 0.9882 - loss: 0.0382 600/750 ━━━━━━━━━━━━━━━━━━━━ 1s 12ms/step - accuracy: 0.9882 - loss: 0.0382 605/750 ━━━━━━━━━━━━━━━━━━━━ 1s 12ms/step - accuracy: 0.9882 - loss: 0.0382 610/750 ━━━━━━━━━━━━━━━━━━━━ 1s 12ms/step - accuracy: 0.9881 - loss: 0.0382 615/750 ━━━━━━━━━━━━━━━━━━━━ 1s 12ms/step - accuracy: 0.9881 - loss: 0.0382 620/750 ━━━━━━━━━━━━━━━━━━━━ 1s 12ms/step - accuracy: 0.9881 - loss: 0.0382 625/750 ━━━━━━━━━━━━━━━━━━━━ 1s 12ms/step - accuracy: 0.9881 - loss: 0.0382 630/750 ━━━━━━━━━━━━━━━━━━━━ 1s 12ms/step - accuracy: 0.9881 - loss: 0.0382 635/750 ━━━━━━━━━━━━━━━━━━━━ 1s 12ms/step - accuracy: 0.9881 - loss: 0.0382 640/750 ━━━━━━━━━━━━━━━━━━━━ 1s 12ms/step - accuracy: 0.9881 - loss: 0.0382 645/750 ━━━━━━━━━━━━━━━━━━━━ 1s 12ms/step - accuracy: 0.9881 - loss: 0.0383 650/750 ━━━━━━━━━━━━━━━━━━━━ 1s 12ms/step - accuracy: 0.9881 - loss: 0.0383 655/750 ━━━━━━━━━━━━━━━━━━━━ 1s 12ms/step - accuracy: 0.9881 - loss: 0.0383 660/750 ━━━━━━━━━━━━━━━━━━━━ 1s 12ms/step - accuracy: 0.9881 - loss: 0.0383 665/750 ━━━━━━━━━━━━━━━━━━━━ 1s 12ms/step - accuracy: 0.9881 - loss: 0.0383 670/750 ━━━━━━━━━━━━━━━━━━━━ 0s 12ms/step - accuracy: 0.9881 - loss: 0.0383 675/750 ━━━━━━━━━━━━━━━━━━━━ 0s 12ms/step - accuracy: 0.9881 - loss: 0.0383 680/750 ━━━━━━━━━━━━━━━━━━━━ 0s 12ms/step - accuracy: 0.9881 - loss: 0.0383 685/750 ━━━━━━━━━━━━━━━━━━━━ 0s 12ms/step - accuracy: 0.9881 - loss: 0.0384 690/750 ━━━━━━━━━━━━━━━━━━━━ 0s 12ms/step - accuracy: 0.9881 - loss: 0.0384 694/750 ━━━━━━━━━━━━━━━━━━━━ 0s 12ms/step - accuracy: 0.9881 - loss: 0.0384 699/750 ━━━━━━━━━━━━━━━━━━━━ 0s 12ms/step - accuracy: 0.9881 - loss: 0.0384 704/750 ━━━━━━━━━━━━━━━━━━━━ 0s 12ms/step - accuracy: 0.9881 - loss: 0.0384 709/750 ━━━━━━━━━━━━━━━━━━━━ 0s 12ms/step - accuracy: 0.9881 - loss: 0.0384 714/750 ━━━━━━━━━━━━━━━━━━━━ 0s 12ms/step - accuracy: 0.9881 - loss: 0.0384 719/750 ━━━━━━━━━━━━━━━━━━━━ 0s 12ms/step - accuracy: 0.9881 - loss: 0.0384 724/750 ━━━━━━━━━━━━━━━━━━━━ 0s 12ms/step - accuracy: 0.9881 - loss: 0.0384 729/750 ━━━━━━━━━━━━━━━━━━━━ 0s 12ms/step - accuracy: 0.9881 - loss: 0.0384 734/750 ━━━━━━━━━━━━━━━━━━━━ 0s 12ms/step - accuracy: 0.9881 - loss: 0.0384 739/750 ━━━━━━━━━━━━━━━━━━━━ 0s 12ms/step - accuracy: 0.9881 - loss: 0.0384 742/750 ━━━━━━━━━━━━━━━━━━━━ 0s 12ms/step - accuracy: 0.9881 - loss: 0.0384 745/750 ━━━━━━━━━━━━━━━━━━━━ 0s 12ms/step - accuracy: 0.9881 - loss: 0.0385 749/750 ━━━━━━━━━━━━━━━━━━━━ 0s 12ms/step - accuracy: 0.9881 - loss: 0.0385 750/750 ━━━━━━━━━━━━━━━━━━━━ 10s 13ms/step - accuracy: 0.9878 - loss: 0.0394 - val_accuracy: 0.9858 - val_loss: 0.0504 Epoch 4/20 1/750 ━━━━━━━━━━━━━━━━━━━━ 18s 25ms/step - accuracy: 1.0000 - loss: 0.0028 4/750 ━━━━━━━━━━━━━━━━━━━━ 14s 19ms/step - accuracy: 0.9977 - loss: 0.0183 9/750 ━━━━━━━━━━━━━━━━━━━━ 10s 14ms/step - accuracy: 0.9970 - loss: 0.0219 14/750 ━━━━━━━━━━━━━━━━━━━━ 9s 13ms/step - accuracy: 0.9961 - loss: 0.0229 19/750 ━━━━━━━━━━━━━━━━━━━━ 9s 13ms/step - accuracy: 0.9948 - loss: 0.0247 24/750 ━━━━━━━━━━━━━━━━━━━━ 9s 12ms/step - accuracy: 0.9939 - loss: 0.0260 29/750 ━━━━━━━━━━━━━━━━━━━━ 8s 12ms/step - accuracy: 0.9934 - loss: 0.0264 34/750 ━━━━━━━━━━━━━━━━━━━━ 8s 12ms/step - accuracy: 0.9931 - loss: 0.0265 39/750 ━━━━━━━━━━━━━━━━━━━━ 8s 12ms/step - accuracy: 0.9930 - loss: 0.0264 44/750 ━━━━━━━━━━━━━━━━━━━━ 8s 12ms/step - accuracy: 0.9929 - loss: 0.0263 48/750 ━━━━━━━━━━━━━━━━━━━━ 8s 12ms/step - accuracy: 0.9928 - loss: 0.0263 53/750 ━━━━━━━━━━━━━━━━━━━━ 8s 12ms/step - accuracy: 0.9927 - loss: 0.0263 58/750 ━━━━━━━━━━━━━━━━━━━━ 8s 12ms/step - accuracy: 0.9927 - loss: 0.0261 63/750 ━━━━━━━━━━━━━━━━━━━━ 8s 12ms/step - accuracy: 0.9927 - loss: 0.0261 68/750 ━━━━━━━━━━━━━━━━━━━━ 8s 12ms/step - accuracy: 0.9927 - loss: 0.0260 73/750 ━━━━━━━━━━━━━━━━━━━━ 8s 12ms/step - accuracy: 0.9927 - loss: 0.0258 78/750 ━━━━━━━━━━━━━━━━━━━━ 8s 12ms/step - accuracy: 0.9927 - loss: 0.0257 83/750 ━━━━━━━━━━━━━━━━━━━━ 7s 12ms/step - accuracy: 0.9927 - loss: 0.0257 88/750 ━━━━━━━━━━━━━━━━━━━━ 7s 12ms/step - accuracy: 0.9927 - loss: 0.0255 93/750 ━━━━━━━━━━━━━━━━━━━━ 7s 12ms/step - accuracy: 0.9928 - loss: 0.0254 98/750 ━━━━━━━━━━━━━━━━━━━━ 7s 12ms/step - accuracy: 0.9928 - loss: 0.0253 103/750 ━━━━━━━━━━━━━━━━━━━━ 7s 12ms/step - accuracy: 0.9928 - loss: 0.0252 107/750 ━━━━━━━━━━━━━━━━━━━━ 7s 12ms/step - accuracy: 0.9928 - loss: 0.0252 111/750 ━━━━━━━━━━━━━━━━━━━━ 7s 12ms/step - accuracy: 0.9928 - loss: 0.0252 115/750 ━━━━━━━━━━━━━━━━━━━━ 7s 12ms/step - accuracy: 0.9928 - loss: 0.0251 119/750 ━━━━━━━━━━━━━━━━━━━━ 7s 12ms/step - accuracy: 0.9928 - loss: 0.0251 123/750 ━━━━━━━━━━━━━━━━━━━━ 7s 12ms/step - accuracy: 0.9928 - loss: 0.0251 127/750 ━━━━━━━━━━━━━━━━━━━━ 7s 12ms/step - accuracy: 0.9928 - loss: 0.0251 130/750 ━━━━━━━━━━━━━━━━━━━━ 7s 12ms/step - accuracy: 0.9928 - loss: 0.0251 133/750 ━━━━━━━━━━━━━━━━━━━━ 7s 13ms/step - accuracy: 0.9928 - loss: 0.0251 136/750 ━━━━━━━━━━━━━━━━━━━━ 7s 13ms/step - accuracy: 0.9928 - loss: 0.0251 140/750 ━━━━━━━━━━━━━━━━━━━━ 7s 13ms/step - accuracy: 0.9928 - loss: 0.0251 144/750 ━━━━━━━━━━━━━━━━━━━━ 7s 13ms/step - accuracy: 0.9928 - loss: 0.0251 148/750 ━━━━━━━━━━━━━━━━━━━━ 7s 13ms/step - accuracy: 0.9928 - loss: 0.0251 152/750 ━━━━━━━━━━━━━━━━━━━━ 7s 13ms/step - accuracy: 0.9928 - loss: 0.0251 155/750 ━━━━━━━━━━━━━━━━━━━━ 7s 13ms/step - accuracy: 0.9928 - loss: 0.0251 158/750 ━━━━━━━━━━━━━━━━━━━━ 7s 13ms/step - accuracy: 0.9928 - loss: 0.0251 162/750 ━━━━━━━━━━━━━━━━━━━━ 7s 14ms/step - accuracy: 0.9927 - loss: 0.0251 165/750 ━━━━━━━━━━━━━━━━━━━━ 7s 14ms/step - accuracy: 0.9927 - loss: 0.0251 169/750 ━━━━━━━━━━━━━━━━━━━━ 7s 14ms/step - accuracy: 0.9927 - loss: 0.0251 173/750 ━━━━━━━━━━━━━━━━━━━━ 7s 14ms/step - accuracy: 0.9927 - loss: 0.0251 177/750 ━━━━━━━━━━━━━━━━━━━━ 7s 14ms/step - accuracy: 0.9927 - loss: 0.0251 180/750 ━━━━━━━━━━━━━━━━━━━━ 7s 14ms/step - accuracy: 0.9927 - loss: 0.0251 183/750 ━━━━━━━━━━━━━━━━━━━━ 7s 14ms/step - accuracy: 0.9926 - loss: 0.0251 186/750 ━━━━━━━━━━━━━━━━━━━━ 7s 14ms/step - accuracy: 0.9926 - loss: 0.0251 189/750 ━━━━━━━━━━━━━━━━━━━━ 7s 14ms/step - accuracy: 0.9926 - loss: 0.0251 192/750 ━━━━━━━━━━━━━━━━━━━━ 7s 14ms/step - accuracy: 0.9926 - loss: 0.0251 195/750 ━━━━━━━━━━━━━━━━━━━━ 7s 14ms/step - accuracy: 0.9926 - loss: 0.0252 199/750 ━━━━━━━━━━━━━━━━━━━━ 7s 14ms/step - accuracy: 0.9926 - loss: 0.0252 202/750 ━━━━━━━━━━━━━━━━━━━━ 7s 14ms/step - accuracy: 0.9925 - loss: 0.0252 205/750 ━━━━━━━━━━━━━━━━━━━━ 7s 14ms/step - accuracy: 0.9925 - loss: 0.0252 208/750 ━━━━━━━━━━━━━━━━━━━━ 7s 14ms/step - accuracy: 0.9925 - loss: 0.0253 211/750 ━━━━━━━━━━━━━━━━━━━━ 7s 15ms/step - accuracy: 0.9925 - loss: 0.0253 214/750 ━━━━━━━━━━━━━━━━━━━━ 7s 15ms/step - accuracy: 0.9925 - loss: 0.0253 217/750 ━━━━━━━━━━━━━━━━━━━━ 7s 15ms/step - accuracy: 0.9925 - loss: 0.0253 220/750 ━━━━━━━━━━━━━━━━━━━━ 7s 15ms/step - accuracy: 0.9925 - loss: 0.0254 223/750 ━━━━━━━━━━━━━━━━━━━━ 7s 15ms/step - accuracy: 0.9924 - loss: 0.0254 227/750 ━━━━━━━━━━━━━━━━━━━━ 7s 15ms/step - accuracy: 0.9924 - loss: 0.0254 231/750 ━━━━━━━━━━━━━━━━━━━━ 7s 15ms/step - accuracy: 0.9924 - loss: 0.0255 235/750 ━━━━━━━━━━━━━━━━━━━━ 7s 15ms/step - accuracy: 0.9924 - loss: 0.0255 239/750 ━━━━━━━━━━━━━━━━━━━━ 7s 15ms/step - accuracy: 0.9924 - loss: 0.0255 243/750 ━━━━━━━━━━━━━━━━━━━━ 7s 15ms/step - accuracy: 0.9923 - loss: 0.0256 247/750 ━━━━━━━━━━━━━━━━━━━━ 7s 15ms/step - accuracy: 0.9923 - loss: 0.0256 251/750 ━━━━━━━━━━━━━━━━━━━━ 7s 15ms/step - accuracy: 0.9923 - loss: 0.0256 255/750 ━━━━━━━━━━━━━━━━━━━━ 7s 15ms/step - accuracy: 0.9923 - loss: 0.0257 259/750 ━━━━━━━━━━━━━━━━━━━━ 7s 15ms/step - accuracy: 0.9923 - loss: 0.0257 263/750 ━━━━━━━━━━━━━━━━━━━━ 7s 15ms/step - accuracy: 0.9922 - loss: 0.0258 268/750 ━━━━━━━━━━━━━━━━━━━━ 7s 15ms/step - accuracy: 0.9922 - loss: 0.0258 272/750 ━━━━━━━━━━━━━━━━━━━━ 6s 15ms/step - accuracy: 0.9922 - loss: 0.0259 276/750 ━━━━━━━━━━━━━━━━━━━━ 6s 15ms/step - accuracy: 0.9922 - loss: 0.0259 280/750 ━━━━━━━━━━━━━━━━━━━━ 6s 15ms/step - accuracy: 0.9921 - loss: 0.0260 284/750 ━━━━━━━━━━━━━━━━━━━━ 6s 15ms/step - accuracy: 0.9921 - loss: 0.0260 288/750 ━━━━━━━━━━━━━━━━━━━━ 6s 15ms/step - accuracy: 0.9921 - loss: 0.0261 292/750 ━━━━━━━━━━━━━━━━━━━━ 6s 15ms/step - accuracy: 0.9921 - loss: 0.0261 296/750 ━━━━━━━━━━━━━━━━━━━━ 6s 15ms/step - accuracy: 0.9920 - loss: 0.0262 300/750 ━━━━━━━━━━━━━━━━━━━━ 6s 15ms/step - accuracy: 0.9920 - loss: 0.0262 305/750 ━━━━━━━━━━━━━━━━━━━━ 6s 14ms/step - accuracy: 0.9920 - loss: 0.0263 310/750 ━━━━━━━━━━━━━━━━━━━━ 6s 14ms/step - accuracy: 0.9920 - loss: 0.0263 314/750 ━━━━━━━━━━━━━━━━━━━━ 6s 14ms/step - accuracy: 0.9919 - loss: 0.0264 319/750 ━━━━━━━━━━━━━━━━━━━━ 6s 14ms/step - accuracy: 0.9919 - loss: 0.0264 323/750 ━━━━━━━━━━━━━━━━━━━━ 6s 14ms/step - accuracy: 0.9919 - loss: 0.0264 328/750 ━━━━━━━━━━━━━━━━━━━━ 6s 14ms/step - accuracy: 0.9919 - loss: 0.0265 333/750 ━━━━━━━━━━━━━━━━━━━━ 5s 14ms/step - accuracy: 0.9918 - loss: 0.0266 338/750 ━━━━━━━━━━━━━━━━━━━━ 5s 14ms/step - accuracy: 0.9918 - loss: 0.0266 343/750 ━━━━━━━━━━━━━━━━━━━━ 5s 14ms/step - accuracy: 0.9918 - loss: 0.0267 348/750 ━━━━━━━━━━━━━━━━━━━━ 5s 14ms/step - accuracy: 0.9918 - loss: 0.0267 353/750 ━━━━━━━━━━━━━━━━━━━━ 5s 14ms/step - accuracy: 0.9917 - loss: 0.0268 358/750 ━━━━━━━━━━━━━━━━━━━━ 5s 14ms/step - accuracy: 0.9917 - loss: 0.0268 363/750 ━━━━━━━━━━━━━━━━━━━━ 5s 14ms/step - accuracy: 0.9917 - loss: 0.0269 368/750 ━━━━━━━━━━━━━━━━━━━━ 5s 14ms/step - accuracy: 0.9917 - loss: 0.0269 373/750 ━━━━━━━━━━━━━━━━━━━━ 5s 14ms/step - accuracy: 0.9917 - loss: 0.0270 378/750 ━━━━━━━━━━━━━━━━━━━━ 5s 14ms/step - accuracy: 0.9916 - loss: 0.0270 383/750 ━━━━━━━━━━━━━━━━━━━━ 5s 14ms/step - accuracy: 0.9916 - loss: 0.0271 388/750 ━━━━━━━━━━━━━━━━━━━━ 5s 14ms/step - accuracy: 0.9916 - loss: 0.0271 392/750 ━━━━━━━━━━━━━━━━━━━━ 4s 14ms/step - accuracy: 0.9916 - loss: 0.0272 397/750 ━━━━━━━━━━━━━━━━━━━━ 4s 14ms/step - accuracy: 0.9916 - loss: 0.0272 402/750 ━━━━━━━━━━━━━━━━━━━━ 4s 14ms/step - accuracy: 0.9915 - loss: 0.0272 406/750 ━━━━━━━━━━━━━━━━━━━━ 4s 14ms/step - accuracy: 0.9915 - loss: 0.0273 411/750 ━━━━━━━━━━━━━━━━━━━━ 4s 14ms/step - accuracy: 0.9915 - loss: 0.0273 415/750 ━━━━━━━━━━━━━━━━━━━━ 4s 14ms/step - accuracy: 0.9915 - loss: 0.0273 420/750 ━━━━━━━━━━━━━━━━━━━━ 4s 14ms/step - accuracy: 0.9915 - loss: 0.0274 425/750 ━━━━━━━━━━━━━━━━━━━━ 4s 14ms/step - accuracy: 0.9915 - loss: 0.0274 430/750 ━━━━━━━━━━━━━━━━━━━━ 4s 14ms/step - accuracy: 0.9915 - loss: 0.0274 434/750 ━━━━━━━━━━━━━━━━━━━━ 4s 14ms/step - accuracy: 0.9914 - loss: 0.0275 438/750 ━━━━━━━━━━━━━━━━━━━━ 4s 14ms/step - accuracy: 0.9914 - loss: 0.0275 442/750 ━━━━━━━━━━━━━━━━━━━━ 4s 14ms/step - accuracy: 0.9914 - loss: 0.0275 445/750 ━━━━━━━━━━━━━━━━━━━━ 4s 14ms/step - accuracy: 0.9914 - loss: 0.0275 448/750 ━━━━━━━━━━━━━━━━━━━━ 4s 14ms/step - accuracy: 0.9914 - loss: 0.0275 451/750 ━━━━━━━━━━━━━━━━━━━━ 4s 14ms/step - accuracy: 0.9914 - loss: 0.0276 455/750 ━━━━━━━━━━━━━━━━━━━━ 4s 14ms/step - accuracy: 0.9914 - loss: 0.0276 459/750 ━━━━━━━━━━━━━━━━━━━━ 4s 14ms/step - accuracy: 0.9914 - loss: 0.0276 463/750 ━━━━━━━━━━━━━━━━━━━━ 3s 14ms/step - accuracy: 0.9914 - loss: 0.0276 467/750 ━━━━━━━━━━━━━━━━━━━━ 3s 14ms/step - accuracy: 0.9913 - loss: 0.0276 471/750 ━━━━━━━━━━━━━━━━━━━━ 3s 14ms/step - accuracy: 0.9913 - loss: 0.0277 475/750 ━━━━━━━━━━━━━━━━━━━━ 3s 14ms/step - accuracy: 0.9913 - loss: 0.0277 479/750 ━━━━━━━━━━━━━━━━━━━━ 3s 14ms/step - accuracy: 0.9913 - loss: 0.0277 483/750 ━━━━━━━━━━━━━━━━━━━━ 3s 14ms/step - accuracy: 0.9913 - loss: 0.0277 486/750 ━━━━━━━━━━━━━━━━━━━━ 3s 14ms/step - accuracy: 0.9913 - loss: 0.0277 490/750 ━━━━━━━━━━━━━━━━━━━━ 3s 14ms/step - accuracy: 0.9913 - loss: 0.0278 494/750 ━━━━━━━━━━━━━━━━━━━━ 3s 14ms/step - accuracy: 0.9913 - loss: 0.0278 498/750 ━━━━━━━━━━━━━━━━━━━━ 3s 14ms/step - accuracy: 0.9913 - loss: 0.0278 502/750 ━━━━━━━━━━━━━━━━━━━━ 3s 14ms/step - accuracy: 0.9913 - loss: 0.0278 506/750 ━━━━━━━━━━━━━━━━━━━━ 3s 14ms/step - accuracy: 0.9912 - loss: 0.0279 510/750 ━━━━━━━━━━━━━━━━━━━━ 3s 14ms/step - accuracy: 0.9912 - loss: 0.0279 514/750 ━━━━━━━━━━━━━━━━━━━━ 3s 14ms/step - accuracy: 0.9912 - loss: 0.0279 518/750 ━━━━━━━━━━━━━━━━━━━━ 3s 14ms/step - accuracy: 0.9912 - loss: 0.0279 522/750 ━━━━━━━━━━━━━━━━━━━━ 3s 14ms/step - accuracy: 0.9912 - loss: 0.0279 526/750 ━━━━━━━━━━━━━━━━━━━━ 3s 14ms/step - accuracy: 0.9912 - loss: 0.0280 530/750 ━━━━━━━━━━━━━━━━━━━━ 3s 14ms/step - accuracy: 0.9912 - loss: 0.0280 534/750 ━━━━━━━━━━━━━━━━━━━━ 2s 14ms/step - accuracy: 0.9912 - loss: 0.0280 538/750 ━━━━━━━━━━━━━━━━━━━━ 2s 14ms/step - accuracy: 0.9912 - loss: 0.0280 542/750 ━━━━━━━━━━━━━━━━━━━━ 2s 14ms/step - accuracy: 0.9912 - loss: 0.0280 546/750 ━━━━━━━━━━━━━━━━━━━━ 2s 14ms/step - accuracy: 0.9912 - loss: 0.0280 550/750 ━━━━━━━━━━━━━━━━━━━━ 2s 14ms/step - accuracy: 0.9911 - loss: 0.0281 554/750 ━━━━━━━━━━━━━━━━━━━━ 2s 14ms/step - accuracy: 0.9911 - loss: 0.0281 557/750 ━━━━━━━━━━━━━━━━━━━━ 2s 14ms/step - accuracy: 0.9911 - loss: 0.0281 561/750 ━━━━━━━━━━━━━━━━━━━━ 2s 14ms/step - accuracy: 0.9911 - loss: 0.0281 565/750 ━━━━━━━━━━━━━━━━━━━━ 2s 14ms/step - accuracy: 0.9911 - loss: 0.0281 569/750 ━━━━━━━━━━━━━━━━━━━━ 2s 14ms/step - accuracy: 0.9911 - loss: 0.0282 573/750 ━━━━━━━━━━━━━━━━━━━━ 2s 14ms/step - accuracy: 0.9911 - loss: 0.0282 578/750 ━━━━━━━━━━━━━━━━━━━━ 2s 14ms/step - accuracy: 0.9911 - loss: 0.0282 582/750 ━━━━━━━━━━━━━━━━━━━━ 2s 14ms/step - accuracy: 0.9911 - loss: 0.0282 586/750 ━━━━━━━━━━━━━━━━━━━━ 2s 14ms/step - accuracy: 0.9911 - loss: 0.0282 590/750 ━━━━━━━━━━━━━━━━━━━━ 2s 14ms/step - accuracy: 0.9911 - loss: 0.0283 594/750 ━━━━━━━━━━━━━━━━━━━━ 2s 14ms/step - accuracy: 0.9910 - loss: 0.0283 599/750 ━━━━━━━━━━━━━━━━━━━━ 2s 14ms/step - accuracy: 0.9910 - loss: 0.0283 604/750 ━━━━━━━━━━━━━━━━━━━━ 2s 14ms/step - accuracy: 0.9910 - loss: 0.0283 609/750 ━━━━━━━━━━━━━━━━━━━━ 1s 14ms/step - accuracy: 0.9910 - loss: 0.0283 613/750 ━━━━━━━━━━━━━━━━━━━━ 1s 14ms/step - accuracy: 0.9910 - loss: 0.0284 617/750 ━━━━━━━━━━━━━━━━━━━━ 1s 14ms/step - accuracy: 0.9910 - loss: 0.0284 621/750 ━━━━━━━━━━━━━━━━━━━━ 1s 14ms/step - accuracy: 0.9910 - loss: 0.0284 626/750 ━━━━━━━━━━━━━━━━━━━━ 1s 14ms/step - accuracy: 0.9910 - loss: 0.0284 631/750 ━━━━━━━━━━━━━━━━━━━━ 1s 14ms/step - accuracy: 0.9910 - loss: 0.0284 636/750 ━━━━━━━━━━━━━━━━━━━━ 1s 14ms/step - accuracy: 0.9910 - loss: 0.0284 641/750 ━━━━━━━━━━━━━━━━━━━━ 1s 14ms/step - accuracy: 0.9910 - loss: 0.0285 646/750 ━━━━━━━━━━━━━━━━━━━━ 1s 14ms/step - accuracy: 0.9909 - loss: 0.0285 651/750 ━━━━━━━━━━━━━━━━━━━━ 1s 14ms/step - accuracy: 0.9909 - loss: 0.0285 656/750 ━━━━━━━━━━━━━━━━━━━━ 1s 14ms/step - accuracy: 0.9909 - loss: 0.0285 661/750 ━━━━━━━━━━━━━━━━━━━━ 1s 14ms/step - accuracy: 0.9909 - loss: 0.0285 666/750 ━━━━━━━━━━━━━━━━━━━━ 1s 14ms/step - accuracy: 0.9909 - loss: 0.0286 671/750 ━━━━━━━━━━━━━━━━━━━━ 1s 14ms/step - accuracy: 0.9909 - loss: 0.0286 676/750 ━━━━━━━━━━━━━━━━━━━━ 1s 14ms/step - accuracy: 0.9909 - loss: 0.0286 681/750 ━━━━━━━━━━━━━━━━━━━━ 0s 14ms/step - accuracy: 0.9909 - loss: 0.0286 686/750 ━━━━━━━━━━━━━━━━━━━━ 0s 14ms/step - accuracy: 0.9909 - loss: 0.0286 691/750 ━━━━━━━━━━━━━━━━━━━━ 0s 14ms/step - accuracy: 0.9909 - loss: 0.0286 696/750 ━━━━━━━━━━━━━━━━━━━━ 0s 14ms/step - accuracy: 0.9909 - loss: 0.0287 700/750 ━━━━━━━━━━━━━━━━━━━━ 0s 14ms/step - accuracy: 0.9909 - loss: 0.0287 704/750 ━━━━━━━━━━━━━━━━━━━━ 0s 14ms/step - accuracy: 0.9909 - loss: 0.0287 708/750 ━━━━━━━━━━━━━━━━━━━━ 0s 14ms/step - accuracy: 0.9909 - loss: 0.0287 713/750 ━━━━━━━━━━━━━━━━━━━━ 0s 14ms/step - accuracy: 0.9909 - loss: 0.0287 718/750 ━━━━━━━━━━━━━━━━━━━━ 0s 14ms/step - accuracy: 0.9908 - loss: 0.0287 723/750 ━━━━━━━━━━━━━━━━━━━━ 0s 14ms/step - accuracy: 0.9908 - loss: 0.0287 728/750 ━━━━━━━━━━━━━━━━━━━━ 0s 14ms/step - accuracy: 0.9908 - loss: 0.0287 733/750 ━━━━━━━━━━━━━━━━━━━━ 0s 13ms/step - accuracy: 0.9908 - loss: 0.0287 738/750 ━━━━━━━━━━━━━━━━━━━━ 0s 13ms/step - accuracy: 0.9908 - loss: 0.0288 743/750 ━━━━━━━━━━━━━━━━━━━━ 0s 13ms/step - accuracy: 0.9908 - loss: 0.0288 748/750 ━━━━━━━━━━━━━━━━━━━━ 0s 13ms/step - accuracy: 0.9908 - loss: 0.0288 750/750 ━━━━━━━━━━━━━━━━━━━━ 11s 15ms/step - accuracy: 0.9899 - loss: 0.0305 - val_accuracy: 0.9862 - val_loss: 0.0501 验证损失最佳轮次(已恢复该轮权重): 2/20
训练日志用于比较训练与验证曲线,具体数值以当前执行输出为准。二十轮是明确的教学计算上限,不是推荐训练长度;早停只读取训练期内部保留的验证损失,并恢复其中最低轮次的权重。测试集仅在模型、轮数和评价口径全部锁定后评价一次。
CNN 模型训练完成后,先用 列表 10.3 留存一次测试访问,再由 图 10.2 展示训练期与验证期曲线;二者分别承担数值核对与图形诊断,不能共用表格标签。
test_loss, test_acc = model.evaluate(test_images, test_labels, verbose=0) # 用已恢复的最佳验证权重进行唯一一次测试评价
print(f'\n测试集准确率: {test_acc:.4f}') # 报告一次性测试分类正确比例
print(f'测试集损失: {test_loss:.4f}') # 同时记录与训练目标一致的测试损失
测试集准确率: 0.9878
测试集损失: 0.0393
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.3 现场报告。阅读 图 10.2 时应同时关注训练—验证差距、随机初始化和数据集难度;本例只说明该架构在 MNIST 上的一个可复现实验流程,不把一次运行的高分外推到更复杂图像任务。
补充说明:在深度学习里,准确率曲线和损失曲线分别告诉你什么
阅读训练图时,不能把“准确率高”直接等同于概率预测已经可靠。准确率曲线和损失曲线传递的信息并不相同。
- 准确率曲线回答的是:在当前阈值和当前任务定义下,模型判对了多少样本;
- 损失曲线回答的是:模型分配给真实类别的概率有多大,以及错误类别上的高置信概率受到多大惩罚。
例如,对同一个分类正确的观测,一个模型给真实类别分配 0.99 概率,另一个分配 0.55 概率;两者对该观测的准确率贡献相同,但前者的交叉熵更低。这个单点比较不能证明前者整体更稳定或校准更好:若 0.99 概率给了错误类别,交叉熵会施加更大惩罚。概率可靠性还需要结合验证损失与校准证据判断。
因此,在深度学习训练中更完整的阅读顺序是:
- 先看训练损失是否稳定下降,判断优化是否正常进行;
- 再看验证损失是否同步下降,判断是否开始过拟合;
- 最后看准确率是否达到业务可接受水平。
对 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\) 个隐藏分量为
若每个时点都需要一个标量预测,则
\[ \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() # 输出递归方程与展开连线的视觉对照
阅读 图 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\) 表示逐元素乘法。三个门的作用分别是
- 遗忘门:控制上一细胞状态沿直接通路保留多少。
- 输入门:控制候选信息向当前细胞状态写入多少。
- 输出门:控制当前细胞状态向隐藏状态暴露多少。
提示:为什么 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、优化不良、序列过长或训练信号不足时,梯度仍可能衰减或爆炸,模型也可能失败。门控机制允许网络
- 遗忘: 通过遗忘门 \(f_t\) 决定保留多少旧信息
- 更新: 通过输入门 \(i_t\) 决定添加多少新信息
- 输出: 通过输出门 \(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 和线性基线的损失。
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 ━━━━━━━━━━━━━━━━━━━━ 1:13 2s/step - loss: 1.9725e-04 3/42 ━━━━━━━━━━━━━━━━━━━━ 1s 29ms/step - loss: 1.6865e-04 5/42 ━━━━━━━━━━━━━━━━━━━━ 1s 30ms/step - loss: 1.5486e-04 7/42 ━━━━━━━━━━━━━━━━━━━━ 1s 29ms/step - loss: 1.4660e-04 9/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 1.5748e-04 11/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 1.7050e-04 13/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 1.8247e-04 15/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 2.1053e-04 17/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 2.3986e-04 19/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 2.9968e-04 21/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 3.6703e-04 23/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 4.1715e-04 25/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 4.5626e-04 27/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 4.9429e-04 29/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 5.4087e-04 31/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 5.9983e-04 33/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 6.6178e-04 35/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 7.3300e-04 37/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 8.1232e-04 39/42 ━━━━━━━━━━━━━━━━━━━━ 0s 31ms/step - loss: 8.8818e-04 41/42 ━━━━━━━━━━━━━━━━━━━━ 0s 31ms/step - loss: 9.7390e-04 42/42 ━━━━━━━━━━━━━━━━━━━━ 3s 38ms/step - loss: 0.0029 - val_loss: 0.0078 Epoch 2/20 1/42 ━━━━━━━━━━━━━━━━━━━━ 1s 48ms/step - loss: 0.0406 3/42 ━━━━━━━━━━━━━━━━━━━━ 1s 30ms/step - loss: 0.0374 5/42 ━━━━━━━━━━━━━━━━━━━━ 1s 29ms/step - loss: 0.0340 7/42 ━━━━━━━━━━━━━━━━━━━━ 1s 29ms/step - loss: 0.0308 9/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 0.0279 11/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 0.0255 13/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 0.0235 15/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 0.0219 17/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 0.0206 19/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 0.0196 21/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 0.0188 23/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 0.0180 25/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 0.0174 27/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 0.0169 29/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 0.0165 31/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 0.0162 33/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 0.0160 35/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 0.0158 37/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 0.0155 39/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 0.0154 41/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 0.0153 42/42 ━━━━━━━━━━━━━━━━━━━━ 1s 32ms/step - loss: 0.0151 - val_loss: 0.0035 Epoch 3/20 1/42 ━━━━━━━━━━━━━━━━━━━━ 1s 43ms/step - loss: 0.0332 3/42 ━━━━━━━━━━━━━━━━━━━━ 1s 29ms/step - loss: 0.0355 5/42 ━━━━━━━━━━━━━━━━━━━━ 1s 29ms/step - loss: 0.0357 7/42 ━━━━━━━━━━━━━━━━━━━━ 1s 30ms/step - loss: 0.0347 9/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 0.0329 11/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 0.0309 13/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 0.0290 15/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 0.0275 17/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 0.0262 19/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 0.0250 21/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 0.0239 23/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 0.0230 25/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 0.0221 27/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 0.0214 29/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 0.0207 31/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 0.0201 33/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 0.0196 35/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 0.0192 37/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 0.0188 39/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 0.0185 41/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 0.0181 42/42 ━━━━━━━━━━━━━━━━━━━━ 1s 32ms/step - loss: 0.0121 - val_loss: 0.0014 Epoch 4/20 1/42 ━━━━━━━━━━━━━━━━━━━━ 1s 45ms/step - loss: 5.2868e-04 3/42 ━━━━━━━━━━━━━━━━━━━━ 1s 29ms/step - loss: 3.9406e-04 5/42 ━━━━━━━━━━━━━━━━━━━━ 1s 29ms/step - loss: 3.3010e-04 7/42 ━━━━━━━━━━━━━━━━━━━━ 1s 29ms/step - loss: 3.0027e-04 9/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 2.8547e-04 11/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 2.7539e-04 13/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 2.6529e-04 15/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 2.5737e-04 17/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 2.5051e-04 19/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 2.4964e-04 21/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 2.5022e-04 23/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 2.5089e-04 25/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 2.5380e-04 27/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 2.7838e-04 29/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 3.4581e-04 31/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 4.2325e-04 33/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 4.9150e-04 35/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 5.5391e-04 37/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 6.1525e-04 39/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 6.7623e-04 41/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 7.4495e-04 42/42 ━━━━━━━━━━━━━━━━━━━━ 1s 31ms/step - loss: 0.0023 - val_loss: 0.0021 Epoch 5/20 1/42 ━━━━━━━━━━━━━━━━━━━━ 2s 53ms/step - loss: 0.0050 3/42 ━━━━━━━━━━━━━━━━━━━━ 1s 30ms/step - loss: 0.0050 5/42 ━━━━━━━━━━━━━━━━━━━━ 1s 30ms/step - loss: 0.0047 7/42 ━━━━━━━━━━━━━━━━━━━━ 1s 33ms/step - loss: 0.0045 9/42 ━━━━━━━━━━━━━━━━━━━━ 1s 32ms/step - loss: 0.0042 11/42 ━━━━━━━━━━━━━━━━━━━━ 0s 32ms/step - loss: 0.0039 13/42 ━━━━━━━━━━━━━━━━━━━━ 0s 32ms/step - loss: 0.0036 15/42 ━━━━━━━━━━━━━━━━━━━━ 0s 32ms/step - loss: 0.0034 17/42 ━━━━━━━━━━━━━━━━━━━━ 0s 33ms/step - loss: 0.0032 19/42 ━━━━━━━━━━━━━━━━━━━━ 0s 33ms/step - loss: 0.0030 21/42 ━━━━━━━━━━━━━━━━━━━━ 0s 33ms/step - loss: 0.0029 23/42 ━━━━━━━━━━━━━━━━━━━━ 0s 33ms/step - loss: 0.0027 25/42 ━━━━━━━━━━━━━━━━━━━━ 0s 33ms/step - loss: 0.0026 27/42 ━━━━━━━━━━━━━━━━━━━━ 0s 33ms/step - loss: 0.0025 29/42 ━━━━━━━━━━━━━━━━━━━━ 0s 33ms/step - loss: 0.0025 31/42 ━━━━━━━━━━━━━━━━━━━━ 0s 33ms/step - loss: 0.0024 33/42 ━━━━━━━━━━━━━━━━━━━━ 0s 33ms/step - loss: 0.0024 35/42 ━━━━━━━━━━━━━━━━━━━━ 0s 33ms/step - loss: 0.0024 37/42 ━━━━━━━━━━━━━━━━━━━━ 0s 33ms/step - loss: 0.0024 39/42 ━━━━━━━━━━━━━━━━━━━━ 0s 33ms/step - loss: 0.0024 41/42 ━━━━━━━━━━━━━━━━━━━━ 0s 32ms/step - loss: 0.0024 42/42 ━━━━━━━━━━━━━━━━━━━━ 1s 34ms/step - loss: 0.0029 - val_loss: 0.0023 Epoch 6/20 1/42 ━━━━━━━━━━━━━━━━━━━━ 1s 48ms/step - loss: 9.5834e-04 3/42 ━━━━━━━━━━━━━━━━━━━━ 1s 32ms/step - loss: 8.4617e-04 5/42 ━━━━━━━━━━━━━━━━━━━━ 1s 31ms/step - loss: 7.5654e-04 7/42 ━━━━━━━━━━━━━━━━━━━━ 1s 31ms/step - loss: 6.8044e-04 9/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 6.2428e-04 11/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 5.9045e-04 13/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 5.8372e-04 15/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 5.8529e-04 17/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 5.8457e-04 19/42 ━━━━━━━━━━━━━━━━━━━━ 0s 31ms/step - loss: 5.9330e-04 21/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 6.0826e-04 23/42 ━━━━━━━━━━━━━━━━━━━━ 0s 31ms/step - loss: 6.1705e-04 25/42 ━━━━━━━━━━━━━━━━━━━━ 0s 31ms/step - loss: 6.2149e-04 27/42 ━━━━━━━━━━━━━━━━━━━━ 0s 31ms/step - loss: 6.3099e-04 29/42 ━━━━━━━━━━━━━━━━━━━━ 0s 31ms/step - loss: 6.6268e-04 31/42 ━━━━━━━━━━━━━━━━━━━━ 0s 31ms/step - loss: 7.0242e-04 33/42 ━━━━━━━━━━━━━━━━━━━━ 0s 31ms/step - loss: 7.4600e-04 35/42 ━━━━━━━━━━━━━━━━━━━━ 0s 31ms/step - loss: 7.9960e-04 37/42 ━━━━━━━━━━━━━━━━━━━━ 0s 31ms/step - loss: 8.5969e-04 39/42 ━━━━━━━━━━━━━━━━━━━━ 0s 31ms/step - loss: 9.2166e-04 41/42 ━━━━━━━━━━━━━━━━━━━━ 0s 31ms/step - loss: 9.9112e-04 42/42 ━━━━━━━━━━━━━━━━━━━━ 1s 34ms/step - loss: 0.0026 - val_loss: 0.0011 Epoch 7/20 1/42 ━━━━━━━━━━━━━━━━━━━━ 1s 46ms/step - loss: 0.0039 3/42 ━━━━━━━━━━━━━━━━━━━━ 1s 31ms/step - loss: 0.0034 5/42 ━━━━━━━━━━━━━━━━━━━━ 1s 31ms/step - loss: 0.0030 7/42 ━━━━━━━━━━━━━━━━━━━━ 1s 30ms/step - loss: 0.0026 9/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 0.0023 11/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 0.0021 13/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 0.0020 15/42 ━━━━━━━━━━━━━━━━━━━━ 0s 31ms/step - loss: 0.0018 17/42 ━━━━━━━━━━━━━━━━━━━━ 0s 31ms/step - loss: 0.0017 19/42 ━━━━━━━━━━━━━━━━━━━━ 0s 31ms/step - loss: 0.0016 21/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 0.0016 23/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 0.0015 25/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 0.0014 27/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 0.0014 29/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 0.0014 31/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 0.0014 33/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 0.0014 35/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 0.0014 37/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 0.0014 39/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 0.0014 41/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 0.0014 42/42 ━━━━━━━━━━━━━━━━━━━━ 1s 32ms/step - loss: 0.0019 - val_loss: 0.0018 Epoch 8/20 1/42 ━━━━━━━━━━━━━━━━━━━━ 1s 45ms/step - loss: 0.0016 3/42 ━━━━━━━━━━━━━━━━━━━━ 1s 31ms/step - loss: 0.0016 5/42 ━━━━━━━━━━━━━━━━━━━━ 1s 30ms/step - loss: 0.0015 7/42 ━━━━━━━━━━━━━━━━━━━━ 1s 30ms/step - loss: 0.0014 9/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 0.0013 11/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 0.0012 13/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 0.0011 15/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 0.0011 17/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 0.0010 19/42 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step - loss: 9.5789e-04 21/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 9.2107e-04 23/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 8.8750e-04 25/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 8.5751e-04 27/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 8.4189e-04 29/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 8.5677e-04 31/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 8.8031e-04 33/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 9.0780e-04 35/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 9.4502e-04 37/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 9.9298e-04 39/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 0.0010 41/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 0.0011 42/42 ━━━━━━━━━━━━━━━━━━━━ 1s 32ms/step - loss: 0.0025 - val_loss: 0.0035 Epoch 9/20 1/42 ━━━━━━━━━━━━━━━━━━━━ 1s 42ms/step - loss: 0.0021 3/42 ━━━━━━━━━━━━━━━━━━━━ 1s 32ms/step - loss: 0.0017 5/42 ━━━━━━━━━━━━━━━━━━━━ 1s 38ms/step - loss: 0.0014 7/42 ━━━━━━━━━━━━━━━━━━━━ 1s 36ms/step - loss: 0.0012 9/42 ━━━━━━━━━━━━━━━━━━━━ 1s 35ms/step - loss: 0.0011 11/42 ━━━━━━━━━━━━━━━━━━━━ 1s 33ms/step - loss: 0.0010 13/42 ━━━━━━━━━━━━━━━━━━━━ 0s 33ms/step - loss: 0.0010 15/42 ━━━━━━━━━━━━━━━━━━━━ 0s 32ms/step - loss: 9.9172e-04 17/42 ━━━━━━━━━━━━━━━━━━━━ 0s 32ms/step - loss: 9.7254e-04 19/42 ━━━━━━━━━━━━━━━━━━━━ 0s 32ms/step - loss: 9.5994e-04 21/42 ━━━━━━━━━━━━━━━━━━━━ 0s 32ms/step - loss: 9.5127e-04 23/42 ━━━━━━━━━━━━━━━━━━━━ 0s 31ms/step - loss: 9.3845e-04 25/42 ━━━━━━━━━━━━━━━━━━━━ 0s 31ms/step - loss: 9.2322e-04 27/42 ━━━━━━━━━━━━━━━━━━━━ 0s 31ms/step - loss: 9.1485e-04 29/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 9.2551e-04 31/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 9.4140e-04 33/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 9.6131e-04 35/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 9.8903e-04 37/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 0.0010 39/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 0.0011 41/42 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step - loss: 0.0011 42/42 ━━━━━━━━━━━━━━━━━━━━ 1s 32ms/step - loss: 0.0022 - val_loss: 0.0012 验证损失最佳轮次(已恢复该轮权重): 6/20
训练与验证损失应从本次 history 对象读取。二十轮只是教学计算上限,不是推荐训练长度;代码已在测试前时期内部按时间保留验证尾段,并按事前早停规则恢复验证损失最低轮次的权重。两条曲线共同下降只说明优化在当前切分上正常进行;是否具有增量预测能力,仍要在选择完成后仅一次检查锁定测试期相对于 last-value 和线性基线的损失。
模型训练完成后,使用测试集进行预测并将归一化的价格还原为原始价格,最后以图表形式展示预测效果。
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.000420
# 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() # 输出用于判断过拟合起点的损失对照
图 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 小结
本章介绍了深度学习的基础概念和应用。与固定线性形式相比,神经网络的主要特点包括:
- 联合表示学习:可从任务损失中学习部分中间表示,但仍需要输入、时点与业务约束设计
- 端到端学习: 从原始数据直接学习到最终输出
- 表达能力:在 小节 10.4.1 的通用逼近条件下,有限宽度单隐层网络对紧致域上的连续函数可以达到任意给定的一致误差;这是存在性结论,不保证优化成功或样本外泛化
- 条件性可扩展: 更多数据和计算可以支持更大模型与更充分的搜索,但性能是否提升仍取决于数据质量、分布漂移、结构偏好、优化和预先固定评价设计
深度学习的挑战
- 数据需求: 通常需要大量标注数据
- 计算资源: 训练深度模型需要大量计算资源
- 可解释性: 深度学习模型的内部表示通常难以直接解释
- 超参数调优: 有许多超参数需要调整
- 过拟合风险: 深度模型容易过拟合训练数据
何时使用深度学习:
- 有大量数据可用
- 问题具有复杂的模式
- 传统方法性能瓶颈
- 有足够的计算资源
Python深度学习库
- TensorFlow/Keras: 提供张量运算、自动微分和高层神经网络训练接口
- PyTorch: 提供动态图、自动微分以及研究与部署工具
- Scikit-learn: 提供MLPClassifier等简单的神经网络实现
10.13 理论来源与前沿
深度学习的发展既依赖算法与理论突破(反向传播、非凸优化、正则化与初始化),也依赖数据与算力的工程进步(GPU、分布式训练)。从统计学习视角看,深度网络是一个高度灵活的函数族,其泛化性能受到隐式正则化、网络结构偏置与优化路径的共同影响。
近年来的前沿趋势包括:
- 大模型与迁移学习:预训练-微调范式降低下游任务的数据需求,在文本、视觉与多模态中尤为突出。
- 对齐与安全:关注模型输出可靠性、偏见控制与对抗鲁棒性,适配高风险部署。
- 可解释性与监控:在金融与医疗等领域,需要可解释方法与上线后漂移监控体系。
10.14 练习
10.14.1 概念题
[核心|难度:1|分值:4|任务:独立] 解释深度神经网络中的“表示学习(representation learning)”是什么意思。它与手工特征工程的关系是什么?
[核心|难度:2|分值:5|任务:独立] 按“连接范围、参数共享、跨时点状态”三列比较 MLP、CNN、RNN/LSTM;再分别指出图像平移分类与月度设备故障序列更匹配哪一种结构偏好,以及这种偏好为什么不保证样本外优胜。
[核心|难度:2|分值:5|任务:独立] Dropout 与\(\ell_2\) 正则化的作用相同吗?它们分别更擅长缓解什么类型的过拟合?
[拓展|难度:2|分值:4|任务:独立] Batch Normalization(或 LayerNorm)在训练中通常带来哪些好处?它是否一定提升泛化?
[核心|难度:2|分值:4|任务:独立] 早停(early stopping)为什么可以被视为一种正则化?
10.14.2 应用题
[核心|难度:3|分值:16|任务:独立] 紧凑时序基线实验:使用海康威视日线数据,以交易日 \(t\) 收盘后可得的最近 5 个日收益率预测 \(t+1\) 日收益率是否为正,比较:
- 逻辑回归概率分类基线
- 多层感知机(MLP)
要求:按日期顺序采用 60%/20%/20% 的训练—验证—测试切分;只用验证期选择网络,最后报告 LR 与 MLP 的测试 AUC、Brier 分数及开发期—测试期 AUC 差距,并给出网络宽度、深度与正则化强度的依据。核心提交物固定为三项可观察证据:一张三段日期与样本数表、一张 MLP 候选验证表、一张锁定后的 LR/MLP 比较表;缺少任一项或用测试集选择结构,均不能判定本题达标。
[拓展|难度:3|分值:12|任务:独立] 设计一个“特征归一化+ 学习率策略”的消融实验:保持模型结构不变,只改变
- 是否标准化输入
- 学习率(常数学习率 vs 余弦衰减)
四格候选只比较验证期收敛速度与最佳验证损失;按事前规则锁定唯一配置后,用训练期与验证期重拟合,并只评价测试集一次。
- [核心|难度:2|分值:8|任务:独立] 如果你的样本量不大(典型的结构化表格金融数据),说明你会如何优先选择模型与训练策略,使其更稳健(例如更小网络、强正则、交叉验证、集成等)。
10.14.3 理论题
[核心|难度:2|分值:8|任务:独立] 对二分类的逻辑回归/神经网络输出层,设\(p=\sigma(z)\),交叉熵损失为\(\ell(y,p)=-[y\log p+(1-y)\log(1-p)]\)。推导\(\frac{\partial \ell}{\partial z}\) 的简洁形式,并解释它为何有利于数值稳定训练。本题是目标 1 的必修梯度桥梁;练习 11 仍是完整前向—反向—更新的终端核心证据。
[拓展|难度:3|分值:10|任务:独立] 以两层网络(输入-隐层-输出)为例,写出反向传播计算梯度的链式法则结构,说明梯度如何从输出层逐层传回。本题推广矩阵形式;核心晋级仍由练习 11 的完整数值证据判定。
[核心|难度: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 概念题参考解答
表示学习:模型自动从原始输入中学习到对任务有用的中间表示(特征),而不是完全依赖手工设计的因子/规则。对结构化金融数据而言,手工特征仍重要,深度模型更多用于自动组合与非线性拟合。
架构比较:MLP 通常全连接、不同位置不共享参数、没有跨时点状态;CNN 只连接局部感受野并在位置间共享卷积核,本身提供平移等变结构;RNN/LSTM 在时间步间共享转移参数并递推隐藏状态。图像任务可优先检验 CNN,月度序列可优先检验 RNN/LSTM,但最终选择仍取决于预先固定验证集与同键简单基线,结构偏好不等于泛化保证。
Dropout vs \(\ell_2\):二者都能抑制过拟合,但机制不同:\(\ell_2\) 直接惩罚权重大小、偏向更平滑的函数;Dropout 训练时随机屏蔽神经元,近似对大量子网络做模型平均,更能缓解共适应(co-adaptation)。
归一化的好处与限制:常见好处是加速收敛、改善条件数、提高训练稳定性并允许更大学习率;但并不保证一定提升泛化,且对小批量、分布漂移等场景需要谨慎。
早停视为正则化:在优化过程中,模型从简单到复杂逐步拟合数据;过长训练会把噪声也拟合进去。早停相当于限制了有效复杂度(类似控制参数范数或隐式正则)。
10.15.2 应用题参考解答
- 逻辑回归 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) # 固定唯一公司内的时间顺序# 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) # 核对当前真实样本的三段规模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 # 输出边界与分母供测试封存审计| 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 |
# 3. 构建Logistic回归管道(标准化 + 分类器)
logistic_regression_pipeline = Pipeline([
('scaler', StandardScaler()), # 标准化预处理
('clf', LogisticRegression(max_iter=2000)) # Logistic回归分类器
]) # 完成带训练期标准化的逻辑回归基线# 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) # 展示每个候选的迭代数、收敛状态与选择标记| 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 |
# 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配置
]) # 完成最终神经网络管道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 # 输出测试前锁定模型的可评分比较证据| 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 没有同时给出有意义的增量,则结论应是“复杂模型没有稳定增量证据”。
- 归一化与学习率策略的完整消融:
本题沿用 小节 10.15.2.1 产生的训练、验证和测试数组。
下例保持 Dense(16)-Dense(1)、30 轮上限、批量 32、固定样本顺序和初始权重不变,形成“原尺度/训练期标准化 × 常数/余弦衰减”四格。每个候选在建模前重置同一种子,使比较不被初始化或批次顺序差异混杂。选择与收敛比较只看验证期;测试期仍只作最终报告。
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 # 返回待训练候选模型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 选定配置后,用训练期与验证期合并数据重拟合,再评价测试集。
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 边界保持一致。结论只能依据验证期选择和最终一次测试结果条件式表述。
- 小样本表格数据的稳健策略:优先考虑更简单的模型(线性/树模型/MLP),严格时间切分,强正则化与早停,必要时做模型集成,并把重心放在特征质量与数据泄露控制。
10.15.3 理论题参考解答(练习 9 为核心梯度桥梁,练习 10 为拓展)
- 交叉熵对 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 合并实现的原因)。
- 两层网络的链式结构:设
\[ 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. \]
这说明梯度从输出层逐层乘上权重转置与激活导数向前传播。
- 一个新初值下的一步完整更新与评分证据
前向传播先给出
\[ 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。样本很小、验证结果不稳定或简单基线表现相当时,优先选择更简单的模型。下一章把“事件是否发生”扩展为带删失的“何时发生”。