transformer学习

前言

考虑到现在各大公司对AI的要求越来越高了,我决定开始全面学习AI,目的是做到基本理解ai原理,能够实现ai应用,能够听得懂各种AI名词。

transformer作为当前AI最基本的训练原理,决定作为第一个开始学习的篇章,我学的是李宏毅的视频课程,链接如下:

https://www.bilibili.com/video/BV1r8nMz4EAj?spm_id_from=333.788.videopod.sections&vd_source=1e3090bc7a88f02cda5247bc11cd548d

一些神经网络名词

神经网络

最简单的神经网络示意图如下,网络可以有多个标量输入,经过神经元处理后,得到一个输出

fully-connected network

全连接网络,理解为给一个输入,经过神经网络处理后,给一个输出

  • 输入:一组固定长度的数字(比如 [年龄,收入,点击数,停留时间])
  • 输出:一个结果(分类 / 预测值)
  • 中间:所有神经元两两全部连在一起,所以叫 “全连接”

全连接网络 = 一次性看所有特征,做判断。

特点

  • 只能吃 固定长度 的输入
  • 不理解 顺序、时序、前后关系

RNN-循环神经网络


理解为可以处理时序或者说有前后顺序的数据,每一个step的输出不仅取决于此次的输入,还取决于前一次的输出。

RNN已经可以处理seq-2-seq的问题了,但是显然它只能串行处理,效率低,且不能看全局信息,每一个step的输出只取决于本次step之前的输入,之后的输入不会影响它。

特点:

  • 输入可以是任意长度
  • 有记忆(状态)
  • 串行

CNN-卷积神经网络

作用:提取局部特征、空间结构。

理解成:一个滑动窗口,在图片 / 数据上扫来扫去,找规律。

CNN 就像:

  • 遍历式特征提取器
  • 像滑动窗口统计
  • 像 filter 过滤器

cnn不关心全局,只关心这一块附近的像素/点,然后做处理。

self-attention机制

attention机制是seq-2-seq的一种处理架构,输入一系列的数据(如时序数据),输出相同长度的数据。

比如输入一段文字描述、或者有时序的声音信号,经过attention机制处理后,输出一串顺序输出。

特点是并行计算,全局视角

如下图所示,self-attention可以当作一个处理黑盒,输入一个向量,经过attention处理后会给出一个相同长度的输出向量,attention只是用于综合整个输入信息,使得每一个位置的输出能参考到整体输入。因此attention并不是处理的结束,其输出的每一个元素还需要经过类似fully-connection network处理得到最终的输出。

下图是计算某个输入与其他输入的attention关系因子的示意图,Wq和Wk均是待训练矩阵

比如计算1号位置的输入和2号位置输入的attention,是通过q1和k2向量做点乘得到的,得到1号位置和所有位置的attention后,一般会做一个soft-max(理解为概率归一化),得到一个相加和为1的输出结果。

再看下图,计算1号位置的最终输出的方法是一个求和,可以看到某个输入位置与1号位置的attention系数越大,其对最终结果的影响最大

其引入了第三个待训练矩阵Wv,因此一次self-attention需要训练3个矩阵,矩阵维度和每一个位置输入向量的维度一致,比如说输入元素长度为10,那么训练矩阵便是10 X 10,注意这里指的输入元素的长度是单个元素,不是整体的全部seq-2-seq的输入总长。

可以看到每一个位置输出结果可以并行计算,互不干扰。并且都参考了整体的输入seq的所有信息。

以下是衍生的概念: multi-head self-attention,只是引入了更多的待训练矩阵,增加输出结果的变数

位置编码: 可以看到以上的计算过程,每一个输入元素其实没有位置信息,所有的位置都是等价的,因此为了引入时序的概念,会在做self-attention计算前,对输入向量整体添加位置信息(+一个位置向量)。

与CNN的关系: cnn只会看局部的位置,而self-attention通过关系系数可以看全部的位置,因此可以认为cnn是简化版的self-attention。显然self-attention的seq输入总长越长,计算量越大,待训练参数也越多。

transformer架构

transformer的核心步骤是前文的attention机制,是一个seq-2-seq的处理架构,可以输入任意长度的序列,输出一个不定长的序列。每一个输出的结果不仅与所有的输入相关,还与该输出位置之前的所有输出相关。

下图是一些简单应用

transformer由编码器和解码器构成,整体架构图如下

encoder

编码器的计算流程示意图如下,请注意左上角是第一步,右上角是把左上角的计算结果做第二步处理。

总结一下步骤:

  1. 对embedding后的输入数据添加位置编码信息,然后做self-attention计算
  2. 得到的结果和原输入结果做和处理,再整体做这一阶段的输出做归一化处理
  3. 对前一阶段的输出经过前馈神经网络(理解为fully-connected network)做单独处理,再做一次和与归一化处理
  4. 得到最终的输出序列

解码器

查看解码器的整体架构图,整体过程也比较清晰, 步骤如下

注意是每一个输出位置的计算过程:

  1. 输出经过位置编码后自己用masked self-attention计算一轮得到结果
  2. 将前一轮的结果跟编码器的结果合并做一次self-attention
  3. 最终用神经网络+线性层+softmax处理一遍得到概率分布
  4. 取概率最大的为该位置的输出结果

masked self-attention: 因为输出某一个位置的结果时无法知道后续位置的输出结果,因此只是看了该输出结果的前面的输出结果,来做self attention,不是全局视角,所以叫masked

解码器特殊标识:解码器起始的输出是用特殊标识BEGIN代替的,来作为解码器的输入来得到该起始位置的输出,而输出的结束是用特殊标识END,程序发现了END标识则结束整个语句的输出。

下图也诠释了每一个输出位置的结果受到之前输出的影响,但无法受到之后输出的影响

  • 线性层linear:核心是「维度映射」,把编码器输出的高维特征向量映射到任务所需维度(如分类的类别数、NER 的标签数),输出无范围限制的原始分数(logits)

  • Softmax:核心是「归一化」,通过指数运算放大分数差异,再除以所有指数的和,把 logits 转换成 0~1 之间的概率分布,且所有概率之和为 1。

这里有个核心困惑点,结合跟AI的问题如下:

Q: transformer输入了一串seq,然后结合输出的BEGIN标识,会得到输出位置的一个结果。但attention机制得到的是和输入seq长度一样的seq向量串,经过linear和softmax也只是得到一串概率分布,缺失了最终选出一个唯一值的过程

A: 必须先把 “和输入等长的向量串” 压缩成「一个单向量」,再对这个单向量做线性层 + Softmax + 选唯一值。

  • 方式 1:特殊 Token(最经典,BERT/LLM 首选)

逻辑:输入序列开头强行加一个特殊 Token(如 [CLS]//),Self-Attention 输出时,只取这个特殊 Token 对应的向量作为 “单向量”。

例子:输入序列:[, 我, 爱, 编, 码](长度 5)→ Self-Attention 输出:[v_cls, v1, v2, v3, v4](长度 5)→ 只取 v_cls(1 个向量)。

  • 方式 2:平均池化(Mean Pooling,最通用)

逻辑:把 Self-Attention 输出的所有向量(v1~v5)做逐维度平均,得到 1 个融合了所有位置信息的单向量。
公式:vpooled​=(v1​+v2​+v3​+v4​+v5​​)/5