引言

为什么要使用双向的RNN? 一般的按序列顺序过来的RNN会记录、保存来自前面序列的信息,这些历史信息对当前的输出是很有帮助的。但是有些问题,序列当前位置历史信息和这个位置未来的信息会共同对计算当前位置的输出有帮助,例如在NLP里面的人名识别里面, 如果我们很确信下一个字符是人名的开始,那么当前位置再是人名的开始的概率就会相当的低。

于是,我们就可以就想啊,能不能搞个子双向的RNN来,让模型当前的输出即能使用历史消息又能使用未来的信息。

显然 这是可以的哇,O(∩_∩)O哈

而且Tensorflow里面就提供了这样的接口。

试想一下,如果是我们来实现Bi-RNN,我们应该怎么做呢?既然是Bi-RNN,那么这个东西对于输入sis_{i}si​ 的输出部分就应该有两部分:1. 从前往后到sis_{i}si​的输出FiF_{i}Fi​。2. 从后往前到sis_{i}si​的输出BiB_{i}Bi​。

假设我们手上已经设计好了单向的Single-RNN。一种可行的思路就是,既然我们的已经设计好了单向的RNN,那么给定一个序列S=[s1,s2,s3,…,sn]S=[s_{1},s_{2},s_{3},\dots,s_{n}]S=[s1​,s2​,s3​,…,sn​],调用Single-RNN就可以从s1s_{1}s1​依次往后传,从前向后得到各个sis_{i}si​上的输出FiF_{i}Fi​。

那么从后向前的怎么处理呢?

很简单,哈哈哈,我们把SSS 做一个转置得到ST=[sn,sn−1,…,s2,s1]S^{T}=[{s_{n},s_{n-1},\dots,s_{2},s_{1}}]ST=[sn​,sn−1​,…,s2​,s1​]。如果我们对这个转置跑一波Single-RNN是不是就得到从后往前的各个输出了呢?此时的第一个输出对应sns_{n}sn​的输出,第二输出对应Sn−1S_{n-1}Sn−1​的输出。第i个输出对应的输入是sn−is_{n-i}sn−i​。如果再对这些输出做一个转置,这个输出的第i个分量刚好就是从后往前处理到sis_{i}si​的输出BiB_{i}Bi​了。
此时对于输入的任意一个sis_{i}si​,我们都拿到了它的前向过来的输出FiF_{i}Fi​和后向过来的输出BiB_{i}Bi​。

这样一拍脑袋想感觉好像是可行的哇;不过,我们先来欣赏一些Tensorflow是如何实现Bi-RNN的。

欣赏Tensorflow的Bi-RNN实现

def bidirectional_dynamic_rnn(cell_fw, cell_bw, inputs, sequence_length=None,initial_state_fw=None, initial_state_bw=None,dtype=None, parallel_iterations=None,swap_memory=False, time_major=False, scope=None):if not _like_rnncell(cell_fw):raise TypeError("cell_fw must be an instance of RNNCell")if not _like_rnncell(cell_bw):raise TypeError("cell_bw must be an instance of RNNCell")with vs.variable_scope(scope or "bidirectional_rnn"):# Forward directionwith vs.variable_scope("fw") as fw_scope:output_fw, output_state_fw = dynamic_rnn(cell=cell_fw, inputs=inputs, sequence_length=sequence_length,initial_state=initial_state_fw, dtype=dtype,parallel_iterations=parallel_iterations, swap_memory=swap_memory,time_major=time_major, scope=fw_scope)# Backward directionif not time_major:time_dim = 1batch_dim = 0else:time_dim = 0batch_dim = 1def _reverse(input_, seq_lengths, seq_dim, batch_dim):if seq_lengths is not None:return array_ops.reverse_sequence(input=input_, seq_lengths=seq_lengths,seq_dim=seq_dim, batch_dim=batch_dim)else:return array_ops.reverse(input_, axis=[seq_dim])with vs.variable_scope("bw") as bw_scope:inputs_reverse = _reverse(inputs, seq_lengths=sequence_length,seq_dim=time_dim, batch_dim=batch_dim)tmp, output_state_bw = dynamic_rnn(cell=cell_bw, inputs=inputs_reverse, sequence_length=sequence_length,initial_state=initial_state_bw, dtype=dtype,parallel_iterations=parallel_iterations, swap_memory=swap_memory,time_major=time_major, scope=bw_scope)output_bw = _reverse(tmp, seq_lengths=sequence_length,seq_dim=time_dim, batch_dim=batch_dim)outputs = (output_fw, output_bw)output_states = (output_state_fw, output_state_bw)return (outputs, output_states)

ok,Tensorflow里面的实现就是我们刚刚拍脑袋想的那样子。
首先是对输入数据inputs,调用dynamic_rnn从前往后跑一波,得到output_fw,output_state,fw。

with vs.variable_scope(scope or "bidirectional_rnn"):# Forward directionwith vs.variable_scope("fw") as fw_scope:output_fw, output_state_fw = dynamic_rnn(cell=cell_fw, inputs=inputs, sequence_length=sequence_length,initial_state=initial_state_fw, dtype=dtype,parallel_iterations=parallel_iterations, swap_memory=swap_memory,time_major=time_major, scope=fw_scope)

然后想办法得到从后往前的输出。
先定义一个局部函数:

def _reverse(input_, seq_lengths, seq_dim, batch_dim):if seq_lengths is not None:return array_ops.reverse_sequence(input=input_, seq_lengths=seq_lengths,seq_dim=seq_dim, batch_dim=batch_dim)else:return array_ops.reverse(input_, axis=[seq_dim])

把输入的input_ 按照长度为seq_lengths 调用array_ops.rerverse_sequence 做一次转置。

之后先把inputs转置成inputs_reverse,然后对这个inputs_reverse跑一波dynamic_rnn得到tmp,output_state_bw.

   with vs.variable_scope("bw") as bw_scope:inputs_reverse = _reverse(inputs, seq_lengths=sequence_length,seq_dim=time_dim, batch_dim=batch_dim)tmp, output_state_bw = dynamic_rnn(cell=cell_bw, inputs=inputs_reverse, sequence_length=sequence_length,initial_state=initial_state_bw, dtype=dtype,parallel_iterations=parallel_iterations, swap_memory=swap_memory,time_major=time_major, scope=bw_scope)

再把这个输出tmp反转一下得到Output_bw向量。

  output_bw = _reverse(tmp, seq_lengths=sequence_length,seq_dim=time_dim, batch_dim=batch_dim)

然后把output_fw和output_bw堆叠在一起得到bi-rnn的输出:

  outputs = (output_fw, output_bw)

把隐藏层状态output_state_fw和output_bw堆叠在一起得到bi-rnn的隐藏层状态:

  output_states = (output_state_fw, output_state_bw)

可能有人会有疑问,为啥不把output_state_bw转置一下嘞??其实dynamic_rnn只输出状态的最后一个,

使用biredictional_dynamic_rnn

拿之前的minist手写体代码简单的改一波试一试
源博文:https://blog.csdn.net/jmh1996/article/details/78821216
就简单的把RNN函数的几行改掉就行。

#coding:utf-8
__author__ = 'jmh081701'
import  tensorflow as tf
from  tensorflow.examples.tutorials.mnist import  input_data
from tensorflow.contrib import  rnnmnist=input_data.read_data_sets("./minist/data",one_hot=True)train_rate=0.001
train_step=10000
batch_size=1280
display_step=100frame_size=28
sequence_length=28
hidden_num=100
n_classes=10#定义输入,输出
x=tf.placeholder(dtype=tf.float32,shape=[None,sequence_length*frame_size],name="inputx")
y=tf.placeholder(dtype=tf.float32,shape=[None,n_classes],name="expected_y")
#定义权值
weights=tf.Variable(tf.truncated_normal(shape=[hidden_num,n_classes]))
bias=tf.Variable(tf.zeros(shape=[n_classes]))def RNN(x,weights,bias):x=tf.reshape(x,shape=[-1,sequence_length,frame_size])rnn_cell_fw=tf.nn.rnn_cell.BasicRNNCell(hidden_num)#前向RNNrnn_cell_bw=tf.nn.rnn_cell.BasicRNNCell(hidden_num)#后向RNN# 其实这是一个双向深度RNN网络,对于每一个长度为n的序列[x1,x2,x3,...,xn]的每一个xi,都会在深度方向跑一遍RNN,跑上hidden_num个隐层单元output,states=tf.nn.bidirectional_dynamic_rnn(rnn_cell_fw,rnn_cell_bw,x,dtype=tf.float32)#注意output有两部分:output_fw和output_bw.#我们需要把这两部结合起来,结合的方法可以是把它们堆叠起来;当然也可以加起来···,在这里,我就直接让他们相加了,这方式不是很合理,但是可以反映出来要注意输出有两部分即可。return tf.nn.softmax(tf.matmul(output[0][:,-1,:]+output[1][:,-1,:],weights)+bias,1)
predy=RNN(x,weights,bias)
cost=tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=predy,labels=y))
train=tf.train.AdamOptimizer(train_rate).minimize(cost)correct_pred=tf.equal(tf.argmax(predy,1),tf.argmax(y,1))
accuracy=tf.reduce_mean(tf.to_float(correct_pred))sess=tf.Session()
sess.run(tf.initialize_all_variables())
step=1
testx,testy=mnist.test.next_batch(batch_size)
while step<train_step:batch_x,batch_y=mnist.train.next_batch(batch_size)
#    batch_x=tf.reshape(batch_x,shape=[batch_size,sequence_length,frame_size])_loss,__=sess.run([cost,train],feed_dict={x:batch_x,y:batch_y})if step % display_step ==0:acc,loss=sess.run([accuracy,cost],feed_dict={x:testx,y:testy})print(step,acc,loss)step+=1

训练效果

100 0.740625 1.7244365
200 0.8148438 1.6486847
300 0.83671874 1.6269295
400 0.8484375 1.6133264
500 0.85078126 1.6091878
600 0.84453124 1.6150846
700 0.8570312 1.6035169
800 0.8570312 1.6019013
900 0.865625 1.5946525
1000 0.865625 1.5940073
1100 0.86796874 1.5910122
1200 0.871875 1.5884101
1300 0.875 1.5867693
1400 0.875 1.5838099
1500 0.8703125 1.5896256
1600 0.96171874 1.5016277
1700 0.9640625 1.5011679
1800 0.9671875 1.4953251
1900 0.975 1.4906135
2000 0.9710938 1.4919841
2100 0.978125 1.4868753
2200 0.97265625 1.4891269
2300 0.97578126 1.4865962
2400 0.9734375 1.4897311
2500 0.9734375 1.489102
2600 0.97734374 1.4861523
2700 0.9765625 1.4852589
2800 0.97578126 1.4861305
2900 0.97578126 1.4850746
3000 0.9742187 1.4873846
3100 0.98046875 1.4815388

不咋地这是因为,我们只是简单的把fw和bw的输出加和在一起,正确的做法是把fw,bw拼成一个长向量,然后输给下一层去处理。

多个方向上的rnn

既然双向RNN可以考虑从前向后和从后向前的信息,那么如果是个二维的图像怎么搞呢?此时可以跑四个RNN。从左向右一个,从右向左一个,从上往下一个,从下往上一个。方法就是类似于tensorflow实现bi-dynamic-rnn那样。

BiLSTM+CRF (一)双向RNN 浅谈相关推荐

  1. BiLSTM+CRF(二)命名实体识别

    前言 前一篇博客[https://blog.csdn.net/jmh1996/article/details/83476061 BiLSTM+CRF (一)双向RNN 浅谈 ]里面,我们已经提到了如何 ...

  2. TensorFlow (RNN)深度学习 双向LSTM(BiLSTM)+CRF 实现 sequence labeling 序列标注问题 源码下载...

    http://blog.csdn.net/scotfield_msn/article/details/60339415 在TensorFlow (RNN)深度学习下 双向LSTM(BiLSTM)+CR ...

  3. 浅谈深度学习:了解RNN和构建并预测

    浅谈深度学习:了解RNN和构建并预测 总包含文章: 一个完整的机器学习模型的流程 浅谈深度学习:了解RNN和构建并预测 浅谈深度学习:基于对LSTM项目LSTM Neural Network for ...

  4. 双向LSTM (BiLSTM) (双向RNN)

    为什么用双向 RNN? 单向的 RNN,是根据前面的信息推出后面的,但有时候只看前面的词是不够的, 例如, 我今天不舒服,我打算____一天. 只根据'不舒服',可能推出我打算'去医院','睡觉',' ...

  5. 浅谈嵌套命名实体识别(Nested NER)

    ©PaperWeekly 原创 · 作者|张成蹊 单位|北京大学硕士生 研究方向|自然语言处理 序 命名实体识别(Named Entity Recognition, 下称 NER)任务,主要目的是从一 ...

  6. 浅谈语音助手的对话管理与策略制定

    本篇文章首先梳理了对话系统中的对话管理的原理,包括中控系统的分发.各类bot处理Query的逻辑.候选回复融合和排序的功能,其中也包含了垂直领域知识图谱的构建.最后从PM角度思考,为了提升bot的表现 ...

  7. NLP-基础任务-中文分词算法(3)-基于字:基于序列标注的分词算法【BiLSTM+CRF】

    CRF:条件随机场,一种机器学习技术.给定一组输入随机变量条件下,另一组输出随机变量的条件概率分布模型. 以一组词性标注为例,给定输入X={我,喜欢,学习},那么输出为Y={名词,动词,名词}的概率应 ...

  8. 信息抽取实战:命名实体识别NER【ALBERT+Bi-LSTM模型 vs. ALBERT+Bi-LSTM+CRF模型】(附代码)

    实战:命名实体识别NER 目录 实战:命名实体识别NER 一.命名实体识别(NER) 二.BERT的应用 NLP基本任务 查找相似词语 提取文本中的实体 问答中的实体对齐 三.ALBERT ALBER ...

  9. BiLSTM+CRF

    上面是传统的CRF模型,状态发射概率加上状态转移概率.CRF++就是用模版来设置两个概率特征函数.在BiLSTM+CRF中发射概率是由BiLSTM(或者其他的什么模型)给出的,所以CRF就是一个状态转 ...

最新文章

  1. 直播电商加速合规,引爆消费潜力
  2. 共赴CIO时代,永洪BI如何推动企业数字化转型与创新?
  3. 人工智能助力生命科学新发展 | 飞桨博士会第十一期
  4. 阿里巴巴数据分析沙龙 杭州站圆满召开
  5. 性能调优:理解Set Statistics Time输出
  6. anguarjs 上传图片预览_前端图片上传那些事儿
  7. EL表达式+JSTL,forEach的两种用法
  8. 计算机资源管理老是未响应,Win10资源管理器总是崩溃怎么办?文件资源管理器未响应解决方法...
  9. oracle主键与索引,oracle 主键 \索引
  10. 数分-理论-大数据2-Hadoop
  11. DSPE-PEG-TAT,磷脂-聚乙二醇-靶向穿膜肽TAT,一种磷脂PEG肽
  12. 基于PC的机器视觉系统设计
  13. torch.masked_select()和Tensor.masked_scatter()的用法
  14. 微信三方平台调试过程中遇到的问题
  15. 网上贵金属交易怎么操作?网上贵金属交易策略有哪些?
  16. 关于syslog4j写syslogd服务器的参考代码
  17. 一、Tensor基础
  18. ecshop 邮件模板 html,ecshop邮件模板默认数据与恢复.doc
  19. 中国超级计算机名单100强,中国高性能计算机TOP100榜单揭晓
  20. 精通正则表达式笔记一---正则表达式基础概念[ ],[^ ],.,^,$,\<,\>,|,(),-

热门文章

  1. 用Python制作mini翻译器
  2. 比特未来:区块链技术的最大价值应用
  3. Bean 的生命周期
  4. 球半篮球分析,NBA总决赛:勇士VS凯尔特人
  5. redis集群报错:(error) MOVED 解决方法
  6. 仿网易云音乐app tab栏滑动效果
  7. 微信免资金代金券(V3版)java代码
  8. 汇编语言程序教程:从入门到精通!
  9. 内容付费时代,你愿意为文章付费吗?
  10. 优思学院:六西格玛的热潮