Datawhale《深度学习-NLP》Task8- 循环神经网络

Datawhale《深度学习-NLP》Task8- 循环神经网络

Datawhale《深度学习-NLP》Task8- 循环神经网络

参考 一份详细的LSTM和GRU图解:一份详细的LSTM和GRU图解 -ATYUN

Tensorflow实战(1): 实现深层循环神经网络:Tensorflow实战(1): 实现深层循环神经网络 - 知乎

lstm:从LSTM到Seq2Seq-大数据算法

RCNN kreas:GitHub - airalcorn2/Recurrent-Convolutional-Neural...

RCNN tf:GitHub - zhangfazhan/TextRCNN: TextRCNN 文本分类

RCNN tf (推荐):GitHub - roomylee/rcnn-text-classification: Tensor...

 

RNN介绍

 

1.RNN怎么来的?

循环神经网络的应用场景比较多,比如暂时能写论文,写程序,写诗,但是,(总是会有但是的),但是他们现在还不能正常使用,学习出来的东西没有逻辑,所以要想真正让它更有用,路还很远。

这是一般的神经网络应该有的结构: 

Datawhale《深度学习-NLP》Task8- 循环神经网络

既然我们已经有了人工神经网络和卷积神经网络,为什么还要循环神经网络? 
原因很简单,无论是卷积神经网络,还是人工神经网络,他们的前提假设都是:元素之间是相互独立的,输入与输出也是独立的,比如猫和狗。 
但现实世界中,很多元素都是相互连接的,比如股票随时间的变化,一个人说了:我喜欢旅游,其中最喜欢的地方是云南,以后有机会一定要去

.这里填空,人应该都知道是填“云南“。因为我们是根据上下文的内容推断出来的,但机会要做到这一步就相当得难了。因此,就有了现在的循环神经网络,他的本质是:像人一样拥有记忆的能力。因此,他的输出就依赖于当前的输入和记忆。

2.RNN的网络结构及原理

它的网络结构如下: 

Datawhale《深度学习-NLP》Task8- 循环神经网络

其中每个圆圈可以看作是一个单元,而且每个单元做的事情也是一样的,因此可以折叠呈左半图的样子。用一句话解释RNN,就是一个单元结构重复使用

RNN是一个序列到序列的模型,假设

是一个输入:“我是中国“,那么就应该对应”是”,”中国”这两个,预测下一个词最有可能是什么?就是

应该是”人”的概率比较大。

因此,我们可以做这样的定义:

。因为我们当前时刻的输出是由记忆和当前时刻的输出决定的,就像你现在大四,你的知识是由大四学到的知识(当前输入)和大三以及大三以前学到的东西的(记忆)的结合,RNN在这点上也类似,神经网络最擅长做的就是通过一系列参数把很多内容整合到一起,然后学习这个参数,因此就定义了RNN的基础:

大家可能会很好奇,为什么还要加一个函数,其实这个函数是神经网络中的**函数,但为什么要加上它呢? 
举个例子,假如你在大学学了非常好的解题方法,那你初中那时候的解题方法还要用吗?显然是不用了的。RNN的想法也一样,既然我能记忆了,那我当然是只记重要的信息啦,其他不重要的,就肯定会忘记,是吧。但是在神经网络中什么最适合过滤信息呀?肯定是**函数嘛,因此在这里就套用一个**函数,来做一个非线性映射,来过滤信息,这个**函数可能为tanh,也可为其他。

假设你大四快毕业了,要参加考研,请问你参加考研是不是先记住你学过的内容然后去考研,还是直接带几本书去参加考研呢?很显然嘛,那RNN的想法就是预测的时候带着当前时刻的记忆

去预测。假如你要预测“我是中国“的下一个词出现的概率,这里已经很显然了,运用softmax来预测每个词出现的概率再合适不过了,但预测不能直接带用一个矩阵来预测呀,所有预测的时候还要带一个权重矩阵V,用公式表示为:

其中就表示时刻t的输出。

RNN中的结构细节: 
1.可以把

当作饮状态,捕捉了之前时间点上的信息。就像你去考研一样,考的时候记住了你能记住的所有信息。 
2.是由当前时间以及之前所有的记忆得到的。就是你考研之后做的考试卷子,是用你的记忆得到的。 
3.很可惜的是,并不能捕捉之前所有时间点的信息。就像你考研不能记住所有的英语单词一样。 
4.和卷积神经网络一样,这里的网络中每个cell都共享了一组参数(U,V,W),这样就能极大的降低计算量了。 
5.

在很多情况下都是不存在的,因为很多任务,比如文本情感分析,都是只关注最后的结果的。就像考研之后选择学校,学校不会管你到底怎么努力,怎么心酸的准备考研,而只关注你最后考了多少分。

3.RNN的改进1:双向RNN

在有些情况,比如有一部电视剧,在第三集的时候才出现的人物,现在让预测一下在第三集中出现的人物名字,你用前面两集的内容是预测不出来的,所以你需要用到第四,第五集的内容来预测第三集的内容,这就是双向RNN的想法。如图是双向RNN的图解: 

Datawhale《深度学习-NLP》Task8- 循环神经网络

这里的做的是一个拼接,如果他们都是1000X1维的,拼接在一起就是1000X2维的了。

双向RNN需要的内存是单向RNN的两倍,因为在同一时间点,双向RNN需要保存两个方向上的权重参数,在分类的时候,需要同时输入两个隐藏层输出的信息。

4.RNN的改进2:深层双向RNN

深层双向RNN 与双向RNN相比,多了几个隐藏层,因为他的想法是很多信息记一次记不下来,比如你去考研,复习考研英语的时候,背英语单词一定不会就看一次就记住了所有要考的考研单词吧,你应该也是带着先前几次背过的单词,然后选择那些背过,但不熟的内容,或者没背过的单词来背吧。

深层双向RNN就是基于这么一个想法,他的输入有两方面,第一就是前一时刻的隐藏层传过来的信息

,和当前时刻上一隐藏层传过来的信息

,包括前向和后向的。 

Datawhale《深度学习-NLP》Task8- 循环神经网络

我们用公式来表示是这样的: 

Datawhale《深度学习-NLP》Task8- 循环神经网络

然后再利用最后一层来进行分类,分类公式如下:

Datawhale《深度学习-NLP》Task8- 循环神经网络

4.1 Pyramidal RNN

其他类似的网络还有Pyramidal RNN: 

Datawhale《深度学习-NLP》Task8- 循环神经网络

我们现在有一个很长的输入序列,可以看到这是一个双向的RNN,上图是谷歌的W.Chan做的一个测试,它原先要做的是语音识别,他要用序列到序列的模型做语音识别,序列到序列就是说,输入一个序列然后就输出一个序列。

由图我们发现,上一层的两个输出,作为当前层的输入,如果是非常长的序列的话,这样做的话,每一层的序列都比上一层要短,但当前层的输入

也会随之增多,貌似看一起相互抵消,运算量并没有什么改进。

但我们知道,对于一层来说,它是从前往后转的,比如要预测一个股市的变化,以天为单位,假如要预测明天的股市变化,你就要用今天,以及今天之前的所有数据,我们暂时无法只用昨天的数据,不用今天的数据,预测明天的数据,也即是说,预测必须具有连续性。 
但每一层的

运算是可以并行的,从这个角度来看,运算量还是可以接受的,特别是在原始输入序列较短的时候还是有优势的。

5.RNN的训练-BPTT

如前面我们讲的,如果要预测t时刻的输出,我们必须先利用上一时刻(t-1)的记忆和当前时刻的输入,得到t时刻的记忆:

然后利用当前时刻的记忆,通过softmax分类器输出每个词出现的概率:为了找出模型最好的参数,U,W,V,我们就要知道当前参数得到的结果怎么样,因此就要定义我们的损失函数,用交叉熵损失函数:其中t时刻的标准答案,是一个只有一个是1,其他都是0的向量;是我们预测出来的结果,与的维度一样,但它是一个概率向量,里面是每个词出现的概率。因为对结果的影响,肯定不止一个时刻,因此需要把所有时刻的造成的损失都加起来:

Datawhale《深度学习-NLP》Task8- 循环神经网络

如图所示,你会发现每个cell都会有一个损失,我们已经定义好了损失函数,接下来就是熟悉的一步了,那就是根据损失函数利用SGD来求解最优参数,在CNN中使用反向传播BP算法来求解最优参数,但在RNN就要用到BPTT,它和BP算法的本质区别,也是CNN和RNN的本质区别:CNN没有记忆功能,它的输出仅依赖与输入,但RNN有记忆功能,它的输出不仅依赖与当前输入,还依赖与当前的记忆。这个记忆是序列到序列的,也就是当前时刻收到上一时刻的影响,比如股市的变化。

因此,在对参数求偏导的时候,对当前时刻求偏导,一定会涉及前一时刻,我们用例子看一下:

Datawhale《深度学习-NLP》Task8- 循环神经网络

假设我们对

的W求偏导:它的损失首先来源于预测的输出,预测的输出又是来源于当前时刻的记忆,当前的记忆又是来源于当前的输出和截止到上一时刻的记忆:


因此,根据链式法则可以有:

但是,你会发现,,也就是里面的函数还包含了W,因此,这个链式法则还没到底,就像图上画的那样,所以真正的链式法则是这样的: 

Datawhale《深度学习-NLP》Task8- 循环神经网络

我们要把当前时刻造成的损失,和以往每个时刻造成的损失加起来,因为我们每一个时刻都用到了权重参数W。和以往的网络不同,一般的网络,比如人工神经网络,参数是不同享的,但在循环神经网络,和CNN一样,设立了参数共享机制,来降低模型的计算量。

6.RNN与CNN的结合应用:看图说话

在图像处理中,目前做的最好的是CNN,而自然语言处理中,表现比较好的是RNN,因此,我们能否把他们结合起来,一起用呢?那就是看图说话了,这个原理也比较简单,举个小栗子:假设我们有CNN的模型训练了一个网络结构,比如是这个

Datawhale《深度学习-NLP》Task8- 循环神经网络

最后我们不是要分类嘛,那在分类前,是不是已经拿到了图像的特征呀,那我们能不能把图像的特征拿出来,放到RNN的输入里,让他学习呢?

之前的RNN是这样的:

我们把图像的特征加在里面,可以得到:其中的X就是图像的特征。如果用的是上面的CNN网络,X应该是一个4096X1的向量。

注:这个公式只在第一步做,后面每次更新就没有V了。

7.RNN项目练手

RNN可以写歌词,写诗等,这有个项目可以玩玩,还不错。 
Tensorflow实现RNN (链接:https://github.com/hzy46/Char-RNN-TensorFlow

 

双向RNN

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

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

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

而且Tensorflow里面就提供了这样的接口。
Datawhale《深度学习-NLP》Task8- 循环神经网络

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 direction
    with 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 direction
    if not time_major:
      time_dim = 1
      batch_dim = 0
    else:
      time_dim = 0
      batch_dim = 1

    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])

    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 direction
    with 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.****.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  rnn

mnist=input_data.read_data_sets("./minist/data",one_hot=True)

train_rate=0.001
train_step=10000
batch_size=1280
display_step=100

frame_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)
    #前向RNN
    rnn_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那样。

参考链接:https://blog.****.net/mpk_no1/article/details/72875185

递归神经网络

递归神经网络可存储记忆神经网络,LSTM是其中一种,在NLP领域应用效果不错。

递归神经网络(RNN),时间递归神经网络(recurrent neural network),结构递归神经网络(recursive neural network)。时间递归神经网络神经元间连接构成有向图,结构递归神经网络利用相似神经网络结构递归构造更复杂深度网络。两者训练属同一算法变体。

时间递归神经网络。传统神经网络FNN(Feed-Forward Neural Networks),前向反馈神经网络。RNN引入定向循环,神经元为节点组成有向环,可表达前后关联关系。隐藏层节点间构成全连接,一个隐藏层节点输出可作另一个隐藏层节点或自己的输入。U、V、W是变换概率矩阵,x是输入,o是输出。RNN关键是隐藏层,隐藏层捕捉序列信息,记忆能力。RNN中U、V、W参数共享,每一步都在做相同事情,输入不同,降低参数个数和计算量。RNN在NLP应用较多,语言模型在已知已出现词情况下预测下一个词概率,是时序模型,下一个词出现取决于前几个词,对应RNN隐藏层间内部连接。

RNN的训练方法。用BP误差反向传播算法更新训练参数。从输入到输出经过步骤不确定,利用时序方式做前向计算,假设x表示输入值,s表示输入x经过U矩阵变换后值,h表示隐藏层**值,o表示输出层值, f表示隐藏层**函数,g表示输出层**函数。当t=0时,输入为x0, 隐藏层为h0。当t=1时,输入为x1, s1 = Ux1+Wh0, h1 = f(s1), o1 = g(Vh1)。当t=2时,s2 = Ux2+Wh1, h2 = f(s2), o2 = g(Vh2)。st = Uxt + Wh(t-1), ht = f(st), ot = g(Vht)。h=f(现有的输入+过去记忆总结),对RNN记忆能力全然体现。 
UVW变换概率矩阵,x输入,s xU矩阵变换后值,f隐藏层**函数,h隐藏层**值,g输出层**函数,o输出。时间、输入、变换(输入、前隐藏)、隐藏(变换)、输出(隐藏)。输出(隐藏(变换(时间、输入、前隐藏)))。反向修正参数,每一步输出o和实际o值误差,用误差反向推导,链式求导求每层梯度,更新参数。

LSTM(Long Short Tem Momery networks)。RNN存在长序列依赖(Long-Term Dependencies)问题。下一个词出现概率和非常久远之前词有关,考虑到计算量,限制依赖长度。http://colah.github.io/posts/2015-08-Understanding-LSTMs 。传统RNN示意图,只包含一个隐藏层,tanh为激发函数,“记忆”体现在t滑动窗口,有多少个t就有多少记忆。

LSTM设计,神经网络层(权重系数和**函数,σ表示sigmoid**函数,tanh表示tanh**函数),矩阵运算(矩阵乘或矩阵加)。历史信息传递和记忆,调大小阀门(乘以一个0到1之间系数),第一个sigmoid层计算输出0到1之间系数,作用到×门,这个操作表达上一阶段传递过来的记忆保留多少,忘掉多少。忘掉记忆多少取决上一隐藏层输出h{t-1}和本层的输入x{t}。上一层输出h{t-1}和本层的输入x{t}得出新信息,存到记忆。计算输出值Ct部分tanh神经元和计算比例系数sigmoid神经元(sigmoid取值范围是[0,1]作比例系数,tanh取值范围[-1,1]作一个输出值)。隐藏层输出h计算,考虑当前全部信息(上一时序隐藏层输出、本层输入x和当前整体记忆信息),本单元状态部分C通过tanh**并做一个过滤(上一时序输出值和当前输入值通过sigmoid**系数)。一句话词是不同时序输入x,在某一时间t出现词A概率可LSTM计算,词A出现概率取决前面出现过词,取决前面多少个词不确定,LSTM存储记忆信息C,得出较接近概率。

聊天机器人是范问答系统。

语料库获取。范问答系统,一般从互联网收集语料信息,比如百度、谷歌,构建问答对组成语料库。语料库分成多训练集、开发集、测试集。问答系统训练在一堆答案里找一个正确答案模型。训练过程不把所有答案都放到一个向量空间,做分组,在语料库里采集样本,收集每一个问题对应500个答案集合,500个里面有正向样本,随机选些负向样本,突出正向样本作用。

基于CNN系统设计,sparse interaction(稀疏交互),parameter sharing(参数共享),equivalent respresentation(等价表示),适合自动问答系统答案选择模型训练。

通用训练方法。训练时获取问题词向量Vq(词向量可用google word2vec训练,和一个正向答案词向量Va+,和一个负向答案词向量Va-, 比较问题和两个答案相似度,两个相似度差值大于一个阈值m更新模型参数,在候选池里选答案,小于m不更新模型。参数更新,梯度下降、链式求导。测试数据,计算问题和候选答案cos距离,相似度最大是正确答案预测。

神经网络结构设计。HL hide layer隐藏层,**函数z = tanh(Wx+B),CNN 卷积层,P 池化层,池化步长 1,T tanh层,P+T输出是向量表示,最终输出两个向量cos相似度。HL或CNN连起来表示共享相同权重。CNN输出维数取决做多少卷积特征。论文《Applying Deep Learning To Answer Selection- A Study And An Open Task》。

深度学习运用到聊天机器人中,1. 神经网络结构选择、组合、优化。2. 自然语言处理,机器识别词向量。3. 相似或匹配关系考虑相似度计算,典型方法 cos距离。4. 文本序列全局信息用CNN或LSTM。5. 精度不高可加层。6. 计算量过大,参数共享和池化。

聊天机器人学习,需要海量聊天语料库。美剧字幕。外文电影或电视剧字幕文件是天然聊天语料,对话比较多美剧最佳。字幕库网站www.zimuku.net。

自动抓取字幕。抓取器代码(https://github.com/warmheartli/ChatBotCourse)。在subtitle下创建目录result,scrapy.Request 
方法调用时增加传参 dont_filter=True:
 

# coding:utf-8

import sys
import importlib
importlib.reload(sys)

import scrapy
from subtitle_crawler.items import SubtitleCrawlerItem

class SubTitleSpider(scrapy.Spider):
    name = "subtitle"
    allowed_domains = ["zimuku.net"]
    start_urls = [
            "http://www.zimuku.net/search?q=&t=onlyst&ad=1&p=20",
            "http://www.zimuku.net/search?q=&t=onlyst&ad=1&p=21",
            "http://www.zimuku.net/search?q=&t=onlyst&ad=1&p=22",
    ]

    def parse(self, response):
        hrefs = response.selector.xpath('//div[contains(@class, "persub")]/h1/a/@href').extract()
        for href in hrefs:
            url = response.urljoin(href)
            request = scrapy.Request(url, callback=self.parse_detail, dont_filter=True)
            yield request

    def parse_detail(self, response):
        url = response.selector.xpath('//li[contains(@class, "dlsub")]/div/a/@href').extract()[0]
        print("processing: ", url)
        request = scrapy.Request(url, callback=self.parse_file, dont_filter=True)
        yield request

    def parse_file(self, response):
        body = response.body
        item = SubtitleCrawlerItem()
        item['url'] = response.url
        item['body'] = body
        return item

# -*- coding: utf-8 -*-

class SubtitleCrawlerPipeline(object):
    def process_item(self, item, spider):
        url = item['url']
        file_name = url.replace('/','_').replace(':','_')+'.rar'
        fp = open('result/'+file_name, 'wb+')
        fp.write(item['body'])
        fp.close()
        return item

ls result/|head -1 , ls result/|wc -l , du -hs result/ 。

 

 

 

 

正在补充。。。。

 

1. https://blog.****.net/KHFlash/article/details/79822227

2. https://blog.****.net/jmh1996/article/details/83476061

3. https://blog.****.net/weixin_38776853/article/details/79773662