43

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

 5 years ago
source link: https://mp.weixin.qq.com/s/N6sSQ3EG_NPFaOGwkC0i6w?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

m636Nnu.jpg!web

大数据文摘 出品

作者:Andy、蒋宝尚

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

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

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

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

模型介绍

YvAVjuN.jpg!web

关于对春联这个任务,可以明显看出是一个序列到序列的任务。于是就得祭出现在自然语言处理里面的大杀器了,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万副对联。

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

baY3AbM.jpg!web

但是,这一珍贵的数据集却在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的字符就会发现,好像都不认识。一般来说,在序列到序列任务里面,我们会在词表里面忽略频率小不常用的字词。但是因为这里我们词表并不大,所以就全部采用了。

UJBFfun.png!web

之后,我们对训练数据的字长进行统计,来决定训练时句子的最大长度。统计后发现,对联数据中单句的长度大部分集中在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中找到:

VV73AfM.png!web

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

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

Z73Izmi.jpg!web

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

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

微软对联与百度对联

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

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

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

byqUbyZ.jpg!web

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

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

nqqua2q.jpg!web

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

嗯,也就是刷脸写对联。

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

NzQ77bM.gifVb2E3mq.gif

微软对联:

http://duilian.msra.cn/

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

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

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

志愿者介绍

后台回复 志愿者 ”加入我们

eMvuUjE.jpg!web

听说点了「好看」的人都变好看了哦


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK