as

1、搬了什么砖,在什么条件下搬的砖

在tensorflow和keras上复现了ACL2016的一篇论文《Text Understanding with the Attention Sum Reader Network》里的模型attention-sum-reader。能够跑CBT的数据。

论文可以在http://arxiv.org/abs/1603.01547下载,具体代码见这里https://github.com/zhanghaoyu1993/attention-sum-reader

搬砖环境:

  • windows10
  • python-3.5.2
  • tensorflow-1.0.1
  • keras-2.0.2

2、遇到了哪些坑,怎么踩过()去的

因为原作者是在theano上的blocks和fuel两个框架下实现的,所以算是改写吧,写的过程中在这么几个点上花的时间比较多:

  • 1.结合keras和tf,简单标准化的层用keras提供的,特殊的操作使用K.*这种API结合Lambda层,再特殊的操作使用tf.*这种API完成。
  • 2.tensorflow的scan函数,如果你想对tensor进行一些迭代操作而库函数里没有直接提供的话,就要使用scan函数了,这个函数在tensorflow和theano下面都有。

tf.scan有下面几个主要参数,依靠他们来完成对一个或者多个tensor的迭代:

fn:一个函数接受两个参数表示上一轮的输出和这一轮的输入,注意!!:这个函数的输出只需要提供这一轮的就可以了,没有必要和上一轮的拼起来,scan会自己帮你concat。

elems:一个tensor或者tensor列表,在scan的过程中,会把elems(或者其中每个元素)取一行提供给fn作为第二个参数;

initializer:初始化,如果不提供那么fn返回的元素结构要和elems一样,否则就返回你提供的这个数据的结构。注意!!:你提供的这个数无关紧要,因为最后并不会返回。

还有一点需要注意的是tf的tf.scan()并没有提供non-sequences这个参数,这个参数里的tensor在scan的过程中直接提供给fn(而不是按照第一阶解包),但是通过全局变量,或者python的partial function(在多层scan中)还是能够实现相同的功能:

1
2
3
4
5
def (tensor1, tensor2, tensor3):
return tf.concat(tensor1 * tensor2, tensor3, axis=-1)
i2 = tf.Variable([1,2])
i3 = tf.Variable([3,4])
tf.scan(fn=lambda prev_output, cur: some_func(cur,i2,i3))
  • 3.在使用RNN的时候如何处理变长的序列?

首先当然是要pad_sequences,因为无论如何keras的层接受的输入都是定长的。(也可能有我不知道的)

如果使用tf,那么可以使用dynamic_rnn(),把序列的实际长度列表作为输入参数,只会unroll实际的步长,对于多余的,返回的output补0,能够减少浪费在pad字符上的处理时间。这里需要注意的是,使用了这个方法后,如果你对于output只是需要部分time step的,那一定要想清楚是不是有效的time step,必要的时候用tensor的操作进行选取。另外这个操作只能返回最后时刻的状态,中间的RNN隐藏状态无法获得。

如果是keras的层,就只有调API了,不知道内部是不是调用dynamic_rnn。

  • 4.在搭建模型的过程中,怎么确定自己写的代码没有小错误呢?

读取和预处理数据的过程中,数据还是numpy array格式的时候,可以依靠Debug和输出确定。

在keras层的模型之间,可以搭一层测试一层的输出,使用如下的代码:

1
2
3
4
5
6
7
8
9
10
11
class TestModel:
def __init__(self):
q_input = Input(batch_shape=(None, self.q_len), dtype="int32", name="q_input")
q_encode = Embedding(input_dim=self.vocab_size,
output_dim=FLAGS.embedding_dim,
weights=[embedding_matrix],
mask_zero=True)(q_input)
self.q_embed = Model(inputs=[q_input], outputs=q_encode)
def test(self):

print(self.q_embed.predict(x))

在搭建tf的模型时,可以采用类似的方法,用notebook(比较方便)进行测试,把中间每层的tensor输出一下,看看长得是不是有点奇怪。

  • 5.keras和tf的区别?

keras很方便快捷,而且可以基本和tf无缝衔接,不过使用keras搭建模型的时候好像内存用的比tf多一些,keras搭建的模型在tensorflow-gpu上运行老是报OOM,但是使用tf搭建就没这个问题。(可能是有哪个用法不知道吧)

  • 6.tf怎么使用crossEntropy交叉熵损失函数?

tf自带的好像只有softmax_cross_entropy这样的交叉熵损失函数,没有只做交叉熵的,下面的代码片段可以实现tf下的交叉熵,并保持数值稳定性防止NaN错误(摘自keras):

1
2
3
4
5
_EPSILON = 10e-8
epsilon = tf.convert_to_tensor(_EPSILON, output.dtype.base_dtype, name="epsilon")
output = tf.clip_by_value(output, epsilon, 1. - epsilon)
self.loss = tf.reduce_mean(- tf.reduce_sum(self.y_true * tf.log(output),
reduction_indices=len(output.get_shape()) - 1))
  • 7.tf怎么实现梯度裁剪?

如下代码:

1
2
3
4
5
6
grad_vars = optimizer.compute_gradients(self.loss)
grad_vars = [
(tf.clip_by_norm(grad, grad_clip), var)
if grad is not None else (grad, var)
for grad, var in grad_vars]
train_op = optimizer.apply_gradients(grad_vars)