暑期信工所工作总结

Posted by grt1stnull on 2017-09-21

0x00.前言

假期有幸在信工所实习,跟着师兄使用tensorflow构造了模型,这里拿着终稿做一个总结。

代码在 https://gist.github.com/grt1st/3f503c701ba89b5242632e2be997fa91 , 回想了一下,使用了seq2seq模型、lstm、注意力机制、词向量。继续学习,继续前进。

0x01.深入代码

1.function model_inputs

首先我定义了一系列的输入tf.placeholder,包括了输入和输出(我这里输入与输出相同,神经网络作为对特征的提取)、学习率(因为学习率过大发生过梯度爆炸)、keep_prob(不dropout的概率)、序列长度及最大序列长度(输入为每天的行为,一天中行为不固定,所以长度变化)。

2.function process_decoder_input

这里为对输出的处理。首先在输入时,在所有序列末尾加入了结束符,而在输出这里,去掉了末尾的结束符,补充了开始符。这是由模型本身决定的。

一般来说,在自然语言模型中比较常见的标识符有,即开始符、结束符、填充符和不知道符(忘记符号是怎么处理的了)。

3.function encoding_layer

这是编码层,seq2seq是由两层神经网络构成的,一层用于编码,一层用于解码。通过编码层将输入进行编码得到中间向量,再由解码层输出得到目标序列。

我觉得seq2seq结构比较有趣,就像自编码器与GAN一样,是两个神经网络之间的协作。

1
2
3
4
5
6
7
8
9
10
import tensorflow as tf
from tensorflow.contrib import rnn
enc_cell = []
for i in range(NUM_LAYERS):
lstm_cell = rnn.BasicLSTMCell(HIDDEN_SIZE)
lstm_cell = tf.contrib.rnn.DropoutWrapper(lstm_cell, output_keep_prob=keep_prob)
enc_cell.append(lstm_cell)
enc_cell = tf.nn.rnn_cell.MultiRNNCell(cells=enc_cell, state_is_tuple=True)

这里定义了一个多层lstm、并且加入了dropout。因为版本问题,所以这里需要以列表形式实现。

1
enc_outputs, enc_state = tf.nn.dynamic_rnn(enc_cell, inputs, sequence_length=source_sequence_length, dtype=tf.float32)

这里实现的是多层rnn,基本的seq2seq会将编码状态(enc_state)返回。但是这里,我实现了注意力机制,所以将编码输出也返回了(enc_outputs)。

4.function decoding_layer_train

这里实现的是解码层的训练结构。

1
2
3
4
5
6
helper = tf.contrib.seq2seq.TrainingHelper(dec_embed_input, target_sequence_length)
decoder = tf.contrib.seq2seq.BasicDecoder(dec_cell, helper, enc_state, output_layer=output_layer)
train_logits, _, _ = tf.contrib.seq2seq.dynamic_decode(decoder, impute_finished=True,\
maximum_iterations=max_target_sequence_length, scope=decoding_scope)

这是解码层基本的结构,除了训练(train),还有推测(infer)。他们的结构都是这样的,并且进行了权值共享(scope)。

5.function decoding_layer_infer

这里定义了解码层的推测结构。

同样是固定的。

6.function decoding_layer

这里将解码层的两个结构综合了起来,合成了这个解码层。

1
attention_mechanism = tf.contrib.seq2seq.LuongAttention(HIDDEN_SIZE, enc_outputs)

引用的注意力机制。

1
2
3
4
5
6
7
8
9
10
11
dec_cell = []
for i in range(NUM_LAYERS):
lstm_cell = rnn.BasicLSTMCell(HIDDEN_SIZE)
# 注意力
lstm_cell = tf.contrib.seq2seq.AttentionWrapper(lstm_cell, attention_mechanism)
lstm_cell = tf.contrib.rnn.DropoutWrapper(lstm_cell, output_keep_prob = keep_prob)
dec_cell.append(lstm_cell)
dec_cell = tf.nn.rnn_cell.MultiRNNCell(cells=dec_cell, state_is_tuple=True)

解码神经元,也是多层lstm,这里除了dropoout又加入了注意力。

1
2
3
4
5
6
# 注意力机制状态
state = dec_cell.zero_state(BATCH_SIZE, tf.float32)
state_list = []
for i in range(len(state)):
state_list.append(state[i].clone(cell_state=enc_state[i]))
init_state = tuple(state_list)

对神经结构(多层lstm)状态进行初始化,不然会报错。参考 https://github.com/tensorflow/tensorflow/issues/11540https://github.com/tensorflow/tensorflow/issues/11077

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from tensorflow.python.layers import core as layers_core
with tf.variable_scope("decoding") as decoding_scope:
# Output Layer
# vocab_size: the output unit of the layer
#output_fn = lambda x: tf.contrib.layers.fully_connected(x, ONEHOT_SIZE, None, scope=decoding_scope)
output_layer = layers_core.Dense(ID_SIZE, kernel_initializer = tf.truncated_normal_initializer(mean = 0.0, stddev=0.1))
train_logits = decoding_layer_train(init_state, dec_cell, dec_embed_input, target_sequence_length,
output_layer, keep_prob, max_target_sequence_length, decoding_scope)
with tf.variable_scope("decoding", reuse=True) as decoding_scope:
infer_logits = decoding_layer_infer(init_state, dec_cell, dec_embeddings, 0, 1,
output_layer, keep_prob, max_target_sequence_length, decoding_scope)

这里对解码的train、infer做了共享,引入了output_layer。

7.function seq2seq_model

整合了编码层与解码层,成了我们的seq2seq模型。

1
2
3
4
dec_embeddings = tf.Variable(tf.random_uniform([ID_SIZE, EMBEDDING_SIZE]))
targets = process_decoder_input(targets)
target_embed = tf.nn.embedding_lookup(dec_embeddings, targets)

首先构造了一个矩阵,即大的向量表,包含了所有向量。这里我们的输入是每天的行为,不同的行为都有不同的id,通过这个矩阵,我们得到id对应的向量,所以我说这个矩阵包含了所有向量。(是不是和词嵌入很像呢,我刚来就这么提议的嘿嘿)

构造了矩阵之后,又读输出序列进行了处理,即去掉最后id,在开头加入起始id。然后得到了对应的矩阵。

1
2
#enc_embed_input = tf.contrib.layers.embed_sequence(inputs, ID_SIZE, EMBEDDING_SIZE)
enc_embed_input = tf.nn.embedding_lookup(dec_embeddings, inputs)

类似对输出编码得到矩阵,这里对输入也进行了编码。

注释掉的是最开始的版本,师兄说本来这是应用在神经网络语言翻译,由于不同语言,所以编码不同,但是这里,输入输出是相同的,所以使用同样的向量。

之后首先调用编码网络,之后返回解码网络(训练回归数与推测回归数)。

8.graph build

前面我们定义的各种函数,这里我们构建图。

正如tensorflow名字,张量流。我们构造的是一个图算法,图算法和正常的程序运行顺序不一样。

1
2
3
train_graph = tf.Graph()
with train_graph.as_default():
#......

构造默认图。

1
2
3
4
5
training_logits = tf.identity(train_logits.rnn_output, name='logits')
inference = tf.identity(inference_logits.rnn_output, name='infer')
nference_logits = tf.identity(inference_logits.sample_id, name='predictions')

这里接收的是我们seq2seq模型返回的训练回归数与推测回归数。xxx_logits.rnn_output输出的是向量,用于下面损失的计算,而xxx_logits.sample_id输出的是id,可以输出用于比较。

后续即定义损失,学习器及反向传播。

9.function get_accuracy

计算正确率。

emmmm…

10.function read_data_sets

读取数据集,这里用了yield哦。

11.build model

之后是一些对数据处理的操作。

1
2
3
with tf.Session(graph=train_graph) as sess:
sess.run(tf.global_variables_initializer())

现在开始构建模型。

1
2
3
4
5
6
7
8
9
10
11
time_begin = time.time()
print("Reading begins @ %f" % time_begin)
if step % 50 == 0:
print("Date: " + one_data + ",Accuracy: " + "{:.6f}".format(train_acc) + ",Loss: " + "{:.6f}".format(loss))
time_end = time.time()
print("Reading ends @ %f" % time_end)
training_time = time_end - time_begin
print("Reading elapsed time: %f s" % training_time)
print("Optimization Finished!")

基本结构,我常用的时间单元。

然后sess.run,输出…恩。

0x02.后记

就在暑假的收尾阶段,这个模型已经构造完成,小修小改。但是虽然有大把时间,我却忙于lctf的出题工作,无暇顾及。

所以过了这么久才开始总结。后续会了解其他seq2seq模型,并尝试cahtbot。

0x03.参考

从Encoder到Decoder实现Seq2Seq模型

seq2seq-advanced.ipynb