Unified Language Model 文本生成从未如此轻松

  • 前言
  • UniLM
  • How to build UniLM
    • Get 2D MASK
    • Send 2D MASK to Bert
  • 使用UniLM实现新闻标题生成
    • 数据处理部分(略)
    • 模型训练
      • 技巧1:用自定义损失层来代替损失函数
      • 技巧2: 结合Embedding信息输出预测文本
    • 模型推理
      • 技巧3: BeamSearch解码
    • 精简你的词汇表
      • 技巧4: 精简你的词汇表,让你的模型收敛更快
  • 测试结果
  • 参考资料
  • 代码地址

前言

一篇19年的微软论文,老规矩先放论文链接:https://arxiv.org/abs/1905.03197

最近开始尝试做长文本的摘要生成任务,因此要拿出了几篇 Transformer 时代的文本生成相关的经典论文参考。当我看到UniLM这个Bert的变体模型(甚至连变体都算不上),口中只能叹出,“相见恨晚” 四个字。接着回顾了之前辛苦辛苦手敲的LSTM+Attention的seq2seq模型,脸上浮现出了嫌弃的表情。文本生成在Self-Attention的加持下从未如此简单。


UniLM

这里只阐述论文的关键思路,具体细节还是看论文来的实在啦~

  1. MLM (Mask language model),之所以在文本生成任务上表现较弱,归根到底是其一个非常重要的先验假设:Token之间是相互独立的。

    Independence Assumption: As emphasized by the ≈ sign in Eq, BERT factorizes the joint conditional probability p(x¯ | xˆ) based on an independence assumption that all masked tokens x¯ are separately reconstructed. (摘自论文XLNet)
  2. 其 Self-Attention 模块使得每一个Position上的Token都能获取到全文的上下文信息,这与文本生成任务相违背:即文本的生成是具有依赖关系的,Xt+1的生成应该依赖于X<=t,并且Xt+1不能获取X>t的信息,因为那是未来的信息,如果仍然使用MLM中的满Self-attention(姑且这么叫)来做训练模型的文本生成能力,那么在一开始模型就已经知道了所有的答案了,这显然是不合理的。
  3. 那该如何是好?使用Seq2Seq的模型:如老家族的RNN,LSTM,GRU等序列模型,或新家族的Transform类,由于在该类模型中Decoder的信息传递是单向的,每一个输出只决定于Encoder部分的输入和之前的输出,因此也能较好的完成文本生成的任务。
  4. 那有没有什么办法,让MLM / Bert的框架既能优秀的完成文本理解,又能轻松实现文本生成任务呢?UNILM给出了答案:给Self-Attention加上MASK!
  1. 既然满Self-Attention会泄密,那么为什么不能通过MASK:将待预测部分的Token的Attention做选择性的屏蔽,让他只能看到获取到上文的信息,而对输入部分保持理解输入部分的上下文信息,但屏蔽待生成部分的信息。
  2. 图中 Bidirectional LM 与Bert一致,使用满 Self-attention 是模型充分学习全文的上下文信息,提高文本理解能力。
  3. 图中 Seq-to-Seq LM 部分:矩阵 Sij i为行 j 为列 Sij 为空白表示以 i 为 Q,j 为 K 的Attention信息没有被MASK,即 i 能获取 j 的信息。Sij 为黑 则表示该Attention信息被Mask,i 无法获取 即 j 的信息。如图中的MASK设计,S1部分仍为 Bidirectional LM,S2部分的每一个Token只能获取前面的Token的信息,而后面的信息是被MASK的。这符合文本生成的逻辑。
  4. 将以上 两种 LM 方式作为模型的 Pretrain 任务,Bidirectional LM 与 Bert一致,随机MASK Token,并进行预测,Seq-to-Seq LM 则 MASK S2的Token 利用 S1的信息去预测。这种Pretrain机制的设计使得 UNILM 完成文本理解和文本生成任务的能力都得到了提升。

How to build UniLM

Tensorflow-GPU 2.0.0
Transformers 3.1.0

Get 2D MASK

  1. 当我们要使用 UniLM 完成文本生成任务时,Self-Attention 的Mask 会变成一个2D动态遮招(每一个sample都不同)这与往常我们通过 Transformers 的 BertTokenizer 模块直接得到的1D的 attention_mask不同。
  2. 2D Mask 取决于我们的输入和输出,而这部分信息可以通过Segment_id进行表示,因此我们只需要将输入文本和目标输出文本同时传递给BertTokenizer,通过返回的Segment_id 构建 2D MASK即可。
def unilm_mask_single(s):'''s = np.array([0,0,0,0,1,1,1,0,0,0])unilm_mask_single(s) = <tf.Tensor: shape=(10, 10), dtype=float32, numpy=array([[1., 1., 1., 1., 0., 0., 0., 0., 0., 0.],[1., 1., 1., 1., 0., 0., 0., 0., 0., 0.],[1., 1., 1., 1., 0., 0., 0., 0., 0., 0.],[1., 1., 1., 1., 0., 0., 0., 0., 0., 0.],[1., 1., 1., 1., 1., 0., 0., 0., 0., 0.],[1., 1., 1., 1., 1., 1., 0., 0., 0., 0.],[1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],[1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],[1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],[1., 1., 1., 1., 1., 1., 1., 1., 1., 1.]], dtype=float32)>'''idxs = K.cumsum(s, axis=0)mask = idxs[None, :] <= idxs[:, None]mask = K.cast(mask, K.floatx())return maskids = np.zeros((self.batch_size,self.Max_len),dtype='int32')
seg_id = np.zeros((self.batch_size,self.Max_len),dtype='int32')
mask_att = np.zeros((self.batch_size,self.Max_len,self.Max_len),dtype='int32')input_dict = self.tokenizer(content,title,max_length=self.Max_len,truncation=True,padding=True)
len_ = len(input_dict['input_ids'])
token_ids = input_dict['input_ids']
segment_ids = input_dict['token_type_ids']
ids[index][:len_] = token_ids
seg_id[index][:len_] = segment_ids
mask_id = unilm_mask_single(seg_id[index])
mask_att[index] = mask_id

Send 2D MASK to Bert

  1. 通过 Trainsformers.TFBertModel 类创建的Bert实例,其默认接受的attention_mask类型为2维 即[batch_size, MAX_LEN] 之后通过广播的形式传播到 Self-attention矩阵的每一行,因此我们需要修改 Trainsformers.TFBertModel 的逻辑,使其允许接受我们提前计算好的Self-attention矩阵,即[batch_size, MAX_LEN, MAX_LEN]
  2. 具体的:
class TFBertMainLayer(tf.keras.layers.Layer):def call(……)if len(attention_mask.shape) == 2:extended_attention_mask = attention_mask[:, tf.newaxis, tf.newaxis, :]elif len(attention_mask.shape) == 3:extended_attention_mask = attention_mask[:, tf.newaxis, :, :]else:raise NotImplementedErrorextended_attention_mask = tf.cast(extended_attention_mask, embedding_output.dtype)extended_attention_mask = (1.0 - extended_attention_mask) * -10000.0
  1. 当你需要使用UniLM完成文本生成任务时,传入你提前计算好的attention_mask矩阵即可,
  2. 当你需要使用UniLM完成文本理解任务时,传入原始的attention_mask序列即可。
  3. 至此你已经完成了 UniLM 模型!

使用UniLM实现新闻标题生成

数据处理部分(略)

数据处理部分只需要将新闻文本和标题同时传给BertTokenizer实例,并通过返回的Segment_id构建attention_mask矩阵即可。

模型训练

技巧1:用自定义损失层来代替损失函数

  1. 通过自定义层,并利用tf.keras.layers.Layer.add_loss 方法,可以允许我们在层的计算中传递Loss,这允许我们利用 input 和 output 计算损失。具体如下:
class Loss(tf.keras.layers.Layer):"""特殊的层,用来定义复杂loss"""def __init__(self, output_axis=None, **kwargs):super(Loss, self).__init__(**kwargs)self.output_axis = output_axisdef call(self, inputs, mask=None):loss = self.compute_loss(inputs, mask)self.add_loss(loss)if self.output_axis is None:return inputselif isinstance(self.output_axis, list):return [inputs[i] for i in self.output_axis]else:return inputs[self.output_axis]def compute_loss(self, inputs, mask=None):raise NotImplementedErrorclass CrossEntropy(Loss):"""交叉熵作为loss,并mask掉输入部分"""def compute_loss(self, inputs, mask=None):y_true, y_mask, y_pred = inputsy_true = tf.cast(y_true,tf.float32)y_mask = tf.cast(y_mask,tf.float32)y_true = y_true[:, 1:]  # 目标token_idsy_mask = y_mask[:, 1:]  # segment_ids,刚好指示了要预测的部分y_pred = y_pred[:, :-1] # 预测序列,错开一位loss = K.sparse_categorical_crossentropy(y_true, y_pred)loss = K.sum(loss * y_mask) / K.sum(y_mask)return loss

技巧2: 结合Embedding信息输出预测文本

  1. 传统上,我们会使用 Dense(vocab_size,activaion=‘softmax’)(weight = [hidden_size, voacb_size) 来输出预测的概率分布,这样虽然可行,但模型的收敛速度非常慢甚至无法收敛,且需要更多的数据支持。因为模型在最开始是不知道vocab_size维度上每一维对应的文字信息。如果我们将这个Dense层的weight替换成我们的word_embedding,将每一维的所代表的token信息提早告诉模型,不仅加快了模型的收敛速度(实测有效!),还提高了模型的性能。模型具体如下:
def build_model(pretrained_path,config,MAX_LEN,vocab_size,keep_tokens):ids = tf.keras.layers.Input((MAX_LEN,), dtype=tf.int32)token_id = tf.keras.layers.Input((MAX_LEN,), dtype=tf.int32)att = tf.keras.layers.Input((MAX_LEN,MAX_LEN), dtype=tf.int32)config.output_hidden_states = Truebert_model = TFBertModel.from_pretrained(pretrained_path,config=config,from_pt=True)x, _ , hidden_states = bert_model(ids,token_type_ids=token_id,attention_mask=att)layer_1 = hidden_states[-1]'''[batch_size,max_len,hidden_size] * [hidden_size,vocab_size]= [batch_size, max_len , vocab_size]'''word_embeeding = bert_model.bert.embeddings.word_embeddingsembeddding_trans = tf.transpose(word_embeeding)sof_output = tf.matmul(layer_1,embeddding_trans)sof_output = tf.keras.layers.Activation('softmax')(sof_output)#加入损失层,计算损失output = CrossEntropy(2)([ids,token_id,sof_output])model = tf.keras.models.Model(inputs=[ids,token_id,att],outputs=output)optimizer = tf.keras.optimizers.Adam(learning_rate=1e-5)model.compile(optimizer=optimizer)model.summary()return model

模型推理

技巧3: BeamSearch解码

  1. 模型推理时为多步迭代解码过程,并用BeamSearch方法寻找较优序列
  2. 具体来说每次只预测下一个词,并将已经生成的token加入到输入中,但segment_id编码为1,传入相应的mask矩阵。(蓝色为输入,红色为输出)
  3. BeamSearch解码代码如下,已补充更多备注:
class AutoRegressiveDecoder(object):"""通用自回归生成模型解码基类包含beam search和random sample两种策略"""def __init__(self, start_id, end_id, maxlen, model,minlen=1):self.start_id = start_idself.end_id = end_idself.maxlen = maxlenself.minlen = minlenself.models = {}self.model = modelif start_id is None:self.first_output_ids = np.empty((1, 0), dtype=int)# array([], shape=(1, 0), dtype=int64)else:self.first_output_ids = np.array([[self.start_id]])@staticmethoddef wraps(default_rtype='probas', use_states=False):"""用来进一步完善predict函数目前包含:1. 设置rtype参数,并做相应处理;2. 确定states的使用,并做相应处理;3. 设置温度参数,并做相应处理。"""def actual_decorator(predict):def new_predict(self,inputs,output_ids,states,temperature=1,rtype=default_rtype):assert rtype in ['probas', 'logits']prediction = predict(self, inputs, output_ids, states)if not use_states:prediction = (prediction, None)if default_rtype == 'logits':prediction = (softmax(prediction[0] / temperature), prediction[1])elif temperature != 1:probas = np.power(prediction[0], 1.0 / temperature)probas = probas / probas.sum(axis=-1, keepdims=True)prediction = (probas, prediction[1])if rtype == 'probas':return predictionelse:return np.log(prediction[0] + 1e-12), prediction[1]return new_predictreturn actual_decoratordef last_token(self,end):"""创建一个只返回输入的最后一个token输出的新Model"""
#         if model not in self.models:outputs = [tf.keras.layers.Lambda(lambda x: x[:,end])(output)for output in self.model.outputs]model_temp = tf.keras.models.Model(self.model.inputs, outputs)return model_tempdef predict(self, inputs, output_ids, states=None):"""用户需自定义递归预测函数说明:定义的时候,需要用wraps方法进行装饰,传入default_rtype和use_states,其中default_rtype为字符串logits或probas,probas时返回归一化的概率,rtype=logits时则返回softmax前的结果或者概率对数。返回:二元组 (得分或概率, states)"""raise NotImplementedErrordef beam_search(self, inputs, topk, states=None, temperature=1, min_ends=1):"""beam search解码说明:这里的topk即beam size;返回:最优解码序列。"""#inputs = [token_ids,segment_ids]output_ids, output_scores = self.first_output_ids, np.zeros(1)# output_ids = [] , output_scores = 0for step in range(self.maxlen):scores, states = self.predict(inputs, output_ids, states, temperature, 'logits')  # 计算当前得分#每一次人输入的拼接在predict里完成if step == 0:  # 第1步预测后将输入重复topk次inputs = [np.repeat(i, topk, axis=0) for i in inputs]scores = output_scores.reshape((-1, 1)) + scores  # 综合累积得分-相加等于相乘,输出的是logist# output_scores = [1.16165232,1.75142511]#上一次最优的两个的值# 分别由上面两个最优值作为x产生,故在各自产生的概率上加上之前的值# [[0.99853728 0.67273463 1.50580529 1.16165232 1.4321206 ]# [1.44454842 1.68150066 1.24661511 1.42612343 1.75142511]]indices = scores.argpartition(-topk, axis=None)[-topk:]  # 仅保留topk#[3 ,9]indices_1 = indices // scores.shape[1] # 候选字数 # 行索引# [0 ,1]indices_2 = (indices % scores.shape[1]).reshape((-1, 1))  # 列索引# [[3],[4]]output_ids = np.concatenate([output_ids[indices_1],indices_2],1)  # 更新输出#[[1,2,2,3,3], + [[3]# [2,3,1,4,4]]    [4]]output_scores = np.take_along_axis(scores, indices, axis=None)  # 更新得分#按indices的一维切片去获得索引 [1.16165232,1.75142511]end_counts = (output_ids == self.end_id).sum(1)  # 统计出现的end标记#[分别统计两条路 end出现次数 0,1]if output_ids.shape[1] >= self.minlen:  # 最短长度判断best_one = output_scores.argmax()  # 得分最大的那个if end_counts[best_one] == min_ends: # =1   # 如果已经终止return output_ids[best_one]  # 直接输出else:  # 否则,只保留未完成部分flag = (end_counts < min_ends)  # 标记未完成序列if not flag.all():  # 如果有已完成的inputs = [i[flag] for i in inputs]  # 扔掉已完成序列output_ids = output_ids[flag]  # 扔掉已完成序列output_scores = output_scores[flag]  # 扔掉已完成序列end_counts = end_counts[flag]  # 扔掉已完成end计数topk = flag.sum()  # topk相应变化# 达到长度直接输出return output_ids[output_scores.argmax()]class AutoTitle(AutoRegressiveDecoder):"""seq2seq解码器"""@AutoRegressiveDecoder.wraps(default_rtype='probas')def predict(self, inputs, output_ids, states):ids,seg_id,mask_att = inputsides_temp = ids.copy()seg_id_temp = seg_id.copy()mask_att_temp = mask_att.copy()len_out_put = len(output_ids[0])for i in range(len(ids)):get_len = len(np.where(ids[i] != 0)[0])end_ = get_len + len_out_putides_temp[i][get_len:end_] = output_ids[i]seg_id_temp[i][get_len:end_] = np.ones_like(output_ids[i])mask_att_temp[i] = unilm_mask_single(seg_id_temp[i])return self.model.predict([ides_temp,seg_id_temp,mask_att_temp])[:,end_-1]def generate(self,text,tokenizer,maxlen,topk=1):max_c_len = maxlen - self.maxleninput_dict = tokenizer(text,max_length=max_c_len,truncation=True,padding=True)token_ids = input_dict['input_ids']segment_ids = input_dict['token_type_ids']ids = np.zeros((1,maxlen),dtype='int32')seg_id = np.zeros((1,maxlen),dtype='int32')mask_att = np.zeros((1,maxlen,maxlen),dtype='int32')len_ = len(token_ids)ids[0][:len_] = token_idsseg_id[0][:len_] = segment_idsmask_id = unilm_mask_single(seg_id[0])mask_att[0] = mask_idoutput_ids = self.beam_search([ids,seg_id,mask_att],topk=topk)  # 基于beam searchreturn tokenizer.decode(output_ids)

精简你的词汇表

技巧4: 精简你的词汇表,让你的模型收敛更快

  1. 加载预训练模型的词汇表,其中所有词汇量达到21127个,所对应的预训练的word_embedding参数为 [21127,hidden_size],而这21127中包含了英文后缀、特殊符号、表情、其他文字等等,考虑到中文文本生成任务,我们可以通过精简词汇表,并调整word_embedding来减少模型需要预测的输出类别,一方面减少了模型参数,加速了模型收敛,另一方面也避免了生成的文本有奇怪的字符掺入。
def load_vocab(dict_path, encoding='utf-8', simplified=False, startswith=None):"""从bert的词典文件中读取词典,如果simplified = True,则对该字典进行精简。return:返回精简留下的字符及其新token_id组成的字典,已经其对应的老token_id。"""def _is_punctuation(ch):"""标点符号类字符判断(全/半角均在此内)提醒:unicodedata.category这个函数在py2和py3下的表现可能不一样,比如u'§'字符,在py2下的结果为'So',在py3下的结果是'Po'。"""code = ord(ch)return 33 <= code <= 47 or \58 <= code <= 64 or \91 <= code <= 96 or \123 <= code <= 126 or \unicodedata.category(ch).startswith('P')def stem(token):"""获取token的“词干”(如果是##开头,则自动去掉##)"""if token[:2] == '##':return token[2:]else:return tokendef _cjk_punctuation():return u'\uff02\uff03\uff04\uff05\uff06\uff07\uff08\uff09\uff0a\uff0b\uff0c\uff0d\uff0f\uff1a\uff1b\uff1c\uff1d\uff1e\uff20\uff3b\uff3c\uff3d\uff3e\uff3f\uff40\uff5b\uff5c\uff5d\uff5e\uff5f\uff60\uff62\uff63\uff64\u3000\u3001\u3003\u3008\u3009\u300a\u300b\u300c\u300d\u300e\u300f\u3010\u3011\u3014\u3015\u3016\u3017\u3018\u3019\u301a\u301b\u301c\u301d\u301e\u301f\u3030\u303e\u303f\u2013\u2014\u2018\u2019\u201b\u201c\u201d\u201e\u201f\u2026\u2027\ufe4f\ufe51\ufe54\u00b7\uff01\uff1f\uff61\u3002'def _is_cjk_character(ch):"""CJK类字符判断(包括中文字符也在此列)参考:https://en.wikipedia.org/wiki/CJK_Unified_Ideographs_(Unicode_block)"""code = ord(ch)return 0x4E00 <= code <= 0x9FFF or \0x3400 <= code <= 0x4DBF or \0x20000 <= code <= 0x2A6DF or \0x2A700 <= code <= 0x2B73F or \0x2B740 <= code <= 0x2B81F or \0x2B820 <= code <= 0x2CEAF or \0xF900 <= code <= 0xFAFF or \0x2F800 <= code <= 0x2FA1Ftoken_dict = {}with open(dict_path, encoding=encoding) as reader:for line in reader:token = line.split()token = token[0] if token else line.strip()token_dict[token] = len(token_dict)if simplified:  # 过滤冗余部分tokennew_token_dict, keep_tokens = {}, []startswith = startswith or []for t in startswith:new_token_dict[t] = len(new_token_dict)keep_tokens.append(token_dict[t])for t, _ in sorted(token_dict.items(), key=lambda s: s[1]):if t not in new_token_dict:keep = Trueif len(t) > 1:for c in stem(t):if (_is_cjk_character(c) or_is_punctuation(c)):keep = Falsebreakif keep:new_token_dict[t] = len(new_token_dict)keep_tokens.append(token_dict[t])return new_token_dict, keep_tokenselse:return token_dict
  1. 通过load_vocab函数,我们可以将传入的词表.txt文件精简后,以dict的形式返回,并附带对应的老token_id,这对我们来说很重要,可以帮助我们重构word_embedding。
  2. 那么我们该如何将这个new_dict载入到BertTokenizer中去呢,简单的替换 tokenizer.vocab 是会出现错误的,而BertTokenizer的call函数仅接受文件路径,因此同样我们需要修改BertTokenizer类的函数,使之可以接受dict形式的词典,具体的:修改BertTokenizer脚本中的load_vocab 函数,使之可以直接返回dict。
def load_vocab(vocab_file):"""Loads a vocabulary file into a dictionary."""if isinstance(vocab_file,dict):return vocab_filevocab = collections.OrderedDict()with open(vocab_file, "r", encoding="utf-8") as reader:tokens = reader.readlines()for index, token in enumerate(tokens):token = token.rstrip("\n")vocab[token] = indexreturn vocab
  1. 加载new_dict,精简后的词汇表从原来的 21127 压缩至13584。
new_token_dict, keep_tokens = load_vocab(vocab_path,simplified=True,startswith=['[PAD]', '[UNK]', '[CLS]', '[SEP]'])
tokenizer = BertTokenizer(new_token_dict)
vocab_size = tokenizer.vocab_size
print(vocab_size) # 13584
  1. 那么精简后的词表改变了各个字符对应的token_id,它和原来的预训练网络的embedding映射关系已经无法匹配,因此我们通过得到的keep_tokens,来对模型的word_embedding进行修改,使之与我们新的词表相对应:
def build_model(pretrained_path,config,MAX_LEN,vocab_size,keep_tokens):ids = tf.keras.layers.Input((MAX_LEN,), dtype=tf.int32)token_id = tf.keras.layers.Input((MAX_LEN,), dtype=tf.int32)att = tf.keras.layers.Input((MAX_LEN,MAX_LEN), dtype=tf.int32)config.output_hidden_states = Truebert_model = TFBertModel.from_pretrained(pretrained_path,config=config,from_pt=True)'''通过set_input_embeddings函数,修改word_embedding矩阵'''bert_model.bert.set_input_embeddings(tf.gather(bert_model.bert.embeddings.word_embeddings,keep_tokens))x, _ , hidden_states = bert_model(ids,token_type_ids=token_id,attention_mask=att)layer_1 = hidden_states[-1]word_embeeding = bert_model.bert.embeddings.word_embeddingsembeddding_trans = tf.transpose(word_embeeding)sof_output = tf.matmul(layer_1,embeddding_trans)sof_output = tf.keras.layers.Activation('softmax')(sof_output)output = CrossEntropy(2)([ids,token_id,sof_output])model = tf.keras.models.Model(inputs=[ids,token_id,att],outputs=output)optimizer = tf.keras.optimizers.Adam(learning_rate=1e-5)model.compile(optimizer=optimizer)model.summary()return model


测试结果

  1. 仅仅通过几个小(1000steps)epoch,模型就能生成基本可读的新闻标题
  2. 充分训练后,找了几篇最新的杭州报道进行测试,效果如下:
'''
杭州日报讯 留下来吧,在杭州过个暖心年!
昨日,市人力社保局、市经信局、市总工会等多部门相继发布倡议书,
倡议大家“就地过年”“留杭过年”,非必要不离杭,减少出行聚集,助力疫情防控。
为确保企业生产稳定有序,多部门鼓励企业通过“留岗红包”“过年大礼包”
“特殊津贴”“错峰调休”“领导带头”等积极措施留工稳岗。
同时,倡议大家尽量选择留杭过节、远程拜年,减少跨区域流动,把疫情传播风险降到最低。
针对确需返乡过年的员工,要做好防疫措施,有条件的企业可开展“点对点”送返,
确保员工往返途中和假期安全。
'''
Generate_title: '杭 州 多 部 门 倡 议 留 杭 过 年 [SEP]''''
都市快报讯 目前,北方强冷空气已严重影响杭州。
杭州市气象台1月7日15时31分发布低温橙色预警信号:
“受北方强冷空气影响,预计明天早晨主城区和钱塘新区最低气温-5℃到-7℃,
有严重冰冻,请注意做好防冻保暖工作。
”根据《杭州市抗雪防冻应急预案》,市防指决定从1月7日19时启动抗雪防冻Ⅳ级应急响应。
要求各地各部门按照预案要求,密切关注天气变化,加强监测预警,及时启动响应,
做好防御低温雨雪冰冻灾害的各项工作,确保安全。
'''
Generate_title: '北 方 强 冷 空 气 严 重 影 响 杭 州 [SEP]''''
杭州日报讯 昨日起,大家熟悉的公共自行车小红车使用有了新的变化:
杭州公共自行车推出“扫码租车免押金”服务,
市民游客只要通过信用免押进行实名认证后便可实现免押金租车。
记者从杭州公共自行车公司了解到,通过App Store、应用市场搜索“叮嗒出行”APP,
下载后点击首页“实名认证”,输入姓名、身份证号后,经校验通过,即可进入“免押通道”,
选择“0元免费开通”,同意“信用免押协议”,完成信用免押。
之后,无论是通过“叮嗒出行”APP,还是通过相应微信、支付宝小程序均可直接租用小红车,
不再需要缴纳信用保证金。
'''
Generate_title: '杭 州 公 共 自 行 车 推 出 免 押 金 服 务 [SEP]'

参考资料

[1] Unified Language Model Pre-training for
Natural Language Understanding and Generationh https://arxiv.org/abs/1905.03197
[2] 苏剑林. (Sep. 18, 2019). 《从语言模型到Seq2Seq:Transformer如戏,全靠Mask 》[Blog post]. Retrieved from https://kexue.fm/archives/6933


代码地址

https://github.com/zhengyanzhao1997/TF-NLP-model/blob/main/model/train/Unified%20Language%20Model/tran.py

文本生成(一)【NLP论文复现】Unified Language Model 文本生成从未如此轻松相关推荐

  1. NLP中的语言模型(language model)

    什么是语言模型 本文参考维基百科语言模型 language model 统计语言模型是一个单词序列上的概率分布,对于一个给定长度为m的序列,它可以为整个序列产生一个概率 P(w_1,w_2,-,w_m ...

  2. 文本生成(二)【NLP论文复现】Relative position representations 相对位置编码突破Bert的文本长度限制!

    Relative position representations 相对位置编码突破Bert文本512长度的限制 前言 Self-Attention with Relative Position Re ...

  3. 信息抽取(四)【NLP论文复现】Multi-head Selection和Deep Biaffine Attention在关系抽取中的实现和效果

    Multi-head Selection和Deep Biaffine Attention在关系抽取中的应用 前言 Multi-head Selection 一.Joint entity recogni ...

  4. 语义匹配(一)【NLP论文复现】Sentence-BERT 句子语义匹配模型的tensorflow实现以及训练Trick

    Sentence-BERT 句子语义匹配模型的tensorflow实现以及训练trick 论文模型回顾 建模与训练 模型代码部分 数据处理 训练 模型训练Trick trick1 warm up 代码 ...

  5. 经典论文复现 | 基于标注策略的实体和关系联合抽取

    过去几年发表于各大 AI 顶会论文提出的 400 多种算法中,公开算法代码的仅占 6%,其中三分之一的论文作者分享了测试数据,约 54% 的分享包含"伪代码".这是今年 AAAI ...

  6. 论文盘点:CVPR 2019 - 文本检测专题

    作者丨燕小花 研究方向丨计算机视觉 CRAFT 论文主要思想 本文的主要思路是先检测单个字符(character region score)及字符间的连接关系(affinity score),然后根据 ...

  7. 经典论文复现 | 基于深度卷积网络的图像超分辨率算法

    过去几年发表于各大 AI 顶会论文提出的 400 多种算法中,公开算法代码的仅占 6%,其中三分之一的论文作者分享了测试数据,约 54% 的分享包含"伪代码".这是今年 AAAI ...

  8. NLP——day37 读论文:自然语言处理中的文本表示研究(综述类 2022 软件学报)

    自然语言处理中的文本表示研究 资源下载地址(原论文和笔记) INTRODUCTION chap1文本表示基础 1.1 什么是文本表示? 1.2 为什么进行文本表示 chap2 主流技术和方法 文本的离 ...

  9. 自然语言处理NLP文本分类顶会论文阅读笔记(一)

    笔记目录 关于Transformer 小样本学习 BERT: Pre-training of Deep Bidirectional Transformers for Language Understa ...

最新文章

  1. C#/.NET基于Topshelf创建Windows服务的守护程序作为服务启动的客户端桌面程序不显示UI界面的问题分析和解决方案
  2. docker 容器退出自动删除 一次性运行
  3. 设计模式复习-职责链模式
  4. wampserver一系列问题总结
  5. 数据结构与算法分析(三)——二项队列
  6. QT二次开发Kvaser
  7. 从数字化视角看飞书产品
  8. 桂林理工大学南宁分校php实训,桂林理工大学南宁分校冶金化工虚拟仿真实验教学中心...
  9. Qt中model/view设计模式
  10. Java实现手机发送短信验证码
  11. 概率预测的评估方法简介
  12. 一键部署oracle,一种Oracle单机一键自动部署方法与流程
  13. 计算机专业英语首字母缩略词,计算机专业英语缩略词计算机专业英语缩略词.doc...
  14. 最新百度站长POST网址自动提交工具+附Aardio源代码
  15. 利用weka进行数据挖掘——基于Apriori算法的关联规则挖掘实例
  16. QNX驱动开发——SD卡SD模式开发实录
  17. 串口服务器控制协议,可二次开发的串口服务器——上海卓岚
  18. ubuntu安装mysql ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES
  19. 检测CO一氧化碳传感器故障
  20. iCloud之在app使用CloudKit

热门文章

  1. 源终端匹配电阻的分析及EMC相关问题
  2. 零基础Python完全自学教程11:Python中的选择语句
  3. 阿里云iot--钉钉防盗大炮
  4. 在CentOS8上安装win10虚拟机
  5. 信息系统安全等级保护备案表
  6. bzoj 5191~5193 口胡题解
  7. 【正点原子FPGA连载】第十一章PL SYSMON测量输入模拟电压 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南
  8. 生产环境10分钟黄金时间快速排障:CPU不定时飙高怎么排查?
  9. CH343PT库使用<四> 搜索WCH串口
  10. Max脚本做成工具栏按钮,便捷启动脚本