这篇文章的作者通过丰富词汇来优化生成效果。作者把生成过程拆成了三个部分,每一部分都做了一点点改动。先看一下他们是怎么写故事的。
首先,通过SRL
工具,把故事转换成一种结构化的数据。在这里,同一个实体都用一种占位符表示,比如上图中,ent0就是我,ent1是爪子,ent1是头。(其实这里我有点不明白,因为ent2后续被填成我的头
,有一个所属词我,而ent1有锋利的爪子
和他们
即爪子的代词,所以这个“我”到底是一个实体呢,还是其他实体的一部分呢?)
然后这个故事中相关的实体都会被占位符替换,
最后再来填空。
数据
提示词是人写好的,数据集用的是WRITINGPROMPTS
,每个故事大概734个字。实验使用的故事字数被限制在1000以内,提示词的词典大小19025,故事的词典大小104960.
一、结构化故事数据
这一步是数据预处理,根据输入的文本,输出其中谓词和参数序列,从本质上确定“谁对谁做了什么”,“何时”和“在哪里”。
感觉像是一个序列标注。通过预训练模型标出句子中的谓词和参数。但是作者在这里又进行了改进,将解码器的multi-head中的一只,对上一个生成的动词进行attention。这样一来,生成的动词种类更丰富,也避免了同一个词语在句子中反复出现的问题。
二、实体建模
像地名、人名这种词频较低的词语,想指望语言模型的字粒度生成难度较大,但是我们从实体这个词粒度去解决这个问题就会好很多。可以理解为我们先生成槽位,再想办法将这些槽位填好,填的既准确又丰富。
这样问题就拆成了两步,抠槽位和填槽位。
2.1 抠槽位
采用的是类似阅读理解的方式去做,将故事中同一个实体都用相同的占位符去表示。
这里具体提了两种:
-
使用NER模型识别人名、地名、机构名,把相同的名称用同一个占位符表示。
-
当同一个实体有多种不同的字符串表示时,就不能使用上述NER进行槽位替换了。所以这时会使用一种基于指代的识别模型这种方式会把相同语义表示的实体进行聚类,然后再用同一个占位符进行替换。但使用这个方法需要注意的是,如果一个实体只有一种表示(即这个故事就出现了一次)那么这个没有指代关系的实体需要被一个独特唯一的占位符来表示。
2.2 填槽位
怎么把同一个实体用不同的方式表述出来呢,是“我”、“我的”还是“那个女孩”?这里就又要用到模型了,这里称为sub-word seq2seq
,因为我们是用一个seq2seq模型来生成这些表述文本的,并且在解码器端加了pointer-copy
机制,这样就会生成一个全新的指代词,或者使用实体之前的名称。
这里我大概理解pointer-copy机制,但是并不是很懂原文,内容如下:
To generate an entity reference, the decoder can either generate a new abstract entity token or choose to copy an already generated abstract entity token, which encourages the model to use consistent
naming for the entities.
作者还在整个模型前加了一些输入。原文如下:
A bag-of-words context window around the
specific entity mention, which allows local
context to determine if an entity should be a
name, pronoun or nominal reference.
Previously generated references for the same
entity placeholder.
下面这个图是我理解中的作者的模型结构,图下面部分是我理解的附加输入信息,上面的则是原始的pointer-generator模型。
2.2.1 word-level
我们说使用的是sub-word seq2seq
,那么什么是sub-word
呢?这个想法最初也是从机器翻译研究中来的,所以我们先回顾一下word-level
翻译模型的方法。
这里我参考了这篇博客,其中介绍说,对于word-level
翻译模型,通常使用back-off dictionary
来处理OOV
词汇,比如将source
和target
两两对应起来,使用OOV
来表示,这样在翻译结果中出现 OOV
时,就用source
所对应的target
来代替。
但是这样做是建立在source-target
中的词总是能一一对应的前提下,因为语言之间的形态合成程度不同,这种假设常常不成立;其次word-level
翻译模型不能生成模型没见过的词(不在词表中),对于这种情况,有论文提出直接从copy unknown
词到target
words中,但是这种处理策略也仅仅限于一些实体名称类的词汇;同时为了节省计算时间和资源,词表大小通常被限制在30k-50k之间,所以词表空间是比较昂贵的,如果一些词义类似的词放到词表中,例如like,liked,liking
等形态上类似的词均放到词表中,直观上感觉有些浪费。
2.2.2 sub-word
在实际翻译时,并不一定都是以word
为基本单位进行翻译,可通过比单词更小的单位sub-word
进行翻译,例如复合词(形态上类似的词,前缀后缀相同,如run、runer、running等,可通过合成(run与er合成等)翻译)、同源词和外来词(通过subword语音和形态转换)论文中提到,从德语数据集中分析,在100个稀有词(不在出现最频繁的5000个词中)中,大多数的词可以通过更小的subword units进行翻译。
那么,如何将word切分成合适的sub-word
呢?论文中提出了采用Byte pair encoding(BPE)
压缩算法,首先以字符划分,然后再合并。也就是不断的以出现频次最高的bi-gram
进行合并操做,直到达到词表大小为止。这种以频次合并是比较符合常识的,例如像’er’,'ing,'ed’这样比较有意义的后缀,'e’和’r’同时出现的频次应该比较多,“ing”和“ed”类似道理。
机器翻译模型通常都会用到注意力机制,在 word-level
模型中,模型每次只能计算在word
级别上的注意力,我们希望模型可以在每一步学习中将注意力放在不同的sub-word
上,显然这样更有意义和效率。
2.2.3 character-level
字符粒度,这种seq2seq模型输出的是根据对所有字符的概率分布得到的结果。一般使用字典来映射词汇char和索引index
baseline
作者将这个模型和2018年的fusion模型做了对比(其实这俩模型都是这个作者的),然后又进行了其他实验:
- 摘要生成:提出了新的baseline,即根据提示词生成摘要,然后根据摘要生成一个故事。???
- 关键词抽取:根据提示词生成关键词,然后根据关键词生成一个故事。
- 句子压缩:根据提示词进行句子压缩,再根据压缩的句子生成故事。
三、评价方法
3.1 自动评价方法
我们先回顾一下问题是怎么拆解的。我们将故事 x x x转换成一种更抽象的表述方式 z z z,所以目标函数变为:
L = − log ∑ z p ( x ∣ z ) p ( z ) \mathcal{L}=-\log \sum_{z} p(x | z) p(z) L=−logz∑p(x∣z)p(z)
但是对
z
z
z进行marginalization
操作很难,特别是我们把所有实体都用一种占位符表示。所以这里我们通过构造一个后验确定deterministic posterior
对损失函数的上界进行优化。
z ∗ = arg max z p ( z ∣ x ) L ≤ − log p ( x ∣ z ∗ ) − log p ( z ∗ ) \begin{aligned} z^{*} &=\arg \max _{z} p(z | x) \\ \mathcal{L} & \leq-\log p\left(x | z^{*}\right)-\log p\left(z^{*}\right) \end{aligned} z∗L=argzmaxp(z∣x)≤−logp(x∣z∗)−logp(z∗)
这种方法可以让模型 p ( z ( ∗ ) ) p(z^(*)) p(z(∗))和 p ( x ∣ z ∗ ) p(x|z^{*}) p(x∣z∗)可以分开而且较为容易的训练。
使用对比结果如下:
可以看到通过生成SRL结构的模型log损失值最小,表明比生成摘要、关键词或者句子压缩更容易,因为他更好的利用了结构化的数格式的优势。
和原来的故事相比,生成的故事怎么样呢?我们通过LongestCommonSubsequence(LCS)
来衡量,主要是最多LSC和平均LSC。LSC值越高证明照搬原句的越多,生成量越少。下图显示本模型的生成能力更好一些。
3.2 人工评价
这里对比的是2018年的fusion模型(下图上面部分)和本模型(下图下面部分),在相同提示词下的生成结果。由标注人员决定哪篇更好(只给故事,不给提示词)。