最近在做一个模型的实时化工作,包括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展开成计算图,可以在时间帧短的情况下达成加速,只不过会消耗更多内存。