L大语言模型从理论到实践
阅读 PDF ↗
CHAPTER 06

Transformer 网络结构

注意力算子解决位置之间如何交换信息,位置表示则使计算能够区分绝对或相对顺序,但二者尚未构成完整的语言模型。每个位置的表示还需要非线性变换,多层网络需要可训练的梯度路径,输入和输出需要与词元空间连接。Transformer 将这些职责组织为重复的网络层,并通过可见性规则定义信息能够沿什么方向传播。

本章从逐位置前馈网络、残差与归一化出发,组装编码器、解码器及编码器—解码器结构,最后完成从输入特征到预测概率的逐步计算。原始 Transformer 的后置归一化与整流激活配置,以及采用前置归一化、平滑激活或门控前馈网络的变体,将分别定义。共享组件不意味着函数相同,更不意味着不同配置的权重可以直接互换。注意力算子的推导见第6章《注意力机制》,位置进入计算的不同方式见第7章《位置表示》;本章着重讨论这些机制怎样组成可训练网络。

6.1网络层的职责及张量约定

位置交互及特征变换

设输入为 \(X\in\Real^{B\times T\times d}\),其中 \(B\) 为批大小,\(T\) 为序列长度,\(d\) 为隐藏维数。多头注意力(Multi-Head Attention,MHA)在自注意力模式下将不同位置的信息汇入当前表示;逐位置前馈网络只沿特征轴计算,所有位置共享同一组参数。两者交替堆叠,使跨位置交互得到的特征能够被非线性重组,再影响下一层的读取。

残差流保持形状 \([B,T,d]\),成为各子层共同读写的表示空间。注意力的输出投影和前馈网络的下投影都必须回到维数 \(d\),才能与输入逐元素相加。残差连接既不是通道拼接,也不是将不同层的特征简单平均。

符号 含义与形状
\(B,S,T\) 批大小、源序列长度、目标序列长度。
\(d,h,d_h\) 隐藏维数、注意力头数、单头维数;标准等宽结构满足 \(d=hd_h\)
\(f\) 前馈网络中间维数,不必固定为 \(4d\)
\(N_e,N_d\) 编码器层数、解码器层数。
\(X,C\) 目标状态 \([B,T,d]\)、源端记忆 \([B,S,d]\)
\(V_{\mathrm{vocab}}\) 输出词表大小,与注意力值矩阵 \(V\) 区分。
\(\gamma,\beta\) 归一化增益与偏置,均为 \(d\) 维向量。
\(J_F\) 将张量展平后,函数 \(F\) 相对于输入的雅可比。

结构推导的共同假设

所有子层在有效位置保持隐藏维数;标准多头结构满足 \(d=hd_h\);注意力每个有效查询至少有一个可见键;LayerNorm 与 RMSNorm 沿特征轴计算,除显式手算例外均使用 \(\epsilon>0\)。掩码与输入位置均视为给定的非训练状态。

矩阵公式使用行向量约定,即 \(XW\);求导时为方便讨论,将单个位置视为列向量,雅可比按“输出分量对输入分量”排列。二者只是记号选择,参数存储布局必须在实现中对应。除明确说明外,结构公式省略 Dropout,其影响将在训练部分单独讨论。

注意力子层的接口

令查询侧输入为 \(X_q\in\Real^{T\times d}\),键值侧输入为 \(X_k\in\Real^{S\times d}\)。第 \(r\) 个头计算

\begin{align} Q_r&=X_qW_{Q,r},&K_r&=X_kW_{K,r},&V_r&=X_kW_{V,r},\tag{6.1}\\ A_r&=\softmax(\frac{Q_rK_r^{\mathsf T}}{\sqrt{d_h}}+M),&& O_r=A_rV_r. \tag{6.2}\end{align}

其中三个投影矩阵为 \(d\times d_h\)\(M\)\(T\times S\) 加性掩码,Softmax 沿键轴归一化。拼接各头再乘 \(W_O\in\Real^{d\times d}\),得到 \(T\times d\) 输出。自注意力令两侧输入相同;交叉注意力则允许两侧来自不同网络、具有不同长度。

“自注意力中 \(Q=K=V\)”通常只是对接口输入来源的简写,投影后的三个矩阵一般并不相等。三个投影可以打包为一次矩阵乘法,但仍具有独立参数区间,并未发生权重共享。交叉注意力的查询与键值来自不同张量,不能用一个输入无条件完成全部三组投影。

6.2前馈网络、激活及门控

两层前馈网络的非线性作用

逐位置前馈网络(Feed-Forward Network,FFN)定义为

\[ F(X)=\phi(XW_1+b_1)W_2+b_2, \quad W_1\in\Real^{d\times f},\quad W_2\in\Real^{f\times d}. \tag{6.3}\]

\(b_1\in\Real^f,b_2\in\Real^d\) 按位置广播,张量路径为 \([B,T,d]\to[B,T,f]\to[B,T,d]\)。隐藏宽度 \(f\) 控制可用中间特征数量;它是结构超参数,不由注意力头数决定。

如果去掉激活函数,两个仿射变换可以合成一个仿射变换:

\[ (XW_1+b_1)W_2+b_2=X(W_1W_2)+(b_1W_2+b_2). \tag{6.4}\]

因此仅增加中间宽度并不能提供同样的非线性表达能力。激活使不同输入采用不同的有效特征组合。例如 ReLU 把负预激活置零,其局部雅可比相当于在两个投影之间插入与输入有关的对角选择矩阵。

把无偏置 FFN 写为逐中间特征的和,可得到

\[ F(x)=\sum_{j=1}^{f}\phi(xw_{1,j})w_{2,j}^{\mathsf T}, \tag{6.5}\]

\(w_{1,j}\) 为上投影第 \(j\) 列,\(w_{2,j}^{\mathsf T}\) 为下投影第 \(j\) 行。上投影检测输入方向,下投影组合输出方向,激活控制该项的系数。这是一种代数解释,不能由此把每个隐藏单元强行命名为某一事实或知识条目。

ReLU 及 GELU

原始 Transformer 使用整流线性单元(Rectified Linear Unit,ReLU) \(\operatorname{ReLU}(u)=\max(0,u)\)(Vaswani 等 2017)。其正半轴导数为一,负半轴导数为零,零点不可微,实现需采用约定的次梯度。ReLU 的输出非负,但经过下投影与残差相加后,最终表示仍可具有任意符号。

高斯误差线性单元(Gaussian Error Linear Unit,GELU)使用标准正态分布函数 \(\Phi\)(Hendrycks 和 Gimpel 2016)

\[ \operatorname{GELU}(u)=u\Phi(u),\qquad \Phi(u)=\int_{-\infty}^{u}\frac{e^{-\frac{t^2}{2}}}{\sqrt{2\pi}}\dif t. \tag{6.6}\]

其导数由乘积规则得到

\[ \operatorname{GELU}'(u)=\Phi(u)+u\frac{e^{-\frac{u^2}{2}}}{\sqrt{2\pi}}. \tag{6.7}\]

与 ReLU 在负区间直接截断不同,GELU 对负输入仍可能输出较小负值。例如 \(u=-1\) 时输出约为 \(-0.1587\)\(u=1\) 时约为 \(0.8413\),零点导数为 \(0.5\)。这描述了平滑变换的形状,并不证明采用 GELU 的模型在所有任务上都优于 ReLU。

一种常用近似为

\[ \operatorname{GELU}(u)\approx\frac u2 \left[1+\tanh\left(\sqrt{\frac2\pi}(u+0.044715u^3)\right)\right]. \tag{6.8}\]

精确形式与近似形式并非逐位等价。迁移检查点或核对数值时,需要匹配所采用的定义,而不能仅检查配置中是否出现“GELU”这一名称。

GLU、GEGLU 及 SwiGLU

门控线性单元(Gated Linear Unit,GLU)将两条投影分支逐元素相乘。省略偏置时,可以统一写为

\[ F_{\mathrm{gate}}(X)=[g(XW_g)\odot(XW_u)]W_d, \quad W_g,W_u\in\Real^{d\times f},\quad W_d\in\Real^{f\times d}. \tag{6.9}\]

经典 GLU 的门函数为 Sigmoid;高斯误差线性门控单元(GELU-Gated Linear Unit,GEGLU)使用 GELU;Swish门控线性单元(Swish-Gated Linear Unit,SwiGLU)采用 Swish 门,本文取其参数为一,即Sigmoid线性单元(Sigmoid Linear Unit,SiLU),写为 \(\operatorname{SiLU}(u)=u\sigma(u)\),其中 \(\sigma(u)=\frac{1}{1+e^{-u}}\)(Shazeer 2020)。门控分支与内容分支都由当前输入决定,最后仍通过下投影回到残差宽度。

两层 FFN 与门控 FFN。门控结构新增输入投影,并通过逐元素乘法形成输入相关的交互。
图 6.1 两层 FFN 与门控 FFN。门控结构新增输入投影,并通过逐元素乘法形成输入相关的交互。

6.1中的两条分支在同一中间维数上对齐。对单个中间分量,若 \(a=xw_g,b=xw_u\),输出乘积为 \(g(a)b\),其微分为

\[ \dif[g(a)b]=b\,g'(a)\dif a+g(a)\dif b. \tag{6.10}\]

这表明两条分支相互调制梯度。Sigmoid 门取值在零与一之间,而 SiLU 门可以为负且没有有限正上界,因而 SwiGLU 的“门”不能简单解释为开启概率,也不会自动形成稀疏专家路由。

无偏置两层 FFN 的参数量为 \(2df\),同宽门控 FFN 为 \(3df\)。公平比较时应区分保持中间维数与保持参数预算:若原 FFN 宽度为 \(4d\),等参数门控宽度约为 \(\frac{8d}{3}\),因为 \(3d(\frac{8d}{3})=2d(4d)\)。实际维数还可能为硬件对齐取整。门控 FFN 不能仅靠更换式(6.3)中的激活名称实现,因为其线性投影数量已经改变。

6.3残差连接及梯度传播

低秩增量更新

将任一保持形状的子层写为 \(F\),残差更新为

\[ x_{\ell+1}=x_\ell+F_\ell(x_\ell). \tag{6.11}\]

\(F_\ell\) 接近零时,该层接近恒等映射。网络不必让一个全新的复合变换从随机初始化中学习如何复制所有输入信息,而可以围绕已有表示学习修正。

对输入求导得到

\[ J_\ell=I+J_{F_\ell},\qquad \frac{\partial\mathcal L}{\partial x_\ell} =(I+J_{F_\ell})^{\mathsf T} \frac{\partial\mathcal L}{\partial x_{\ell+1}}. \tag{6.12}\]

上游梯度既直接传回,也经过子层雅可比传回。堆叠多层后,端到端雅可比是这些矩阵的有序乘积,不能把矩阵乘积随意改写成标量乘积,也不能忽略各层的输入依赖。

恒等路径的稳定性边界

即使每层都有 \(I\) 项,梯度仍可能放大或衰减。一维构造中,若 \(F_\ell(x)=ax\),则 \(L\) 层导数为 \((1+a)^L\)\(a=0.1\) 时随深度增长,\(a=-0.1\) 时随深度衰减,\(a=-1\) 时甚至直接为零。残差提供可用路径,参数、尺度、归一化和优化过程共同决定实际稳定性。

残差流方差也不必固定。若输入与增量近似不相关,则相加后的方差约为两者方差之和;若相关,还包含协方差项。因此深层网络常配合归一化、初始化或残差缩放。采用 \(x_{\ell+1}=x_\ell+\alpha_\ell F_\ell(x_\ell)\) 会把雅可比改为 \(I+\alpha_\ell J_{F_\ell}\),缩放影响前向和反向,不能当作对网络函数无影响的实现优化。

6.4LayerNorm 及 RMSNorm

LayerNorm 的统计轴

层归一化(Layer Normalization,LayerNorm)按一个位置的特征维计算统计量(Ba, Kiros, 和 Hinton 2016)。对 \(x\in\Real^d\),定义

\begin{align} \mu&=\frac1d\sum_{i=1}^{d}x_i,& c&=x-\mu\mathbf1,& s&=\sqrt{\frac1d c^{\mathsf T}c+\epsilon},\tag{6.13}\\ \widehat x&=\frac cs,& \operatorname{LN}(x)&=\gamma\odot\widehat x+\beta.&& \tag{6.14}\end{align}

\(\epsilon>0\) 防止零方差造成除零,\(\gamma,\beta\in\Real^d\) 是可训练向量。这里除以 \(d\),不是用于估计总体方差的无偏修正 \(d-1\)。归一化目标是变换当前向量,不是进行无偏统计估计。

LayerNorm 不在批轴或时间轴聚合,因此批大小和其他样本不会直接改变当前位置的统计量。但此前注意力若错误读取填充位置,污染已经进入当前向量,LayerNorm 并不能消除这种信息泄漏。它也不同于依赖训练阶段移动统计量的典型 BatchNorm。

不变性及其边界

对整体平移 \(x+a\mathbf1\),中心化结果不变,所以 LayerNorm 精确消除该方向。忽略 \(\epsilon\) 且方差非零时,正比例缩放也被消除;有非零 \(\epsilon\) 时只在其相对方差足够小时近似成立。负比例缩放会改变中心化向量的符号,不能称为相同输出。

\(x=(1,3)\) 为例,在 \(\gamma=(1,1),\beta=(0,0)\) 下,均值为二,方差为一,输出为 \(\frac{-1,1}{\sqrt{1+\epsilon}}\)\(x'=(11,13)\) 的输出相同。加上可学习仿射变换后,输出未必仍为零均值或单位方差;\(\gamma\) 的各分量不同,\(\beta\) 也可以引入偏移。

LayerNorm 雅可比推导

令中心化矩阵 \(P=I-\frac{\mathbf1\mathbf1^{\mathsf T}}{d}\),则 \(c=Px\)\(P^{\mathsf T}=P\)\(Pc=c\)。有

\[ \dif c=P\dif x,\qquad \dif s=\frac{c^{\mathsf T}\dif x}{ds}. \tag{6.15}\]

\(\widehat x=\frac{c}{s}\) 应用商法则,得到

\[ \dif\widehat x= \left(\frac Ps-\frac{cc^{\mathsf T}}{ds^3}\right)\dif x, \qquad J_{\mathrm{LN}}=\operatorname{diag}(\gamma) \left(\frac Ps-\frac{cc^{\mathsf T}}{ds^3}\right). \tag{6.16}\]

这不是一个简单的对角缩放:均值和尺度计算使同一位置的各特征相互耦合。设输出上游梯度为 \(g\),令 \(h=\gamma\odot g\),则输入梯度也可写为

\[ \nabla_x\mathcal L=\frac1s \left[h-\overline h\mathbf1-\widehat x\, \overline{h\odot\widehat x}\right], \quad \overline h=\frac1d\sum_i h_i. \tag{6.17}\]

仿射参数梯度分别为 \(g\odot\widehat x\)\(g\),训练时再对批及位置求和。式(6.17)说明某一特征上的损失会通过统计量影响其他特征,因此不能把均值与方差当作不参与求导的常数。

RMSNorm 的尺度归一化

均方根归一化(Root Mean Square Normalization,RMSNorm)保留均值,仅用均方根缩放(Zhang 和 Sennrich 2019)

\[ r=\sqrt{\frac1d x^{\mathsf T}x+\epsilon},\qquad \operatorname{RMSNorm}(x)=\gamma\odot\frac xr. \tag{6.18}\]

这里采用无偏置版本,参数量为 \(d\);LayerNorm 的增益与偏置共为 \(2d\)。RMSNorm 不具有整体平移不变性。对 \((1,3)\),其输出为 \(\frac{1,3}{\sqrt{5+\epsilon}}\),与 LayerNorm 的 \(\frac{-1,1}{\sqrt{1+\epsilon}}\) 明显不同。

相同的微分方法给出

\[ J_{\mathrm{RMS}}=\operatorname{diag}(\gamma) \left(\frac Ir-\frac{xx^{\mathsf T}}{dr^3}\right). \tag{6.19}\]

LayerNorm 多出中心化投影 \(P\),RMSNorm 则保留均值方向。二者都将尺度变换与梯度传播联系起来,但不存在由这两条公式就能推导出的“RMSNorm 必然更准确”结论。计算代价也应区分算子开销与整网开销,不能把某项归一化的局部加速直接当作模型吞吐收益。

6.5Pre-LN 及 Post-LN 的结构差异

两种子层的定义

对一个注意力或 FFN 子层 \(F\)后置层归一化(Post-Layer Normalization,Post-LN)将归一化放在残差相加之后;前置层归一化(Pre-Layer Normalization,Pre-LN)将其放在子层输入端:

\begin{align} y_{\mathrm{post}}&=\operatorname{LN}(x+F(x)),\tag{6.20}\\ y_{\mathrm{pre}}&=x+F(\operatorname{LN}(x)). \tag{6.21}\end{align}

同样的 \(F\) 和同样的归一化参数通常产生不同结果。Post-LN 的残差分支最终也经过归一化,Pre-LN 的主残差流则绕过本子层归一化。因此,“Add & Norm”应展开为具体运算顺序,不能作为一个不区分位置的方框。

归一化位置改变前向函数和反向路径。Pre-LN 的恒等项绕过本子层归一化,末端归一化仍影响最终损失。
图 6.2 归一化位置改变前向函数和反向路径。Pre-LN 的恒等项绕过本子层归一化,末端归一化仍影响最终损失。

沿图6.2的路径应用链式法则,二者雅可比分别为

\begin{align} J_{\mathrm{post}}&=J_{\mathrm{LN}}(x+F(x))[I+J_F(x)],\tag{6.22}\\ J_{\mathrm{pre}}&=I+J_F(\operatorname{LN}(x))J_{\mathrm{LN}}(x). \tag{6.23}\end{align}

Pre-LN 有显式不经过本子层归一化的恒等项;Post-LN 的两条局部路径都需乘归一化雅可比。关于初始化附近梯度的研究说明,归一化位置会改变深层训练行为(Xiong 等 2020)。但该结构分析并没有消除学习率、初始化、深度和数据分布的影响。

归一化位置不能作为等价替换

Pre-LN 的局部雅可比保留显式恒等项,Post-LN 的全部局部路径经过归一化。两者具有不同函数与优化性质;改变顺序后必须重新核对训练配置和权重语义,而不能仅比较张量形状。

堆叠末端及配置一致性

典型 Pre-LN 堆叠在最后添加一次归一化,再进入输出头,以控制主残差流到预测层的尺度。末端归一化应作为完整模型的一部分记录,不能因为每层已有归一化就省略。其雅可比仍处于损失到主干的路径中,因此不能说 Pre-LN 的全部梯度均不受归一化影响。

本章将原论文配置记为“Post-LN、LayerNorm、ReLU”,将一个便于比较的变体记为“Pre-LN、LayerNorm、GELU、末端 LayerNorm”。RMSNorm 与 SwiGLU 是另外两项独立设计选择,不由 Pre-LN 自动推出。逐项改变配置才能判断差异来源;一次同时替换三项设置后观察到损失变化,不能全部归因于其中一项。

6.6三类模块结构及四种可见性

编码器及因果解码器

仅编码器(Encoder-only)结构堆叠自注意力和 FFN,通常允许有效位置双向读取,输出上下文表示,之后连接分类、标注或检索任务头。仅解码器(Decoder-only)结构通常堆叠因果自注意力和 FFN,再预测下一词元。它不是把原始解码器原封不动保留,因为通常已经删除交叉注意力子层。

模块组成与可见性是两个维度。一个堆叠是否含交叉注意力属于模块问题;某个位置是否能读取另一个位置属于掩码问题。可通过不同掩码在相似模块上训练不同目标,不能仅凭“Decoder”名称判断全部信息路径。

编码器—解码器的数据流

编码器—解码器(Encoder–Decoder)先将源输入 \(x_{1:S}\) 编码为记忆 \(C\in\Real^{B\times S\times d}\);解码器根据右移后的目标前缀查询这份记忆,输出 \([B,T,d]\) 表示。交叉注意力的查询来自目标侧,键和值来自源侧,权重形状为 \([B,h,T,S]\)

原始 Transformer 的编码器—解码器架构(Post-LN)。主数据流自下而上;灰色旁路显式表示残差连接,加法后执行 LayerNorm。蓝色跨列箭头将最终源记忆送入每个解码器层。依据原始结构绘制\cite{vaswani2017attention}。
图 6.3 原始 Transformer 的编码器—解码器架构(Post-LN)。主数据流自下而上;灰色旁路显式表示残差连接,加法后执行 LayerNorm。蓝色跨列箭头将最终源记忆送入每个解码器层。依据原始结构绘制

阅读图6.3时,先沿源侧观察记忆的形成,再沿目标侧观察三个子层如何更新目标表示。交叉注意力的查询来自目标侧,因此输出长度为 \(T\);键和值来自源侧,其长度为 \(S\)。训练时,右移操作使位置 \(t\) 的输入不包含待预测的 \(y_t\),因果掩码再阻止该位置读取后续目标输入。图中各子层均先残差相加、后归一化。

图中的上下游连接需要由层内公式具体化。以原始 Post-LN 顺序定义编码器第 \(\ell\) 层,且各归一化参数独立:

\begin{align} U_\ell&=\operatorname{LN}_{\ell,1} [C_{\ell-1}+\operatorname{MHA}_{e,\ell}(C_{\ell-1},C_{\ell-1})],\tag{6.24}\\ C_\ell&=\operatorname{LN}_{\ell,2}[U_\ell+F_{e,\ell}(U_\ell)]. \tag{6.25}\end{align}

这里 MHA 的两个输入分别表示查询侧与键值侧。源嵌入加位置表示得到 \(C_0\),最终 \(C=C_{N_e}\)。不同层通常具有独立参数;“堆叠相同结构”不等于重复使用同一组权重。

对应解码器层包含三个子层:

\begin{align} U_\ell&=\operatorname{LN}_{\ell,1} [X_{\ell-1}+\operatorname{MHA}_{c,\ell}(X_{\ell-1},X_{\ell-1})],\tag{6.26}\\ Z_\ell&=\operatorname{LN}_{\ell,2} [U_\ell+\operatorname{MHA}_{x,\ell}(U_\ell,C)],\tag{6.27}\\ X_\ell&=\operatorname{LN}_{\ell,3}[Z_\ell+F_{d,\ell}(Z_\ell)]. \tag{6.28}\end{align}

下标 \(c\) 表示目标侧因果自注意力,\(x\) 表示交叉注意力。每个目标层使用同一份最终源记忆,但各层的交叉注意力投影参数独立。源序列不随目标生成增长,因此编码器可以在生成前运行一次。

Pre-LN 解码器则可写为

\begin{align} U_\ell&=X_{\ell-1}+\operatorname{MHA}_{c,\ell} (\operatorname{LN}_{\ell,1}(X_{\ell-1}),\operatorname{LN}_{\ell,1}(X_{\ell-1})),\tag{6.29}\\ Z_\ell&=U_\ell+\operatorname{MHA}_{x,\ell}(\operatorname{LN}_{\ell,2}(U_\ell),C),\tag{6.30}\\ X_\ell&=Z_\ell+F_{d,\ell}(\operatorname{LN}_{\ell,3}(Z_\ell)). \tag{6.31}\end{align}

这里明确选择只归一化交叉注意力查询侧输入,\(C\) 已由源端网络输出;若另一配置还归一化源记忆,应另作定义,不能在实现中无说明地增加运算。

编码器、因果及前缀掩码对照

\(A_{ij}=1\) 表示位置 \(i\) 允许读取位置 \(j\),并暂时不含填充。对长度四的序列,双向、因果和前两位置为条件前缀的掩码分别为

\[ A_{\mathrm{bi}}=\begin{bmatrix}1&1&1&1\\1&1&1&1\\1&1&1&1\\1&1&1&1\end{bmatrix},\quad A_{\mathrm{causal}}=\begin{bmatrix}1&0&0&0\\1&1&0&0\\1&1&1&0\\1&1&1&1\end{bmatrix},\quad A_{\mathrm{prefix}}=\begin{bmatrix}1&1&0&0\\1&1&0&0\\1&1&1&0\\1&1&1&1\end{bmatrix}. \tag{6.32}\]

前缀解码器(Prefix Decoder)的条件部分可双向交互,生成部分读取全部前缀及生成历史。设前缀长度为 \(P\),其规则为

\[ A_{ij}=\mathbf1[(i\le P\land j\le P)\lor(i>P\land j\le i)]. \tag{6.33}\]

训练若把前缀内部各位置也按普通下一词元目标评分,可能发生泄漏,因为前缀状态可读取前缀未来;应按实际目标选择监督位置。前缀双向条件化不等于拥有独立编码器和交叉注意力,两者的信息变换与参数结构仍不同。

第四种情况是交叉注意力,其矩阵为 \(T\times S\),一般允许每个目标查询读取全部有效源位置。它不在源轴应用目标侧因果三角形。填充、文档隔离或流式源输入可以进一步限制各类掩码,但应在任务定义中明确。

多层因果性定理

如果初始位置 \(i\) 的状态只依赖输入 \(x_{\le i}\),且每层位置 \(i\) 只读取前层 \(j\le i\) 的状态,那么下一层位置 \(i\) 也只依赖 \(x_{\le i}\)。逐位置 FFN、残差和特征轴归一化都不会引入其他时间位置,因此归纳可知任意层均满足因果性。

编码器—解码器中,同样的结论是目标位置仅依赖源输入及真实目标前缀,而非未来目标。这个证明的前提包括正确右移:若把当前位置真实目标直接作为同位置输入,即使使用下三角掩码,也已在初始表示中泄漏答案。所谓因果注意力指生成顺序上的信息约束,并非统计学中的因果识别。1

6.7张量路径及参数核算

单层形状表

标准等宽多头注意力要求 \(d=hd_h\)。以下以目标自注意力和普通两层 FFN 为例,偏置按末轴广播。

运算 输入或参数 输出
QKV 投影 \([B,T,d]\),三个 \(d\times d\) 矩阵 三个 \([B,T,d]\)
拆头 \(d=hd_h\) 三个 \([B,h,T,d_h]\)
缩放点积 \(\frac{QK^{\mathsf T}}{\sqrt{d_h}}\) \([B,h,T,T]\)
归一化与读取 掩码后 Softmax,再乘 \(V\) \([B,h,T,d_h]\)
拼头与输出投影 拼接末轴,乘 \(d\times d\) \([B,T,d]\)
残差与归一化 两条 \([B,T,d]\) 支路 \([B,T,d]\)
FFN 上投影与激活 \(d\times f\) \([B,T,f]\)
FFN 下投影 \(f\times d\) \([B,T,d]\)
词表输出头 \(d\times V_{\mathrm{vocab}}\) \([B,T,V_{\mathrm{vocab}}]\)

交叉注意力将键值长度换为 \(S\),分数形状为 \([B,h,T,S]\),输出仍为目标侧 \([B,T,d]\)。其输出长度由查询数决定,不由源长度决定。

参数量核算

带偏置的标准 MHA 具有四个 \(d\times d\) 投影及四个 \(d\) 维偏置,共 \(4d^2+4d\) 个参数;带偏置普通 FFN 为 \(2df+f+d\);两个 LayerNorm 共为 \(4d\)。因此,编码器层或仅解码器层的参数量为

\[ P_{\mathrm{layer}}=4d^2+2df+f+9d. \tag{6.34}\]

含交叉注意力的解码器层有两组 MHA、一个 FFN 和三个 LayerNorm,总计

\[ P_{\mathrm{encdec\ layer}}=8d^2+2df+f+15d. \tag{6.35}\]

两式不含嵌入、位置参数、末端归一化及输出头,也不适用于直接删偏置、共享键值头或门控 FFN 的配置。

例如 \(d=512,f=2048\) 时,前者为 \(3,152,384\),后者为 \(4,204,032\)。六个编码器层与六个解码器层合计 \(44,138,496\) 个参数,但这不是模型总参数量。若使用无偏置 SwiGLU 和两次无偏置 RMSNorm,仅解码器层对应为 \(4d^2+3df+2d\),比较时必须同时说明投影是否含偏置。

权重共享的前提及梯度

若目标嵌入为 \(E\in\Real^{V_{\mathrm{vocab}}\times d}\),输出头可以共享其转置:

\[ z_t=h_tE^{\mathsf T}+b_{\mathrm{out}}. \tag{6.36}\]

独立输出矩阵被省去,减少 \(V_{\mathrm{vocab}}d\) 个参数。共享矩阵收到输入查表和输出投影两条路径的梯度之和。输出路径的 Softmax 通常使许多词表行得到梯度,因而即使某行在嵌入查表中被设为不更新,也不能推断它在共享输出路径中绝对不变。

源嵌入、目标嵌入和输出头是否能够进一步共享,需要兼容的词表编号及维数。仅仅词表大小相同不够,同一行必须指向同一词元。共享降低参数自由度,并非保证输入表示与输出语义天然一致;这一约束是否有益仍由训练目标和数据决定。

计算及存储规模

标准自注意力投影规模为 \(O(BTd^2)\),两次注意力矩阵乘法为 \(O(BT^2d)\),普通 FFN 为 \(O(BTdf)\)。显式保存逐头权重需要 \(O(BhT^2)\) 元素。交叉注意力包含 \(O(BTSd)\) 的读取计算,并有源侧和目标侧投影。

保持总宽度 \(d\) 不变而增加头数,标准投影总参数量并不会线性增加;每头维数相应缩小。然而显式注意力矩阵含头轴,存储开销可能改变。高效内核可以避免完整物化权重矩阵,这改变访存和执行路径,不能据此说密集注意力在数学上已经不包含位置对之间的交互。

以下构造一个两位置、单头、隐藏维数为二的因果 Pre-LN 层,使用普通 ReLU FFN。它是为了便于逐步核对的具体数学网络,不代表推荐训练配置。输入已包含嵌入和位置表示:

\[ X=\begin{bmatrix}1&3\\4&2\end{bmatrix},\qquad W_Q=W_K=W_V=W_O=I_2. \tag{6.37}\]

所有偏置为零,所有归一化增益为一,省略 Dropout。本例各次方差非零,为获得简洁精确值取 \(\epsilon=0\);实际实现应保持正 \(\epsilon\) 并相应计算数值。

前馈矩阵取

\[ W_1=\begin{bmatrix}1&0&1\\0&1&-1\end{bmatrix},\qquad W_2=\begin{bmatrix}1&0\\0&1\\1&-1\end{bmatrix}. \tag{6.38}\]

层后再做一次最终 LayerNorm,并采用三词元输出头

\[ W_{\mathrm{out}}=\begin{bmatrix}1&0&-1\\0&1&1\end{bmatrix}, \tag{6.39}\]

三列依次对应词元 \(A,B,C\)。本例使用独立输出矩阵,不进行权重共享。

逐行归一化得到

\[ \widehat X=\operatorname{LN}(X)=\begin{bmatrix}-1&1\\1&-1\end{bmatrix}. \tag{6.40}\]

因投影为恒等矩阵,\(Q=K=V=\widehat X\)。缩放分数与因果权重为

\[ S=\frac{QK^{\mathsf T}}{\sqrt2} =\begin{bmatrix}\sqrt2&-\sqrt2\\-\sqrt2&\sqrt2\end{bmatrix},\qquad A=\begin{bmatrix}1&0\\1-p&p\end{bmatrix}, \quad p=\frac{e^{\sqrt2}}{e^{\sqrt2}+e^{-\sqrt2}}. \tag{6.41}\]

\(p\approx0.944193\)。第一行只允许一个键,故权重严格为一;第二行比较两个键。记 \(c=2p-1\approx0.888386\),则

\[ O=AV=\begin{bmatrix}-1&1\\c&-c\end{bmatrix},\qquad U=X+O=\begin{bmatrix}0&4\\4+c&2-c\end{bmatrix}. \tag{6.42}\]

注意力改变了表示,输出维数仍为二,因而可以与输入相加。

\(U\) 两行的特征大小关系没有逆转,因此

\[ \operatorname{LN}(U)=\begin{bmatrix}-1&1\\1&-1\end{bmatrix}. \tag{6.43}\]

进入 FFN 后,

\begin{align} \operatorname{LN}(U)W_1&=\begin{bmatrix}-1&1&-2\\1&-1&2\end{bmatrix},\tag{6.44}\\ \operatorname{ReLU}(\operatorname{LN}(U)W_1)&=\begin{bmatrix}0&1&0\\1&0&2\end{bmatrix},\tag{6.45}\\ F(\operatorname{LN}(U))&=\begin{bmatrix}0&1\\3&-2\end{bmatrix}. \tag{6.46}\end{align}

第二条残差得到

\[ H=U+F(\operatorname{LN}(U)) =\begin{bmatrix}0&5\\7+c&-c\end{bmatrix}. \tag{6.47}\]

此处 FFN 的中间维数为三,输出重新回到二。若遗漏下投影,则无法进行这次残差相加。

末端归一化后,\(\overline H=[(-1,1);(1,-1)]\)。于是词表分数为

\[ Z=\overline H W_{\mathrm{out}}= \begin{bmatrix}-1&1&2\\1&-1&-2\end{bmatrix}. \tag{6.48}\]

逐行 Softmax 得到近似概率

\[ P=\begin{bmatrix} 0.035119&0.259496&0.705385\\ 0.843795&0.114195&0.042010 \end{bmatrix}. \tag{6.49}\]

若两个监督目标分别为 \(B,A\),平均负对数似然为

\[ \mathcal L=-\frac12[\log(0.259496)+\log(0.843795)]\approx0.759429. \tag{6.50}\]

至此,输入、归一化、注意力、两条残差、FFN、末端归一化、词表投影和损失已经形成完整计算链。

二维且 \(\epsilon=0\) 的 LayerNorm 把任意两个不相等分量归一化为相同或相反的符号模式,这使本例中若干归一化结果重复。它是特意选择低维数带来的性质,不代表高维 Transformer 会将注意力信息普遍抹除。改变归一化位置、增加维数或使用正 \(\epsilon\),数值路径都会相应改变。2

6.8训练及推理接口

分类概率模型

Transformer 特征主干接收连续表示并返回连续表示。完整语言模型还需要分词器、嵌入、位置机制、输出词表层、目标函数与生成循环。仅得到 \([B,T,d]\) 输出不意味着已经定义了词元概率,更不意味着已经实现训练和推理。

训练时,源文本 \(x\) 和真实目标 \(y\) 定义条件似然

\[ p_\theta(y\mid x)=\prod_{t=1}^{T}p_\theta(y_t\mid y_{<t},x). \tag{6.51}\]

教师强制(Teacher Forcing)使用真实目标前缀,右移后的目标输入与因果掩码使各位置可并行计算。目标为 \((A,B,\mathrm{EOS})\) 时,输入为 \((\mathrm{BOS},A,B)\),同位置输出分别监督三个目标。填充标签应排除,提示或条件前缀是否监督则由目标定义决定。

生成时用模型已选词元替代真实前缀。编码器记忆可复用,解码器逐步追加目标词元,并在 EOS 或长度上限处终止。缓存可以避免重复计算历史键值,但不会把自回归决策依赖变成完全并行。缓存正确性要求位置编号、掩码、精度和参数与完整前向一致。

算法6.1 教师强制的训练前向

输入:源词元、含 EOS 的目标词元、模型参数、目标有效长度。输出:有效目标位置的平均损失。状态:源记忆、各目标层表示及监督位置集合。

  1. 对源序列构造填充掩码,计算嵌入和位置表示,经编码器得到源记忆。

  2. 构造相对于目标右移一位的解码器输入:首位置为启动词元,其余位置取前一个真实目标;保持输入与目标逐位置配对。

  3. 构造目标因果掩码、目标填充掩码和交叉注意力源掩码,依次执行全部解码器层。

  4. 经规定的末端归一化及词表投影得到分数,只在选中目标位置累计交叉熵并按有效位置数归一化。

  5. 本次前向在全部目标位置处理完毕时结束;若有效监督数为零,跳过该批或按明确规则报告,而不进行零分母平均。

不变量:位置 \(t\) 的预测只依赖源输入与 \(y_{<t}\);显式右移与库内部的下一词元配对只能采用一套,不能重复移位。

算法6.2 条件生成的自回归执行

输入:源序列、启动与终止词元、最大生成长度、固定模型和词元选择策略。输出:每条样本的生成词元序列。状态:源记忆、已生成前缀、可选键值缓存、样本终止标志。

  1. 关闭训练随机性,编码源序列一次;将目标前缀初始化为启动词元,各样本标为未终止。

  2. 对未终止样本计算当前最后有效目标位置的分布。使用缓存时同步更新位置编号、可见性和当前层键值。

  3. 按规定策略选择下一词元并追加到前缀;生成 EOS 的样本标为终止,后续不再追加普通生成内容。

  4. 若全部样本终止或达到长度上限,返回结果;否则回到步骤二。

不变量:缓存内容与当前模型参数及前缀对应;终止样本不再改变其已生成答案。模型前向、词元选择和停止策略分别承担不同职责。

正则化及评价口径

训练中的随机失活(Dropout)可以作用于注意力权重、FFN 中间表示或残差增量。按保留概率校正的 Dropout 在期望上保持对应分量的尺度,但一次采样会改变前向输出;注意力权重经过 Dropout 后,单次实现的行和也不必仍为一。进行确定性结构对照时必须关闭随机性,或对齐全部随机状态。

标签平滑将独热目标替换为 \(q=(1-\eta)e_y+\eta u\)\(u\) 为规定的参考分布。损失为 \(-\sum_vq_v\log p_v\),与普通真实标签负对数似然不同。若训练记录平滑损失而验证记录未平滑损失,不能直接将二者差值解释为泛化间隙。

训练集用于参数更新,验证集用于选择检查点和停止时机,测试集用于最终评价。固定小语料中的损失下降或少量正确译文,只能支持有限范围内的优化与行为观察。精确匹配会把合法同义表达判错,字符或词片段指标也不能覆盖全部语义质量;评价指标与划分应在专门评估中明确。注意力热力图可用于定位读取模式,但仍不构成独立的因果归因。

结构等价性及制品边界

从原理实现迁移到框架时,应先对齐参数,再对齐投影布局、归一化位置、激活定义、偏置、掩码和末端输出层。两个独立随机初始化的网络不具有直接数值可比性。形状相同只是必要条件,前向和反向应在指定精度容差内比较。

布尔掩码的真值方向是接口约定,不能脱离具体接口解释。将“允许读取”矩阵转换为“禁止读取”矩阵时需要取反;加性掩码则直接作用于分数。优化提示也不能替代对实际可见性矩阵的核验。错误提示可能使后端选择不适用的执行路径,故接口封装应显式保存掩码语义。

模型制品还包含非训练参数状态,例如固定位置编码、频率、最大长度及归一化常数。可保存这些状态,也可按固定配置精确重建;只保存可训练权重并不能普遍保证重载一致。现成翻译模型的分词器、特殊词元、位置机制与权重是一组配套对象,不应与另一个原理模型的整数输入混用。

因果性检验可以固定参数并关闭随机性,扰动未来目标,检查更早位置输出是否保持不变;填充隔离检验可改变被屏蔽位置的内容,检查有效输出;归一化检验则应覆盖常量向量和很小方差。这些证据验证数学与接口一致性,不直接证明训练质量、吞吐或生产可用性,后者还需要相应的数据和系统验证。


  1. 改变未来输入检验早期输出,是检验特定前向路径的信息隔离;它既不证明模型理解了因果关系,也不穷尽所有数据泄漏来源。↩︎

  2. 二维例题适合检验计算顺序,不适合据此评价归一化的表达能力或训练效果。结构示例与效果证据必须区分。↩︎

WORKBOOK / 习题

配套习题与解析

先独立作答,再展开参考解析。选修题保留原书标记。

习题 6.1

证明不含激活的两层 FFN 可合并为一个仿射变换。加入 ReLU 后,写出给定激活区间内的局部雅可比,并解释为何跨区间不再是同一个仿射函数。

展开参考解析

没有激活时 \((xW_1+b_1)W_2+b_2=x(W_1W_2)+(b_1W_2+b_2)\)。在固定ReLU激活区域令 \(D=\operatorname{diag}(\mathbf1[xW_1+b_1>0])\),则行向量扰动满足 \(\dif F=\dif x\,W_1DW_2\),列雅可比为 \((W_1DW_2)^{\mathsf T}\)。跨越折点会改变D,因此不同区域具有不同仿射参数。

习题 6.2

\(u=-1,0,1\) 计算 GELU、SiLU 及其导数,比较二者在负区间的行为。说明“平滑”为什么不足以推出训练效果更好。

展开参考解析

采用精确 \(\mathrm{GELU}(u)=u\Phi(u)\),其导数为 \(\Phi(u)+u\phi(u)\);SiLU为 \(u\sigma(u)\),导数为 \(\sigma(u)+u\sigma(u)(1-\sigma(u))\)。在 \(u=-1,0,1\),GELU值约 \((-.158655,0,.841345)\),导数 \((-.083315,.5,1.083315)\);SiLU值约 \((-.268941,0,.731059)\),导数 \((.072329,.5,.927671)\)。负区间允许负输出,且两者局部斜率不同。光滑性不能独自决定训练质量,还取决于尺度、结构、数据和优化。

习题 6.3选修

设普通 FFN 宽度为 \(f=4,096\),求等无偏置参数预算的门控宽度。若宽度必须是 \(256\) 的倍数,分别给出向下、向上取整后的参数差异。

展开参考解析

普通无偏置FFN参数 \(2df\),门控FFN参数 \(3dg\),故 \(g=\frac{2f}{3}=\frac{8192}{3}\approx2730.667\)。256倍数向下为2560、向上2816;相对原 \(8192d\) 参数分别少 \(512d\)(6.25%)和多 \(256d\)(3.125%)。计算效率还受内核对齐与实际矩阵形状影响,不能仅按参数差判断快慢。

习题 6.4

\(c=Px\) 出发独立推导 LayerNorm 雅可比。验证该雅可比作用于全一向量为零,并说明这与平移不变性的关系。

展开参考解析

\(P=I-\frac{\mathbf1\mathbf1^{\mathsf T}}{d}\)\(c=Px\)\(s=\sqrt{\frac{c^{\mathsf T}c}{d}+\epsilon}\)。有 \(\dif c=P\dif x\)\(\dif s=\frac{c^{\mathsf T}\dif x}{ds}\),故无仿射输出雅可比为 \(J=\frac{P}{s}-\frac{cc^{\mathsf T}}{ds^3}\);有增益时左乘 \(\operatorname{diag}(\gamma)\)。因 \(P\mathbf1=0\)\(c^{\mathsf T}\mathbf1=0\)\(J\mathbf1=0\),对应整体平移不改变中心化表示。

习题 6.5

\(x=(1,2,3)\) 分别计算无仿射 LayerNorm 和 RMSNorm。给向量整体加十,比较输出变化;不要把非零 \(\epsilon\) 情况下的比例缩放近似写成精确不变性。

展开参考解析

\(x=(1,2,3)\),归一化均不含可训练仿射参数。其结果为 \[\begin{aligned} \mathrm{LN}(x)&=\frac{(-1,0,1)}{\sqrt{\frac{2}{3}+\epsilon}},\\ \mathrm{RMSNorm}(x)&=\frac{(1,2,3)}{\sqrt{\frac{14}{3}+\epsilon}}. \end{aligned}\] 各坐标整体加十后,令 \(x'=(11,12,13)\)。LN不变,RMSNorm则变为 \[\mathrm{RMSNorm}(x')=\frac{(11,12,13)}{\sqrt{\frac{434}{3}+\epsilon}}.\]\(\epsilon=0\) 时,LN约为 \((-1.224745,0,1.224745)\);保留正 \(\epsilon\) 时应使用上述式。RMSNorm不具有平移不变性。

习题 6.6

取一维 \(F(x)=ax\),分析十层残差网络在 \(a=0.1,-0.1,-1\) 时的导数。结合归一化雅可比,解释恒等路径不能独立保证稳定训练。

展开参考解析

每层 \(x\mapsto(1+a)x\),十层导数为 \((1+a)^{10}\)。分别为 \(1.1^{10}\approx2.593742\)\(.9^{10}\approx.348678\) 和0。即使每层含恒等分支,其他分支仍可放大、衰减或抵消它;加入归一化后,还需分析其雅可比与方向,不能单凭网络图上的直通线保证稳定。

习题 6.7

写出长度六、条件前缀长度三的前缀可见性矩阵。若对前缀内部使用普通下一词元监督,指出可能出现泄漏的具体位置。

展开参考解析

允许矩阵为 \[\begin{bmatrix}1&1&1&0&0&0\\1&1&1&0&0&0\\1&1&1&0&0&0\\1&1&1&1&0&0\\1&1&1&1&1&0\\1&1&1&1&1&1\end{bmatrix}.\] 前缀位置1若预测词元2、位置2若预测词元3,答案已在可见输入中,形成泄漏。位置3预测首个回答词元4时没有直接看到4,但应按定义仅对回答区域监督,避免把双向条件区当作普通因果训练样本。

习题 6.8选修

\(d=768,f=3072\) 计算带偏置编码器层与含交叉注意力解码器层的参数量,列出未包含的模型组件。再将普通 FFN 改为等预算无偏置 SwiGLU,重新计算。

展开参考解析

按本章带偏置普通FFN及两次归一化约定,编码器为 \(4d^2+2df+9d+f\),代入得7087872;解码器增加一套注意力和一次归一化,为 \(8d^2+2df+15d+f=9451776\)。未计嵌入、位置参数、词表头、堆叠外末端归一化。无偏置SwiGLU等矩阵预算取 \(g=\frac{2f}{3}=2048\),只移除原FFN的 \(f+d=3840\) 个偏置,得到7084032、9447936;这里仍保留注意力偏置和归一化参数。

习题 6.9选修

将完整例题的因果掩码改为双向掩码,逐步重算输出。解释为什么此时位置一可能依赖位置二;再讨论二维末端 LayerNorm 可能隐藏哪些数值差异。

展开参考解析

仍记 \(p=\sigma(2\sqrt2)\)\(c=2p-1\)。双向权重为 \(\left[\begin{smallmatrix}p&1-p\\1-p&p\end{smallmatrix}\right]\),输出 \(O=[(-c,c);(c,-c)]\),第一残差为 \(U=[(1-c,3+c);(4+c,2-c)]\)。两行LN仍为 \((-1,1),(1,-1)\),FFN输出仍为 \((0,1),(3,-2)\);故 \(H=[(1-c,4+c);(7+c,-c)]\)。末端二维、\(\epsilon\)为零的LN仍给相同符号模式,最终概率和损失恰好不变。这是退化低维例的遮蔽效应;中间表示已变,第一行通过注意力依赖第二行,不能据末端相同否认泄漏。

习题 6.10

目标嵌入与输出头共享权重时,说明一个未出现在输入中的词元行为什么仍可能获得梯度。设计重载检验,同时覆盖共享关系、固定位置状态和生成首个词元。

展开参考解析

\(Z=HE^{\mathsf T}\),则 \(\overline E_{\mathrm{out}}=\overline Z^{\mathsf T}H\)。未作为输入的词元仍作为词表候选参与Softmax,通常有非零分数梯度,故对应E行可更新。重载应核对共享参数关系或等价共享实现、形状和词表ID、固定位置配置,在评估模式下比较同输入首步logits及确定解码首词元;不能仅检查两个矩阵初值数值相等。

习题 6.11

将手写 Transformer 迁移到库实现后,参数均能加载且张量形状一致,但首步 logits 不同。设计从输入到输出的逐层对照方案,至少覆盖掩码语义、归一化位置、位置表示和权重布局,并说明何时应开始比较完整生成结果。

展开参考解析

固定权重、输入、精度与评估模式,先关闭 Dropout。核对词元 ID、嵌入缩放、张量布局以及 QKV 权重的拼接顺序或转置约定,再比较投影结果。使用短输入核对掩码的真假含义、广播维度和因果位置,随后逐层比较位置变换后的 Q/K、注意力输出、残差、归一化、激活及前馈输出;归一化的前后位置和数值稳定项都应一致。最后检查词表输出权重是否共享、偏置及 logits。训练损失还需单独核对标签移位。中间量及首步输出在约定容差内一致后,再检查缓存递增位置和完整生成;采样需固定设置,微小数值差异仍可能改变离散生成路径。

REFERENCES

参考文献

Ba, Jimmy Lei, Jamie Ryan Kiros, 和 Geoffrey E. Hinton. 2016. 《Layer Normalization》. 2016年. https://arxiv.org/abs/1607.06450.
Hendrycks, Dan, 和 Kevin Gimpel. 2016. 《Gaussian Error Linear Units (GELUs)》. 2016年. https://arxiv.org/abs/1606.08415.
Shazeer, Noam. 2020. 《GLU Variants Improve Transformer》. 2020年. https://arxiv.org/abs/2002.05202.
Vaswani, Ashish, Noam Shazeer, Niki Parmar, Jakob Uszkoreit, Llion Jones, Aidan N. Gomez, Łukasz Kaiser, 和 Illia Polosukhin. 2017. 《Attention Is All You Need》. Advances in Neural Information Processing Systems 30. https://arxiv.org/abs/1706.03762.
Xiong, Ruibin, Yunchang Yang, Di He, Kai Zheng, Shuxin Zheng, Chen Xing, Huishuai Zhang, Yanyan Lan, Liwei Wang, 和 Tieyan Liu. 2020. 《On Layer Normalization in the Transformer Architecture》. 收入 Proceedings of the 37th International Conference on Machine Learning, 10524–33. https://proceedings.mlr.press/v119/xiong20b.html.
Zhang, Biao, 和 Rico Sennrich. 2019. 《Root Mean Square Layer Normalization》. 2019年. https://arxiv.org/abs/1910.07467.

搜索全书

搜索全书正文、习题与解析