为了写春联,我用 Transformer 训练了一个 “对穿肠”

 

大数据文摘 出品

 

作者:Andy、蒋宝尚

 

今天是腊月二十九,按照黄历,是解除、破屋、坏垣的好日子。

 

中国人的传统也是在这一天贴对联,准确来说是春联。

 

对联来源于对偶句和骈俪句,讲究工整对仗,充分展现了我们中国人独特精致的艺术美感。

 

古人常以“吟诗作对”交友比试,但到了今天, 为了让写出的对联“出奇不意”,文摘菌决定用最近NLP领域大火的Transfomer模型,训练一个聪明的AI, 我出上联,它对下联, 将这个“对穿肠”的任务交给它。

 

模型介绍

 

 

关于对春联这个任务,可以明显看出是一个序列到序列的任务。于是就得祭出现在自然语言处理里面的大杀器了,Seq2Seq + Attention!

 

于是,文摘菌便采用了 Transformer 序列到序列模型。Transformer由论文《Attention is All You Need》提出,渐渐有取代RNN成为NLP中主流模型的趋势,现在更是谷歌云TPU推荐的参考模型,包括谷歌给自己TPU打广告的 Bert 就是Transformer模型。

 

Transformer架构完全舍弃了深度学习里经典的RNN和CNN结构,而只用注意力机制还搭建。最早在机器翻译任务上进行测试就超越 RNN,达到了当时SOTA的效果。Transformer 不光像 CNN 一样克服了 RNN 最被人诟病的训练慢问题,利用self-attention 机制实现快速并行,层数还可以增加到比较深,充分发掘DNN模型的特性,提升模型准确率。同时,在NLP任务上,还比CNN和RNN的性能都普遍要好,它不为王,谁为王。

 

大数据文摘之前与百度NLP联合出品了一篇文章:BERT大火却不懂 Transformer?读这一篇就够了。对Transformer不熟悉的同学可以点击超链接阅读文章,保你理解的透透的。

 

这里我们用6层Transformer单元,里面的Self-Attention的头数为8,隐单元数512个。模型采用 tensorflow 框架,GPU 则用深度学习标配的 Tesla K80。

 

好了,下面先介绍一下数据集。

 

数据集

 

文摘菌所使用的数据集来自一位名为冯重朴_梨味斋散叶的博主的新浪博客,总共包含超过70万副对联。

 

然而,由于未知的原因,这位博主的博客已经无法访问了……

 

 

但是,这一珍贵的数据集却在GitHub上广为流传。

 

github地址(脚本已无法使用,可以用我们上传的couplets,google drive需科学翻墙):

 

https://github.com/wb14123/couplet-dataset

 

google drive:

 

https://drive.google.com/file/d/13cJWWp_ST2Xqt76pEr5ZWa6LVJwGYC4-/view?usp=sharing

 

整个数据集解压过后总共56.9兆,其中训练数据集56.4兆,测试数据集400多KB。另外,数据是以TXT格式储存的。全文不含标点15,153,840字,如果每天看100条的话,20多年都看不完。而文摘菌采用Tesla K80,2个小时就能来一个Epoch。

 

数据预处理

 

首先进行数据预处理,因为拿到的数据比较干净,所以这任务主要就是创建词表。值得一提的是,数据集的压缩文件中存在着一个创建好的词表。你也可以用数据集给出的词表。因为对联任务并不难,所以我们直接使用字粒度来作为输入,也就是像“我们”这样的词,我们是把其当做两个单元进行输入,而不是一个。

 

关键代码如下:

 

defmake_vocab(fpaths, out):
     '''Constructs vocabulary.      Args:        fpaths: Astring. Input file path.        fname: Astring. Output file name.      '''  
     char2cnt = Counter()
     for path in fpaths:
         for line inopen(path, 'r'):
             line = line.strip()
             ifnot line: *# detect the empty line*
                 continue
             chars = line.split()
            char2cnt.update(chars)
     withopen(out, 'w') as fout:
        fout.write("{}\t1000000000\n{}\t1000000000\n{}\t1000000000\n{}\t1000000000\n".format("<PAD>", "<UNK>", "<S>", "</S>")) # special tokens
         for word, cnt in char2cnt.most_common(len(char2cnt)):
            fout.write(u"{}\t{}\n".format(word, cnt))
         print("%d chars written!"% (len(char2cnt)))

 

建立完词表之后,所有单个汉字加上一些特殊字符,总共9126个字符。其实我们看看频率为1的字符就会发现,好像都不认识。一般来说,在序列到序列任务里面,我们会在词表里面忽略频率小不常用的字词。但是因为这里我们词表并不大,所以就全部采用了。

 

 

之后,我们对训练数据的字长进行统计,来决定训练时句子的最大长度。统计后发现,对联数据中单句的长度大部分集中在5-10字之间,最长大概30字左右,所以训练的最大长度文摘菌选的也是30。这个长度并不长,所以之后整个 Transformer 的大小都不是很大。

 

模型编写与训练

 

这里我们首先搭建好模型,之后通过数据将模型中的参数训练成我们所希望的,之后存储起来。这里便是一个epoch存储一个模型。而测试的时候,就可以直接读入参数获得一个训练好的模型来使用。

 

此外,我们也可以将保存的模型分享给别人,让其他人也可以直接利用我们的训练结果。

 

模型和训练的代码太多,这里就全不放出来了,感兴趣的读者可以去github上下载完整代码,也可以在大数据微信公众号后台回复“对联”下载完整代码以及数据集。

 

github地址:

 

https://github.com/andy-yangz/coupletsseq2seqtransformer

 

于是整个模型的训练过程主要分为四个步骤:

 

第一步,先下载对联数据 couplets, 然后解压到 data 里去

 

第二步,调整超参数

 

第三步,运行 prepro.py预处理数据,产生词表,也可以用提供的词表

 

第四步,运行 train.py 训练模型

 

评估

 

训练完之后,保存模型,评估脚本会自动读入最新模型来进行评估。

 

评估的主要步骤包括:

 

建立模型

 

读入训练好的参数

 

读入测试数据

 

将数据喂入模型

 

编码解码获得结果

 

最主要代码应该是解码这一块:

 

### Autoregressive inference
                    preds = np.zeros((hp.batch_size, hp.maxlen), np.int32)
                    for j in range(hp.maxlen):
                        _preds = sess.run(g.preds, {g.x: x, g.y: preds})
                        preds[:, j] = _preds[:, j]

 

最开始喂入x,还有一个空的preds,然后获得一个预测的字,将这个字放在preds相应的位置,之后循环,不断将preds填满。最后就能获得完整的预测结果。

 

这里每次预测的字,取得都是预测概率最大的那个字,这叫做 greedy decoding(贪婪搜寻解码),因为每次都是选最大的。这样虽然快,但是也会出现陷入局部最优解的情况。更好些解码方法,可以使用Beam search(束搜索)。

 

训练结果

 

最后,来看看文摘菌的训练结果如何。

 

先皮一下!

 

话说改革开放四十周年,当然首先要响应一下我们的改革开放。先来段霸榜b站数月,念诗之王的作品。

 

接招,我出上联:

 

上联: 改革春风吹满地,中国人民真争气

 

AI 对:

 

下联: 和谐时雨润千山,小康社稷好生活

 

好湿好湿,充分说明了改革开放带给我们的美好生活。似乎还不错,我们系统的觉悟非常高啊。

 

再来考考它,博大精深的六学知识吧:

 

上联: 文体两开花

 

下联: 武功一代人

 

emmm,比较微妙,还行吧。

 

看到武功一代人,文摘菌突然想到了金庸先生。先生为我们创造了一个庞大的武学世界,为了纪念先生,就以下面两句为上联吧:

 

上联:飞雪连天射白鹿

 

下联:落花遍地戏黄蜂

 

嗯,确实有点世外桃源桃花岛的味道。

 

上联:笑书神侠倚碧鸳

 

下联:闲看仙女下红尘

 

这就有意思了,金庸武侠世界里的女子确实都是洒脱般的人物,尤其是小龙女和它的后辈黄衫女子活脱脱的一尘不染。

 

上联: 盘他

 

下联: 还我

 

好像有点都对不上了。

 

再来考考我们机器人2018年热点。

 

上联: 小猪佩奇社会人

 

下联: 大圣传世文明家

 

居然对上了。

 

再来,今年最火剧。

 

上联: 延禧攻略

 

下联: 棣华家声

 

莫非 AI 在暗示我们下一个会火的电视剧吗,根据名字,似乎是讲一个家族中两兄弟的故事,有意思有意思。

 

再看看,综艺节目:

 

上联: 创造一零一

 

下联: 和谐万事兴

 

嗯,嗯,非常和谐。

 

上联:锦鲤

 

下联:银蛇

 

普普通通。

 

再看看美国领导人们。

 

上联:特朗普

 

下联:小康庄

 

上联:奥巴马

 

下联:罗汉松

 

没想到虽然都身为世界领导人,对出来的东西都这幺朴素。

 

好,那在看看贸易战:

 

上联:中美贸易战

 

下联:大中华国风

 

对的好,对的好,思想很正确。

 

好了不玩了。再来看几个正经点的对联吧。

 

我们从测试集里面挑几个,这里上联和下联是正确的,模型是机器人给出的下联:

 

上联: 腾飞上铁,锐意改革谋发展,勇当千里马
下联: 和谐南供,安全送电保畅通,争做领头羊
模型: 奋进开篇,激情发展促和谐,更上一层楼

 

还有:

 

上联: 一句相思吟岁月
下联: 千杯美酒醉风情
模型: 几杯寂寞醉春秋

 

看上去对的还算挺工整。

 

当然我们也看到了一些问题。比如说模型学到的都是一些和宽泛的对应,名词动词相对应做到了,也能保证通顺,但是却在一些需要典故和外界知识的例子中,无法做出合适的回应。有时就会出现,不知道“上铁”和“南供”这俩典故,或者出现所谓无情对,上下联字词对仗工整但是意思毫不相关。

 

这是我们训练了6个epoch,也就是模型将数据看了六遍的结果。这次我们总共保存了7个模型,从Epoch1到Epoch7模型,其实要说那个好只能是见仁见智了,比如说我就觉得6个Epoch会好些。但是可以确认的是随着Epoch增加模型就越能学到对联数据里的模式。

 

是不是还挺好玩,感兴趣的同学,也跟我们一起训练一个自己的“对穿肠”吧。

 

其他模型

 

关于AI写对联,其实之前已经有了基于深度学习seq2seq的模型,同样其用到的也是TensorFlow和那位冯重朴_梨味斋散叶的博主提供的数据集。

 

目前,代码已经开源,你可以在下面的github中找到:

 

 

https://github.com/wb14123/seq2seq-couplet

 

当然,除了模型代码之外,你也可以打开作者给出的网址,随意的出对子。

 

 

网址:https://ai.binwang.me/couplet/

 

对了,上面那个AI创作者名叫王斌,英国莱斯特大学读计算机硕士,现在是一名软件工程师。

 

微软对联与百度对联

 

除了单打独斗的程序员,尝试了对联AI的科技巨头也不少,文摘菌这里介绍两个最有名的项目,不想自己训练模型的同学,可以直接取用。

 

比较早出现的对联AI来自微软亚洲研究院,自然语言计算组研究开发,沈向洋主导的研究项目。

 

微软的对联AI要谨慎且复杂一点,如果以“改革春风吹满地”作为上联,它也会给你许多个下联作为备选。

 

 

最令人感动的是,如果你不喜欢,还可以换一种方式!

 

为了让对联更加对仗工整,你可以选择分词的方式。如果你仍然不满意,可以点击反馈意见按钮告诉它“家长”。最后,还能生成横批。

 

 

除了微软这个老前辈,还有一个江湖新秀,那就是今年百度联合央视网、网易推出的智能春联。据说,他能根据每个人的五官特性生成专属于你的春联。

 

嗯,也就是刷脸写对联。

 

文摘菌用李彦宏的大头像试了试,可以感受一下这魔幻的过程:

 

 

微软对联:

 

http://duilian.msra.cn/

 

百度对联(用手机打开哟):

 

https://chunlian.news.cntv.cn/

 

好了,文摘菌要去贴春联啦, 就到这里吧 。

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注