漫谈四种神经网络序列解码模型以及示例代码

人工智能 深度学习
利用通俗的语言来谈谈神经网络模型中4种序列解码模型,主要是从整体概念和思路上进行通俗解释帮助理解。

[[189448]]

以下为漫谈,即瞎聊,利用通俗的语言来谈谈神经网络模型中4种序列解码模型,主要是从整体概念和思路上进行通俗解释帮助理解。预警,以下可能为了偷懒就不贴公式了,一些细节也被略过了,感兴趣的可以直接去阅读原文[1][2][3]。

[1] Sequence to Sequence Learning with Neural Networks

[2] Learning Phrase Representations using RNN Encoder–Decoder for Statistical Machine Translation

[3] Neural Machine Translation by Jointly Learning to Align and Translate

利用神经网络进行序列编码的模型主要为RNN,目前比较火的一些变种模型有LSTM和GRU,只是cell单元不同而已。以下统统用RNN来代表。

编码模型比较简单,如下图所示,输入文本{X1-X6}经过循环迭代编码,在每个时刻得到当前时刻的一个隐层状态,最后序列结束后进行特征融合得到句子的表示。注意,一种比较常用的方式是将编码模型最后一个时刻的隐层状态做为整个序列的编码表示,但是实际应用中这种效果并不太好,因而我们的图例中直接采用了整个序列隐层编码进行求和平均的方式得到序列的编码向量。

早期的一些任务主要是做一些主题分类、情感检测等等分类任务,那么在编码向量上面添加一个softmax就可以解决问题。但是对于机器翻译和语音识别等问题则需要进行序列化解码。

注意到,编码时RNN每个时刻除了自己上一时刻的隐层状态编码外,还有当前时刻的输入字符,而解码时则没有这种输入。那么,一种比较直接的方式是把编码端得到的编码向量做为解码模型的每时刻输入特征。如下图所示:

简单直观而且解码模型和编码模型并没有任何区别,然而学者感觉该模型并不优雅,那么接下来我们就来介绍一些精巧点的吧。

我们用考试作弊来做为一个通俗的例子来解释一下模型。

首先我们假设输入文本是所学课本,编码端则是对课本的理解所整理的课堂笔记。解码端的隐层神经网络则是我们的大脑,而每一时刻的输出则是考试时要写在卷子上的答案。在上面最简单的解码模型中,可以考虑成是考试时一边写答案一边翻看课堂笔记。如果这是一般作弊学生的做法,学霸则不需要翻书,他们有一个强大的大脑神经网络,可以记住自己的课堂笔记。解码时只需要回顾一下自己前面写过什么,然后依次认真的把答案写在答卷上,就是下面这种模型了[1]:

还有很多学弱,他们不只需要作弊,而且翻看笔记的时候还需要回顾自己上一时刻写在答卷上的答案(学弱嘛,简直弱到连自己上一时刻写在答卷上的文字都记不住了),就是下面的答题模式了[2]:

然而学渣渣也是存在的,他们不只需要作弊,不只需要回顾自己上一时刻卸载答卷上的答案,还需要老师在课本上画出重点才能整理出自己的课题笔记(这就是一种注意力机制Attention,记笔记的时候一定要根据考题画出重点啊!),真的很照顾渣渣了,他们的答题模式如下[3]:

可见,除了学霸以外,其他人都作弊了,在答题的时候翻看课堂笔记(很多文献中叫这种解码模型结构为peek(偷看),是不是很像在作弊?),而且学渣渣还去找过老师给画过重点,有了清楚的重点之后就不用翻书偷看了,瞄一眼就可以了,文献中叫glimpse(一瞥),是不是很像?

如果我们将他们的大脑网络设定为同样结构的话(将他们的IQ强制保持一致),肯定是作弊的同学得分最高了,学霸模式好吃亏啊。我们来简单做一个模型测试。

测试数据:

输入序列文本 = [‘1 2 3 4 5’

, ‘6 7 8 9 10′

, ’11 12 13 14 15′

, ’16 17 18 19 20′

, ’21 22 23 24 25’]

目标序列文本 = [‘one two three four five’

, ‘six seven eight nine ten’

, ‘eleven twelve thirteen fourteen fifteen’

, ‘sixteen seventeen eighteen nineteen twenty’

, ‘twenty_one twenty_two twenty_three twenty_four twenty_five’]

设定一些参数如下:

(‘Vocab size:’, 51, ‘unique words’)

(‘Input max length:’, 5, ‘words’)

(‘Target max length:’, 5, ‘words’)

(‘Dimension of hidden vectors:’, 20)

(‘Number of training stories:’, 5)

(‘Number of test stories:’, 5)

观察训练过程:

其中,第一种解码模型为 普通作弊,第二种解码模型为 学霸模式,第三种解码模型为 学弱作弊,第四种解码模型为 学渣作弊。

可以看到在IQ值(解码模型的神经网络结构)相同的情况下,学渣作弊模式答题(训练收敛速度)更快,而学霸模式答题最慢。

文章[1]中已经提到过,想通过学霸模式达到一个好的性能需要模型隐层有4000个节点(学霸的IQ果然是高的,有一颗强大的大脑网络)。

可以想想,在课本内容很多很多时,学霸也会累的,而且学弱们你们确定课上能听懂吗?学渣就会笑啦,因而老师给他们画重点了!!!!

本博文中测试的示例代码见【Github地址】:

  1. # -*- encoding:utf-8 -*-    
  2. “”" 
  3.     测试Encoder-Decoder 2016/03/22   
  4. “”"   
  5. from keras.models import Sequential    
  6. from keras.layers.recurrent import LSTM    
  7. from keras.layers.embeddings import Embedding    
  8. from keras.layers.core import RepeatVector, TimeDistributedDense, Activation    
  9. from seq2seq.layers.decoders import LSTMDecoder, LSTMDecoder2, AttentionDecoder    
  10. import time   
  11. import numpy as np    
  12. import re   
  13.   
  14. __author__ = ’http://jacoxu.com’    
  15.   
  16.   
  17. def pad_sequences(sequences, maxlen=None, dtype=’int32′,    
  18.                   padding=’pre’, truncating=’pre’, value=0.):    
  19.     ”’Pads each sequence to the same length: 
  20.     the length of the longest sequence.   
  21.   
  22.     If maxlen is provided, any sequence longer   
  23.     than maxlen is truncated to maxlen.   
  24.     Truncation happens off either the beginning (defaultor   
  25.     the end of the sequence.   
  26.   
  27.     Supports post-padding and pre-padding (default).   
  28.   
  29.     # Arguments   
  30.         sequences: list of lists where each element is a sequence   
  31.         maxlen: int, maximum length   
  32.         dtype: type to cast the resulting sequence.   
  33.         padding: ’pre’ or ’post’, pad either before or after each sequence.   
  34.         truncating: ’pre’ or ’post’, remove values from sequences larger than   
  35.             maxlen either in the beginning or in the end of the sequence   
  36.         value: float, value to pad the sequences to the desired value.   
  37.   
  38.     # Returns   
  39.         x: numpy array with dimensions (number_of_sequences, maxlen)   
  40.     ”’   
  41.     lengths = [len(s) for s in sequences]    
  42.   
  43.     nb_samples = len(sequences)    
  44.     if maxlen is None:    
  45.         maxlen = np.max(lengths)    
  46.   
  47.     # take the sample shape from the first non empty sequence    
  48.     # checking for consistency in the main loop below.    
  49.     sample_shape = tuple()    
  50.     for s in sequences:    
  51.         if len(s) > 0:    
  52.             sample_shape = np.asarray(s).shape[1:]    
  53.             break   
  54.   
  55.     x = (np.ones((nb_samples, maxlen)   sample_shape) * value).astype(dtype)    
  56.     for idx, s in enumerate(sequences):    
  57.         if len(s) == 0:    
  58.             continue  # empty list was found    
  59.         if truncating == ’pre’:    
  60.             trunc = s[-maxlen:]    
  61.         elif truncating == ’post’:    
  62.             trunc = s[:maxlen]    
  63.         else:    
  64.             raise ValueError(‘Truncating type ”%s” not understood’ % truncating)    
  65.   
  66.         # check `trunc` has expected shape    
  67.         trunc = np.asarray(trunc, dtype=dtype)    
  68.         if trunc.shape[1:] != sample_shape:    
  69.             raise ValueError(‘Shape of sample %s of sequence at position %s is different from expected shape %s’ %    
  70.                              (trunc.shape[1:], idx, sample_shape))    
  71.   
  72.         if padding == ’post’:    
  73.             x[idx, :len(trunc)] = trunc    
  74.         elif padding == ’pre’:    
  75.             x[idx, -len(trunc):] = trunc    
  76.         else:    
  77.             raise ValueError(‘Padding type ”%s” not understood’ % padding)    
  78.     return x    
  79.   
  80.   
  81. def vectorize_stories(input_list, tar_list, word_idx, input_maxlen, tar_maxlen, vocab_size):    
  82.     x_set = []    
  83.     Y = np.zeros((len(tar_list), tar_maxlen, vocab_size), dtype=np.bool)    
  84.     for _sent in input_list:    
  85.         x = [word_idx[w] for w in _sent]    
  86.         x_set.append(x)    
  87.     for s_index, tar_tmp in enumerate(tar_list):    
  88.         for t_index, token in enumerate(tar_tmp):    
  89.             Y[s_index, t_index, word_idx[token]] = 1    
  90.   
  91.     return pad_sequences(x_set, maxlen=input_maxlen), Y    
  92.   
  93.   
  94. def tokenize(sent):    
  95.     ”’Return the tokens of a sentence including punctuation. 
  96.   
  97.     >>> tokenize(‘Bob dropped the apple. Where is the apple?’)   
  98.     ['Bob''dropped''the''apple''.''Where''is''the''apple''?']   
  99.     ”’   
  100.     return [x.strip() for x in re.split('(\W )?', sent) if x.strip()]    
  101.   
  102.   
  103. def main():    
  104.     input_text = ['1 2 3 4 5'    
  105.                   , '6 7 8 9 10'    
  106.                   , '11 12 13 14 15'    
  107.                   , '16 17 18 19 20'    
  108.                   , '21 22 23 24 25']    
  109.     tar_text = ['one two three four five'    
  110.                 , 'six seven eight nine ten'    
  111.                 , 'eleven twelve thirteen fourteen fifteen'    
  112.                 , 'sixteen seventeen eighteen nineteen twenty'    
  113.                 , 'twenty_one twenty_two twenty_three twenty_four twenty_five']    
  114.   
  115.     input_list = []    
  116.     tar_list = []    
  117.   
  118.     for tmp_input in input_text:    
  119.         input_list.append(tokenize(tmp_input))    
  120.     for tmp_tar in tar_text:    
  121.         tar_list.append(tokenize(tmp_tar))    
  122.   
  123.     vocab = sorted(reduce(lambda x, y: x | y, (set(tmp_list) for tmp_list in input_list   tar_list)))    
  124.     # Reserve 0 for masking via pad_sequences    
  125.     vocab_size = len(vocab)   1  # keras进行embedding的时候必须进行len(vocab) 1    
  126.     input_maxlen = max(map(len, (x for x in input_list)))    
  127.     tar_maxlen = max(map(len, (x for x in tar_list)))    
  128.     output_dim = vocab_size    
  129.     hidden_dim = 20    
  130.   
  131.     print(‘-’)    
  132.     print(‘Vocab size:’, vocab_size, ’unique words’)    
  133.     print(‘Input max length:’, input_maxlen, ’words’)    
  134.     print(‘Target max length:’, tar_maxlen, ’words’)    
  135.     print(‘Dimension of hidden vectors:’, hidden_dim)    
  136.     print(‘Number of training stories:’, len(input_list))    
  137.     print(‘Number of test stories:’, len(input_list))    
  138.     print(‘-’)    
  139.     print(‘Vectorizing the word sequences…’)    
  140.     word_to_idx = dict((c, i   1) for i, c in enumerate(vocab))  # 编码时需要将字符映射成数字index    
  141.     idx_to_word = dict((i   1, c) for i, c in enumerate(vocab))  # 解码时需要将数字index映射成字符    
  142.     inputs_train, tars_train = vectorize_stories(input_list, tar_list, word_to_idx, input_maxlen, tar_maxlen, vocab_size)    
  143.   
  144.     decoder_mode = 1  # 0 最简单模式,1 [1]向后模式,2 [2] Peek模式,3 [3]Attention模式    
  145.     if decoder_mode == 3:    
  146.         encoder_top_layer = LSTM(hidden_dim, return_sequences=True)    
  147.     else:    
  148.         encoder_top_layer = LSTM(hidden_dim)    
  149.   
  150.     if decoder_mode == 0:    
  151.         decoder_top_layer = LSTM(hidden_dim, return_sequences=True)    
  152.         decoder_top_layer.get_weights()    
  153.     elif decoder_mode == 1:    
  154.         decoder_top_layer = LSTMDecoder(hidden_dim=hidden_dim, output_dim=hidden_dim    
  155.                                         , output_length=tar_maxlen, state_input=False, return_sequences=True)    
  156.     elif decoder_mode == 2:    
  157.         decoder_top_layer = LSTMDecoder2(hidden_dim=hidden_dim, output_dim=hidden_dim    
  158.                                          , output_length=tar_maxlen, state_input=False, return_sequences=True)    
  159.     elif decoder_mode == 3:    
  160.         decoder_top_layer = AttentionDecoder(hidden_dim=hidden_dim, output_dim=hidden_dim    
  161.                                              , output_length=tar_maxlen, state_input=False, return_sequences=True)    
  162.   
  163.     en_de_model = Sequential()    
  164.     en_de_model.add(Embedding(input_dim=vocab_size,    
  165.                               output_dim=hidden_dim,    
  166.                               input_length=input_maxlen))    
  167.     en_de_model.add(encoder_top_layer)    
  168.     if decoder_mode == 0:    
  169.         en_de_model.add(RepeatVector(tar_maxlen))    
  170.     en_de_model.add(decoder_top_layer)    
  171.   
  172.     en_de_model.add(TimeDistributedDense(output_dim))    
  173.     en_de_model.add(Activation(‘softmax’))    
  174.     print(‘Compiling…’)    
  175.     time_start = time.time()    
  176.     en_de_model.compile(loss=’categorical_crossentropy’, optimizer=’rmsprop’)    
  177.     time_end = time.time()    
  178.     print(‘Compiled, cost time:%fsecond!’ % (time_end - time_start))    
  179.     for iter_num in range(5000):    
  180.         en_de_model.fit(inputs_train, tars_train, batch_size=3, nb_epoch=1, show_accuracy=True)    
  181.         out_predicts = en_de_model.predict(inputs_train)    
  182.         for i_idx, out_predict in enumerate(out_predicts):    
  183.             predict_sequence = []    
  184.             for predict_vector in out_predict:    
  185.                 next_index = np.argmax(predict_vector)    
  186.                 next_token = idx_to_word[next_index]    
  187.                 predict_sequence.append(next_token)    
  188.             print(‘Target output:’, tar_text[i_idx])    
  189.             print(‘Predict output:’, predict_sequence)    
  190.   
  191.         print(‘Current iter_num is:%d’ % iter_num)    
  192.   
  193. if __name__ == ’__main__‘:    
  194.     main() 
责任编辑:武晓燕 来源: 我爱机器学习
相关推荐

2022-06-14 13:55:30

模型训练网络

2017-06-18 16:20:57

神经网络单元

2017-07-05 15:14:30

神经网络连接方式网络单元

2019-03-26 19:00:02

神经网络AI人工智能

2009-09-08 17:20:01

C#排序算法

2022-11-01 16:02:31

架构神经网络模型

2021-10-24 08:37:18

网络监控网络架构网络

2016-08-04 15:10:12

服务器虚拟化网络

2020-04-20 13:45:32

神经网络模型代码

2019-04-01 07:36:47

深度学习神经网络机器学习

2019-01-05 08:40:17

VGG神经网络

2020-09-16 10:02:08

神经网络模型

2020-06-24 07:49:13

Kubernetes场景网络

2022-01-05 08:30:31

BIONIO AIO

2020-11-10 10:08:41

Kubernetes容器开发

2023-02-10 11:13:42

网络功耗无线网络设备

2018-07-03 16:10:04

神经网络生物神经网络人工神经网络

2019-10-22 14:06:13

Docker软件Linux

2017-08-28 21:31:37

TensorFlow深度学习神经网络

2023-09-17 23:09:24

Transforme深度学习
点赞
收藏

51CTO技术栈公众号