最近在做一个模型的实时化工作,包括CNN和LSTM的实时化,感觉里面坑挺多的,语音模型对实时性的要求真的苦了开发者了。
我使用的是tensorflow 1.14进行开发,在1.14版本之后tensorflow支持keras 和tensorflow operator的混合编程,在更早的版本里面你要在keras 里面使用tensorflow的张量操作就得包装成Lambda层。
今天主要介绍一下LSTM的实时性推理,主要的几个参数是stateful return_state 和 unroll 。
首先LSTM的结构类似HMM,有隐状态和输出的观测,一般来说keras.layers.LSTM不给return_sequences,模型就会输出最后一个时间步骤的观测h,return_sequences = True,就会把每一个时间
步骤的观测输出出来,return_state 这个参数的作用就是输出最后一个时间步骤的观测h和cell状态c:
out,h,c = LSTM(units=128, return_sequences=True,implementation = 2,recurrent_activation = 'hard_sigmoid',use_bias = False,return_state = True)(LSTM_input)
同样LSTM层在输入的时候不仅可以输入观测,还可以输入隐状态的初始化:
x, h_state, c_state = LSTM(128, return_sequences=True,return_state=True)(x, initial_state=in_state)
如果推断的时候没有给初始化的状态,LSTM将在每一个batch里面对初始状态进行初始化。
在进行模型实时化改造的时候,每一次输入的数据仅仅是当前时间帧的数据,不同时间帧算是不同batch了,因此我们必须把上一个时间帧的状态输出,传给当前的初始隐状态。如果对tensorflow
来说的话,相当于一部分数据在计算图之外被处理了。为了避免这个麻烦可以使用stateful参数,这个参数为True时,上一个batch的隐状态将被保留到下一个batch,下面的对比了是否使用stateful
时的计算结果,可以发现stateful LSTM即使推断了两次,这两次的数据在不同batch里面,但是计算结果和将两次数据放在一个batch里面推断的结果一致。
LSTM_data = np.random.randn(10,2,128)
def LSTM_not_stateful():
LSTM_input = Input(batch_shape = (10,None,128))
h1 = LSTM(units=128, return_sequences=True,implementation = 2,recurrent_activation = 'hard_sigmoid',use_bias = False,stateful = False,return_state = True)(LSTM_input)
LSTM_model = Model(LSTM_input,h1)
LSTM_model.load_weights('./LSTM_weights.h5')
return LSTM_model
def LSTM_stateful():
LSTM_input = Input(batch_shape = (10,None,128))
h1 = LSTM(units=128, return_sequences=True,implementation = 2,recurrent_activation = 'hard_sigmoid',use_bias = False,stateful = True,return_state = True)(LSTM_input)
LSTM_model = Model(LSTM_input,h1)
LSTM_model.load_weights('./LSTM_weights.h5')
return LSTM_model
LSTM_model_not_stateful = LSTM_not_stateful()
LSTM_model_stateful = LSTM_stateful()
LSTM_output,h,c = LSTM_model_not_stateful.predict(LSTM_data)
LSTM_output1,h1,c1 = LSTM_model_stateful.predict(LSTM_data[:,0:1,:])
LSTM_output2,h2,c2 = LSTM_model_stateful.predict(LSTM_data[:,1:2,:])
LSTM_output[:,-1:,:]-LSTM_output2
Out[14]:
array([[[0., 0., 0., ..., 0., 0., 0.]],
[[0., 0., 0., ..., 0., 0., 0.]],
[[0., 0., 0., ..., 0., 0., 0.]],
...,
[[0., 0., 0., ..., 0., 0., 0.]],
[[0., 0., 0., ..., 0., 0., 0.]],
[[0., 0., 0., ..., 0., 0., 0.]]], dtype=float32)
注意在同一batch里面不同的sample是并行计算的,他们之间的状态不会传递,传递只会在不同batch对应的同一个sample间进行。
最后说一下unroll参数,这个参数如果为True,必须要求输入模型的batchsize不能为None,在这个情况下,tensorflow将会将原本使用符号计算的LSTM展开成计算图,可以在时间帧短的情况下达成加速,只不过会消耗更多内存。