基于CNN和VAE的作诗机器人:随机成诗
By 苏剑林 | 2018-03-24 | 124806位读者 |前几日写了一篇VAE的通俗解读,也得到了一些读者的认可。然而,你是否厌倦了每次介绍都只有一个MNIST级别的demo?不要急,这就给大家带来一个更经典的VAE玩具:机器人作诗。
为什么说“更经典”呢?前一篇文章我们说过用VAE生成的图像相比GAN生成的图像会偏模糊,也就是在图像这一“仗”上,VAE是劣势。然而,在文本生成这一块上,VAE却漂亮地胜出了。这是因为GAN希望把判别器(度量)也直接训练出来,然而对于文本来说,这个度量很可能是离散的、不可导的,因此纯GAN就很难训练了。而VAE中没有这个步骤,它是通过重构输入来完成的,这个重构过程对于图像还是文本都可以进行。所以,文本生成这件事情,对于VAE来说它就跟图像生成一样,都是一个基本的、直接的应用;对于(目前的)GAN来说,却是艰难的象征,是它挥之不去的“心病”。
嗯,古有曹植七步作诗,今有VAE随机成诗,让我们开始吧~
模型 #
对于很多人来说,诗是一个很美妙的玩意,美妙之处在于大多数人都不真正懂得诗,但大家对诗的模样又有一知半解的认识。因此,只要生成的“诗”稍微像模像样一点,我们通常都会认为机器人可以作诗了。因此,所谓作诗机器人,是一个纯粹的玩具了,能作几句诗,也不意味着普通语言的生成能力有多好,也不意味着我们对NLP的理解有多深。
CNN + VAE #
就本文的玩具而言,其实是一个比较简单的模型,主要是把一维CNN和VAE结合了起来。因为生成的诗长度是固定的,所以不管是encoder还是decoder,我都只是用了纯CNN来做。模型的结构图大概是:
具体来说,是先将每个字embedding为向量,然后用层叠CNN来做编码,接着池化得到一个encoder的结果,根据这个结果生成计算均值和方差,然后生成正态分布并重新采样。在解码截断,由于现在只有一个encoder的输出结果,而最后要输出多个字,所以先接了多个不同的全连接层,得到多样的输出,然后再接着全连接层。
GCNN #
这里的CNN不是普通的CNN+ReLU,而是facebook提出的GCNN,其实就是做两个不同的、外形一样的CNN,一个不加激活函数,一个用sigmoid激活,然后把结果乘起来。这样一来sigmoid那部分就相当于起到了一个“门(gate)”的作用。
我第一次看到GCNN是在论文《Language Modeling with Gated Convolutional Networks》中,然后在《Convolutional Sequence to Sequence Learning》中再次看到了。在笔者的《分享一个slide:花式自然语言处理》也有简单介绍。
经过实测,在很多NLP任务上,GCNN比普通的CNN+ReLU明显要好。
实验 #
实验还是基于Python2.7和Keras(Tensorflow后端)完成~
代码 #
有了前述讨论,并且结合Keras自带的VAE例子,其实把整个模型实现就不困难了。由于只为了演示,这里仅选了最简单的五言诗,而且也不要求生成完整的一首诗,只要生成一句(10个字),这样其实更像对对联了。
代码:https://github.com/bojone/vae/blob/master/vae_shi.py
训练语料用的是全唐诗,也已经放在github上面了。模型并没有经过充分的调参,这留给有兴趣的读者慢慢折腾和增强了。
训练 #
为了观察训练过程中生成的诗句变化,所以写了个evaluator,效果如图:
可以看到,随着训练次数的增加,诗句质量确实在提高。
测试 #
下面是训练好的模型随机生成的一些诗句,严格来看并不怎么样,这毕竟只是随机数到诗句的映射,随机出诗罢了~可以看得出,这些“诗句”在对仗和平仄方面还是像模像样的。
出上无花客,相来一日时。
从瞻大车策,萧盖偃车矛。
今见青衣去,萧凉白叶风。
帝城今不战,征罪在天兵。
鹤仰临山里,逶留出太回。
画关斜过水,残色迥过杨。
上道皆有战,四门不如兵。
涧烟含雨沥,风影动风风。
登回一落景,一处更相期。
芳酒不无醉,长楼酒更春。
天月满云管,楚女长南闻。
明今今有矣,不得道无生。
朝明开绿菊,香影下红枝。
世外相扁处,逍遥无旧目。
唯闻含玉色,讵见月光光。
春风将乐节,风服未谁娱。
万年君何在,今年酒未新。
回辔参相召,乘歌会使程。
今有千人醉,不然一子心。
今风泛云会,清日白衣新。注:演示模型只是生成单句诗,因此这些诗句的行与行之间并没有关联。
最后 #
本文的实验并不是为了生出多高质量的诗,而是做一个基于VAE的文本生成的演示。网上也有类似的作诗机器人了,不过多数流行的作诗机器人是基于“RNN + 语言模型”的思路的,一般需要一些种子词输入才可以完成作诗。而VAE能真正实现把随机数映射到诗句的过程~
转载到请包括本文地址:https://spaces.ac.cn/archives/5332
更详细的转载事宜请参考:《科学空间FAQ》
如果您还有什么疑惑或建议,欢迎在下方评论区继续讨论。
如果您觉得本文还不错,欢迎分享/打赏本文。打赏并非要从中获得收益,而是希望知道科学空间获得了多少读者的真心关注。当然,如果你无视它,也不会影响你的阅读。再次表示欢迎和感谢!
如果您需要引用本文,请参考:
苏剑林. (Mar. 24, 2018). 《基于CNN和VAE的作诗机器人:随机成诗 》[Blog post]. Retrieved from https://spaces.ac.cn/archives/5332
@online{kexuefm-5332,
title={基于CNN和VAE的作诗机器人:随机成诗},
author={苏剑林},
year={2018},
month={Mar},
url={\url{https://spaces.ac.cn/archives/5332}},
}
March 22nd, 2019
苏老师,我用了vae来进行评论生成,限定序列长度为200,用的重构损失函数是mse,损失函数值一直停留在几百万,这个结果是不是有问题?如果用本文的做法,接多个全连接层然后softmax能行吗?
说错了,是做评论的特征提取==
文本怎么用mse?一般就只能用softmax交叉熵呀
感谢指点~
September 18th, 2019
苏神,我使用pytorch复现这个项目,反复对照您的代码,应该是没错的。。但是KLloss瞬间降到0,重构的损失始终降不下来。。您做这个项目的时候有出现这样的情况吗。
我的没有。
我也用pytorch复现了一遍,一开始遇到了和你类似的问题。最后发现,pytorch的CrossEntropyLoss在默认参数下的计算方式上与苏神版本是不一致的。最终改成如下方式解决了这个问题,可供参考:
loss_fn = nn.CrossEntropyLoss(reduction='sum')
...
recon_loss = loss_fn(logits, targets) / batch_size
可以分享一下代码吗,感谢
December 1st, 2020
这个可能是有梯度爆炸的情况