一、机器的输出
(1)sequence labeling(序列标注):一类常见的自然语言处理(NLP)任务,其目的是为输入的序列中的每个元素(通常是单词、字符或标记)分配一个标签。序列标注的核心目标是处理具有顺序依赖性的任务,也就是说,模型需要理解输入序列中的每个元素之间的关系,并根据这个上下文为每个元素打上合适的标签。输入与输出数量一致。
- 举例说明:
- 对”Steve Jobs founded Apple Inc. in Cupertino.“命名实体识别,
Steve Jobs
被标记为PERSON
(人名),Apple Inc.
被标记为ORG
(公司名),Cupertino
被标记为LOC
(地点名),而founded
和in
等普通词被标记为O
(其他)。
- 对”Steve Jobs founded Apple Inc. in Cupertino.“命名实体识别,
- 应用:
- 词性标注(POS tagging):为每个单词分配一个词性标签(如动词、名词、形容词等)。
- 命名实体识别(NER):识别文本中的专有名词(如人名、公司名、地点名)。
- 信息抽取:从文本中提取出结构化的信息。
- 句子分块(Chunking):识别并标记出语法上的句子块(如名词短语、动词短语等)。
- 实现方法:
- 传统方法
- 条件随机场(CRF)和隐马尔可夫模型(HMM)
- 深度学习方法
- 基于 循环神经网络(RNN)和 Transformer 架构
- 传统方法
(2)Sequence Classification(序列分类):输入是一个完整的序列(如文本、时间序列、音频数据等),输出是一个单一的标签,表示整个序列的类别或属性。
- 举例说明:
- 情感分类:我很开心 --积极 我非常沮丧--消极
- 实现方法:
- 基于 RNN 的模型:如 LSTM、GRU,可以捕获序列的时间依赖特性。
- 基于 Transformer 的模型:如 BERT、RoBERTa,适用于长文本。
- 卷积神经网络(CNN):可用于短文本分类,尤其是当文本长度固定时。
(3)Sequence to Sequence:是一种广泛应用于序列学习任务的神经网络模型结构,特别适用于那些输入和输出都是序列的任务,例如机器翻译、语音识别、文本摘要等。Seq2Seq 模型的主要特点是,它能够处理可变长度的输入序列并生成可变长度的输出序列。即输入和输出的长度是随机的,不需要固定。Seq2Seq 模型可以端到端训练,即可以直接通过训练数据学习输入与输出之间的映射关系,不需要手动提取特征。
- 基本架构:
- 编码器(Encoder):编码器的任务是将输入序列映射到一个固定长度的上下文向量(通常称为“隐状态”或“编码器的最终隐藏状态”)。这个过程是通过对输入序列进行逐步处理(如逐个词处理)来实现的。编码器接受一个序列 x=[x1,x2,...,xT] 作为输入,其中 T 是输入序列的长度,通常每个 xi 是词嵌入向量。
- 工作流程:
- 输入序列 x依次通过编码器的每个时刻,经过一个 RNN 或 LSTM 单元进行处理。
- 每个时刻的输出是当前词的表示(或隐藏状态),并且通常会与前一个时刻的隐藏状态一同作为输入传递到下一个时刻。
- 最终,编码器将生成一个固定长度的上下文向量(或隐藏状态),该向量表示了输入序列的所有信息。
- 公式:
- 编码器的输出 hT通常被视为输入序列的 上下文向量,用于传递给解码器。
- 工作流程:
- 解码器(Decoder):基于编码器提供的上下文向量生成输出序列。解码器的每个时刻生成一个词(或标记),直到生成完整的输出序列。
- 工作流程:
- 解码器接受编码器的最终隐藏状态作为其初始输入,初始化其隐藏状态。
- 解码器的每个时刻会生成一个输出词,并将生成的词作为输入反馈给解码器的下一个时刻,同时更新隐藏状态。
- 解码器通常通过一个 softmax 层来输出一个概率分布,从中选择下一个词。
- 公式:
- Seq2Seq的挑战:
- 长序列的依赖问题:对于较长的序列,传统的 RNN 或 LSTM 网络在传递信息时可能会遇到 梯度消失 或 梯度爆炸 问题,导致模型难以捕捉长距离的依赖关系。
- 信息丢失:编码器的最终隐藏状态(上下文向量)通常被视为输入序列的整体表示,但在长序列中,这种表示可能会丢失一些细节,导致解码器无法有效生成输出。
- 输出的多样性:Seq2Seq 模型生成的输出序列通常是确定性的(即每次生成相同的输出),这在某些应用中可能不够灵活,尤其是在生成任务中。
- Seq2Seq的改进:
- 引入注意力机制(Attention Mechanism)
- 双向编码器(Bidirectional Encoder)
- Transformer 模型
- 工作流程:
- 编码器(Encoder):编码器的任务是将输入序列映射到一个固定长度的上下文向量(通常称为“隐状态”或“编码器的最终隐藏状态”)。这个过程是通过对输入序列进行逐步处理(如逐个词处理)来实现的。编码器接受一个序列 x=[x1,x2,...,xT] 作为输入,其中 T 是输入序列的长度,通常每个 xi 是词嵌入向量。
二、自注意力机制(self.attention)
(1)为什么需要self.attention?
当我们将内容输入期望得到输出结果的过程中,输入的内容如果相互不关联做处理,那将准确获取输入的内涵,比如I saw a saw。两个saw是一样的输入,机器如何知道他们具备不同的含义表示不同的词性呢?这时我们考虑关联上下文可以解决这部分问题。但是关联上下文设定的Window大小应该如何设定呢?设定一个固定大小的窗口对所有输入都是有效的吗?在传统的模型(如 RNN 或 CNN)中,理解这个关系依赖于窗口大小的选择。然而,窗口大小 是固定的,限制了模型对长距离依赖的捕捉能力。Self-Attention 机制通过直接计算序列中各个元素之间的关系,无需依赖窗口大小。RNN 等递归网络必须按顺序处理输入数据,导致计算过程不可并行化,限制了训练效率。而 Self-Attention 基于矩阵运算,能够一次性计算整个序列的注意力权重,极大提高了训练效率。
(2)工作原理: 是一种能够在序列中计算每个元素与其他元素之间关系的机制。它通过为每个输入元素分配一个权重(即“注意力”),来决定该元素对其他元素的关注程度,从而实现信息的动态聚合。Self-Attention 的核心思想是:每个元素通过对整个序列进行自我关注来获取有用的上下文信息。
- 输入表示(Input Representation): 假设输入是一个序列,例如一个文本句子。每个元素(如单词、字符)首先会通过嵌入层(embedding layer)转换为向量表示。例如下图的ai
-
计算 Query、Key 和 Value 向量: 对于输入序列中的每个元素,通过三个不同的线性变换(权重矩阵)将其转换为三个向量:
-
Query (Q):表示当前词希望从其他词中获取的信息,
。举例说明在图书馆找机器学习的书,Q表示“我想找关于 机器学习 的书,谁有相关信息?”
-
Key (K):表示其他词提供的信息,
。a书表示"历史",b书"机器学习",c书"文学",K表示图书馆中每本书的主题标签。
- Value (V):表示与该词相关的实际信息,
。a 书的内容是 "关于古代文明的详细介绍";b 书的内容是 "机器学习的算法和实践";C 书的内容是 "一部浪漫小说"。V就是这些书的具体内容。
-
-
计算注意力权重αi:
- 如何获取αi呢?目前有多种计算方式这里以dot-product为例。
-
对于每个 Query 向量 qi,与所有 Key 向量进行点积计算,得到每个 Key 与 Query 的相似度:
- 这个点积的结果反映了当前词ai与其他词aj的关联度。然后,通过 Softmax 操作,将这些得分转化为概率分布,使得所有注意力权重和为1:
- 注意 Softmax并不是固定的,也可以采用其他方式,如Sigmoid 函数、加权点积等
- 如何获取αi呢?目前有多种计算方式这里以dot-product为例。
- 加权求和(聚合信息): 使用上一步计算得到的注意力权重aij,对 Value 向量进行加权求和,得到最终的输出表示bi,如下图:
-
多个头的自注意力(Multi-Head Attention): 为了让模型能够从不同的子空间学习不同类型的关系,Self-Attention 通常会通过多个独立的注意力头(Heads)来计算。每个头都有自己独立的 Query、Key 和 Value 变换。最后,将多个头的输出拼接在一起,通过一个线性层得到最终的输出。
-
-
h 是头的数量,Wb是用于映射的权重矩阵。
-
-
三、transformer(seq2seq的model)
(1)位置编码(Positional Encoding)
为每个词添加了一个与位置相关的向量,从而在计算注意力时考虑词的顺序。绝对位置编码(Absolute Positional Encoding):使用一个固定的、预定义的向量来表示每个词在序列中的绝对位置。通常,位置编码的维度与词嵌入的维度相同。相对位置编码(Relative Positional Encoding):不仅仅关注绝对位置,还关注词与词之间的相对位置关系。这个方法通常用于提升模型对不同序列长度的适应性。
-
transformer的input=input_embedding + positional_encoding
input_embedding =单个token从vocab_size映射成d_model维度的向量(在原论文里,d_model = 512)
因为两向量相加,因此positional_encoding与input_embedding维度相同
-
为什么需要引入位置信息?
在 Self-Attention 机制中,由于其计算方式依赖于输入序列的元素之间的关系,而不是序列中元素的顺序,因此它本身没有内建的 顺序信息。这意味着 Self-Attention 无法自动感知词汇在序列中的相对或绝对位置(例如,哪一个词在句子中是第一个,哪个词在句子中是最后一个)。然而,语言的理解离不开位置信息,因此我们需要一种方法将 位置信息 引入 Self-Attention 模型中。
- 绝对位置信息:基于固定参考点(全局坐标)的位置信息。以abcd为例,a是第一个,b是第二个...
- 相对位置信息:基于另一个对象或当前状态的相对位置。a在b前一位,d在b后两位。
- 不同位置间的距离:a与c差两个位置,a与d差三个位置。
-
位置编码的方法演变
①用整型值标记位置:第一个token为1,第二个token为2....;但是模型可能遇见比训练时所用的序列更长的序列。不利于模型的泛化。模型的位置表示是无界的。随着序列长度的增加,位置值会越来越大。
②用[0,1]范围标记位置:考虑将位置值的范围限制在[0, 1]之内,其中,0表示第一个token,1表示最后一个token。比如有3个token,那么位置信息就表示成[0, 0.5, 1];若有四个token,位置信息就表示成[0, 0.33, 0.69, 1]。但会导致当序列长度不同时,token间的相对距离是不一样的。
③用二进制向量标记位置:
例如d_model = 3,位置向量则表示为下图,从图中可以看出所有的值是有界的[0,1],且只有d_model够大,基本上可以把每个位置的token都覆盖。
但是这样的位置向量是离散的,不同位置的变化是不连续的,以一个d_model=2为例。从图中红色路线可以看出编码后向量结果并不连续,我们希望结果能像绿色路线那样连续。
- ④用sin表示:把位置向量当中的每一个元素都用一个sin函数来表示,则第t个token的位置向量可以表示为:
图中每一行表示一个 PEt ,每一列表示 PEt 中的第i个元素。旋钮用于调整精度,越往右边的旋钮,需要调整的精度越大,因此指针移动的步伐越小。每一排的旋钮都在上一排的基础上进行调整(函数中t的作用)。通过频率
来控制sin函数的波长,频率不断减小,则波长不断变大,此时sin函数对t的变动越不敏感,以此来达到越向右的旋钮,指针移动步伐越小的目的。 这也类似于二进制编码,每一位上都是0和1的交互,越往低位走(越往左边走),交互的频率越慢。
由于sin是周期函数,因此从纵向来看,如果函数的频率偏大,引起波长偏短,则不同t下的位置向量可能出现重合的情况。比如在下图中(d_model = 3),图中的点表示每个token的位置向量,颜色越深,token的位置越往后,在频率偏大的情况下,位置响亮点连成了一个闭环,靠前位置(黄色)和靠后位置(棕黑色)竟然靠得非常近.解决这个问题只需要将波长拉长即频率降成非常小的值。
- ⑤sin和cos交替
位置向量需要满足:每个token的向量唯一(每个sin函数的频率足够小);位置向量的值是有界的,且位于连续空间中。模型在处理位置向量时更容易泛化,即更好处理长度和训练数据分布不一致的序列(sin函数本身的性质);不同的位置向量是可以通过线性转换得到的。即满足
,通过
可以实现。
基于上述采用sin和cos交替的方式表示位置向量:
利用线性变换从PEt→
-
Transformer 位置编码
- 公式介绍
![]()
- t是这个token在序列中的实际位置(例如第一个token为1,第二个token为2...)
-是这个token的位置向量,
表示这个位置向量里的第i个元素
- dmodel 是这个token的维度(在论文中,是512)
- 重要性质介绍
①两个位置编码的点积可以反应出两个位置编码间的距离。
②位置编码的点积是无向的,即
- 代码实现
(2)编码Encoder
- 工作原理图
简单解释一下编码的流程:
inputs经过(inputs_initial = 位置编码+tokens embedding)传入多头的self.attention得到inputs_multi_head_attention;add将(residual_output = inputs_initial + inputs_multi_head_attention )传给layer norm 得到 inputs_norm;将inputs_norm进行feed forward处理得到inputs_feed_forward;add将(inputs_norm+inputs_feed_forward)传给layer norm。
- add残差连接ResNet(残差网络)-CSDN博客
- layer Normalization归一化处理
-
补充内容:
1.第一层 MatMul
- 输入:Query Q 和 Key K(Q 为[])
- 操作:计算
(即 Query 和 Key 的点积)。
- 目的:衡量 Query 和 Key 的相关性(注意力得分)。点积越大,表示对应的 Query 和 Key 更相关。
- 输出:一个矩阵(注意力得分矩阵),大小为 (序列长度,序列长度)
2. Scale
- 输入:第一步生成的注意力得分矩阵。
- 操作:将得分矩阵除以一个缩放因子(通常是
,其中 dk是 Key 的维度)。
- 公式:
- 目的:防止得分值过大导致 SoftMax 输出趋于极端(梯度不稳定)。
- 输出:缩放后的注意力得分矩阵。
3. Mask (可选)
- 输入:缩放后的注意力得分矩阵。
- 操作:
- 如果是解码阶段的自注意力机制,应用 Mask 矩阵,屏蔽当前词之后的词(即设置分数为负无穷,避免模型看到未来的信息)。
- 如果是填充短句的 Padding Token,屏蔽填充部分的分数。
- 目的:确保模型只利用合法的信息计算注意力。
- 输出:应用 Mask 后的注意力得分矩阵。
4. SoftMax
- 输入:应用 Mask 后的注意力得分矩阵。
- 操作:
- 对每一行(即 Query)进行 SoftMax 操作,将注意力得分转化为概率分布。
- 公式:
- 目的:归一化注意力得分,生成每个词对所有词的注意力分布。
- 输出:注意力权重矩阵。
5. 第二层 MatMul
- 输入:注意力权重矩阵 和 Value V。
- 操作:将注意力权重矩阵与 Value 相乘。
- 公式:
- 目的:根据注意力权重加权组合 Value,生成最终的注意力输出。
- 输出:最终的注意力值,大小为 (序列长度,dv)。
(3)解码Decoder
将Encoder的输出先读进去,然后传递特殊符号”begin”【这时候机器才能够辨识是否开始,通常利用bos(Begin of Sequence )表示】到decoder中,decoder输出一个概率最高的token【token可能是汉字或者英语字符等,它从所有的vocabulary,即size_v中选择概率最高的作为输出。】的向量,随后再根据”begin”+“token1”→“token2”,根据”begin”+“token1”+“token2”→“token3”,以此类推。
- decoder中mask self-attention是什么?是self-attention的变体,它只考虑自身和自身左半边的内容,因为右边是需要生成的内容无法获取
- 机器如何停止呢?在解码的过程中,机器会不断的生成新的token,但是我们只需要机器输入正确内容,而不是一直输出。这个时候就需要准备一个特殊的token(end)作为输出一段正确字符后的截断
- 相关内容学习:
- 相关名词缩写
- EOS(End of Sequence):表示序列结束。
- PAD(Padding):用来补齐序列长度。
- UNK(Unknown):表示未知或未被词表覆盖的词。
- Decoder可能出现的问题:
- 由于Decoder是把自己的输出,当作下次的输入,可能会出现中途输出错误导致接下来的输出都错误的情况。
- 相关名词缩写
(4)Encoder如何把输出传递给Decoder?
(5)training
1) 数据准备
- 预处理数据:
- 分词(Tokenization):将文本拆分为子词或单词。
- 编码(Encoding):将分词后的文本转换为数字 ID(通常通过词汇表 Vocabulary)。
- 添加特殊标记:为序列加上
[BOS]
和[EOS]
。 - 填充(Padding):将序列长度对齐,方便批量处理。
- 创建数据集:
- 构造输入-输出对,例如输入序列和目标序列。
- 按照批次(batch)分组。
2)构建模型
-
定义模型结构:
- 输入嵌入(Embedding):将词 ID 转换为词向量。
- 位置编码(Positional Encoding):为序列中的每个位置加入位置信息。
- 自注意力机制(Self-Attention):计算序列中每个词与其他词的相关性。
- 多头注意力(Multi-Head Attention):将多个注意力头并行计算,捕捉不同的关系。
- 前馈网络(Feed-Forward Network, FFN):对每个位置的向量进行非线性变换。
- 残差连接(Residual Connection)和层归一化(Layer Normalization):提高训练稳定性。
- 解码器(Decoder):生成目标序列,使用注意力机制对齐输入和已生成的目标。
-
初始化参数:用随机分布初始化模型权重。
3)设置损失函数和优化器
- 损失函数:通常使用交叉熵损失(Cross-Entropy Loss)来衡量模型输出和目标的差异。
- 考虑序列中的填充标记([PAD]),忽略其贡献。
- 优化器:常用 Adam 优化器,结合学习率调度器(Learning Rate Scheduler),例如 Warmup 策略:
4)训练过程
- 正向传播:
- 输入通过模型,计算预测的输出(logits)。
- 计算损失:
- 使用损失函数计算预测输出和目标输出的差距。
- 反向传播:
- 通过梯度下降算法,根据损失函数计算梯度。
- 更新模型参数。
- 梯度剪裁(可选):
- 避免梯度爆炸问题,限制梯度的最大值。
- 更新模型:
- 应用优化器更新模型参数。
5)模型评估,保存和加载模型
- 在验证集上计算损失,监控模型性能;使用评估指标(如 BLEU、ROUGE、Perplexity)评估生成结果质量。
- 定期保存训练过程中模型的权重和优化器状态。
- 在需要时恢复训练或进行推理。
6) 推理阶段(Inference)
- 使用训练好的模型进行预测:
- 输入序列通过编码器。
- 解码器逐步生成目标序列。
- 使用 Beam Search 或 Greedy Search 策略提高生成质量。
- 后处理:
- 移除特殊标记([BOS]、[EOS]、[PAD])。
- 将子词拼接成完整的句子。
7)超参数调整
- 模型大小(Transformer 层数、隐藏层维度、注意力头数)。
- 学习率和 warmup 步数。
- Dropout 概率。
四、speculation decoding(可以加速llm模型生成内容,适用任何模型)
- 其核心思想是通过并行生成候选输出并验证这些输出的有效性,从而减少推理时间,同时保持生成质量。
- 详解:传统的解码方法(如 Greedy Search 和 Beam Search)是逐步进行的:一个时间步生成一个输出,下一时间步依赖于前一个时间步的结果。如上图蓝色向量→黄色向量(t1);蓝色向量+黄色向量→粉色向量(t2);总耗费时(t1+t2); Speculative Decoding 引入了辅助模型(helper model),以并行化的方式生成候选输出,从而加速解码过程。如上图蓝色向量→黄色向量,辅助模型预测生成黄色向量,蓝色向量+黄色向量→粉色向量,所有的耗时是同一时间进行的,而辅助模型预测生成黄色向量这个速度如果足够快就可以忽略不计,总耗时约等于t1.
- 辅助模型输入一定是正确的吗?不一定,他存在错误的可能性,那么错误造成的影响是什么?如何知道输入是错误的还是正确的呢?
- 上图,辅助模型输入两个token,此时并行输出:蓝色向量→粉色向量,蓝色向量+粉色向量→黄色向量,蓝色向量+粉色向量+灰色向量(错误向量)→绿色向量;此时从流程1知道第二个流程中生成向量是正确的,从流程2知道第三个向量是错误的,直接忽略第三个流程生成的结果进行下一步。最差的结果就是损耗辅助模型生成token的时间和运算资源的损耗。
- 辅助模型:
- 搜索引擎:使用搜索引擎的索引和查询能力快速检索相关内容。适合知识问答、文档生成等需要外部知识的任务。高效查找相关信息,生成候选内容具有知识性和关联性。但生成语言的流畅性和分布可能与主模型差异较大。并且需要额外处理搜索结果到语言模型分布的映射。
- 缩小版本的主模型:①使用 知识蒸馏(Knowledge Distillation) 从主模型学习。②减少层数、隐藏维度或注意力头数。【分布与主模型最为接近,因此候选 token 的接受率较高。但需要额外训练一个小模型,且仍然有一定的计算成本。】
- 小型预训练语言模型(如 DistilGPT、小型 BERT),现成可用,部署简单,且计算效率高。但是分布可能与主模型差距较大,导致接受率降低。可以通过微调缩小与主模型之间的差距。
- 层次分解模型:将主模型拆解成不同层次,仅使用主模型的部分层作为辅助模型。使用主模型的前几层(如前一半)作为辅助模型,后续层用于验证。但并未完全摆脱主模型的计算开销,速度提升有限。
- 基于规则的语言生成模型:利用基于频率或统计语言模型(如 N-gram 模型)快速生成候选。简单高效,计算开销极低。但与主模型分布差异较大,接受率低。
- 多模型协作:结合多个小型模型,以集成的方式生成候选 token。候选生成质量可能更高,但是实现复杂。
参考:
1、李宏毅大模型入门到进阶