RNN(0)从文本生成到Attention机制

本篇文章参考《Packt.Deep.Learning.with.Keras》 电子版网上一搜就能找到,主要关注于使用RNN进行文本相关处理的流程。

实现效果

以《爱丽丝梦游仙境》的英文版为例,给出i am alice的开头,网络能实现续写故事的效果,如下:

i am alice , and the project gutenberg-tm electronic work in a low the white rabbit again the gryphon as she co

不过毕竟只是个字符级的生成Demo,效果一般,我们会在后续讨论如何改进效果。

预处理

文本读取

首先从http://www.gutenberg.org/下载alice_in_wonderland.txt,读出所有文本,消除空行

with open('alice_in_wonderland.txt','r') as fin:
    lines=[]
    for line in fin:
        line=line.strip().lower()
        #空行
        if(len(line)==0):
            continue
        lines.append(line)
text=" ".join(lines)

字符编码

这一步是为了把字符转换成网络能看懂的形式

#所有字符的集合
chars=set(c for c in text)
nb_chars=len(chars)
#构建映射表
char2index=dict((c,i) for i,c in enumerate(chars))
index2char=dict((i,c) for i,c in enumerate(chars))

训练集生成

我们文本生成的方式是通过前10个字符生成第11个,然后以第2个到第11个字符再来生成第12个,以此往复。所以训练集的X就是原小说中的10个字符,Y就是第11个。代码如下

SEQLEN=10
STEP=1
input_chars=[]
label_chars=[]
for i in range(0,len(text)-SEQLEN,STEP):
    input_chars.append(text[i:i+SEQLEN])
    label_chars.append(text[i+SEQLEN])

打印一下看下效果

for i in range(5):
    print(input_chars[i],"->",label_chars[i])
 anyone an -> y
anyone any -> w
nyone anyw -> h
yone anywh -> e
one anywhe -> r

字符串有了,再用上面生成的映射表转为one-hot编码。以26个字母为例,X的形状即为(data_size,10,26),其中的data_size为有多少条X数据,10指每条数据由10个字符组成,26为one-hot编码长度。
这里是手工进行one-hot编码,采用keras.utils.to_categorical也是可以的。

import numpy as np
# 转Index
# Input:(batch_size=len(input_chars),dim1=SEQLEN, dim2=nb_chars)
X=np.zeros((len(input_chars),SEQLEN,nb_chars),dtype=np.bool)
y=np.zeros((len(label_chars),nb_chars),dtype=np.bool)
for i, input_char in enumerate(input_chars):
    for j,ch in enumerate(input_char):
        X[i,j,char2index[ch]]=1
    y[i,char2index[label_chars[i]]]=1

网络构建

书上写的是用字符串的形式去描述误差,比如loss='categorical_crossentropy',不过这样手打容易出错,如果是Jupyter notebook环境的话,可以从keras.losses里面import一下,也是一样的效果。

from keras import Sequential
from keras.layers import SimpleRNN,Dense,Activation
from keras.activations import softmax
from keras.losses import categorical_crossentropy
from keras.optimizers import RMSprop
HIDDEN_SIZE=128
BATCH_SIZE=128
NUM_ITERATIONS=25
NUM_EPOCHS_PER_ITERATION=1
NUM_PREDS_PER_EPOCH=100
model=Sequential()
model.add(SimpleRNN(HIDDEN_SIZE,return_sequences=False,
                   input_shape=(SEQLEN,nb_chars),
          unroll=True))
model.add(Dense(nb_chars))
model.add(Activation(softmax))
#这里的RMSprop优化器需要构造,所以要加上括号
model.compile(loss=categorical_crossentropy,optimizer=RMSprop())

网络结构如下。需要注意书上对RNN进行讲解的时候会把RNN按时间展开


这里的一个圆圈表示RNN的一层(而不只是一个Cell),包含HIDDEN_SIZE个RNN Cell(可以理解成抽取HIDDEN_SIZE个特征)

_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
simple_rnn_1 (SimpleRNN)     (None, 128)               24192     
_________________________________________________________________
dense_1 (Dense)              (None, 60)                7740      
_________________________________________________________________
activation_1 (Activation)    (None, 60)                0         
=================================================================

文本生成

for iteration in range(NUM_ITERATIONS):
    print("_"*50)
    print("Iteratipn #:%d"%iteration)
    model.fit(X,y,batch_size=BATCH_SIZE,epochs=NUM_EPOCHS_PER_ITERATION)
    next_chars="i am alice"
    for i in range(NUM_PREDS_PER_EPOCH):
        Xtest=np.zeros((1,SEQLEN,nb_chars),dtype=np.bool)
        index_list=[char2index[c] for c in next_chars]
        for i,ci in enumerate(index_list):
            Xtest[0][i][ci]=True
        pred=model.predict(Xtest)
        pred_char=index2char[np.argmax(pred)]
        print(pred_char,end="")
        next_chars=next_chars[1:]+pred_char

这里预测出来的字符直接取了概率最大的,但如果概率是0.51,0.49这样,对0.49就不是很公平,也可以采用如下的sample函数,按概率取出字符以增加文本的多样性。

最后几次迭代效果如下:

Iteratipn #:22
Epoch 1/1
161794/161794 [==============================] - 8s 49us/step - loss: 1.4239
, and the project gutenberg-tm electronic work in a low the white rabbit again the gryphon as she co__________________________________________________
Iteratipn #:23
Epoch 1/1
161794/161794 [==============================] - 8s 51us/step - loss: 1.4177
 was a little to see while the this agreement in the door and she was a little to see while the this__________________________________________________
Iteratipn #:24
Epoch 1/1
161794/161794 [==============================] - 8s 50us/step - loss: 1.4101
 was a little course were and donations to the queen was a little course were and donations to the q

改进与思考

首先网络从前几个字符预测下一个字符比较类似n-gram语言模型,不过由于是按照字符级别构造的,生成效果当然没有按照单词自然,下篇文章我们采用LSTM来对句子进行单词级别的处理,用于分析情感;另外从结果中可以看到,可能是因为原文中下划线_经常连续出现,导致最后生成的文本也是连续的下划线,可以在一开始的文本处理中选择剔除掉除字母之外的字符。
应用角度上来看,除了文本的预测,还可以是语音的生成,图像的补全(PixelRNN)等等。
RNN倒是能够简单地模拟大脑对时间序列的记忆模式,但是对记忆比较久远的事情就无能为力了,但是人类却可以做到对很久远的、例如童年时期事情的回忆,这样的模式如何在网络上复现?这样的记忆似乎可以看成是重要程度(对这个人人生观价值观的冲击大小)的影响,对于网络如何衡量?有点像Attention机制。再有就是人类看到一个单词往往会联想到其他与之相关的单词,这些单词组成了一张知识网络,只需要记住其中一个知识就能推导出其他知识,这样的联想相似性倒是可以由Word2Vec推导出来,在网络上可以怎样应用呢?图像可以根据特征相似度提取出相似的图片,例如从一个圆就可以联想到篮球、太阳、胖头鱼什么的,这样的联想对人类来说有什么用可能就是网络应用这些机制所需要思考的。
对RNN来说,每次计算除开X之外还加上了H(t-1),将各个时间点都联系在了一起。人类的记忆似乎也是这样,仅仅从大脑的上一个状态转移到当前状态就行、然而RNN模式对于大脑来说相当于把每个时刻的信息都储存到同一个神经元里面然后再更新,而真实情况应该是不同时间的信息存储在不同细胞里面,然后互相调用。从这个角度上来说感觉Wavenet的模式更像一些。(虽然调用的时候是固定的几个信息结合在一起,但是更深的层能够决定是哪些记忆需要被提取出来)

wavenet

关于记忆还可以搜索Memory Network,网络起到的作用相当于CPU,外部的储存器相当于RAM,CPU起到找出RAM地址的作用。详细信息可以参考李宏毅课程的Attention-based Model。


以阅读理解为例,为了回答一个问题Query,我们需要从文章中找出与这个问题相关的回答句子。下图中的q就是我们要查询的RAM的“地址”,也可以说这个q是对文章中句子的编码组合,通过这个组合找到哪些句子是相关的。假如把文章中的每个句子都用x来表示,a就是每个句子的重要程度了,也就是Attention。

Attention机制

Ref: Deep Learning基础--理解LSTM/RNN中的Attention机制
目前采用编码器-解码器 (Encode-Decode) 结构的模型非常热门,是因为它在许多领域较其他的传统模型方法都取得了更好的结果。这种结构的模型通常将输入序列编码成一个固定长度的向量表示,对于长度较短的输入序列而言,该模型能够学习出对应合理的向量表示。然而,这种模型存在的问题在于:当输入序列非常长时,模型难以学到合理的向量表示。
类似于人类的记忆,一大堆记忆涌入的时候是记不住的,只会记住关键的一些信息,Attention机制做的就是找出哪些记忆是需要重点关注的。
“在文本翻译任务上,使用attention机制的模型每生成一个词时都会在输入序列中找出一个与之最相关的词集合。之后模型根据当前的上下文向量 (context vectors) 和所有之前生成出的词来预测下一个目标词。
… 它将输入序列转化为一堆向量的序列并自适应地从中选择一个子集来解码出目标翻译文本。这感觉上像是用于文本翻译的神经网络模型需要“压缩”输入文本中的所有信息为一个固定长度的向量,不论输入文本的长短。”
也就是说,它不再使用固定长度的Encode Vector(上下文向量c)作为记忆,而使用整个Input序列,Memory不再是被编码的形态,而是类似于人脑多个神经元的形态。每个被翻译出来的单词yi对于Input序列来说都有一个独立的需要关注的上下文向量ci,相当于从Memory中找到了和文本翻译最相关的Memory出来(对齐)作为结果。
举个例子,在翻译出machine的时候不再使用整个机器学习的被压缩后的上下文向量c,而是使用其独有的c0,即重点关注机器这两个字的上下文向量。


除开文字到文字以外,图像到文字也有相同原理的论文。

这样的Attention有什么用处呢?我们将在后续文章的Encoder-Decoder机制中见到他的威力。

最后编辑于
?著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,100评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,308评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,718评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,275评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,376评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,454评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,464评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,248评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,686评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,974评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,150评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,817评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,484评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,140评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,374评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,012评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,041评论 2 351

推荐阅读更多精彩内容