论文链接:https://www.cs.ubc.ca/~amuham01/LING530/papers/radford2018improving.pdf
代码链接:https://github.com/openai/finetune-transformer-lm
参考文档:https://mp.weixin.qq.com/s/VI5hvcZejJL9ftdDcgMZQA

导读

今天阅读的是 OpenAI 2018 年的论文《Improving Language Understanding by Generative Pre-Training》,截止目前共有 600 多引用。

在这篇论文中,作者提出了一种半监督学习方法——Generative Pre-Training(以下简称 GPT),GPT 采用无监督学习的 Pre-training 充分利用大量未标注的文本数据,利用监督学习的 Fine-tuning 来适配具体的具体的 NLP 任务(如机器翻译),并在 12 个 NLP 任务中刷新了 9 个记录。

1、引言

NLP 领域中只有小部分标注过的数据,而有大量的数据是未标注,如何只使用标注数据将会大大影响深度学习的性能,所以为了充分利用大量未标注的原始文本数据,需要利用无监督学习来从文本中提取特征,最经典的例子莫过于词嵌入技术。

但是词嵌入只能 word-level 级别的任务(同义词等),没法解决句子、句对级别的任务(翻译、推理等)。出现这种问题原因有两个:

  • 首先,是因为不清楚下游任务,所以也就没法针对性的进行行优化;
  • 其次,就算知道了下游任务,如果每次都要大改模型也会得不偿失。

为了解决以上问题,作者提出了 GPT 框架,用一种半监督学习的方法来完成语言理解任务,GPT 的训练过程分为两个阶段:Pre-training 和 Fine-tuning。目的是学习一种通用的 Representation 方法,针对不同种类的任务只需略作修改便能适应。

2、论文动机与创新点

  • 现实世界中,无标签的文本语料库非常巨大,而带有标签的数据则显得十分匮乏,如何有效利用无标签的原始文本,对缓解自然语言处理相关任务对有监督学习方式的依赖显得至关重要。

  • 有效的从无标签文本中利用超单词级信息有两个主要的难点:
    ①无法确定什么样的优化目标能学到最有效的文本表征,使之更好的用于迁移目的。
    ②对于学习到的文本表征,采用何种方式将其迁移到目标任务上,目前尚无共识。

  • 论文中提出了半监督的方式来做语言理解,也就是无监督的pre-train,和有监督的fine-tune。该方法首先利用无监督的pre−train模型,学习到更加普遍、更适用的表征,然后模型以很小的微调迁移到众多特定的有监督学习任务上。在实验效果上,大幅超过了众多任务的state-of-art。不同于word Embedding、ELMo 以无监督的方式学习到一些特征,然后利用这些特征喂给一些特定的有监督模型,这里是先无监督的pre−train模型,然后直接fine-tune预训练后的模型,迁移到一些特定的有监督任务上。

  • ELMo方法中,训练基于LSTM的双向语言模型能结合上下文内容学习到语义更丰富的词表征,而本论文中预训练的语言模型中使用了transformer(Masked Multi-Head Attention,单向)结构,相对而言,transformer更加鲁棒,在长距离依赖上的效果更好,迁移效果也更好。

  • 适用场景:无标签样本量(只有X)远大于有标签样本量的数据集(同时有X,y),如果只用这少量的带标签样本训练出的模型泛化能力肯定比较弱,这个时候我们可以先用无标签样本(也就是只用X)预训练好一个语言模型,然后在该语言模型基础上用少量带标签的样本(同时有X,y)进行fine-tune,有监督的训练。

3、GPT模型

GPT 训练过程分为两个阶段:第一个阶段是 Pre-training 阶段,主要利用大型语料库完成非监督学习;第二阶段是 Fine-tuning,针对特定任务在相应数据集中进行监督学习,通过 Fine-tuning 技术来适配具体任务。下图为 GPT 的架构图:


上图中,每一层的所有Trm属于一个自左向右的单向transformer,故在embedding输入和上一层的输出到下一层的输入时,都会做self attention操作,而这个self attention操作相当于当前位置cell会结合上一层所有位置的状态信息,这样就相当于双向连接了,因此需要乘以一个mask矩阵,用来屏蔽当前位置后面的位置的隐藏层状态信息。这是transformer decoder的一个关键。可看代码:

def mask_attn_weights(w):n = shape_list(w)[-1]b = tf.matrix_band_part(tf.ones([n, n]), -1, 0)## 下三角b = tf.reshape(b, [1, 1, n, n])w = w*b + -1e9*(1-b)return w

如果不做这样的一个屏蔽操作,那么就变成双向的了。

分两步走,第一步:利用海量无标签的样本集预训练一个语言模型;第二步:利用预训练后的模型通过fine-tuning迁移到有监督的任务上。

3.1、Pre-train

从上图我们可以看出,GPT 采用 Transformer 来代替 LSTM 作为特征提取器,并基于语言模型进行训练。这里只使用了 Transformer 的 Decoder 部分,并且每个子层只有一个 Masked Multi Self-Attention(768 维向量和 12 个 Attention Head)和一个 Feed Forward,共叠加使用了 12 层的 Decoder。

这里简单解释下为什么只用 Decoder 部分:语言模型是利用上文预测下一个单词的,因为 Decoder 使用了 Masked Multi Self-Attention 屏蔽了单词的后面内容,所以 Decoder 是现成的语言模型。又因为没有使用 Encoder,所以也就不需要 encoder-decoder attention 了。

对于给定的非监督语料库的 Token 序列 ,基于语言模型的目标函数:maxL1(U)=∑ilog⁡P(ui∣ui−k,⋯,ui−1;Θ)max\space L_1(U)=\sum_i\log P(u_i|u_{i-k},\cdots,u_{i-1};\Theta)maxL1(U)=ilogP(uiuik,,ui1;Θ)其中,kkk 是上下文窗口的大小,PPP 为条件概率,Θ\ThetaΘ 为条件概率的参数,参数更新采用 SGD。

GPT 输入文本和位置 Embedding(采用使用 one-hot 编码),经过 12 层的 Transformer 的 Decoder 后通过 Softmax 得到输出:h0=UWe+Wph_0=UW_e+W_ph0=UWe+Wphl=transformer_block(hl−1)∀l∈[1,n]h_l=transformer\_block(h_{l-1})\space\forall l\in[1,n]hl=transformer_block(hl1)l[1,n]P(u)=softmax(hnWeT)P(u)=softmax(h_nW_e^T)P(u)=softmax(hnWeT)其中,U={u−k,⋯,u−1}U=\{u_{-k},\cdots,u_{-1}\}U={uk,,u1} 是当前单词的前面 kkk 个 Token,nnn 为神经网络的层数, WeW_eWe是 Token 的 Embedding 矩阵,WpW_pWp 是位置编码的 Embedding 矩阵。

3.2、Fine-tuning

完成预训练后,我们会得到一个训练好的 Transformer 模型,接下来我们要用这个训练好的模型来完成特定的监督学习的任务。

假设我们有个带标签的数据集 CCC,即每一个 Token 序列 x1,x2,⋯,xmx^1,x^2,\cdots,x^mx1,x2,,xm 都有一个标签 yyy。我们将 Token 序列输入,并通过 Transformer 模型得到输出的状态 ,然后将这个加到线性层进行输出并预测标签 y:P(y∣x1,x2,⋯,xm)=softmax(hlmWy)P(y|x^1,x^2,\cdots,x^m)=softmax(h_l^mW_y)P(yx1,x2,,xm)=softmax(hlmWy)其中,WyW_yWy 是线性层的权重。

所以针对该监督学习,我们也有新的目标函数:L2(C)=∑(x,y)log⁡P(y∣x1,⋯,xm)L_2(C)=\sum_{(x,y)}\log P(y|x^1,\cdots,x^m)L2(C)=(x,y)logP(yx1,,xm)另外,将预训练好的语言模型作为辅助目标进行 Fine-tuning 不仅可以使监督模型更具泛化性,还可以加速收敛。于是我们有:L3(C)=L2(C)+λL1(C)L_3(C)=L_2(C)+\lambda L_1(C)L3(C)=L2(C)+λL1(C)其中,λ\lambdaλ 为权重。

3.3、Task-specific input transformations

对于某些任务如文本分类等 word-level 的任务,我们可以像上述描述的方式来 Fine-tuning 模型;但是有些任务如问题回答等句子、句子对等结构化输入的任务需要稍作修改才能应用。

针对这种情况,作者提出了一种遍历式的方法(traversal-style),将结构化输入转换成预训练模型可以处理得到有序序列。

对输入转换避免了兼容不同任务,防止对模型进行大量更改,所有的转换包括添加随机初始化的开始标记(<s><s><s>)和结束标记(<e><e><e>)。下图提供了一个可视化说明:

上图是对不同任务进行微调的输入转换。将所有的结构化输入转换为 Token 序列,然后使用预训练模型(Transformer)进行处理,最后使用线性和 Softmax 层完成特定的监督学习任务。

对于文本蕴涵(Text Entailment)来说,作者将前提 p 和假设 h 令牌序列连接起来,并使用分隔符($)分开。

文本蕴含是指两个文本片段有指向关系。当认为一个文本片段真实时,可以推断出另一个文本片断的真实性。也就是说一个文本片段蕴涵了另一个文本片段的知识,可以分别称蕴涵的文本为前提,被蕴涵的文本为假设。

对于句子相似(Similarity)来说,为了消除两个句子之间的内在的顺序,作者以不同顺序合并了两个句子并以分隔符进行分割,然后独立地处理每一种顺序并得到两个句子的表征,对两个句子进行元素求和后送给 Linear 层。

对于问答和常识推理(Question Answering and Commonsense Reasoning)来说,有上下文文档 zzz 、问题 qqq 和可能答案的集合 {ak}\{a_k\}{ak},作者将上下文和问题与每个可能的答案连接起来并在中间添加分隔符令牌$[z;q;$;a_k]$ 。每个序列都将由模型独立处理,然后通过 Linear 层和 Softmax 层,从而在可能的答案上产生一个输出分布。

4、实验

下图展示了推理任务的实验结果:

下图展示了问题回答和常识推理的实验结果:

下图展示了语义相似度和分类的实验结果:

下图左边展示的预训练语言模型中 Transformer 层数对结果的影响;右图展示了预训练不用 Fine-tuning 而直接使用预训练网络来解决多种类型任务的结果,横坐标为更新次数,纵坐标为模型相对表现:

5、关键代码分析

def clf(x, ny, w_init=tf.random_normal_initializer(stddev=0.02), b_init=tf.constant_initializer(0), train=False):with tf.variable_scope('clf'):nx = shape_list(x)[-1]w = tf.get_variable("w", [nx, ny], initializer=w_init)b = tf.get_variable("b", [ny], initializer=b_init)return tf.matmul(x, w)+bdef model(X, M, Y, train=False, reuse=False):with tf.variable_scope('model', reuse=reuse):we = tf.get_variable("we", [n_vocab+n_special+n_ctx, n_embd], initializer=tf.random_normal_initializer(stddev=0.02))we = dropout(we, embd_pdrop, train)X = tf.reshape(X, [-1, n_ctx, 2])M = tf.reshape(M, [-1, n_ctx])h = embed(X, we)for layer in range(n_layer):h = block(h, 'h%d'%layer, train=train, scale=True)lm_h = tf.reshape(h[:, :-1], [-1, n_embd]) ##得到最后一个block的输出, 也就是上面所说的$h_l^m$lm_logits = tf.matmul(lm_h, we, transpose_b=True)lm_losses = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=lm_logits, labels=tf.reshape(X[:, 1:, 0], [-1])) ## 注意看预训练的语言模型的label只是将x向后移了一步lm_losses = tf.reshape(lm_losses, [shape_list(X)[0], shape_list(X)[1]-1])lm_losses = tf.reduce_sum(lm_losses*M[:, 1:], 1)/tf.reduce_sum(M[:, 1:], 1) ## 得到预训练模型的损失函数clf_h = tf.reshape(h, [-1, n_embd]) // h 为transformer中最后一个block的输出pool_idx = tf.cast(tf.argmax(tf.cast(tf.equal(X[:, :, 0], clf_token), tf.float32), 1), tf.int32)clf_h = tf.gather(clf_h, tf.range(shape_list(X)[0], dtype=tf.int32)*n_ctx+pool_idx)clf_h = tf.reshape(clf_h, [-1, 2, n_embd])if train and clf_pdrop > 0:shape = shape_list(clf_h)shape[1] = 1clf_h = tf.nn.dropout(clf_h, 1-clf_pdrop, shape)clf_h = tf.reshape(clf_h, [-1, n_embd])clf_logits = clf(clf_h, 1, train=train) ## 执行 上面公式中$softmax(h_l^m *W_y)$clf_logits = tf.reshape(clf_logits, [-1, 2])clf_losses = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=clf_logits, labels=Y) ## 得到监督学习的损失函数return clf_logits, clf_losses, lm_losses

最终的loss函数为:

train_loss = tf.reduce_mean(clf_losses) + lm_coef*tf.reduce_mean(lm_losses)

6、结论

GPT 是一种半监督学习,采用两阶段任务模型,通过使用无监督的 Pre-training 和有监督的 Fine-tuning 来实现强大的自然语言理解。在 Pre-training 中采用了 12 层的修改过的 Transformer Decoder 结构,在 Fine-tuning 中会根据不同任务提出不同的微调方式,从而达到适配各类 NLP 任务的目的。

GPT 与 ELMo 有很多相似的地方,比如说都采用了预训练的方式,但是 ELMo 是针对某一任务定制了一个架构,而 GPT 的目的在于适配多种任务;此外 ELMo 使用了 2 层的双向的 LSTM 结构而 GPT 使用了 12 层单向的 Transformer Dncoder 结构,更大的深度也加强了模型的学习能力(ELMo 不是不想用更深的,而是再深的话就学不动了)。

GPT语言模型:通过生成式预训练改善语言理解 OpenAI 2018相关推荐

  1. GPT系列:生成式预训练与零样本学习

    GPT系列:生成式预训练与零样本学习 本文的主要参考是李沐老师关于 GPT 系列的解读:GPT,GPT-2,GPT-3 论文精读[论文精读]. 关于BERT和GPT Transformer/BERT/ ...

  2. 通过预训练提升语言理解

    官方地址:https://blog.openai.com/language-unsupervised/ 文章:https://s3-us-west-2.amazonaws.com/openai-ass ...

  3. ACL 2022丨香港大学华为诺亚方舟新工作:生成式预训练语言模型的量化压缩

    点击上方"视学算法",选择加"星标"或"置顶" 重磅干货,第一时间送达 近日,香港大学与华为诺亚方舟实验室在 ACL 2022 上联合发表了 ...

  4. 《预训练周刊》第25期:HyperCLOVA:数十亿级韩语生成式预训练变换器、GPT-3在生物医学领域不是好的小样本学习器...

    No.25 智源社区 预训练组 预 训 练 研究 观点 资源 活动 关于周刊 超大规模预训练模型是当前人工智能领域研究的热点,为了帮助研究与工程人员了解这一领域的进展和资讯,智源社区整理了第25期&l ...

  5. 文本生成与自动摘要:基于生成式预训练Transformer的实现与优化

    作者:禅与计算机程序设计艺术 1.简介 文本生成是自然语言处理领域中非常重要的问题之一.在不断地探索学习新知识和技能的同时,越来越多的人也需要通过自己创造或整合的手段,将自己的想法.观点和信息转化成语 ...

  6. KDD 2020 开源论文 | GPT-GNN:图神经网络的生成式预训练

    论文标题:GPT-GNN: Generative Pre-Training of Graph Neural Networks 论文链接:https://arxiv.org/abs/2006.15437 ...

  7. GPT-GNN:图神经网络的生成式预训练 KDD 2020

    论文链接:https://arxiv.org/pdf/2006.15437.pdf 代码链接:https://github.com/acbull/GPT-GNN 论文来源:KDD 2020 参考文档: ...

  8. 【LLM系列之GPT】GPT(Generative Pre-trained Transformer)生成式预训练模型

    GPT模型简介 GPT(Generative Pre-trained Transformer)是由OpenAI公司开发的一系列自然语言处理模型,采用多层Transformer结构来预测下一个单词的概率 ...

  9. 最强 NLP 预训练模型库 PyTorch-Transformers 正式开源:支持 6 个预训练框架,27 个预训练模型...

    先上开源地址: https://github.com/huggingface/pytorch-transformers#quick-tour 官网: https://huggingface.co/py ...

最新文章

  1. Android webview 加载的html 无法显示弹框
  2. OpenCV 【十九】图像金字塔/基本的阈值操作/实现自己的线性滤波器
  3. node 获取表单数据 为空_Java实现数据结构之【链表】
  4. mysql查询语句习题._MySql数据库基本select查询语句练习题,初学者易懂。
  5. Java项目 常用包的命名及理解【dao包、domain包、service包、utils包、web包、impl包】
  6. NSMutable属性声明时为什么不能使用copy
  7. Opencv--从CalibrateCamera到SolvePnp(二)
  8. android studio访问webservice如何传递类对象报错_小白学习web service,这是最最最基础的了,只用JDK还不会吗?...
  9. c语言中文纠错,c语言纠错。。急急急。。
  10. Mac 有道词典 无法取词、划词
  11. c语言 2,有一函数: y= 写一程序,输入x,输出y值.,有一函数 ,编写一段程序,输入x的值,输出相应的y值....
  12. 宋宝华:Linux文件读写(BIO)波澜壮阔的一生
  13. 网络工程师考试知识点总结
  14. java定义苹果类Apple_Java开发笔记(七十)Java8新增的几种泛型接口
  15. 拟一维喷管流动的数值解——亚声速-超声速等熵喷管流动的非守恒型CFD解法(MacCormack方法)
  16. 3.1 腾讯云AI能力矩阵
  17. 机器学习之R语言caret包trainControl函数(控制调参)
  18. word2vec 中的数学原理详解(六)若干源码细节
  19. 最近几年我买的一些技术书的随书光盘CD
  20. binutils学习笔记

热门文章

  1. Go进阶:如何开发多彩动感的终端UI应用
  2. Docker(三) 使用容器数据卷实现数据持久化与容器数据共享
  3. windows安装使用jaeger链路追踪
  4. golang中,new和make的区别
  5. 安装虚拟机Centos系统并安装Docker过程记录
  6. GIT提交记录和Revert commit过程分析
  7. mybatis源码解析 - mapper代理对象的生成
  8. Go中对两个 nil 进行比较的结果是什么?
  9. Laravel核心解读--服务提供器(ServiceProvider)
  10. LCN分布式事务框架实战