Memory Networks
正如该方法名:memory,关于神经网络的以及功能LSTM,GRU等方法能够实现一部分记忆功能了,但大量的实验和研究者们都证明了LSTM在更长时间内在处理数据的时间结构方面不够有效,并不能达到记录更多更长的记忆效果。所以对比起来,记忆网络更像是想要尝试通过一些记忆组件和方法来保存一些场景或者状态的信息,以实现更为长期记忆功能(实际上这种大量的先验知识对推理是非常重要的,所以某种程度上来说,memory就是一个知识的外部存储器)。
所以就外部存储器的角度来说,memory需要完成两个操作:1write 2read。
对于write, 书写操作分为擦除和添加两部分,公式如下,w,e,a分别是权重,擦除和添加向量,t是当前时刻,j是写入磁头的位置:
M t j ( i ) = M t − 1 j ( i ) [ 1 − w t j ( i ) e t j ] + M t j ( i ) a t j M_t^j(i)=M_{t-1}^j(i)[1-w^j_t(i)e^j_t]+M_t^j(i)a^j_t Mtj(i)=Mt−1j(i)[1−wtj(i)etj]+Mtj(i)atj
对于read:
训练的损失函数则是采用margin ranking loss,这个与支持向量机的损失函数类似,即是选出最合适中间结果,和得到最好的预测输出。
Multi Memory Layers
如果要建立多层memory,即从如下图的左侧到右侧,大致思路不变,需要注意的是层与层之间可以有多种联系方式,使用门控单元进行记忆选择是很自然的结果:
多层的End-To-End Memory Networks实现代码:
#构建一个memory network
def build_memory(self):
self.global_step = tf.Variable(0, name="global_step")
#如图中的A,B,C三个地方的embedding
self.A = tf.Variable(tf.random_normal([self.nwords, self.edim], stddev=self.init_std))
self.B = tf.Variable(tf.random_normal([self.nwords, self.edim], stddev=self.init_std))
self.C = tf.Variable(tf.random_normal([self.edim, self.edim], stddev=self.init_std))
# Temporal Encoding
self.T_A = tf.Variable(tf.random_normal([self.mem_size, self.edim], stddev=self.init_std))
self.T_B = tf.Variable(tf.random_normal([self.mem_size, self.edim], stddev=self.init_std))
# m_i = sum A_ij * x_ij + T_A_i
Ain_c = tf.nn.embedding_lookup(self.A, self.context)
Ain_t = tf.nn.embedding_lookup(self.T_A, self.time)
Ain = tf.add(Ain_c, Ain_t)
# c_i = sum B_ij * u + T_B_i
Bin_c = tf.nn.embedding_lookup(self.B, self.context)
Bin_t = tf.nn.embedding_lookup(self.T_B, self.time)
Bin = tf.add(Bin_c, Bin_t)
#number of hops
for h in xrange(self.nhop):
self.hid3dim = tf.reshape(self.hid[-1], [-1, 1, self.edim])
Aout = tf.matmul(self.hid3dim, Ain, adjoint_b=True)
Aout2dim = tf.reshape(Aout, [-1, self.mem_size])
#计算权重P
P = tf.nn.softmax(Aout2dim)
probs3dim = tf.reshape(P, [-1, 1, self.mem_size])
Bout = tf.matmul(probs3dim, Bin)
Bout2dim = tf.reshape(Bout, [-1, self.edim])
#层与层之间的处理
Cout = tf.matmul(self.hid[-1], self.C)
Dout = tf.add(Cout, Bout2dim)
self.share_list[0].append(Cout)
#首层,尾层和中间层
if self.lindim == self.edim:
self.hid.append(Dout)
elif self.lindim == 0:
self.hid.append(tf.nn.relu(Dout))
else:
F = tf.slice(Dout, [0, 0], [self.batch_size, self.lindim])
G = tf.slice(Dout, [0, self.lindim], [self.batch_size, self.edim-self.lindim])
K = tf.nn.relu(G)
self.hid.append(tf.concat(axis=1, values=[F, K]))
总之,记忆网络是一个组件形式的模型,每个模型相互对立又相互影响。每个组件没有固定的模型,可以是传统的模型,也可以是神经网络,所以独立的每层可以搭积木的拼接起来完成长时间记忆的任务。
Gates
所谓的门控其实就是对于两个或多个部分,都有一个权重矩阵W来适时分配重要程度,还可以再通过一个激活函数得到。
o u t = σ ( W A + ( 1 − W ) B ) out=\sigma(WA+(1-W)B) out=σ(WA+(1−W)B)同样的,在RNN变体系列中门控单元用的很多,主要起一些信息过滤的作用。门控的好处在于它是可以全自适应的学习方式,即每一时刻节点的权重都由当前的输入决定(如LSTM等的“门”,往往就是当前数据和上一时间点隐藏单元的输入共同决定),从而信息累积的时间尺度也可以随着输入序列而动态改变。通过memory记忆了长时间的信息后,使用门做处理是很自然的事情,所以在前面处理多层之间的融合方法中门控单元很适用。