前置知识#
学习目标与记号#
准确解释自回归、词元化与词表;
根据公式和张量维度分析嵌入,并识别常见实现错误;
用
Python实现或验证语言模型;在其他条件相同的情况下,只改变一个需要研究的因素进行比较,并根据实验结果分析该因素可能带来的影响。
本节沿用统一记号:普通小写字母表示标量,粗体小写字母表示向量,粗体大写字母表示矩阵或高阶张量;层编号写作上标 \([l]\),样本或时间编号写作下标;转置写作 \(\trans\)。除非另有说明,单个向量均为列向量;需要写成行向量时,必须显式使用转置符号 \(\trans\)。若把 \(n\) 个列向量按行组成矩阵,则统一写为 \(\bU=[\bu_1\trans;\ldots;\bu_n\trans]\)。正文与练习中的程序都应同时检查数值结果和数组维度。本节练习的参考答案见 序列建模预备知识答案。
这些概念为 RNN 和 Transformer 提供输入表示与概率目标;分类或语言建模损失可回看 交叉熵。
数值时间序列与文本看似不同,却共享一个核心问题:怎样利用已有上下文形成对下一步或整个序列的表示。本节用自回归模型说明滞后依赖,再梳理文本进入神经网络前的数值化过程。
自回归模型#
一阶自回归模型 AR(1) 可写为
其中噪声通常还假设在时间上不相关。若 \(|\phi|<1\),该过程存在平稳解;若包含截距,其无条件均值为 \(c/(1-\phi)\)。一般的 AR( \(P\) ) 模型为
普通回归并不要求解释变量彼此独立;自回归的特点是解释变量由同一序列的滞后值构成,因而训练、验证和测试必须尊重时间顺序。
估计参数后,一步预测使用已观测历史。多步递归预测则把前一步预测当作后一步输入,例如 AR(1) 中
并令 \(\widehat x_{t\mid t}=x_t\)。递归预测计算简单,但误差会逐步传递;直接多步预测为不同预测跨度分别学习模型,减少递归依赖,却增加参数和训练成本。
备注
时间序列不能默认随机打散后切分。常见做法是用较早区间训练、随后区间验证、最后区间测试,或采用滚动窗口评估。标准化器也只能用相应训练窗口拟合,否则未来分布信息会泄漏。
从文本到张量#
文本进入模型通常经历以下步骤:
语料(corpus):用于训练或评估的文本集合。数据来源、时间范围、重复样本、标签质量和许可都会影响结论。
词元化(tokenization):把字符串拆成词、子词、字符或字节级单元。子词方法可以用有限词表表示罕见词,同时避免纯字符序列过长。
词表(vocabulary):把每个词元映射到整数 ID,并定义
[UNK]、[PAD]、序列起止符等特殊词元。[UNK]是未知词元(unknown token),用于表示词表中没有收录的词元;[PAD]是填充词元(padding token),用于把同一批次中长度不同的序列补到相同长度。[UNK]表示一段真实但未被词表收录的文本,通常仍需参与模型计算;[PAD]只是为了方便批量计算而补入的位置,通常需要通过掩码排除,不让它参与注意力或损失计算。词表通常由训练语料或预训练模型确定,不能根据测试集重新构造。批处理:不同长度的序列需要填充或截断,并用注意力掩码标明哪些位置是有效词元。
嵌入(embedding):用可训练矩阵把离散 ID 映射为稠密向量。
设词表大小为 \(V\)、嵌入维度为 \(d_e\),并把 ID 为 \(k\) 的词元嵌入记为列向量 \(\be_k\in\mathbb{R}^{d_e}\)。嵌入矩阵统一写为 \(\bE=[\be_1\trans;\ldots;\be_V\trans]\in\mathbb{R}^{V\times d_e}\),所以第 \(k\) 行为 \(\be_k\trans\),即
其中 \(\bx_k\in\{0,1\}^{V}\) 是词元 \(k\) 的独热编码列向量,只有第 \(k\) 个分量为 1,其余分量均为 0。等价的行向量写法为 \(\be_k\trans=\bx_k\trans\cdot\bE\)。实际进行嵌入查表时,不必显式构造这个高维稀疏向量。
对第 \(b\) 条长度为 \(T\) 的序列,各位置的嵌入 \(\be_{b,t}\) 都是 \(d_e\) 维列向量。为了组成便于批量计算的矩阵,可以把这些列向量转置后按行堆叠:
再把 \(m\) 条序列的表示矩阵 \(\bX^{(1)},\ldots,\bX^{(m)}\) 沿批量方向堆叠,就得到大小为 \(m\times T\times d_e\) 的张量。这里按行存放只是批量计算的组织方式,并没有改变“单个向量默认是列向量”的规定。
图 23 静态词嵌入的示意图#
Word2Vec、GloVe 等静态嵌入为每个词元提供一个固定向量,因此同一词元在不同语境中的初始表示相同。RNN 或 Transformer 会根据上下文进一步产生动态表示。词向量中的单个坐标通常没有预先规定的“年龄”“性别”等唯一语义;二维示意图只能帮助形成直觉,不能当作严格解释。
语言模型#
令 \(w_t\in\mathcal{V}\) 表示第 \(t\) 个离散词元。自回归语言模型利用概率链式法则分解联合分布:
顺序信息体现在条件事件中。\(p(\text{人},\text{咬},\text{狗})\) 与 \(p(\text{狗},\text{咬},\text{人})\) 是两个不同序列事件,没有任何概率公理要求它们相等。
对训练序列,常最小化平均负对数似然
其中 \(\mathcal{I}\) 只包含非填充、需要预测的位置,\(n=|\mathcal{I}|\)。困惑度(perplexity)是 \(\exp(\mathcal{J})\);只有在分词方式、词表和评价数据一致时,困惑度才可直接比较。
训练时可一次提供右移后的真实目标序列,这称为 teacher forcing。生成时未来词元未知,模型按已生成前缀逐步计算概率并选择或采样下一个词元。因此相同提示能否得到不同输出,取决于解码策略:贪心或固定束搜索可以是确定的,随机采样才会引入生成随机性。
语言模型可以计算一个序列出现的概率,也可以为文本生成包含上下文信息的数值表示。若要进一步完成分类、翻译或问答等具体任务,还需要明确模型应当学习什么,增加把这些数值表示转换成具体预测结果的网络层,并准备相应的训练数据。下一节将用共享的循环状态实现 \(p(w_t\mid w_{<t})\) 或其他序列条件预测。
Shiny 交互演示:变长序列怎样组成批量张量
交互页面接受两个长度可以不同的数值序列。每个位置的输入 \(\bx_{i,t}\in\mathbb{R}^{d_x}\) 均为列向量;程序将行向量 \(\bx_{i,t}\trans\) 放在批量张量的第 \((i,t)\) 个位置,从而组成大小为 \(m\times T\times d_x\) 的张量,并显示补齐位置和有效位置掩码。页面随后用同一组 RNN 参数处理各时间步,帮助区分序列长度、批量轴和特征维度。
核心推导与实现核验#
核心关系
推导路径。 由概率乘法公式逐项展开联合分布;自回归模型以过去词元 \(x_{<t}\) 为条件估计每一项,训练时通常通过目标右移同时构造所有位置。
关键条件
对数把乘积变成和,因此序列负对数似然等于各有效位置交叉熵之和;排除 padding 后除以有效词元数得到可比较的平均损失。
数据规模
若一个批次有 \(m\) 条序列、每条序列有 \(T\) 个词元,则词元编号表的大小为 \(m\times T\)。每个词元查表得到一个 \(d\) 维列向量;程序把该向量的转置沿张量的最后一个方向存放,因此批量表示的大小为 \(m\times T\times d\)。若词表有 \(V\) 个词,模型在每个位置得到的输出也是 \(V\) 维列向量;按同样方式堆叠后,输出张量的大小为 \(m\times T\times V\)。
常见误区
应先划分训练集、验证集和测试集,再只使用训练集中的文本构造词表。如果先使用全部文本构造词表,测试集中出现的词元及其出现情况就会提前影响数据处理过程。为了让同一批次中的序列长度相同而加入的 padding 不是真实词元,因此计算损失和评价指标时必须用掩码排除这些位置,否则计算结果会失真。
动手检查
把公式 \(p(x_{1:T})=\prod_{t=1}^{T}p(x_t\mid x_{<t})\) 分解为可独立检查的中间量,使用简单数据手工计算结果,并检查输出是否满足模型必须具备的性质;如果已有可靠的程序库实现,还应将两者的结果进行比较。
数值稳定性与规模
对长度不同的序列组成批次时,通常需要在较短序列的末尾加入 padding。由于这些位置不包含真实词元,因此不应参与损失计算。程序可以使用布尔掩码标记有效词元,将有效位置的交叉熵相加,再除以有效词元的总数。输出层的线性运算结果可以直接传给交叉熵函数,无须事先手动计算 Softmax 函数。这样得到的平均损失不会仅仅因为序列长度不同而改变。
本节小结#
自回归分解来自联合概率的条件概率分解公式。
词元化决定序列长度、词表和可表达单位。
只能使用训练集构造词表,并计算数据处理所需的均值、标准差等数值;处理验证集和测试集时,应直接使用这些已经确定的结果,避免提前利用其中的信息。
综合练习#
程序题应写出维度断言并报告运行环境。全部参考答案见 序列建模预备知识答案。
AR(1) 的平稳矩。 对 \(x_t=c+\phi x_{t-1}+\varepsilon_t\),假设 \(|\phi|<1\)、噪声独立同分布且均值为 0、方差为 \(\sigma^2\)。推导平稳均值和方差,并说明 \(|\phi|<1\) 在推导中的作用。
递归预测计算。 已知 AR(1) 模型的估计结果为 \(\widehat c=1\)、\(\widehat\phi=0.5\),且 \(x_t=6\)。计算未来三步预测 \(\widehat x_{t+1\mid t}\)、\(\widehat x_{t+2\mid t}\) 和 \(\widehat x_{t+3\mid t}\),并说明递归预测误差为何会沿序列向后传递。
序列似然推导。 从概率乘法公式出发,推导 \(p(w_{1:T})=\prod_{t=1}^{T}p(w_t\mid w_{<t})\),再证明序列负对数似然等于各有效预测位置负对数概率之和。说明 padding 位置为何不能计入求和。
掩码损失计算。 某批次共有四个预测位置,目标词元的预测概率依次为 \(0.8,0.5,0.25,0.1\),掩码列向量为 \(\boldsymbol{m}=(1,1,0,1)\trans\)。使用自然对数计算平均负对数似然和困惑度,并说明若错误地把第三个位置计入会怎样改变结果。
词表与批处理实现。 仅根据训练文本实现
build_vocab、encode、decode和collate_batch。词表必须包含[UNK]与[PAD];批处理函数返回词元编号、有效位置掩码和原始长度,并断言三者维度一致。用验证集中未登录词和不同长度序列展示运行结果。稳定的掩码交叉熵。 实现直接接收输出层线性运算结果、目标编号和布尔掩码的
masked_cross_entropy。沿词表轴使用稳定的 log-sum-exp,按有效词元数归一化;测试单个有效位置、含 padding 的批次、全部位置无效以及极大绝对值输入,并按照 手算结果与可信实现对照 的原则与可信库函数比较。循环版与批量版核验。 分别用逐位置循环和批量数组运算实现第 6 题的损失。固定同一输入,逐项比较每个有效位置的负对数概率、总和和平均值;再用 中心差分 或 自动微分 检查梯度,并测量二者处理相同词元数时的运行时间。
词元化方法比较。 在同一文本分类或小型语言建模数据集上比较字符、词和子词三种表示。固定训练/验证/测试划分、随机种子集合、模型主体、参数量范围和训练更新次数;分别报告预测质量、训练时间、固定批量预测时间和峰值内存,并同时记录词表大小、平均序列长度与未登录率。
填充策略效率比较。 比较全局定长填充、批内动态填充和按长度分桶三种批处理方式。固定数据划分、随机种子集合、模型参数、优化器、训练更新次数和批次中的原始样本数;报告测试指标、训练时间、固定批量预测时间、填充比例和峰值内存,并核对三种方式在同一未填充样本上的预测结果。
上下文长度比较。 在同一序列预测任务上比较二元、三元统计语言模型或等价的不同上下文窗口。固定词表、数据划分、随机种子集合、训练样本数和评价代码,并为各方法规定相同的训练遍历次数或相同词元预算;报告困惑度或预测准确率、训练时间、固定批量预测时间和模型内存,讨论更长上下文带来的收益与稀疏性代价。