精华内容
下载资源
问答
  • bert_seq2seq 一个轻量级的小框架。 pytorch实现bertseq2seq任务,使用unilm方案。如果喜欢的话欢迎star~ 谢谢谢谢。如果遇到问题也可以提issue,保证会回复。 也欢迎加入交流群~ 可以提问题,提建议,互相交流 ...
  • 本文代码: https://github.com/saiwaiyanyu/tensorflow-bert-seq2seq-dream-decoder 介绍 在参与的项目产品中,涉及到模型算法的需求,主要以自然语言处理(NLP)知识图谱(KG)为主。NLP涉及面太广,而聚焦...

    本文代码: https://github.com/saiwaiyanyu/tensorflow-bert-seq2seq-dream-decoder

    介绍

    在参与的项目和产品中,涉及到模型和算法的需求,主要以自然语言处理(NLP)和知识图谱(KG)为主。NLP涉及面太广,而聚焦在具体场景下,想要生产落地的还需要花很多功夫。

    作为NLP的主要方向,情感分析,文本多分类,实体识别等已经在项目中得到应用。例如

    • 通过实体识别,抽取文本中提及到的公司、个人以及金融产品等。
    • 通过情感分析,判别新闻资讯,对其提到的公司和个人是否利好?
    • 通过文本多分类,判断资讯是否是高质量?判断资讯的行业和主题?

    具体详情再找时间分享。而文本生成、序列到序列(Sequence to Sequence)在机器翻译、问答系统、聊天机器人中有较广的应用,在参与的项目中暂无涉及,本文主要通过tensorflow+bert+seq2seq实现一个简单的问答模型,旨在对seq2seq的了解和熟悉。

    数据

    关于seq2seqdemo数据有很多,例如小黄鸡聊天语料库,影视语料库,翻译语料库等等。由于最近总是做些奇怪的梦,便想着,做一个AI解梦的应用玩玩,just for fun

    通过采集从网上采集周公解梦数据,通过清洗,形成

    • dream:梦境;
    • decode:梦境解析结果。

    这样的序列对,总计33000+ 条记录。数据集

    {
        "dream": "梦见商人或富翁",
        "decode": "是个幸运的预兆,未来自己的事业很有机会成功,不过如果梦中的富翁是自己,则是一个凶兆。。"
    }
    

    模型准备

    bert

    下载 bert

        $ git clone https://github.com/google-research/bert.git
    

    下载中文预训练模型

        $ wget -c https://storage.googleapis.com/bert_models/2018_11_03/chinese_L-12_H-768_A-12.zip
        $ unzip chinese_L-12_H-768_A-12.zip 
    

    bert + seq2seq

    bertinput:

        
    self.input_ids = tf.placeholder(
        dtype=tf.int32,
        shape=[None, None],
        name="input_ids"
    )
    self.input_mask = tf.placeholder(
        dtype=tf.int32,
        shape=[None, None],
        name="input_mask"
    )
    self.segment_ids = tf.placeholder(
        dtype=tf.int32,
        shape=[None, None],
        name="segment_ids"
    )
    self.dropout = tf.placeholder(
        dtype=tf.float32,
        shape=None,
        name="dropout"
    )
    
    

    bertmodel :

    self.bert_config = modeling.BertConfig.from_json_file(bert_config)
        model = modeling.BertModel(
            config=self.bert_config,
            is_training=self.is_training,
            input_ids=self.input_ids,
            input_mask=self.input_mask,
            token_type_ids=self.segment_ids,
            use_one_hot_embeddings=False
        )
    

    seq2seqencoder_embedding 替换:

    # 默认seq2seq model_inputs
    # self.encoder_embedding = tf.Variable(tf.random_uniform([from_dict_size, embedded_size], -1, 1),name ="encoder_embedding")
    # self.model_inputs = tf.nn.embedding_lookup(self.encoder_embedding, self.X),
    #  替换成bert
    self.embedded = model.get_sequence_output()
    self.model_inputs = tf.nn.dropout(self.embedded, self.dropout)
    

    seq2seqdecoder_embedding 替换:

    # 默认seq2seq decoder_embedding
    # self.decoder_embedding = tf.Variable(tf.random_uniform([to_dict_size, embedded_size], -1, 1),name="decoder_embedding")
    #  替换成bert
    self.decoder_embedding = model.get_embedding_table()
    self.decoder_input = tf.nn.embedding_lookup(self.decoder_embedding, decoder_input),
    

    数据预处理

    for i in range(len(inputs)):
        tokens = inputs[i]
        inputs_ids = model.tokenizer.convert_tokens_to_ids(inputs[i])
        segment_ids = [0] * len(inputs_ids)
        input_mask = [1] * len(inputs_ids)
        tag_ids = model.tokenizer.convert_tokens_to_ids(outputs[i])
        data.append([tokens, tag_ids, inputs_ids, segment_ids, input_mask])
        
    
    def pad_data(data):
        c_data = copy.deepcopy(data)
        max_x_length = max([len(i[0]) for i in c_data])
        max_y_length = max([len(i[1]) for i in c_data]) 
        # 这里生成的序列的tag-id 和 input-id 长度要分开
        # print("max_x_length : {} ,max_y_length : {}".format( max_x_length,max_y_length))
        padded_data = []
        for i in c_data:
            tokens, tag_ids, inputs_ids, segment_ids, input_mask = i
            tag_ids = tag_ids + (max_y_length - len(tag_ids)) * [0]
            # 注意tag-ids 的长度补充,和预测的序列长度一致。
            inputs_ids = inputs_ids + (max_x_length - len(inputs_ids)) * [0]
            segment_ids = segment_ids + (max_x_length - len(segment_ids)) * [0]
            input_mask = input_mask + (max_x_length - len(input_mask)) * [0]
            assert len(inputs_ids) == len(segment_ids) == len(input_mask)
            padded_data.append(
                [tokens, tag_ids, inputs_ids, segment_ids, input_mask]
            )
        return padded_data
    
    

    训练

    $ python3 model.py --task=train \
        --is_training=True \
        --epoch=100 \
        --size_layer=256 \
        --bert_config=chinese_L-12_H-768_A-12/bert_config.json \
        --vocab_file=chinese_L-12_H-768_A-12/vocab.txt \
        --num_layers=2 \
        --learning_rate=0.001 \
        --batch_size=16 \
        --checkpoint_dir=result
    

    预测

    $ python3 model.py --task=predict \
            --is_training=False \
            --epoch=100 \
            --size_layer=256 \
            --bert_config=chinese_L-12_H-768_A-12/bert_config.json \
            --vocab_file=chinese_L-12_H-768_A-12/vocab.txt \
            --num_layers=2 \
            --learning_rate=0.001 \
            --batch_size=16 \
            --checkpoint_dir=result
    

    Just For Fun _

    本文代码: https://github.com/saiwaiyanyu/tensorflow-bert-seq2seq-dream-decoder

    展开全文
  • MASS: 一统GPT和BERTSeq to Seq框架

    千次阅读 2019-05-20 07:06:09
    MASS: 一统GPT和BERTSeq to Seq框架MASS功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右...

    MASS的功能

    MASS的全名叫Masked Sequence to Sequence Pre-training for Language Generation,这个其实已经隐含了和GPT及BERT(前文有介绍)的关系: "Masked"对应了BERT原创的"Masked LM"算法,"Language Generation"指的是GPT用到的Language Modeling。
    而"Sequence to Sequence"算是点明了主题:这是一个Seq2Seq的框架。

    做Seq2Seq这么一个框架可以说是针对了GPT和BERT的痛点:在序列到序列(Seq2Seq)的语言生成任务中,二者都需要分别训练编码器和解码器,这导致在Seq2Seq任务中效果最好的的编码器-注意力-解码器结构没有被联合训练。因此GPT和BERT在这类任务中只能达到次优效果。

    MASS非常巧妙的解决了这个问题,它利用Seq2Seq的结构同时把GPT和BERT两种不同的pretrain方法容纳在一个计算框架之下。这样做的好处有两方面:

    1. GPT和BERT可以提供强大的pretrain的模型,这有利于下游的transfer learning的任务。
    2. Seq2Seq保证了高质量的完成语言生成任务。

    粗略的用一句话表示:MASS是利用GPT+BERT预训练出的Seq2Seq模型。

    GPT和BERT

    最好还是先介绍一下2018的双子星GPT和BERT。放在一起比较一下吧。

    比较

    BERT原论文反复引用了GPT,应该说BERT从GPT中吸取了很多营养。它们最大的共同点有两点:其一是都用transformer。因为二者的成功,相信transformer在NLP领域会取代(bi)LSTM成为最受欢迎的模型结构。LSTM因其在序列结构上的优点(包含long distance dependency的提取能力,以及对有序性的自动包含等等),transformer通过(multi-head)self-attention+positional embedding全部继承了下来。而且因为其摒弃了LSTM的序列结构,并没有并行能力上的限制。

    去年的其他一些同一方向上的好工作比如ELMo(前文介绍)和ULMFiT(介绍)利用了(bi)LSTM,由于GPT和BERT的背书,今后的工作中恐怕transformer会成为当仁不让的主流模型。

    第二个共同点是大数据加超大模型。具体的数据不再赘述了,原论文里都有。我觉得能够使大数据加大模型训练成为可能,得益于两点,一是计算能力的不断提升,另一个在于足够复杂的模型(特别是transformer)能够消化巨大的数据。

    再看一下不同的地方。最大的不同点应该是pretrain的方法。GPT用的是LM, 而BERT用的是Masked LM + Next Sentence。这个不同直接导致在做下游的任务时,GPT需要针对句子对类型的任务(句子相似性,QA等等)做多次编码,因为它的预训练方式导致它不能理解多个句子并存作为输入的情况。对照看一下GPT论文中的配图:
    在这里插入图片描述
    BERT因为它的Next Sentence的预训练方法,它能够理解作为输入的句子对。原论文关于输入的图示:
    在这里插入图片描述

    它的jointly-trainining得到的Segment Embeddings以及token [SEP]都能够帮助模型理解和区别不同的句子。所以在针对下游任务时,BERT可以自然的处理句子对类型。如下图:
    在这里插入图片描述
    简单说,对BERT来讲,一次编码就可以解决句子对问题。

    痛点

    有很多文本生成类的任务比如machine translation,text summarization,conversational response generation都依赖一个Sequence to Sequence的框架。对于GPT和BERT这样的结构来讲,充其量只能做一个编码器或者解码器,不能支撑一个Seq2Seq的结构。除非做两次预训练,一次做编码器,另一次做解码器。但这样做编码-注意力-解码的联合训练机制就不存在了。MASS就是针对这个问题而提出的方案。

    MASS模型

    模型解释

    MASS希望能够兼顾两点:

    1. 仍然采取文本生成类任务表现最优秀的编码-注意力-解码模型。
    2. 为了在少样本甚至零样本的任务中取得好成绩,也为了表现出很好的迁移学习的能力,同时容纳GPT和 BERT的预训练方式。

    一图顶万言:
    在这里插入图片描述

    这是MASS的模型框架。Encoder和Decoder都利用transformer作为基础模型。Encoder端有连续的几个"_"代表被masked输入token [M]。直觉上看,这有点像BERT里的Masked LM, 因为整体来讲,在Decoder那一段我们希望利用两边的信息预测被Masked的部分(即图中的利用x1,x2和x7,x8来预测x3,x4,x5,x6)。唯一的区别在于BERT仅仅是利用两端的信息预测一个Masked token,这里是在预测一连串的tokens。

    注意,在这个框架中“两边的信息”来自于Encoder,而不是Decoder。因为我们看到在Decoder那一端x1,x2,x7,x8都是Masked。这样做的好处体现在编码和解码两个方面:

    1. MASS迫使Encoder学会对Unmasked部分进行很好的理解和编码,以帮助Decoder进行正确的预测。
    2. 为进行正确的预测,Decoder会更加依赖于来自于Encoder的输入,而这导致了更好的Encoder-Attention-Decoder联合训练。

    在k=1和k=m(句子输入长度)时,我们可以观察到BERT的Unmaksed LM和GPT的LM在MASS上分别“复活”:

    在这里插入图片描述

    k=1的时候,如前所述,本质上正是BERT的Maksed LM。
    k=m的时候,Encoder那一段没有任何实质性的信息,而Decoder那一端回归到标准的LM模型!因为transformer+LM恰恰是GPT的预训练方式。

    可以想象,k在1和m之间的时候称为GPT和BERT的过渡形式。如下表:
    在这里插入图片描述

    思想根源

    这个框架非常漂亮的整合了GPT和BERT最核心的预训练方法,其实验(后面会略述)结果也达到顶尖水准。我个人觉得我今年看到最好的文章。如此优美的做法,如果结果不好反而会让人觉得说不过去。:)

    不过,如果我们切换一下角度,单从文本生成问题比如machine translation的研究看,可以观察到一些相似的思想的闪光。某种程度上,如果我们把这里Encoder的输入看成是一个corrupted版本的对输入的抽样的话,MASS其实可以看成是一个Denosing Auto-Encoder。

    追述一下Lample在其PBSMT(曾经的Unsupervised NMT的SOTA,现在被MASS超过)实践过的一个做法。

    为了在无监督的情况下做machine translation, 文章希望用encoder-decoder的框架做LM(注意这个需要是和MASS一样的)。具体方法就是利用denoising auto-encoding:

    L l m = E x ∼ S [ − log ⁡ P s → s ( x ∣ C ( x ) ) ] + E y ∼ T [ − log ⁡ P t → t ( y ∣ C ( y ) ) ] L^{lm} = E_{x\sim S}[-\log P_{s\rightarrow s}(x|C(x))] + E_{y\sim T}[-\log P_{t\rightarrow t}(y|C(y))] Llm=ExS[logPss(xC(x))]+EyT[logPtt(yC(y))]

    C C C是一个噪音模型,一般可以通过drop某些token或者shuffle句子来达到目的。 P s → s P_{s\rightarrow s} Pss P t → t P_{t\rightarrow t} Ptt分别指在源语言和目标语言领域的"翻译模型"。解释一下:这个翻译模型也包含两部分即encoder和decoder,特殊之处在于其输入和输出都是同样一种语言(即汉语译汉语,英语译英语),为了避免在attention module的强力加持下,产生逐字照搬的记忆式翻译的情况,这里故意使用了噪音模型 C C C

    如果在这里我们只考虑一种语言,而且规定噪音模型 C C C只能随机的drop掉句子中连续的tokens,那么这个方法会变得很接近MASS的做法。不同地方在于在DAE的decoder那一端不存在任何masked tokens。有趣的是,论文也有意在实验中实现了DAE(denoising auto-encoder)的做法,并进行了比较。Anyways,Seq2Seq的框架下MASS采取了这样相似的做法,不算很奇怪。

    实验

    限于篇幅这里介绍一下MASS在Unsupervised NMT里的表现。

    在预训练的数据方面,MASS用了WMT News Crawl datasets的monolingual data, 具体有英法德三国语言各50M的句子,也包含了2.9M的低资源语言罗马尼亚语。经过试验表明,k的长度大概为50%的句子长度时效果最好。

    在Unsupervised NMT任务中, 模型利用了同样的monolingual data来做fine tuning。具体的做法还是推荐阅读经典论文PBSMT,以后有机会也可以介绍一下这篇论文。

    下表是一些实验结果:
    在这里插入图片描述另外一个有趣的实验是使用不同的encoder和decoder,比如利用BERT里的Unmaske LM来预训练encoder,用LM来预训练decoder。再比如直接用前文提到的DAE即denoising auto-encoder来预训练encoder+decoder。下面是实验结果:
    在这里插入图片描述不出意外MASS的效果要好。作者的解释令人信服:DAE的decoder可以把整个句子都作为输入来预测下一个token,不存在从encder那里提取有用的表征的压力。这导致模型的信息提取能力相对MASS较弱。

    结语

    MASS很好的把GPT和BERT的训练方法内嵌在了Seq2Seq模型里。实验效果上也超过了过去的SOTA。这也是一次"跨领域"(其实这个领域跨度不算大,但是方法的融合度很高。) 不同方法的成功结合。

    关注公众号《没啥深度》有关自然语言处理的深度学习应用,偶尔也有关强化学习。
    这里写图片描述

    展开全文
  • UniLM:基于bertSeq2Seq

    千次阅读 2020-02-20 17:09:51
    论文地址:Unified Language Model Pre-training for Natural Language Understanding and Generation ...它可以完成单向、序列到序列双向预测任务,可以说是结合了ARAE两种语言模型的优点,Unilm在抽象摘要、...

    论文地址:Unified Language Model Pre-training for Natural Language Understanding and Generation

    概述:

      UniLM是微软研究院在Bert的基础上,最新产出的预训练语言模型,被称为统一预训练语言模型。它可以完成单向、序列到序列和双向预测任务,可以说是结合了AR和AE两种语言模型的优点,Unilm在抽象摘要、生成式问题回答语言生成数据集的抽样领域取得了最优秀的成绩。

    一、AR与AE语言模型

    AR: Aotoregressive Lanuage Modeling,又叫自回归语言模型。它指的是,依据前面(或后面)出现的tokens来预测当前时刻的token,代表模型有ELMO、GTP等。

    AE:Autoencoding Language Modeling,又叫自编码语言。通过上下文信息来预测当前被mask的token,代表有BERT ,Word2Vec(CBOW)。

    AR 语言模型:

    • 缺点:它只能利用单向语义而不能同时利用上下文信息。ELMO 通过双向都做AR 模型,然后进行拼接,但从结果来看,效果并不是太好。

    • 优点: 对自然语言生成任务(NLG)友好,天然符合生成式任务的生成过程。这也是为什么 GPT 能够编故事的原因。

    AE 语言模型:

    • 缺点: 由于训练中采用了 [MASK] 标记,导致预训练与微调阶段不一致的问题。此外对于生成式问题, AE 模型也显得捉襟见肘,这也是目前 BERT 为数不多实现大的突破的领域。

    • 优点: 能够很好的编码上下文语义信息, 在自然语言理解(NLU)相关的下游任务上表现突出。

    二、UniLM介绍

       预训练语言模型利用大量的无监督数据,结合上下文语境来预测缺失的文字或者单词,进而使模型学习到上下文相关的文本特征表示。当前已经存在多种预训练模型,但它们的预测任务和训练目标不尽相同。

    1)ELMO

      ELMO学习的是两个单向的语言模型,由一个前向(从左到右学习)和一个后向(从右向左学习)语言模型构成,其主干网络是LSTM组成,目标函数就是取这两个方向语言模型的最大似然。

    前向LSTM:

    后向LSTM:

    最大似然函数:

    2)GPT

       GPT是由Transformer组成的语言模型,采用从左到右一个一个学习word的方式,其实相当于利用了Transformer的解码器,因为解码器是从左到右一个个来的,左边的word看不到右边的信息,右边的word可以看到左边的信息。通过最大化以下似然函数来训练语言模型:

    3)BERT

      BERT也是由Transformer组成的语言模型,采用的是双向的学习的模式。为什么是双向呢?因为BERT利用的是Transformer的编码器,Transformer的编码器可以同时看到左右的信息。bert的双向学习使得其在NLU任务上有非常出色的表现,但是它在NLG任务上的表现就没有那么优越了。

    4)UniLM

      UniLM也是一个多层Transformer网络,跟bert类似,但是UniLM能够同时完成三种预训练目标,如上述表格所示,几乎囊括了上述模型的几种预训练方式,而且新增了sequence-to-sequence训练方式,所以其在NLU和NLG任务上都有很好的表现。UniLM模型基于mask词的语境来完成对mask词的预测,也是完形填空任务。对于不同的训练目标,其语境是不同的。

    • 单向训练语言模型,mask词的语境就是其单侧的words,左边或者右边。

    • 双向训练语言模型,mask词的语境就是左右两侧的words。

    • Seq-to-Seq语言模型,左边的seq我们称source sequence,右边的seq我们称为target sequence,我们要预测的就是target sequence,所以其语境就是所有的source sequence和其左侧已经预测出来的target sequence。

      三大优势:

    1. 三种不同的训练目标,网络参数共享。

    2. 正是因为网络参数共享,使得模型避免了过拟合于某单一的语言模型,使得学习出来的模型更加general,更具普适性。

    3. 因为采用了Seq-to-Seq语言模型,使得其在能够完成NLU任务的同时,也能够完成NLG任务,例如:抽象文摘,问答生成。

     

    三、模型架构与实验设置

    网络设置:24层Transformer,1024个hidden size,16个attention heads。

    参数大小:340M

    初始化:直接采用Bert-Large的参数初始化。

    激活函数:GELU,与bert一样

    dropout比例:0.1

    权重衰减因子:0.01

    batch size:330

    混合训练方式:对于一个batch,1/3时间采用双向(bidirectional)语言模型的目标,1/3的时间采用seq-to-seq语言模型目标,最后1/3平均分配给两种单向学习的语言模型,也就是left-to-right和right-to-left方式各占1/6时间。

    masking 方式:总体比例15%,其中80%的情况下直接用[MASK]替代,10%的情况下随机选择一个词替代,最后10%的情况用真实值。还有就是80%的情况是每次只mask一个词,另外20%的情况是mask掉bigram或者trigram。

     Attention 控制:不同的训练方式,其关注的语境是不一样的,上面也有介绍,如上图所示,灰色的方格就是不能看到的信息,白色的就是需要attention的信息。如何实现这种控制呢?不让当前预测词看掉的信息就采用掩码隐藏掉,只留下能让当前词可看的信息,换句话说,论文使用了掩码来控制在计算基于上下文的表征时 token 应该关注的上下文的量。下面有详细实现方式。

     

    四、模型输入与骨干网络

      模型输入X是一串word序列,该序列要么是用于单向语言模型的一段文本片段,要么是一对文本片段,主要用于双向或者seq-to-seq语言模型 。在输入的起始处会添加一个[SOS]标记,结尾处添加[EOS]标记。[EOS]一方面可以作为NLU任务中的边界标识,另一方面还能在NLG任务中让模型学到何时终止解码过程。其输入表征方式与 BERT 的一样,包括token embedding,position embedding,segment embedding,同时segment embedding还可以作为模型采取何种训练方式(单向,双向,序列到序列)的一种标识。

       骨干网络由24层Transformer组成,输入向量 {xi}首先会被转换成H0=[x1,...,x|x|] ,然后送入该24层Transformer网络,每一层编码输出如下:

    在每一层通过掩码矩阵M来控制每个词的注意力范围,0表示可以关注,负无穷表示不能关注,会被掩码掉。对于第 l个 Transformer 层,自注意头 AI 的输出的计算方式为:

     

     

    五、预训练的目标

       针对不同语言模型的训练目标,文章设计了四种完形填空任务。在某个完形填空任务中,会随机选择一些WordPiece替换为[MASK],然后通过Transformer网络计算得到相应的输出向量,再把输出向量喂到softmax分类器中,预测被[MASK]的word。UniLM参数优化的目标就是最小化被[MASK] token的预测值和真实值之间的交叉熵。值得注意的是,由于使用的是完形填空任务,所以可以为所有语言模型(无论是单向还是双向)都使用同样的训练流程。

    单向语言模型:

      单向语言模型,分别采取从左到右和从右到左的训练目标。以从左到右为例子,例如去预测序列"X1X2[MASK]X4"中的掩码,仅仅只有X1,X2和它自己的信息可用,X4的信息是不可用的。这通过上文提到的掩码M矩阵来控制,结合结构图可以看出,主要是上三角的信息不可见,所以在掩码矩阵M中将上三角矩阵的值置为-∞。

    双向语言模型:

       双向语言模型,还以"X1X2[MASK]X4"为例子,其中X1,X2,X4及自己的信息都可用,所以相比单向语言模型,能够生成更好的与上下文相关token表征。这也是通过掩码矩阵M来控制,都可见,将M的值都置为0即可

    序列到序列语言模型

      左侧的序列其实就是我们的已知序列,叫source sequence,右侧的序列就是我们想要的序列,叫target sequence。左侧的序列属于编码阶段,所以相互的上下文信息都能看到;右侧的序列属于解码阶段,能看到source sequence的信息、target sequence中其左侧的信息及自己的信息。以T1T2->T3T4T5举例说明,我们的输入就变成[SOS]T1T2[EOS]T3T4T5[EOS],T1和T2相互都能看到,并能看到两边的[SOS]和[EOS];而T4能看到[SOS]、T1、T2、[EOS]、T3及自己的信息。

      在训练的时候,source sequence和target sequence中的token都会被随机替换为[MASK],以达到模型学习训练的目的。在预测[MASK]的同时,因为这两个语句对被打包在一起了,其实模型也无形中学到了两个语句之间存在的紧密关系。这在NLG任务中,比如抽象文摘,非常有用。

     

    六、下游任务NLU和NLG的Fine-tuning方法

       对于NLU任务而言,做fine-tuning的时候,直接微调为双向的transformer编码器,跟bert一样。以文本分类为例子,使用 [SOS] 的编码向量作为输入的编码,表示为,也就是将UniLM骨干网络的最后一层的[SOS]编码位置输出,作为分类器的初始输入,然后将其输入一个随机初始化的 softmax 分类器(即特定于任务的输出层),其中类别概率的计算方式为 ,其中 是参数矩阵,C 是类别数量。后面微调的时候,可以同时调整UniLM模型参数及分类器的Wc参数即可。

      对于 NLG 任务,以seq-to-seq任务为例,微调过程类似于使用自注意掩码进行预训练。令 S1 和 S2 分别表示源序列和目标序列,构建出输入[SOS] S1 [EOS] S2 [EOS]。该模型的微调是通过随机掩盖target序列中一定比例的 token,让模型学习恢复被掩盖的词,其训练目标是基于上下文最大化被掩盖 token 的似然度。这点与预训练中略有不同,预训练的时候是随机掩盖掉source序列和target序列的token,也就是两端都参与了训练,而微调的时候只有target参与,因为微调更多关注的是target端。值得注意的是,fine-tuning的时候,target端的结束标识[EOS]也可以被掩盖掉,让模型学习预测,这样模型就可以学习出来自动结束NLG任务了。

     

    七、结合实例具体分析下游Fine-tuning任务

      NLU的任务有抽取式问答,NLG的任务主要有抽象文摘、问题生成、生成问题答案和对话响应生成。

    1、抽象式文摘(abstractive summarization)

      抽象文摘就是根据机器的理解自动生成文摘内容。本实验利用UniLM的sequence-to-sequence模型来完成,在 CNN/DailyMail 数据集上完成摘要任务在训练集上fine-tuning 30个epochs,微调的超参数与预训练的时候一致,target token被mask的比例为0.7,batch size设置为32,最大长度768,标签平滑设置为0.1。

      标签平滑:假设多分类的原label=[0,1,0],经过骨干网络+softmax之后输出预测值=[0.15,0.7,0.15],根据交叉熵公式会得到一个loss,其实这个时候从预测标签中就能看出哪个类别概率大了,但是基于loss函数的导向,会继续减少损失,让中间的0.7越来越靠近1,这样再学习就容易过拟合了,所以引入了标签平滑。标签平滑就是把原label修正一下,相互之间差别不要那么大,例如修改为[0.1,0.8,0.1],这样模型就不至于过分学习了。

      解码的时候,采用集束搜索策略,束宽设置为5。

    2、生成式问答(Generative QA)

      该实验是给一段文本和一个问题,生成形式自由的答案,这也是一个NLG任务,我们这里也是利用sequence-to-sequence模型来完成。soure序列就是一段文本和问题,target序列就是答案。在CoQA数据集上完成实验,共进行10个epochs。target token被mask的比例为0.5,batch size设置为32,最大长度为512,标签平滑设置为0.1。解码的时候束宽设置为3。对于文本特别长的情况下,会利用一个滑动窗口,将文本分成几段,最后把与问答重合度最高的片段送入模型。

    3、问题生成(Question Generation)

      该任务是给定一个输入段落和一个答案范围,目标是生成一个询问该答案的问题。该任务同样可以转化为一个sequence-to-sequence问题来解决。oure序列就是一段文本和答案,target序列就是问题。target token被mask的比例为0.7,batch size设置为32。

    4、多轮对话响应生成(Response Generation)

      给定一段多轮对话历史,并将网络文档作为知识源,系统需要生成适合对话并且能反映网络文档内容的自然语言响应。该任务依然采用sequence-to-sequence语言模型来解决。源序列S1=对话历史+知识源,目标序列S2=响应的对话。在DSTC7数据集上进行实验,进行20个epochs,batch size=64,target token被mask的比例为0.5。

    展开全文
  • 在参与的项目产品中,涉及到模型算法的需求,主要以自然语言处理(NLP)知识图谱(KG)为主。NLP涉及面太广,而聚焦在具体场景下,想要生产落地的还需要花很多功夫。 作为NLP的主要方向,情感分析,文本多分类...

    介绍

    在参与的项目和产品中,涉及到模型和算法的需求,主要以自然语言处理(NLP)和知识图谱(KG)为主。NLP涉及面太广,而聚焦在具体场景下,想要生产落地的还需要花很多功夫。
    作为NLP的主要方向,情感分析,文本多分类,实体识别等已经在项目中得到应用。例如
    通过实体识别,抽取文本中提及到的公司、个人以及金融产品等。
    通过情感分析,判别新闻资讯,对其提到的公司和个人是否利好?
    通过文本多分类,判断资讯是否是高质量?判断资讯的行业和主题?
    具体详情再找时间分享。而文本生成、序列到序列(Sequence to Sequence)在机器翻译、问答系统、聊天机器人中有较广的应用,在参与的项目中暂无涉及,本文主要通过tensorflow+bert+seq2seq实现一个简单的问答模型,旨在对seq2seq的了解和熟悉。

    数据

    关于seq2seq的demo数据有很多,例如小黄鸡聊天语料库,影视语料库,翻译语料库等等。由于最近总是做些奇怪的梦,便想着,做一个AI解梦的应用玩玩,just for fun。
    通过采集从网上采集周公解梦数据,通过清洗,形成
    dream:梦境;
    decode:梦境解析结果。
    这样的序列对,总计33000+ 条记录。数据集下载地址:后台回复“解梦”
    {
    "dream": "梦见商人或富翁",
    "decode": "是个幸运的预兆,未来自己的事业很有机会成功,不过如果梦中的富翁是自己,则是一个凶兆。。"
    }

    模型准备

    #下载 bert
    $ git clone https://github.com/google-research/bert.git
    #下载中文预训练模型
    $ wget -c https://storage.googleapis.com/bert_models/2018_11_03/chinese_L-12_H-768_A-12.zip
    $ unzip chinese_L-12_H-768_A-12.zip 

    bert 的input:

    self.input_ids = tf.placeholder(
        dtype=tf.int32,
        shape=[None, None],
        name="input_ids"
    )
    self.input_mask = tf.placeholder(
        dtype=tf.int32,
        shape=[None, None],
        name="input_mask"
    )
    self.segment_ids = tf.placeholder(
        dtype=tf.int32,
        shape=[None, None],
        name="segment_ids"
    )
    self.dropout = tf.placeholder(
        dtype=tf.float32,
        shape=None,
        name="dropout"
    )

    bert 的model :

    self.bert_config = modeling.BertConfig.from_json_file(bert_config)
        model = modeling.BertModel(
            config=self.bert_config,
            is_training=self.is_training,
            input_ids=self.input_ids,
            input_mask=self.input_mask,
            token_type_ids=self.segment_ids,
            use_one_hot_embeddings=False
        )

    seq2seq 的encoder_embedding 替换:

    # 默认seq2seq model_inputs
    # self.encoder_embedding = tf.Variable(tf.random_uniform([from_dict_size, embedded_size], -1, 1),name ="encoder_embedding")
    # self.model_inputs = tf.nn.embedding_lookup(self.encoder_embedding, self.X),
    #  替换成bert
    self.embedded = model.get_sequence_output()
    self.model_inputs = tf.nn.dropout(self.embedded, self.dropout)

    seq2seq 的decoder_embedding 替换:

    # 默认seq2seq decoder_embedding
    # self.decoder_embedding = tf.Variable(tf.random_uniform([to_dict_size, embedded_size], -1, 1),name="decoder_embedding")
    #  替换成bert
    self.decoder_embedding = model.get_embedding_table()
    self.decoder_input = tf.nn.embedding_lookup(self.decoder_embedding, decoder_input),

    数据预处理

    for i in range(len(inputs)):
        tokens = inputs[i]
        inputs_ids = model.tokenizer.convert_tokens_to_ids(inputs[i])
        segment_ids = [0] * len(inputs_ids)
        input_mask = [1] * len(inputs_ids)
        tag_ids = model.tokenizer.convert_tokens_to_ids(outputs[i])
        data.append([tokens, tag_ids, inputs_ids, segment_ids, input_mask])
        
    
    def pad_data(data):
        c_data = copy.deepcopy(data)
        max_x_length = max([len(i[0]) for i in c_data])
        max_y_length = max([len(i[1]) for i in c_data]) 
        # 这里生成的序列的tag-id 和 input-id 长度要分开
        # print("max_x_length : {} ,max_y_length : {}".format( max_x_length,max_y_length))
        padded_data = []
        for i in c_data:
            tokens, tag_ids, inputs_ids, segment_ids, input_mask = i
            tag_ids = tag_ids + (max_y_length - len(tag_ids)) * [0]
            # 注意tag-ids 的长度补充,和预测的序列长度一致。
            inputs_ids = inputs_ids + (max_x_length - len(inputs_ids)) * [0]
            segment_ids = segment_ids + (max_x_length - len(segment_ids)) * [0]
            input_mask = input_mask + (max_x_length - len(input_mask)) * [0]
            assert len(inputs_ids) == len(segment_ids) == len(input_mask)
            padded_data.append(
                [tokens, tag_ids, inputs_ids, segment_ids, input_mask]
            )
        return padded_data
    

    训练

    $ python3 model.py --task=train \
        --is_training=True \
        --epoch=100 \
        --size_layer=256 \
        --bert_config=chinese_L-12_H-768_A-12/bert_config.json \
        --vocab_file=chinese_L-12_H-768_A-12/vocab.txt \
        --num_layers=2 \
        --learning_rate=0.001 \
        --batch_size=16 \
        --checkpoint_dir=result

    image

    预测

    $ python3 model.py --task=predict \
            --is_training=False \
            --epoch=100 \
            --size_layer=256 \
            --bert_config=chinese_L-12_H-768_A-12/bert_config.json \
            --vocab_file=chinese_L-12_H-768_A-12/vocab.txt \
            --num_layers=2 \
            --learning_rate=0.001 \
            --batch_size=16 \
            --checkpoint_dir=result

    image

    Just For Fun ^_^

    本文代码: https://github.com/saiwaiyanyu/tensorflow-bert-seq2seq-dream-decoder

    作者:saiwaiyanyu
    链接:https://juejin.im/post/5dd9e07b51882572f00c4523
    来源:掘金

    8

    本文由博客一文多发平台 OpenWrite 发布!

    展开全文
  • 【深度学习】从seq2seq到Transformer学习记录小透明苞谷关注22019.06.23 15:03:40字数 3,520阅读 603背景 Encoder-Decoder是个非常通用的计算框架,至于EncoderDecoder具体使用什么模型都是由研究者自己定的,常见...
  • 文章目录1.UNILM简介2.实战2.0 租服务器2.0.1 基础配置...传统的seq2seq模型需要用一个encoder把输入的语料处理成向量,一个decoder把向量转换为词表里的词,生成目标语句。 Bert是一个预训练好的语言模型,有三层

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 5,011
精华内容 2,004
关键字:

bert和seq2seq