tensorflow/lite/delegates/gpu/common/lstm_parser.cc
// Builds subgraph for a single LSTM gate.
// Returns a Value representing the gate's output.
// High-level parameters:
//   - Has normalization (if true: provide normalization weights).
//   - Has peephole connection (if true: provide peephole weights).
//   - Which activation function to use.
// Note: no support for aux input.
//
// Implements the following:
// (*: matrix multiply, .*: elementwise multiply, +: elementwise add):
//   temp = input_weights * input_tensor + recurrent_weights * output_state;
//   if (peephole):
//     temp += peephole_weights .* cell_state;
//   if (layer normalization):
//     gate = activate(normalization_weights .* mean_stddev_norm(temp) + bias);
//   else:
//     gate = activate(temp + bias);
//
Builds subgraph for a single LSTM gate.
Returns a Value representing the gate's output.
High-level parameters:
Has normalization (if true: provide normalization weights).
Has peephole connection (if true: provide peephole weights).
Which activation function to use.
Note: no support for aux input.


Builds subgraph for a single LSTM gate . Returns a Value representing the gate ' s output . High - level parameters : Has normalization ( if true : provide normalization weights ) . Has peephole connection ( if true : provide peephole weights ) . Which activation function to use . Note : no support for aux input .
absl::Status BuildLstmGate(GraphFloat32* graph, ObjectReader* reader,
                           Value* output_state, Value* cell_state,
                           int input_weight_id, int recurrent_weight_id,
                           int cell_weight_id, int bias_id,
                           int normalization_weight_id,
                           const TfLiteFusedActivation activation,
                           bool has_peephole, bool has_normalization,
                           Value** gate_out) {
  Value* input_times_weights = CreateNewSimilarValue(graph, cell_state);
  {
    Node* node = graph->NewNode();
    int input_bias_id = !has_normalization ? bias_id : -1;
    RETURN_IF_ERROR(
        GetFullyConnectedNode(input_weight_id, input_bias_id, reader, node));
    RETURN_IF_ERROR(
        reader->AddInput(node, tflite::ops::builtin::lstm::full::kInputTensor));
    RETURN_IF_ERROR(graph->SetProducer(node->id, input_times_weights->id));
  }
  Value* output_state_times_weights = CreateNewSimilarValue(graph, cell_state);
  {
    Node* node = graph->NewNode();
    RETURN_IF_ERROR(
        GetFullyConnectedNode(recurrent_weight_id, -1, reader, node));
    RETURN_IF_ERROR(graph->AddConsumer(node->id, output_state->id));
    RETURN_IF_ERROR(
        graph->SetProducer(node->id, output_state_times_weights->id));
  }
  Value* cell_state_times_weights;
  if (has_peephole) {
    cell_state_times_weights = CreateNewSimilarValue(graph, cell_state);
    Node* node = graph->NewNode();
    node->operation.type = ToString(OperationType::MUL);
    ElementwiseAttributes attr;
    Tensor<Linear, DataType::FLOAT32> weights;
    RETURN_IF_ERROR(reader->ReadTensor(cell_weight_id, &weights));
    attr.param = std::move(weights);
    node->operation.attributes = std::move(attr);
    RETURN_IF_ERROR(graph->AddConsumer(node->id, cell_state->id));
    RETURN_IF_ERROR(graph->SetProducer(node->id, cell_state_times_weights->id));
  }
  Value* gate_before_normalization = CreateNewSimilarValue(graph, cell_state);
  Node* add_node = graph->NewNode();
  {
    add_node->operation.type = ToString(OperationType::ADD);
    RETURN_IF_ERROR(graph->AddConsumer(add_node->id, input_times_weights->id));
    RETURN_IF_ERROR(
        graph->AddConsumer(add_node->id, output_state_times_weights->id));
    if (has_peephole) {
      RETURN_IF_ERROR(
          graph->AddConsumer(add_node->id, cell_state_times_weights->id));
    }
    RETURN_IF_ERROR(
        graph->SetProducer(add_node->id, gate_before_normalization->id));
  }
  if (!has_normalization) {
    RETURN_IF_ERROR(MaybeFuseActivation(activation, graph, add_node));
    *gate_out = gate_before_normalization;
    return absl::OkStatus();
  }
  Value* normalized_gate =
      CreateNewSimilarValue(graph, gate_before_normalization);
  {
    Node* node = graph->NewNode();
    node->operation.type = ToString(OperationType::MEAN_STDDEV_NORMALIZATION);
    RETURN_IF_ERROR(
        graph->AddConsumer(node->id, gate_before_normalization->id));
    RETURN_IF_ERROR(graph->SetProducer(node->id, normalized_gate->id));
  }
  Value* reweighted_normalized_gate =
      CreateNewSimilarValue(graph, normalized_gate);
  {
    Node* node = graph->NewNode();
    node->operation.type = ToString(OperationType::MUL);
    ElementwiseAttributes attr;
    Tensor<Linear, DataType::FLOAT32> norm_weights;
    RETURN_IF_ERROR(reader->ReadTensor(normalization_weight_id, &norm_weights));
    attr.param = std::move(norm_weights);
    node->operation.attributes = std::move(attr);
    RETURN_IF_ERROR(graph->AddConsumer(node->id, normalized_gate->id));
    RETURN_IF_ERROR(
        graph->SetProducer(node->id, reweighted_normalized_gate->id));
  }
  Value* gate = CreateNewSimilarValue(graph, reweighted_normalized_gate);
  {
    Node* node = graph->NewNode();
    node->operation.type = ToString(OperationType::ADD);
    ElementwiseAttributes attr;
    Tensor<Linear, DataType::FLOAT32> bias;
    RETURN_IF_ERROR(reader->ReadTensor(bias_id, &bias));
    attr.param = std::move(bias);
    node->operation.attributes = std::move(attr);
    RETURN_IF_ERROR(
        graph->AddConsumer(node->id, reweighted_normalized_gate->id));
    RETURN_IF_ERROR(graph->SetProducer(node->id, gate->id));
    RETURN_IF_ERROR(MaybeFuseActivation(activation, graph, node));
  }
  *gate_out = gate;
  return absl::OkStatus();
}
absl :: Status BuildLstmGate ( GraphFloat32 * graph , ObjectReader * reader , Value * output_state , Value * cell_state , int input_weight_id , int recurrent_weight_id , int cell_weight_id , int bias_id , int normalization_weight_id , const TfLiteFusedActivation activation , bool has_peephole , bool has_normalization , Value * * gate_out ) { Value * input_times_weights = CreateNewSimilarValue ( graph , cell_state ) ; { Node * node = graph -> NewNode ( ) ; int input_bias_id = ! has_normalization ? bias_id : -1 ; RETURN_IF_ERROR ( GetFullyConnectedNode ( input_weight_id , input_bias_id , reader , node ) ) ; RETURN_IF_ERROR ( reader -> AddInput ( node , tflite :: ops :: builtin :: lstm :: full :: kInputTensor ) ) ; RETURN_IF_ERROR ( graph -> SetProducer ( node -> id , input_times_weights -> id ) ) ; } Value * output_state_times_weights = CreateNewSimilarValue ( graph , cell_state ) ; { Node * node = graph -> NewNode ( ) ; RETURN_IF_ERROR ( GetFullyConnectedNode ( recurrent_weight_id , -1 , reader , node ) ) ; RETURN_IF_ERROR ( graph -> AddConsumer ( node -> id , output_state -> id ) ) ; RETURN_IF_ERROR ( graph -> SetProducer ( node -> id , output_state_times_weights -> id ) ) ; } Value * cell_state_times_weights ; if ( has_peephole ) { cell_state_times_weights = CreateNewSimilarValue ( graph , cell_state ) ; Node * node = graph -> NewNode ( ) ; node -> operation . type = ToString ( OperationType :: MUL ) ; ElementwiseAttributes attr ; Tensor < Linear , DataType :: FLOAT32 > weights ; RETURN_IF_ERROR ( reader -> ReadTensor ( cell_weight_id , & weights ) ) ; attr . param = std :: move ( weights ) ; node -> operation . attributes = std :: move ( attr ) ; RETURN_IF_ERROR ( graph -> AddConsumer ( node -> id , cell_state -> id ) ) ; RETURN_IF_ERROR ( graph -> SetProducer ( node -> id , cell_state_times_weights -> id ) ) ; } Value * gate_before_normalization = CreateNewSimilarValue ( graph , cell_state ) ; Node * add_node = graph -> NewNode ( ) ; { add_node -> operation . type = ToString ( OperationType :: ADD ) ; RETURN_IF_ERROR ( graph -> AddConsumer ( add_node -> id , input_times_weights -> id ) ) ; RETURN_IF_ERROR ( graph -> AddConsumer ( add_node -> id , output_state_times_weights -> id ) ) ; if ( has_peephole ) { RETURN_IF_ERROR ( graph -> AddConsumer ( add_node -> id , cell_state_times_weights -> id ) ) ; } RETURN_IF_ERROR ( graph -> SetProducer ( add_node -> id , gate_before_normalization -> id ) ) ; } if ( ! has_normalization ) { RETURN_IF_ERROR ( MaybeFuseActivation ( activation , graph , add_node ) ) ; * gate_out = gate_before_normalization ; return absl :: OkStatus ( ) ; } Value * normalized_gate = CreateNewSimilarValue ( graph , gate_before_normalization ) ; { Node * node = graph -> NewNode ( ) ; node -> operation . type = ToString ( OperationType :: MEAN_STDDEV_NORMALIZATION ) ; RETURN_IF_ERROR ( graph -> AddConsumer ( node -> id , gate_before_normalization -> id ) ) ; RETURN_IF_ERROR ( graph -> SetProducer ( node -> id , normalized_gate -> id ) ) ; } Value * reweighted_normalized_gate = CreateNewSimilarValue ( graph , normalized_gate ) ; { Node * node = graph -> NewNode ( ) ; node -> operation . type = ToString ( OperationType :: MUL ) ; ElementwiseAttributes attr ; Tensor < Linear , DataType :: FLOAT32 > norm_weights ; RETURN_IF_ERROR ( reader -> ReadTensor ( normalization_weight_id , & norm_weights ) ) ; attr . param = std :: move ( norm_weights ) ; node -> operation . attributes = std :: move ( attr ) ; RETURN_IF_ERROR ( graph -> AddConsumer ( node -> id , normalized_gate -> id ) ) ; RETURN_IF_ERROR ( graph -> SetProducer ( node -> id , reweighted_normalized_gate -> id ) ) ; } Value * gate = CreateNewSimilarValue ( graph , reweighted_normalized_gate ) ; { Node * node = graph -> NewNode ( ) ; node -> operation . type = ToString ( OperationType :: ADD ) ; ElementwiseAttributes attr ; Tensor < Linear , DataType :: FLOAT32 > bias ; RETURN_IF_ERROR ( reader -> ReadTensor ( bias_id , & bias ) ) ; attr . param = std :: move ( bias ) ; node -> operation . attributes = std :: move ( attr ) ; RETURN_IF_ERROR ( graph -> AddConsumer ( node -> id , reweighted_normalized_gate -> id ) ) ; RETURN_IF_ERROR ( graph -> SetProducer ( node -> id , gate -> id ) ) ; RETURN_IF_ERROR ( MaybeFuseActivation ( activation , graph , node ) ) ; } * gate_out = gate ; return absl :: OkStatus ( ) ; }
Builds subgraph for a single LSTM gate.
Builds subgraph for a single LSTM gate .
// #1 matrix multiplication: input_weights * input_tensor // If has no normalization, also adds bias. // #2 matrix multiplication: recurrent_weights * output_state // #3 elementwise multiplication: cell_weight .* cell_state // #4 elementwise addition: #1 + #2 + #3 // #5 Activation function: activate(temp + bias) // Bias is added in node #1. // #6 Normalization: normalize(temp) // #7 Elementwise multiplication: norm_weights .* #6 // #8 Elementwise add: #7 + bias // #9: Activation function