¿Qué son c_state y m_state en Tensorflow LSTM?

La documentación de tensorflow r0.12 para tf.nn.rnn_cell.LSTMCell describe esto como el inicio:

tf.nn.rnn_cell.LSTMCell.__call__(inputs, state, scope=None) 

donde el state es el siguiente:

state: si state_is_tuple es False, este debe ser un Tensor de estado, 2-D, batch x state_size. Si state_is_tuple es True, debe ser una tupla de Tensors de estado, ambos 2D, con tamaños de columna c_state y m_state.

¿Cuáles son los c_state aare y m_state y cómo encajan en los LSTM? No puedo encontrar referencia a ellos en ningún lugar en la documentación.

Aquí hay un enlace a esa página en la documentación.

Me he topado con la misma pregunta, así es como lo entiendo. Ejemplo minimalista de LSTM:

 import tensorflow as tf sample_input = tf.constant([[1,2,3]],dtype=tf.float32) LSTM_CELL_SIZE = 2 lstm_cell = tf.nn.rnn_cell.BasicLSTMCell(LSTM_CELL_SIZE, state_is_tuple=True) state = (tf.zeros([1,LSTM_CELL_SIZE]),)*2 output, state_new = lstm_cell(sample_input, state) init_op = tf.global_variables_initializer() sess = tf.Session() sess.run(init_op) print sess.run(output) 

Observe que state_is_tuple=True por lo tanto, al pasar el state a esta cell , debe estar en forma de tuple c_state y m_state son probablemente “Estado de memoria” y “Estado de celda”, aunque sinceramente NO estoy seguro, ya que estos términos solo se mencionan en los documentos. En el código y documentos sobre LSTM , las letras h y c se usan comúnmente para indicar “valor de salida” y “estado de celda”. http://colah.github.io/posts/2015-08-Understanding-LSTMs/ Esos tensores representan el estado interno combinado de la célula, y se deben pasar juntos. La antigua forma de hacerlo era simplemente concatenarlos, y la nueva forma es utilizar tuplas.

VIEJA FORMA:

 lstm_cell = tf.nn.rnn_cell.BasicLSTMCell(LSTM_CELL_SIZE, state_is_tuple=False) state = tf.zeros([1,LSTM_CELL_SIZE*2]) output, state_new = lstm_cell(sample_input, state) 

NUEVA MANERA:

 lstm_cell = tf.nn.rnn_cell.BasicLSTMCell(LSTM_CELL_SIZE, state_is_tuple=True) state = (tf.zeros([1,LSTM_CELL_SIZE]),)*2 output, state_new = lstm_cell(sample_input, state) 

Entonces, básicamente, todo lo que hicimos fue cambiar el state de ser 1 tensor de longitud 4 a dos tensores de longitud 2 . El contenido se mantuvo igual. [0,0,0,0] convierte en ([0,0],[0,0]) . (Se supone que esto lo hace más rápido)

Estoy de acuerdo en que la documentación no está clara. Mirando tf.nn.rnn_cell.LSTMCell.__call__ clarifica (tomé el código de TensorFlow 1.0.0):

 def __call__(self, inputs, state, scope=None): """Run one step of LSTM. Args: inputs: input Tensor, 2D, batch x num_units. state: if `state_is_tuple` is False, this must be a state Tensor, `2-D, batch x state_size`. If `state_is_tuple` is True, this must be a tuple of state Tensors, both `2-D`, with column sizes `c_state` and `m_state`. scope: VariableScope for the created subgraph; defaults to "lstm_cell". Returns: A tuple containing: - A `2-D, [batch x output_dim]`, Tensor representing the output of the LSTM after reading `inputs` when previous state was `state`. Here output_dim is: num_proj if num_proj was set, num_units otherwise. - Tensor(s) representing the new state of LSTM after reading `inputs` when the previous state was `state`. Same type and shape(s) as `state`. Raises: ValueError: If input size cannot be inferred from inputs via static shape inference. """ num_proj = self._num_units if self._num_proj is None else self._num_proj if self._state_is_tuple: (c_prev, m_prev) = state else: c_prev = array_ops.slice(state, [0, 0], [-1, self._num_units]) m_prev = array_ops.slice(state, [0, self._num_units], [-1, num_proj]) dtype = inputs.dtype input_size = inputs.get_shape().with_rank(2)[1] if input_size.value is None: raise ValueError("Could not infer input size from inputs.get_shape()[-1]") with vs.variable_scope(scope or "lstm_cell", initializer=self._initializer) as unit_scope: if self._num_unit_shards is not None: unit_scope.set_partitioner( partitioned_variables.fixed_size_partitioner( self._num_unit_shards)) # i = input_gate, j = new_input, f = forget_gate, o = output_gate lstm_matrix = _linear([inputs, m_prev], 4 * self._num_units, bias=True, scope=scope) i, j, f, o = array_ops.split( value=lstm_matrix, num_or_size_splits=4, axis=1) # Diagonal connections if self._use_peepholes: with vs.variable_scope(unit_scope) as projection_scope: if self._num_unit_shards is not None: projection_scope.set_partitioner(None) w_f_diag = vs.get_variable( "w_f_diag", shape=[self._num_units], dtype=dtype) w_i_diag = vs.get_variable( "w_i_diag", shape=[self._num_units], dtype=dtype) w_o_diag = vs.get_variable( "w_o_diag", shape=[self._num_units], dtype=dtype) if self._use_peepholes: c = (sigmoid(f + self._forget_bias + w_f_diag * c_prev) * c_prev + sigmoid(i + w_i_diag * c_prev) * self._activation(j)) else: c = (sigmoid(f + self._forget_bias) * c_prev + sigmoid(i) * self._activation(j)) if self._cell_clip is not None: # pylint: disable=invalid-unary-operand-type c = clip_ops.clip_by_value(c, -self._cell_clip, self._cell_clip) # pylint: enable=invalid-unary-operand-type if self._use_peepholes: m = sigmoid(o + w_o_diag * c) * self._activation(c) else: m = sigmoid(o) * self._activation(c) if self._num_proj is not None: with vs.variable_scope("projection") as proj_scope: if self._num_proj_shards is not None: proj_scope.set_partitioner( partitioned_variables.fixed_size_partitioner( self._num_proj_shards)) m = _linear(m, self._num_proj, bias=False, scope=scope) if self._proj_clip is not None: # pylint: disable=invalid-unary-operand-type m = clip_ops.clip_by_value(m, -self._proj_clip, self._proj_clip) # pylint: enable=invalid-unary-operand-type new_state = (LSTMStateTuple(c, m) if self._state_is_tuple else array_ops.concat([c, m], 1)) return m, new_state 

Las líneas clave son:

 c = (sigmoid(f + self._forget_bias) * c_prev + sigmoid(i) * self._activation(j)) 

y

 m = sigmoid(o) * self._activation(c) 

y

 new_state = (LSTMStateTuple(c, m) 

Si compara el código para calcular c y m con las ecuaciones de LSTM (vea a continuación), puede ver que corresponde al estado de la celda (típicamente denotado con c ) y al estado oculto (típicamente denotado con h ), respectivamente:

introduzca la descripción de la imagen aquí

new_state = (LSTMStateTuple(c, m) indica que el primer elemento de la tupla de estado devuelto es c (estado de célula también c_state como c_state ), y el segundo elemento de la tupla de estado devuelto es m (estado oculto conocido como m_state ).

Tal vez este extracto del código ayude

 def __call__(self, inputs, state, scope=None): """Long short-term memory cell (LSTM).""" with vs.variable_scope(scope or type(self).__name__): # "BasicLSTMCell" # Parameters of gates are concatenated into one multiply for efficiency. if self._state_is_tuple: c, h = state else: c, h = array_ops.split(1, 2, state) concat = _linear([inputs, h], 4 * self._num_units, True) # i = input_gate, j = new_input, f = forget_gate, o = output_gate i, j, f, o = array_ops.split(1, 4, concat) new_c = (c * sigmoid(f + self._forget_bias) + sigmoid(i) * self._activation(j)) new_h = self._activation(new_c) * sigmoid(o) if self._state_is_tuple: new_state = LSTMStateTuple(new_c, new_h) else: new_state = array_ops.concat(1, [new_c, new_h]) return new_h, new_state 

https://github.com/tensorflow/tensorflow/blob/r1.2/tensorflow/python/ops/rnn_cell_impl.py

Línea # 308 – 314.

class LSTMStateTuple (_LSTMStateTuple): “” “Tupla utilizada por las células LSTM para state_size , zero_state y output state. Almacena dos elementos: (c, h) , en ese orden. Sólo se usa cuando state_is_tuple=True .” “”