NLP笔记
@@@@@@@@@ 序列 (自回归)
定义公式:$x_t \sim P(x_t|x_{t-1},…,x_1)$ 或 $x_t \sim P(x_t|x_{t-1},…,x_{\tau})$
- $\hat x_t \sim P(x_t|h_t)$ : $h_t = g(h_{t-1},x_{t-1})$
如果 $\tau=1$ 就得到一个一阶的马尔可夫模型,也就是上一个状态影响下一个状态
原理:和马尔可夫模型有关系
- 马尔可夫性
马尔可夫性也叫做无后效性、无记忆性,即是过去只能影响现在,不能影响将来。- 这样才能构成马尔可夫过程的马尔可夫链(状态机),变换矩阵不会随意变
- 隐马尔可夫模型
- 状态不是能直接看到,而是只能看到间接的状态,比如生病的症状和病症的显隐关系
简单神经网络预测问题:
内插法(在现有观测值之间进行估计)和外推法(对超出已知观测范围进行预测)在实践的难度上差别很大。因此,对于所拥有的序列数据,在训练时始终要尊重其时间顺序,即最好不要基于未来的数据进行训练。
外推就是用模型进行预测后再预测(用推测数据去推测新数据)
@@@@@@@@@ 文本处理
做 NLP 首先就要进行文本处理,所以需要更多的统计学知识对文本进行处理。
这里参考毕业设计数据处理部分做的事情。
后续补全方式即可。
语料生成
- 词元化:分词(英语可以简单分)
- 利用词元建立词表 Vocab(可正向和逆向查询)
- 可用 char 或者 wold 分词
- 然后分词行就能对应词表索引
- 然后根据词表能够获取语料(corpus)= 文本分词 + 词表
- 语料是把输入文本经过词表处理后的索引 flatten 成为一个时间序列
- 语料独热编码后能够直接用于训练
文本划分
因为张量的输入长度是固定的,所以需要固定语料输入的数量
随机偏移
|
|
@@@@@@@@@ 循环神经网络原理
隐变量模型,对过去的时间影响进行了一个累积。
- $P(x_t|h_{t-1})$
- $h_t=f(x_t, h_{t-1})$
网络结构因为引入隐状态(过去时间步的影响,也就是过去的神经元数值),公式变化如下:
- $\mathbf H_t = \phi(\mathbf{X_t W_{xh}+H_{t-1} W_{hh} + b_h})$
- 加入了新权重 $\mathbf{W_{hh}}$ 用于隐藏层自身和过去的关系
- $\mathbf{X_t} \in \mathbb{R}^{n\times d}$ ,d 是词向量大小
- 一般 X 是多个时间步数的之一
- 激活函数 $\phi$ 一般用 tanh
- num_hiddens:隐藏单元数/神经元,也就 H 中的 h,代表隐藏状态数量,也是词表大小
- H 的另外一个维度代表批次
- $\mathbf{O = HW_{hq} + b_q}$ ,q 是输出维度
- 后续考虑其他 RNN 就不特意点出 O 了
主要是理解实现上输入的维度含义 (时间步数/序列长度,批次,词向量大小/词表独热编码)
- 时间步长 x 批次是选中的语料长度,
- 时间步长也叫词元数量(NLP 中一般会用
<pad>补全或者截断到固定长度) - 时间步长是指一次循环中需要计算隐藏状态和输出的连续次数
- 当然每次输出都会记在输出列表中
- 时间不长内部是一个连续的 token 构成的句子,在一次循环中,能保证时间步长内的句子是连续的,如果预测长度超出时间步长的长度,可能就会预测不准
- 所以如果取时间步长的长度的输入输出对,那么就是一个 seq2seq 的模型,不过输出不会大于输入
梯度累积问题:
- 利普希茨连续:用某种目标函数限制梯度的上限,避免梯度累积
- 梯度裁剪(投影到球):
- $\displaystyle{g = min(1, \frac{\theta}{|g|})g}$
- 避免 g 的范数超过 $\theta$
- $\displaystyle{g = min(1, \frac{\theta}{|g|})g}$
预热:
- 在预测前需要先输入预热一下隐藏状态
梯度分离:
- 顺序分区要分离梯度,因为隐藏状态计算来自上一批次的数据,如果不从计算图分离,还会进行多余的梯度计算(反向传播)
输入输出张量:
- 一般来说,输入张量的形状是 (num_step, batch_size, vocab_size);但是 vocab_size 是最后在网络中 one_hot 得到的,在此之前只是词表的序号。
- 而输出张量则是有多个,每个 step 会对应一个 batch 的 Y 输出,Y 中每组代表着预测词的分布。
@@@@@@@@@ 困惑度评估
困惑度(perplexity)
- 本质是对于一个句子概率的计算
- 公式:$\exp{(-\frac{1}{n}\sum\limits_{t=1}^n \log{P(x_t | x_{t-1} … x_1)})}$
- 利用信息论知识评估语言序列预测效果
- 计算中使用 loss 在各个批次的平均值得到
- 实际可以通过输出矩阵的对角线依次得到概率 P,全部相乘则为输出序列词的联合概率
- 实际计算困惑度不会依次计算
- 设定一个长度,超过就重制 context
- 滑动窗口,在窗口内计算 context
- 参考



@@@@@@@@@ 统计语言模型
这里描述的是偏机器学习的内容
- 语言模型是一个统计模型(词频统计等)
- $x_t \sim P(x_t | x_{t-1} … x_{1})$ 一个词元被抽取的概率
- 一个序列的概率是词元序列的联合分布:$P(x_1, x_2 … x_{T}) = \prod\limits_{t=1}^T P(x_t | x_{t-1} … x_{1})$
- 可以用贝叶斯估计某个词的条件概率,前提是有足够多的数据(这不太常见)
- 拉普拉斯平滑:略 Wood et al., 2011
- 总之这种一个推一个基于纯概率的语言模型,性能都不太佳,所以要利用深度学习
- 齐普夫定律:单词位置的概率关系
- 多元语法:满足马尔可夫性质的多随机变量,即多词元
- 一元就是单个词元的模型,但是由于很多 the 等词元虽然频率高,但是和语言含义无关,所以
- 二元:
(('of', 'the'), 309) - 三元:
(('the', 'time', 'traveller'), 59)
@@@@@@@@ 门控循环单元(GRU)
原理

新增隐状态
- $R_t$ 重置门:$\mathbf{\sigma(X_t W_{xr} + H_{t-1} W_{hr} + b_r)}$
- $Z_t$ 更新门:$\mathbf{\sigma(X_t W_{xz} + H_{t-1} W_{hz} + b_z)}$
候选隐状态
- $\mathbf{\widetilde{H_t} = \tanh(X_t W_{xh} + (R_t \odot H_{t-1}) W_{hh} + b_h)}$
- 使用重置门和上一时间步决定候选状态
隐状态
- $\mathbf{H_t = Z_t \odot H_{t-1} + (1-Z_t)\odot \widetilde{H_t}}$
- Z 接近 1 时,只使用过去的旧状态
- 接近 0 时使用候选状态
- 而候选状态是包括当前输入的新信息
- 使用新信息时还使用 R 来控制新信息结合旧状态的程度(代表是否断开旧信息的联系)
当然除此之外最后还有一个 h -> r 从隐状态到输出层的过程
@@@@@@@ 技巧
- 如果要观察训练过程中,语言模型的预测能力,则在训练过程中进行预测输出(参考 d2l 的 train_ch8)
- 这里要注意,要改为
.eval()模式(不会反向传播,也不会使用 BN 和 Dropout) - 然后在下一 epoch/batch 要改为
.train() - 不然 BN 和 Dropout 只作用于第一个 epoch/batch
- 这两个模式都会计算梯度,如果要停止梯度计算则使用
with torch.no_grad()停止自动微分
- 这里要注意,要改为
torch.repeat_interleave逐个复制- 软对齐
- 原理和注意力查询流程一致,不过替换了注意力权重的计算方式;使用同一个注意力矩阵,不过一个按行使用,一个按列使用
- 分解计算
- 两个相同函数计算相乘,可以计算完再相乘
@@@@@@@@ 长短期记忆网络(LSTM)
原理

门单元
- 输入(Input)、遗忘(Forget)、输出(Output)门
- 使用 sigmoid 函数
$$ \begin{split}\begin{aligned} \mathbf{I}t &= \sigma(\mathbf{X}t \mathbf{W}{xi} + \mathbf{H}{t-1} \mathbf{W}_{hi} + \mathbf{b}i),\ \mathbf{F}t &= \sigma(\mathbf{X}t \mathbf{W}{xf} + \mathbf{H}{t-1} \mathbf{W}{hf} + \mathbf{b}f),\ \mathbf{O}t &= \sigma(\mathbf{X}t \mathbf{W}{xo} + \mathbf{H}{t-1} \mathbf{W}{ho} + \mathbf{b}_o), \end{aligned}\end{split} $$
候选记忆元(candidate memory cell)
$$ \tilde{\mathbf{C}}t = \text{tanh}(\mathbf{X}t \mathbf{W}{xc} + \mathbf{H}{t-1} \mathbf{W}_{hc} + \mathbf{b}_c), $$
- 候选记忆元是最简单 RNN 的 $\mathbf{H_t}$ 的计算
- GRU 在这个位置直接加入了重置门控制上次隐状态的输入,并且直接作为隐状态的输出
- 而 LSTM 则是加入一个记忆元 Cell 把控制是否选中的信息写到记忆元 $\mathbf{C_t}$ 中
记忆元
$$ \mathbf{C}_t = \mathbf{F}t \odot \mathbf{C}{t-1} + \mathbf{I}_t \odot \tilde{\mathbf{C}}_t. $$
- 根据遗忘门和旧记忆元控制过去的信息联系,而输入和候选记忆来控制是否需要新信息,最后生成下一个记忆元,
隐状态
$\mathbf{H}_t = \mathbf{O}_t \odot \tanh(\mathbf{C}_t).$
- 新的隐状态由输出门和记忆 Cell 决定
- 意味着下一次的隐状态由记忆元和输出门决定
- 输出门是来自正常的 RNN 隐状态就是由旧的隐状态和输入张量决定
- 意味着下一次的隐状态由记忆元和输出门决定
- 其实记忆元也相当于一个隐状态,也就是要存储的信息,那么 LSTM 的网络中除了各个门的权重还需要两个张量来存储过去状态(和 GRU 以及一般的 RNN 不一样,它们只需要一个)
@@@@@@@@ 问题
循环神经网络对旧输入的依赖性会导致很大的求导关系
@@@@@@@@ 深度循环网络
原理
- 把 RNN 在垂直方向上多个叠加,也就是说不论是什么 RNN 核都行。
- 那么为什么不用多个网络连接呢?因为多个网络之间不能共享隐藏状态

公式
隐藏层
$$ \mathbf{H}t^{(l)} = \phi_l(\mathbf{H}t^{(l-1)} \mathbf{W}{xh}^{(l)} + \mathbf{H}{t-1}^{(l)} \mathbf{W}_{hh}^{(l)} + \mathbf{b}_h^{(l)}), $$
输出层
$\mathbf{O}_t = \mathbf{H}t^{(L)} \mathbf{W}{hq} + \mathbf{b}_q,$
@@@@@@@@ 双向循环网络
原理
- 数学说明
- 提供隐马尔可夫模型(HMM)的相关能力
- 用的少,有兴趣再看原理
- 因为预测时没有未来的词,所以适合进行填空、情感分析等特殊应用
- 因为同一个词在不同上下文的含义不同,所以双向神经网络能做这个处理
- 输入输出都是填空或者含义标注
- 因为预测时没有未来的词,所以适合进行填空、情感分析等特殊应用

公式
$$ \begin{split}\begin{aligned} \overrightarrow{\mathbf{H}}t &= \phi(\mathbf{X}t \mathbf{W}{xh}^{(f)} + \overrightarrow{\mathbf{H}}{t-1} \mathbf{W}_{hh}^{(f)} + \mathbf{b}h^{(f)}),\ \overleftarrow{\mathbf{H}}t &= \phi(\mathbf{X}t \mathbf{W}{xh}^{(b)} + \overleftarrow{\mathbf{H}}{t+1} \mathbf{W}{hh}^{(b)} + \mathbf{b}_h^{(b)}), \end{aligned}\end{split} $$
对于输出层:
$\mathbf{O}_t = \mathbf{H}t \mathbf{W}{hq} + \mathbf{b}_q.$ 其中 W 中 h 的纬度是前向和反向隐藏层的和,也就是 2h,是把前向和反向隐藏层的隐状态个数拼起来。(当然前后的隐状态数量可以不一样)
@@@@@@@@ Seq2Seq
_filters: []
_contexts: []
_links: []
_sort:
field: rank
asc: false
group: false
@@@@@@@@ 数据处理(机器翻译)
词元化
- 按空格划分(非中文类型)
- 标点前添加空格
- 小写化
- 使用统计词表
- 使用
<pad> <eos>划分固定长度句子
@@@@@@@@ 编码器解码器

- 编码器接受变长输入
- 解码器可以变长输出(有一个固定长度输入)
- 解码器接收编码器的状态输出
- 这个状态输出要转化成解码器的状态(根据一些参数)
- 编码器是变长转固定状态,可以用循环神经网络
- 解码器接收编码器的状态输出
- 对于编码 - 解码器结构有两个输入
- 中间的固定状态结构是隐状态结构,这样就能用固定的神经网络模型去解决变长输入输出的序列模型实现
- 使用嵌入层做到把变长序列变为固定长度词向量的转换
- 然后再通过词向量做循环神经网络
- 通过
<eos>的标记停止过程 - 这是在深度学习工程上的一种结构设计(就像是软件工程的 MVC)

- 可以观察到,解码器的输出是依赖于自身的输出的,成为一个词语接龙
编码器
循环神经网络实现
$$ \mathbf{h}_t = f(\mathbf{x}t, \mathbf{h}{t-1}). \to \mathbf{c} = q(\mathbf{h}_1, \ldots, \mathbf{h}_T). $$
- 转化为上下文(context)变量 $\mathbf{c}$
- 做个函数 $q$ 是一种选择、处理隐状态的方法
- 有两个输出一个是 rnn 的 output 一个是 state
嵌入层(embedding layer)
nn.Embedding(vocab_size, embed_size)- 有词表大小(vocab_size)和每个词的特征向量大小(embed_size)作为参数
- 可以参考 RNN 处理数据时的情况:从字符构建一个词表,然后 one_hot 构建词向量
- 不过在潜入层这里词表大小和词向量不一样,说明不需要对每个词独热编码,因为多个词之间并不是完全独立的关系
- 这样数据处理时只需要考虑 batch_size 和 num_step 的划分即可
解码器
$$ P(y_{t’} \mid y_1, \ldots, y_{t’-1}, \mathbf{c}) \to \mathbf{s}{t^\prime} = g(y{t^\prime-1}, \mathbf{c}, \mathbf{s}_{t^\prime-1}). $$
循环神经网络实现
- 解码器依赖编码器输出的 state
- 因为解码器有两个输入,所以输入纬度要包括解码器本身输入的词向量纬度(embed_size)加上编码器的最后一层状态(state)纬度(num_hiddens)
- 这里要保证的是 batch_size, num_steps 的纬度是一致的
- 不一致怎么处理?
- 在 RNN 中要注意时不时注意转换
(batch_size,num_steps,vocab_size)
- 最后输出是 RNN 到 vocab_size 的一个全连接层

损失函数
- 需要先用 sequence_mask 屏蔽填充元素,用 0 乘使之无效
- 先假设屏蔽权重为 1,即所有元素都选择
- 然后根据输入 X 和 X 的有效长度序列把屏蔽元素找出来设置为 0
- 最后吧原始损失函数输出乘上 sequence_mask 的权重
- 把加上 sequence_mask 的交叉熵函数作为损失函数
[!NOTE] 注意
CrossEntropyLoss == nll_loss(log_softmax(input, dim=1), target)
在 RNN 的例子中,先对 vocab_size 的分类进行交叉熵计算,再对 num_steps 个步数取平均
- 实际 CrossEntropyLoss 的 target 只需传入标签即可因为这边会自动 one_hot 因为每个标签值一定是一类(其他都为 0)
训练
- X 是给编码器的,Y 是加上起始位给解码器的(强制教学)
预测
- 如果是翻译,则把待翻译句子给编码器生成解码器需要的状态输出
- 然后根据预测步数,给编码器一个
<bos>预测一个 Y 出来,反复遍历,直到eos出现或者超过指定预测步数
评估
机器翻译
BLEU(bilingual evaluation understudy)
- 被广泛用于测量许多应用的输出序列的质量
- 原理:略
@@@@@@@@ 搜索算法
- 贪心搜索(greedy search)
- 找可能的最高概率输出结果 $y_{t’} = \operatorname*{argmax}{y \in \mathcal{Y}} P(y \mid y_1, \ldots, y{t’-1}, \mathbf{c})$

- 问题,因为每次输出来自于上一次输出,这样不能保证整体的最优
- 穷举搜索(exhaustive search)
- 遍历所有情况找出全局最优;基本不可能实现
- 束搜索(beam search)
- 选取一个宽度进行穷举:束宽(beam size)
- 时间复杂度:$\mathcal{O}(k\left|\mathcal{Y}\right|T’)$
- 贪心搜索是宽度为 1
用于 seq2seq 预测时的方法,是一个个从 <bos> 开始预测,然后选择一个搜索算法去选择预测词,因为给出的输出是词表的分布
@@@@@@@@ 说明
注意力提示


说明注意力是会间接影响的,这就是为什么 transformer 中多层注意力结合在一起能够有效果
注意力汇聚/集中(attention pooling)

- Key 相当于是环境,Query 相当于是脑袋的动机,然后会生成某个概念 Value(环境和动机生成权重对概念进行加权平均)
- 注意力汇聚是通过不同查询和不同键得到的权重和值进行加权平均得到的
理论部分
@@@@@@@@ Nadaraya-Watson 核回归
- 1964 年提出
- 利用注意力机制的雏形
非参数的注意力汇聚(nonparametric attention pooling)
$f(x) = \sum_{i=1}^n \frac{K(x - x_i)}{\sum_{j=1}^n K(x - x_j)} y_i,$
- K 是核(kernel)
- 整个式子像是一个平均值
- $x$ 是待查询的值
- 非参数模型
以此参考得到注意力汇聚公式:
$f(x) = \sum_{i=1}^n \alpha(x, x_i) y_i,$
- $\alpha(x, x_i)$ 为注意力权重(attention weight),是一个概率分布,和为 1
- 表示的查询 $x$ 和键 $x_i$ 的关系
- $\displaystyle{\alpha(x, x_i)=\frac{K(x - x_i)}{\sum_{j=1}^n K(x - x_j)}}$ 形成通用的注意力汇聚形式
高斯核:
$K(u) = \frac{1}{\sqrt{2\pi}} \exp(-\frac{u^2}{2}).$
$\begin{split}\begin{aligned} f(x) &=\sum_{i=1}^n \alpha(x, x_i) y_i\ &= \sum_{i=1}^n \frac{\exp\left(-\frac{1}{2}(x - x_i)^2\right)}{\sum_{j=1}^n \exp\left(-\frac{1}{2}(x - x_j)^2\right)} y_i \&= \sum_{i=1}^n \mathrm{softmax}\left(-\frac{1}{2}(x - x_i)^2\right) y_i. \end{aligned}\end{split}$
[!tip]
如果一个键 $x_i$ 越是接近给定的查询 $x$, 那么分配给这个键对应值𝑦𝑖的注意力权重就会越大, 也就“获得了更多的注意力”。
带参数注意力汇聚
$\begin{split}\begin{aligned}f(x) &= \sum_{i=1}^n \alpha(x, x_i) y_i \&= \sum_{i=1}^n \frac{\exp\left(-\frac{1}{2}((x - x_i)w)^2\right)}{\sum_{j=1}^n \exp\left(-\frac{1}{2}((x - x_j)w)^2\right)} y_i \&= \sum_{i=1}^n \mathrm{softmax}\left(-\frac{1}{2}((x - x_i)w)^2\right) y_i.\end{aligned}\end{split}$
- 有学习参数 $w$
- 不过因为带参数,所以曲线不如之前平滑
@@@@@@@@ 注意力评分函数(attention scoring function)
- 参考 Nadaraya-Watson 核回归 中的高斯核的注意力汇聚函数

- 可以看到因为是加权平均所以键和值对是一个固定数量
- 这也是后面注意力的关注范围
- 不同的注意力评分函数处理方式不一样,因为可能是纬度要求不一样,算法不一样
注意力汇聚公式
一个查询的注意力汇聚,对所有键值查询的加权平均
$f(\mathbf{q}, (\mathbf{k}_1, \mathbf{v}_1), \ldots, (\mathbf{k}_m, \mathbf{v}m)) = \sum{i=1}^m \alpha(\mathbf{q}, \mathbf{k}_i) \mathbf{v}_i \in \mathbb{R}^v,$
其中注意力评分函数为:
$\alpha(\mathbf{q}, \mathbf{k}_i) = \mathrm{softmax}(a(\mathbf{q}, \mathbf{k}_i)) = \frac{\exp(a(\mathbf{q}, \mathbf{k}i))}{\sum{j=1}^m \exp(a(\mathbf{q}, \mathbf{k}_j))} \in \mathbb{R}.$
- 对查询和键进行评分
- softmax 处理权重
- 然后点积进行加权平均
掩蔽 softmax 操作(masked softmax operation)
- 遮盖部分权重不进行注意力汇聚和 softmax 计算
- 使用一个很大的负值做遮盖(参考指数函数的负无穷)
- 如果是对张量进行遮盖要根据张量的形状进行处理,要知道输入是什么样,然后遮盖的位置如何标识
- 所以具体的遮盖实践算法需要积累
加性注意力(additive attention)
$a(\mathbf q, \mathbf k) = \mathbf w_v^\top \text{tanh}(\mathbf W_q\mathbf q + \mathbf W_k \mathbf k) \in \mathbb{R},$
- 查询和键是不同长度的矢量时
- 注意力汇聚可以连接到 MLP 中
缩放点积注意力(scaled dot-product attention)
$a(\mathbf q, \mathbf k) = \mathbf{q}^\top \mathbf{k} /\sqrt{d}.$
- 点积意味着查询和键的纬度要一致
- 方差为 $d$(纬度数) 所以要除 $\sqrt{d}$ 保证方差为 1
$\mathrm{softmax}\left(\frac{\mathbf Q \mathbf K^\top }{\sqrt{d}}\right) \mathbf V \in \mathbb{R}^{n\times v}.$
- 使用 n 个元素的小批量
@@@@@@@@ Bahdanau 注意力
前言:
- 编码 - 解码器中,编码器把序列转化为上下文信息/变量 $c_t$
- 通过 Bahdanau 注意力对上下文信息进行修改
$\mathbf{c}{t’} = \sum{t=1}^T \alpha(\mathbf{s}_{t’ - 1}, \mathbf{h}_t) \mathbf{h}_t,$
- $h$ 是编码器的隐状态, $s$ 是解码器的隐状态
- 这个公式说的是,解码器使用 $c$ 时使用编码器和解码器的注意力集中关系更新自身,即解码器不简单使用上下文变量
- 所以说的是对解码器的逻辑调整
- 至于注意力评分函数可以选择

@@@@@@@@ 多头注意力(multihead attention)
- 使用多个注意力汇聚

公式
$\mathbf{h}_i = f(\mathbf W_i^{(q)}\mathbf q, \mathbf W_i^{(k)}\mathbf k,\mathbf W_i^{(v)}\mathbf v) \in \mathbb R^{p_v},$
- $f$ 可以是加性注意力和缩放点积注意力
$\begin{split}\mathbf W_o \begin{bmatrix}\mathbf h_1\\vdots\\mathbf h_h\end{bmatrix} \in \mathbb{R}^{p_o}.\end{split}$ - $\mathbf W_o\in\mathbb R^{p_o\times h p_v}$
- 实际中使用 $p_q = p_k = p_v = p_o / h$ 可以并行计算 h 个头(也就是 transformer 中的情况)
- 实际中指定一个隐藏层数
num_hiddens作为 $p_o$ 然后根据多头数量得到 $p_q,p_k,p_v$(num_hiddens/num_heads)- 并要根据这个划分输入模型给注意力的计算(把多头放到 batch 中并行计算,输出时再转回来)
- Masking 也需要多头处理(复制多个)
- why?
- 实际中指定一个隐藏层数
- q 是类似循环神经网络中序列的长度,也就是要提问的数量
- 如果是自注意力那么查询数量和键值数量是一样的
- 如果是交叉注意力则看两方的查询和键值数量
@@@@@@@@ 自注意力机制
公式
$\mathbf{y}_i = f(\mathbf{x}_i, (\mathbf{x}_1, \mathbf{x}_1), \ldots, (\mathbf{x}_n, \mathbf{x}_n)) \in \mathbb{R}^d$
- 即 Q K V 都是输入序列本身的注意力机制
- 复杂度:$\mathcal{O}(n^2d)$ 和序列长度有关系
位置编码(positional encoding)
- 输入序列的位置信息,相当于空间换时间,使用并行 + 位置信息代替顺序执行
公式
$\begin{split}\begin{aligned} p_{i, 2j} &= \sin\left(\frac{i}{10000^{2j/d}}\right),\p_{i, 2j+1} &= \cos\left(\frac{i}{10000^{2j/d}}\right).\end{aligned}\end{split}$
- 是位置嵌入矩阵的元素:$\mathbf{P} \in \mathbb{R}^{n \times d}$
- 最后词嵌入矩阵输入加上位置嵌入矩阵即可
- 实现上 P 要设置一个最大数量,即支持序列长度的上限
- 原理解释:略
绝对位置信息
- 使用三角函数编码(比二进制编码节省空间)
- 使用三角函数的频率变化对位置编码
- 原理:略
相对位置信息
- $\omega_j = 1/10000^{2j/d}$
- $(p_{i, 2j}, p_{i, 2j+1}) \to (p_{i+\delta, 2j}, p_{i+\delta, 2j+1})$
$$ &\begin{bmatrix} \cos(\delta \omega_j) & \sin(\delta \omega_j) \ -\sin(\delta \omega_j) & \cos(\delta \omega_j) \ \end{bmatrix} \begin{bmatrix} p_{i, 2j} \ p_{i, 2j+1} \ \end{bmatrix}\ =&\begin{bmatrix} \cos(\delta \omega_j) \sin(i \omega_j) + \sin(\delta \omega_j) \cos(i \omega_j) \ -\sin(\delta \omega_j) \sin(i \omega_j) + \cos(\delta \omega_j) \cos(i \omega_j) \ \end{bmatrix}\ =&\begin{bmatrix} \sin\left((i+\delta) \omega_j\right) \ \cos\left((i+\delta) \omega_j\right) \ \end{bmatrix}\ =& \begin{bmatrix} p_{i+\delta, 2j} \ p_{i+\delta, 2j+1} \ \end{bmatrix}, \end{aligned}\end{split}$$
@@@@@@@@ Transformer
![[Transformer原理.png]]
- 在每个子网络输出部分使用了[[残差网络(ResNet)]]
- 要保证子网络输出和输入纬度一致
- $\mathbf{x} + \mathrm{sublayer}(\mathbf{x}) \in \mathbb{R}^d$
- 每个字层输出还使用了层规范化(LayerNorm)
- 对特征进行规范化,NLP 中比 BatchNorm 效果更好
- LayerNorm 许要输入步长和特征
- 所以模型的
num_hiddens要对应norm_shape
- 所以模型的
- 在解码器使用了 masked 让预测只依赖过去的输出
- 自回归(auto-regressive)
- 解码器的多头注意力包含了解码器的输入还加入了编码器的上下文变量
基于位置(positionwise)的前馈网络 FFN
- 使用两个全连接层,转化为 ffn 的输出
- 含义是表征序列的位置
AddNorm
$$
\mathbf{O} = \mathrm{LN}(Dropout(\mathbf{Y})+\mathbf{X})
$$
- 残差网络
- 需要给定特征形状和 Dropout 比例
编码器
- 位置编码输入需要:
- 嵌入值乘以嵌入维度的平方根进行缩放
编码器块
$$
\begin{align}
\mathbf{Y} &= \mathrm{AddNorm}(\mathbf{X}, \mathrm{SelfAttention}(\mathbf{X},\mathbf{X},\mathbf{X},Masked)), \\
\mathbf{O} &= \mathrm{AddNorm}(\mathbf{Y}, \mathrm{AddNorm}(\mathbf{Y}))
\end{align}
$$
解码器
- 预测阶段要保留自回归的输出作为输入序列
- 训练阶段需要生产序列长度递增的掩码用于遮盖注意力
- 有两个注意力模块
- 第一个的键值在训练阶段都是自己(因为有整个序列的输入);在预测阶段是不断把过去的输入记录并拼接起来
- ?这样输入作为 Q 是不是和键值的长度不一致
- 第二个是由第一个注意力机制的输出和编码器的输出进行注意力集中,还接受编码器提供的掩码序列
- 第一个的键值在训练阶段都是自己(因为有整个序列的输入);在预测阶段是不断把过去的输入记录并拼接起来
训练
- 结合 Transformer 的结构研究
train_seq2seq函数逻辑
预测
- 结合 Transformer 的结构研究
predict_seq2seq函数逻辑
@@@@@@@@ 预训练
@@@@@@@@ 词嵌入(word embedding)
- 词嵌入是为了将自然语言用数值表示的的技术
- 就是把不可计算变为可计算
- 先使用语言模型来定义词汇的统计模型,然后进行推断
- 和语言学也会有关,因为是在研究语言本身
- 如果使用预训练词嵌入,则复制参数即可
- 一般是查表得到所需训练词表潜入
- 然后固定参数即可(1. 优化器中排除,2. 不计算梯度,3. nn.Embedding 设置 freeze 参数)
- 因为独热编码没有考虑近义词关系
- 所以本质是从 OneHot 转向学习一个 Embed 矩阵
- 词向量是用于表示单词意义的向量,也可以看作词的特征向量。将词映射到实向量的技术称为词嵌入。
- 词嵌入的原理需要扎实的数学基础
- 提出的模型和算法种类很多,有需要积累即可
词嵌入模型(word2vec)
跳元模型(Skip-Gram)
- 假设一个词可以用来在文本序列中生成其周围的单词
- 即考虑:$P(\textrm{“the”},\textrm{“man”},\textrm{“his”},\textrm{“son”}\mid\textrm{“loves”}).$
- 因为不像语言模型是 n-gram 的连续排列关系,所以叫跳元 ![[Pasted image 20240512193851.png]]
- 每个词用两个向量 $\mathbf{v}_i\in\mathbb{R}^d$ ,$\mathbf{u}_i\in\mathbb{R}^d$ ,表示其用作中心词和上下文词时的两个向量
- 说明一个词可以是中心词也可以是被参考的上下文词
- 可以计算概率为:$\displaystyle{P(w_o \mid w_c) = \frac{\text{exp}(\mathbf{u}_o^\top \mathbf{v}c)}{ \sum{i \in \mathcal{V}} \text{exp}(\mathbf{u}_i^\top \mathbf{v}_c)},}$
- $w_o$ 是某个上下文词
- $w_c$ 是某个中心词
- 有似然函数(一个模型估计,然后可以通过这个估计去计算位未知参数):$\prod_{t=1}^{T} \prod_{-m \leq j \leq m,\ j \neq 0} P(w^{(t+j)} \mid w^{(t)}),$
- m 为上下文窗口
- 有损失函数:$- \sum_{t=1}^{T} \sum_{-m \leq j \leq m,\ j \neq 0} \text{log}, P(w^{(t+j)} \mid w^{(t)}).$
- 其中:$\log P(w_o \mid w_c) =\mathbf{u}_o^\top \mathbf{v}c - \log\left(\sum{i \in \mathcal{V}} \text{exp}(\mathbf{u}_i^\top \mathbf{v}_c)\right).$
- 那么就有梯度(注意有来自整个词表的求和): $$
\begin{split}\begin{aligned}\frac{\partial \text{log}, P(w_o \mid w_c)}{\partial \mathbf{v}_c}&= \mathbf{u}o - \frac{\sum{j \in \mathcal{V}} \exp(\mathbf{u}_j^\top \mathbf{v}_c)\mathbf{u}j}{\sum{i \in \mathcal{V}} \exp(\mathbf{u}_i^\top \mathbf{v}_c)}\&= \mathbf{u}o - \sum{j \in \mathcal{V}} \left(\frac{\text{exp}(\mathbf{u}_j^\top \mathbf{v}c)}{ \sum{i \in \mathcal{V}} \text{exp}(\mathbf{u}_i^\top \mathbf{v}_c)}\right) \mathbf{u}_j\&= \mathbf{u}o - \sum{j \in \mathcal{V}} P(w_j \mid w_c) \mathbf{u}_j.\end{aligned}\end{split}
$$
连续词袋(CBOW)模型
-
中心词由上下文词生成的,与跳元相反
-
$P(\textrm{“loves”}\mid\textrm{“the”},\textrm{“man”},\textrm{“his”},\textrm{“son”}).$ ![[Pasted image 20240512193910.png]]
-
$\displaystyle{P(w_c \mid w_{o_1}, \ldots, w_{o_{2m}}) = \frac{\text{exp}\left(\frac{1}{2m}\mathbf{u}c^\top (\mathbf{v}{o_1} + \ldots, + \mathbf{v}{o{2m}}) \right)}{ \sum_{i \in \mathcal{V}} \text{exp}\left(\frac{1}{2m}\mathbf{u}i^\top (\mathbf{v}{o_1} + \ldots, + \mathbf{v}{o{2m}}) \right)}.}$
-
简化:
- $\bar{\mathbf{v}}o = \left(\mathbf{v}{o_1} + \ldots, + \mathbf{v}{o{2m}} \right)/(2m)$
- $\displaystyle{P(w_c \mid \mathcal{W}_o) = \frac{\exp\left(\mathbf{u}_c^\top \bar{\mathbf{v}}o\right)}{\sum{i \in \mathcal{V}} \exp\left(\mathbf{u}_i^\top \bar{\mathbf{v}}_o\right)}.}$
-
似然函数:$\prod_{t=1}^{T} P(w^{(t)} \mid w^{(t-m)}, \ldots, w^{(t-1)}, w^{(t+1)}, \ldots, w^{(t+m)}).$
-
梯度:
-
$$\begin{align}\frac{\partial \log, P(w_c \mid \mathcal{W}o)}{\partial \mathbf{v}{o_i}} &= \frac{1}{2m} \left(\mathbf{u}c - \sum{j \in \mathcal{V}} \frac{\exp(\mathbf{u}_j^\top \bar{\mathbf{v}}_o)\mathbf{u}j}{ \sum{i \in \mathcal{V}} \text{exp}(\mathbf{u}_i^\top \bar{\mathbf{v}}_o)} \right) \ &= \frac{1}{2m}\left(\mathbf{u}c - \sum{j \in \mathcal{V}} P(w_j \mid \mathcal{W}_o) \mathbf{u}_j \right).\end{align}$$
训练和技巧
负采样
[!NOTE] GPT4 负采样的核心思想是将一个复杂的问题简化为一个更简单的问题。原始的问题是,给定一个中心词$w^{(t)}$,我们想要计算它的每一个可能的上下文词$w^{(t+j)}$的条件概率$P(w^{(t+j)}|w^{(t)})$。这是一个多类别分类问题,因为我们的目标是在所有可能的上下文词中选择一个。这个问题的复杂性在于,我们需要计算和比较所有可能的上下文词的概率,这在词汇表非常大时是非常耗时的。
负采样的做法是,将这个多类别分类问题转化为一个二元分类问题。对于每一个中心词和上下文词的组合$(w^{(t)}, w^{(t+j)})$,我们不再计算$w^{(t+j)}$是$w^{(t)}$的上下文词的概率,而是计算这个组合是一个正样本(即,$w^{(t+j)}$确实是$w^{(t)}$的上下文词)的概率。这就是正样本事件$S$,即$P(D=1|w^{(t)}, w^{(t+j)})$。
然后,我们随机选择$K$个噪声词,对于每一个噪声词$w_k$,我们计算它和$w^{(t)}$组成的组合是一个负样本(即,$w_k$不是$w^{(t)}$的上下文词)的概率。这就是负样本事件$N_k$,即$P(D=0|w^{(t)}, w_k)$。
这样,我们就将原来的问题简化为了一个二元分类问题。我们的目标是最大化正样本的概率,同时最小化负样本的概率。这种做法的好处是,我们只需要考虑一个正样本和$K$个负样本,而不是所有可能的上下文词。这大大减少了计算的复杂性,使得模型的训练变得更加高效。
这个假设并不是完全准确的,因为它只是近似地表示了原始的条件概率。但是,在实践中,这种近似已经被证明是有效的,能够生成高质量的词嵌入。
- 建模:$P(D=1\mid w_c, w_o) = \sigma(\mathbf{u}_o^\top \mathbf{v}_c),$
- 似然:$\prod_{t=1}^{T} \prod_{-m \leq j \leq m,\ j \neq 0} P(D=1\mid w^{(t)}, w^{(t+j)}).$
- 这样只有正样本,意义不大
负采样
- 从预定义分布 $P(w)$ 中采样上下文词的负样本(因为假设不选中,也就是噪声,从词表中随机采样得到(除去上下文词情况下))
- 然后能得到负样本的分布
- 变成二分类问题
- 实践中,上下文词的噪声词根据整个词表的频次的 0.75 次幂作为离散采样权重(
离散频率*总数)- 每个上下文词对应 K 个噪声词 $$
P(w^{(t+j)} \mid w^{(t)}) =P(D=1\mid w^{(t)}, w^{(t+j)})\prod_{k=1,\ w_k \sim P(w)}^K P(D=0\mid w^{(t)}, w_k).
$$ 对数损失为(只依赖 K 的求和): $$
\begin{split}\begin{aligned}
-\log P(w^{(t+j)} \mid w^{(t)})
=& -\log P(D=1\mid w^{(t)}, w^{(t+j)}) - \sum_{k=1,\ w_k \sim P(w)}^K \log P(D=0\mid w^{(t)}, w_k)\\
=&- \log, \sigma\left(\mathbf{u}{i{t+j}}^\top \mathbf{v}{i_t}\right) - \sum{k=1,\ w_k \sim P(w)}^K \log\left(1-\sigma\left(\mathbf{u}{h_k}^\top \mathbf{v}{i_t}\right)\right)\\
=&- \log, \sigma\left(\mathbf{u}{i{t+j}}^\top \mathbf{v}{i_t}\right) - \sum{k=1,\ w_k \sim P(w)}^K \log\sigma\left(-\mathbf{u}{h_k}^\top \mathbf{v}{i_t}\right).
\end{aligned}\end{split}
$$
分层 Softmax
- 使用二叉树来表示词的选择
- 原理:略
$$
P(w_o \mid w_c) = \prod_{j=1}^{L(w_o)-1} \sigma\left( [![ n(w_o, j+1) = \text{leftChild}(n(w_o, j)) ]!] \cdot \mathbf{u}_{n(w_o, j)}^\top \mathbf{v}_c\right),
$$
![[Pasted image 20240514115224.png]]
$$
P(w_3 \mid w_c) = \sigma(\mathbf{u}_{n(w_3, 1)}^\top \mathbf{v}c) \cdot \sigma(-\mathbf{u}{n(w_3, 2)}^\top \mathbf{v}c) \cdot \sigma(\mathbf{u}{n(w_3, 3)}^\top \mathbf{v}_c).
$$
下采样
- 有一些无用高频词汇 a the 之类的可以进行去除(高频词汇有用信息少,训练慢) $$
P(w_i) = \max\left(1 - \sqrt{\frac{t}{f(w_i)}}, 0\right),
$$
- $t$ 为超参数
- 当词的占比 $f(w_i)$ 大于 $t$ 时,就会开始有概率被丢弃
- 实践中是,从均匀分布中采样(即随机抽样)和该概率比较,得到一个按照概率丢弃
中心词和上下文词提取
- 根据语料库,设定一个最大的上下文窗口,然后进行随机窗口大小抽取上下文词
- 但是因为随机窗口导致一个中心词对应的上下文词和噪声词列表长度是不同的
- 所以使用一个长列表同时装进上下文词和噪声词,列表的长度为最大的上下文、噪声词列表长度的和
- 即
max(len(c)+len(n)) # c 为上下文词列表,n为噪声词列表 - 然后不足部分填充0
- 即
- 并且要根据上下文词和噪声词给出正负样本标签
- 这样最终就得到:中心词、上下文-噪声、掩码、标签四个列表
- 实际中 DataLoader 可以通过
collate_fn=指定批次处理函数- 因为 DataSet 输入只有中心词、上下文词和噪声词,需要在每个批次对其进行处理得到计算数据
填充掩码
- 使用随机窗口得到的上下文词、噪声词联结的部分是不用于损失计算的,所以需要对应的填充掩码,计算损失时去除
前向传播
- 分别对中心词和上下文用两个词的嵌入矩阵表示
- 然后计算中心词和多个上下文词嵌入后的的点积(计算距离)
损失函数
- 带掩码的二元交叉熵损失
- 计算与一般的训练有所不同,要考虑掩码的数量
- 说明:略,实践就是损失乘上 掩码的占比,如(1,1,0,0)就乘 1/2.
预测
- 根据中心词的嵌入矩阵,可用计算余弦相似度得到相似性
- $\frac{\mathbf{x}^\top \mathbf{y}}{|\mathbf{x}| |\mathbf{y}|} \in [-1, 1].$
- 实际是计算嵌入矩阵与查询单词相近的词
- 通过嵌入矩阵,可以通过某个词的编号得到一个向量
- 然后在通过这个向量和其他所有向量的相似度计算
- 可以得到这个向量和所有向量的相似度
- 算法解释:就是最k临近算法(knn)
cos = torch.mv(W, x) torch.sqrt(torch.sum(W * W, dim=1) * torch.sum(x * x) + 1e-9)
- 然后通过 topk 可以得到相似的几个单词(top1 是自己本身,要去除)
全局向量的词嵌入(GloVe)
- 引入全局语料统计
- 多重集 $\mathcal{C}_i$ 由 $w_i$ 生成的所有上下文窗口中其他词的索引构成
- 可以重复的集合,重复的数量称之为该元素的重数
有跳元模型: $$
q_{ij}=\frac{\exp(\mathbf{u}_j^\top \mathbf{v}i)}{ \sum{k \in \mathcal{V}} \text{exp}(\mathbf{u}_k^\top \mathbf{v}_i)},
$$
- $\mathcal{V} = {0, 1, \ldots, i/j,, \ldots, |\mathcal{V}|-1}$
那么引入全局词嵌入的模型: $$
-\sum_{i\in\mathcal{V}}\sum_{j\in\mathcal{V}} x_{ij} \log,q_{ij}.
$$
- $x_{ij}$ 为 $w_i$ 多重集中上下文词的重数
再有 $p_{ij}$ 为给定 $w_i$ 的上下文词的条件概率(共现概率),可以重写为:
$$
-\sum_{i\in\mathcal{V}} x_i \sum_{j\in\mathcal{V}} p_{ij} \log,q_{ij}.
$$
- 交叉熵为 $-\sum_{j\in\mathcal{V}} p_{ij} \log,q_{ij}$ ,对词表进行交叉熵不太好
- 公式本身是对交叉熵进行加权平均
- $x_i$ 表示上下文窗口中的所有上下文词的数量
GloVe模型
- 使用 $p’{ij}=x{ij}$ 和 $q’_{ij}=\exp(\mathbf{u}_j^\top \mathbf{v}_i)$
- 去平方损失 $\left(\log,p’{ij} - \log,q’{ij}\right)^2 = \left(\mathbf{u}_j^\top \mathbf{v}i - \log,x{ij}\right)^2$
- 损失函数:$\displaystyle{\sum_{i\in\mathcal{V}} \sum_{j\in\mathcal{V}} h(x_{ij}) \left(\mathbf{u}_j^\top \mathbf{v}i + b_i + c_j - \log,x{ij}\right)^2.}$
- 权重函数 $h(x) = (x/c) ^\alpha$ ,$h(x)=1\quad if (x >= c)$
- 因为 $h(0)=0$ 所以可以忽略 $x_{ij}=0$ 的计算
- $x_{ij}$ 就是全局向量
- 权重函数 $h(x) = (x/c) ^\alpha$ ,$h(x)=1\quad if (x >= c)$
[!Tip] 应该强调的是,当词 $w_i$ 出现在词 $w_j$ 的上下文窗口时,词𝑤𝑗也出现在词𝑤𝑖的上下文窗口。因此,$x_{ij}=x_{ji}$。与拟合非对称条件概率 $p_{ij}$ 的word2vec不同,GloVe拟合对称概率$log;x_{ij}$。因此,在GloVe模型中,任意词的中心词向量和上下文词向量在数学上是等价的。但在实际应用中,由于初始值不同,同一个词经过训练后,在这两个向量中可能得到不同的值:GloVe将它们相加作为输出向量。
共现概率比值的说法
- 设共现概率的拟合函数:$\displaystyle{f(\mathbf{u}_j, \mathbf{u}k, {\mathbf{v}}i) \approx \frac{p{ij}}{p{ik}}.}$
- $p_{ij}$ 为共现概率,这个比值接近 1,说明 $w_i$ 同时和 $w_j$ $w_k$ 接近,或者都不接近;否则只偏向一方
- 假设 $f(x)=\exp(x)$
- $\displaystyle{f(\mathbf{u}_j, \mathbf{u}_k, {\mathbf{v}}_i) = \frac{\exp\left(\mathbf{u}_j^\top {\mathbf{v}}_i\right)}{\exp\left(\mathbf{u}k^\top {\mathbf{v}}i\right)} \approx \frac{p{ij}}{p{ik}}.}$
- 设 $\exp\left(\mathbf{u}_j^\top {\mathbf{v}}i\right) \approx \alpha p{ij}$
- 取对数有 $\mathbf{u}_j^\top {\mathbf{v}}i \approx \log,\alpha + \log,x{ij} - \log,x_i$
- 得到:$\mathbf{u}_j^\top \mathbf{v}i + b_i + c_j \approx \log, x{ij}.$
- 使用偏置代替常数
- 然后最小化这个式子的误差即可
子词嵌入
- 对一个单词的内部进行探讨
- 如各种单词的变体,加 ing、s、ed 等和单词本身的关系
- 如果用前面的模型是把两者单独看待的
fastText模型
- 字符级别的跳元模型,即对一个单词进行处理
- 前后使用
<>包起来作为一个词的 “句子”
- 前后使用
$$\mathbf{v}w = \sum{g\in\mathcal{G}_w} \mathbf{z}_g.$$
- $\mathcal{G}_w$ 表示 $w$ 其长度在 3 到 6 之间的所有字词和特殊子词(单词本身)的并集
- $\mathbf{z}_g$ 是字词的向量
- $\mathbf{v}_w$ 是跳元模型的词向量
- 计算量非常大
字节对编码(Byte Pair Encoding)
- 使用固定大小词表,在固定大小的词表中允许可变长度的子词
- 引出 BPE 作为压缩算法来提取子词
- 根据统计分析得到单词内部的公共部分,如 ing
- 从字母表开始,使用
_作为词尾标记,并添加一个[UNK]作为未知词
分割-合并算法(贪心算法):
-
先对所有 token 的基本字符进行拆分,然后迭代合并
-
返回所有 token 中相邻两次符号的最大频率
- 目的得到哪些符号是最可能组合起来的
- 这样就能产生新的符号,不断扩充符号表
-
根据上述得到的相邻符号,对原始拆分的 token 列表中的符号进行合并
-
这样循环迭代,会不断的把小符号拓展成大的符号组
-
这样就能找到合适的字词分割
- 通过遍历扩充的符号表,能够找到用最少符号表示的单词组合方式
-
检索算法(双指针,start 和 end):
- 遍历所有单词 tokens
- 为符号表包含基本字母,所以一定能找到字词
- 如果 start 到 end 构成一个子词,那就添加一个子词部分
- end 从后往前,这样每次都能得到最长子词
- 当找到一个 start 开始的子词,记录子词,然后移动 start
- 最坏情况就是 end 和 start 碰面,也就是一个基本字母作为子词
预训练词嵌入
- 便于下游的自然语言处理任务
- 通过预训练的词嵌入数据,能够简单构建词表
- 可以做到 token,idx 和 vec 的互相转换
- 可以通过 knn 来使用相似度计算次和词之间关系
- 可以找到目标词相近的几个词
- 也可以做类比操作,对词向量进行加减得到平移类比的向量
@@@@@@@ 说明
- NLP 很多概率模型,可能是是基于推断的概率图
- 为什么和概率相关,因为我们采集到的数据都来自于某些分布
- 图片的每个像素是概率的,每个字符是概率的
- 所以可以假设这些分布(正态分布、t分布)或者这些分布之间的关系(KL散度),然后进行统计估计即可(矩估计、最大似然估计)
@@@@@@@@ BERT
- ELMo:来自预训练的双向长短期记忆网络
- 上下文敏感,特定专有的任务
- GPT:从左到右,任务无关
- BERT:双向编码器,任务无关
![[Pasted image 20240514183254.png]]
- 特殊词元:
<cls><sep> - $e$ 表示词嵌入
- 段嵌入表示是单输入序列还是双输入序列
- 不同段有着不同的嵌入
- 段的大小不一定一样
![[Pasted image 20240514184157.png]]
- 编码器
- 带位置、段和词元嵌入的 Transformer 编码器
- 掩蔽语言模型(Masked Language Modeling)
- 随机掩蔽词元
- 选取 15% 的词元,使用
<mask>、错误的词,或者不变进行遮蔽
- 选取 15% 的词元,使用
- 使用来自双向上下文的词元
- 编码双向上下文
- 随机掩蔽词元
- 下一句预测(Next Sentence Prediction)
- 二分类预测第二个句子是否为 BERT 的下一句
- 是一个 MLP,其中输入来自一个隐藏层(获取编码器的 cls)
- 输出则是一个二分类 $$

