作者:张贵发 研究方向:自然语言处理 NLPNLP:自然语言处理(NLP)是信息时代最重要的技术之一。理解复杂的语言也是人工智能的重要组成部分。而自google在2018年10月底公布BERT在11项nlp任务中的卓越表后,BERT(Bidirectional Encoder Representation from Transformers)就成为NLP一枝独秀,本文将为大家层层剖析bert。
Word Embedding首先我们需要对文本进行编码,使之成为计算机可以读懂的语言,在编码时,我们期望句子之间保持词语间的相似行,词的向量表示是进行机器学习和深度学习的基础。 如上图英语和西班牙语映射到语义空间,语义相同的数字他们在语义空间分布的位置是相同的 在句子的空间结构上我们期望获取更底层的之间的关系比如:
king和queen之间的关系相比与man与woman的关系大体应该相同的,那么他们通过矩阵运算,维持住这种关系 简单回顾一下word embedding,对于nlp来说,我们输入的是一个个离散的符号,对于神经网络来说,它处理的都是向量或者矩阵。所以第一步,我们需要把一个词编码成向量。最简单的就是one-hot的表示方法。如下图所示: one-hot encoding编码 通常我们有很多的词,那只在出现的位置显示会,那么势必会存在一些问题
两个词语义上无法正确表示,我们更希望低维的相似的比较接近,语义相近的词距离比较近,语义不想近的词,距离也比较远。 Neural Network Language Model(神经网络语言模型) 我们都知道word2vec,glove。其实更早之前的神经网络语言模型里出现。已经有比较早的一个词向量了。语言模型是nlp的一个基本任务,是给定一个句子w,包括k个词,我们需要计算这个句子的概率。使用分解成条件概率乘积的形式。变成条件概率的计算。 传统的方法,统计的n-gram的,词频统计的形式,出现的多,概率就高,出现少概率就低,。
神经网络语言模型架构如上图: 将每个词向量拼接成句子矩阵。每一列都是一个词, 如北京、上海、 天津比较近,大致相同一块区域,所以当预测时,可以给出大概相同的概率,不仅仅与预料中统计结果有关系。矩阵相乘就可以提取出这个词,但是为了提取一个词,我们要进行一次矩阵运算,这个比较低效,所以比较成熟的框架都提供了查表的方法,他的效率更高。 因为上下文环境很相似,会共享类似的context,在问我要去 (__)概率会比较大。这也是神经网络语言模型的一个好处。我们通过神经网络语言模型得到一个词向量。当然我们也可以用其他的任务来做,一样得到词向量,比如句法分析,但是那些任务大部分是有监督的学习,需要大量的标注信息。 语言模型是非监督的,资料获取不需要很大的成本。 word2vec和神经网络语言模型不同,直接来学习这个词向量,使用的基本假设是分布式假设,如果两个词的上下文时相似的,那么他们语义也是相似的。 word2vec分为cbow(根据context预测中心词)和skip-gram(根据中心词预测context)两种。 我们可以通过word2vec或者 glove这种模型在大量的未标注的语料上学习,我们可以学习到比较好的向量表示,可以学习到词语之间的一些关系。比如男性和女性的关系距离,时态的关系,学到这种关系之后我们就可以把它作为特征用于后续的任务,从而提高模型的泛化能力。
RNN/LSTM/GRU那么这种上下文的语义可以通过RNN/LSTM/GRU来解决,RNN与普通深度学习不同的是,RNN是一种序列的模型,会有一定的记忆单元,能够记住之前的历史信息,从而可以建模这种上下文相关的一些语义。RNN中的记忆单元可以记住当前词之前的信息。 RR可以解决,理论上我们希望学到很长的关系,但是由于梯度消失的问题,所以长时依赖不能很好的训练。 其实lstm可以解决RNN长时依赖梯度消失的问题。 seq2seq对于翻译,我们不可能要求英语第一个词一定对应法语的第一个词,不能要求长度一样,对于这样一个rnn不能解决这一问题。我们使用两个rnn拼接成seq2seq来解决。
比如经典的翻译例子法语到英语的翻译,由encoder编码到语义空间和decoder根据语义空间解码翻译成一个个的英语句子。 pay attention 做内积。越大越相近 约重要, 后续的attention、transformer都是对seq2seq的一个改进,通过这种可以解决word embbeing没有上下文的一个问题。 加上attention机制,我们就取得了很大的成绩,但是仍然存在一个问题, ![]()
Contextual Word Embedding要解决RNN的问题,就引入了contextual word embedding。
attention是需要两个句子的,我们很多时候只有一个句子,这就需要self-attention。提取信息的时候、编码时self-atenntion是自驱动的,self-attention关注的词的前后整个上下文。 ![]() transformer:本质也是一个encoder与decoder的过程,最起初时6个encoder与6个decoder堆叠起来,如果是LSTM的话,通常很难训练的很深,不能很好的并行 ![]() 每一层结构都是相同的,我们拿出一层进行解析,每一层有self-attention和feed-forward,decoder还有普通的attention输入来自encoder,和seq-2seq一样,我在翻译某一个词的时候会考虑到encoder的输出,来做一个普通的attention ![]() 如下图例子给定两个词 thinking和machies,首先通过word embedding把它变成向量,通过self-attention,把它变成一个向量,这里的sefl-attention时考虑上下文的。然后再接全连接层,计算z1的时候我要依赖x1 、x2 、x3整个序列的,才能算z1,z2也一样,我算r1的时候时不需要z2的,只要有z1我就可以算r1.只要有z2就能算r2,这个是比较大的一个区别,这样就可以并行计算。 ![]() 我们来看看self-attention具体是怎么计算的 ![]() 假设只有两个词,映射成长度只有四的向量,接下来使用三个变换矩阵wq wk wv,分别把每个向量变换成三个向量 q1 k1 v1 q2 k2 v2这里是与设映的向量相乘得到的 ![]() 得到向量之后就可以进行编码了,考虑上下文,如上文提到的bank同时有多个语义,编码这个词的时候要考虑到其他的词,具体的计算是q1 k1做内积 q2 k2 做内积得到score,内积越大,表示约相似,softmax进行变成概率。花0.88的概率注意Thinking,0.12注意macheins这个词 ![]() 就可以计算z1了,z1=0.88v1+0.12z2 z2的计算也是类似的, ![]() q表示为了编码自己去查询其他的词,k表示被查询,v表示这个词的真正语义,经过变换就变成真正的包含上下文的信息,普通attention可以理解为self-attention的一个特例, ![]() 实际中是多个head, 即多个attention(多组qkv),通过训练学习出来的。不同attention关注不同的信息,指代消解 上下位关系,多个head,原始论文中有8个,每个attention得到一个三维的矩阵 ![]() ![]() 将8个3维的拼成24维,信息太多 经过24 *4进行压缩成4维。 ![]() 位置编码:
self-attention是不考虑位置关系的,两个句子中北京,初始映射是一样的,由于上下文一样,qkv也是一样的,最终得到的向量也是一样的。这样一个句子中调换位置,其实attention的向量是一样的。实际是不一样的,一个是出发城市,一个是到达城市。 ![]() 引入位置编码,绝对位置编码,每个位置一个 Embedding 每个位置一个embedding,同样句子,多了个词 就又不一样了,编码就又不一样了
tranformer原始论文使用相对位置编码,后面的bert open gpt使用的是简单绝对位置编码: ![]() 大家可以尝试bert换一下相对位置会不会更好: transformer中encoder的完整结构,加上了残差连接和layerNorm ![]() decoder加上了普通的attention,最后一刻的输出,会输入 ![]() transformer的decoder不能利用未知的信息,即单向信息流问题。 transformer 解决的问题: 如何解决transformer的问题,就引入了elmo ![]() 一个个的预测的语言模型: 双向的lstm,每个向量2n,是一种特征提取的方法,考虑的上下文的,编码完,就定住了, elmo:将上下文当作特征,但是无监督的语料和我们真实的语料还是有区别的,不一定的符合我们特定的任务,是一种双向的特征提取。 openai gpt就做了一个改进,也是通过transformer学习出来一个语言模型,不是固定的,通过任务 finetuning,用transfomer代替elmo的lstm。 openAI gpt虽然可以进行fine-tuning,但是有些特殊任务与pretraining输入有出入,单个句子与两个句子不一致的情况,很难解决,还有就是decoder只能看到前面的信息。 bertbert从这几方面做了改进:
bert为什么更好呢?
bert的输入是两个句子,分割符sep,cls表示开始,对输入的两个句子,使用位置编码, segment embeding 根据这个可以知道 该词属于哪个句子,学习会更加简单。可以很清楚知道第一句子需要编码什么信息,第二个句子可以编码什么信息。 ![]() 单向信息流问题:mask ml 有点类似与完形填空,根据上下文信息猜其中信息,计算出最大概率,随机丢掉15%的词来bert来进行预测,考虑前后双向的信息,怎么搞两个句子? -50%概率抽连续句子 正样本1
这样学习到两个句子的关系,可以预测句子关系,在一些问答场景下很重要。 finetuning:
bert的实际应用比较简单,不过多赘述内容,推荐简单的demo样例: |
|