| 1450 | } // namespace |
| 1451 | |
| 1452 | void ConvertLstmCellOperator(const Model& model, const LstmCellOperator& src_op, |
| 1453 | GraphDef* tensorflow_graph) { |
| 1454 | // Find the base name |
| 1455 | const string base( |
| 1456 | FindLongestCommonPrefix(src_op.outputs[LstmCellOperator::STATE_OUTPUT], |
| 1457 | src_op.outputs[LstmCellOperator::ACTIV_OUTPUT])); |
| 1458 | |
| 1459 | // Concatenate inputs |
| 1460 | const string concat_output = base + "basic_lstm_cell/concat"; |
| 1461 | // Op names have been chosen to match the tf.slim LSTM naming |
| 1462 | // as closely as possible. |
| 1463 | const int axis = |
| 1464 | model.GetArray(src_op.inputs[LstmCellOperator::PREV_ACTIV_INPUT]) |
| 1465 | .shape() |
| 1466 | .dimensions_count() - |
| 1467 | 1; |
| 1468 | // Note that DATA_INPUT may have extra size 1 dimensions, but TF concat |
| 1469 | // works the same since the tensor has the same underlying data layout. |
| 1470 | const string axis_output = concat_output + "/axis"; |
| 1471 | CreateDummyConcatDimTensorConst(axis_output, axis, tensorflow_graph); |
| 1472 | tensorflow::NodeDef* concat_op = tensorflow_graph->add_node(); |
| 1473 | concat_op->set_op("ConcatV2"); |
| 1474 | concat_op->set_name(concat_output); |
| 1475 | *concat_op->add_input() = src_op.inputs[LstmCellOperator::DATA_INPUT]; |
| 1476 | *concat_op->add_input() = src_op.inputs[LstmCellOperator::PREV_ACTIV_INPUT]; |
| 1477 | *concat_op->add_input() = axis_output; |
| 1478 | (*concat_op->mutable_attr())["T"].set_type(DT_FLOAT); |
| 1479 | (*concat_op->mutable_attr())["Tidx"].set_type(DT_INT32); |
| 1480 | (*concat_op->mutable_attr())["N"].set_i(2); // Number of inputs |
| 1481 | |
| 1482 | // Write weights |
| 1483 | const string weights_output = base + "weights"; |
| 1484 | CHECK(model.HasArray(src_op.inputs[LstmCellOperator::WEIGHTS_INPUT])); |
| 1485 | const string weights_name = WalkUpToConstantArray( |
| 1486 | model, src_op.inputs[LstmCellOperator::WEIGHTS_INPUT]); |
| 1487 | const auto& weights_array = model.GetArray(weights_name); |
| 1488 | // Convert 4D FullyConnected weights into 2D matrix |
| 1489 | const auto& weights_shape = weights_array.shape(); |
| 1490 | CHECK_EQ(weights_shape.dimensions_count(), 2); |
| 1491 | CHECK(weights_array.buffer); |
| 1492 | CHECK(weights_array.buffer->type == ArrayDataType::kFloat); |
| 1493 | const float* weights_data = |
| 1494 | weights_array.GetBuffer<ArrayDataType::kFloat>().data.data(); |
| 1495 | ConvertFloatTensorConst(weights_output, weights_shape, weights_data, |
| 1496 | AxesOrder::kCR, AxesOrder::kRC, tensorflow_graph); |
| 1497 | |
| 1498 | // Fully connected matrix multiply |
| 1499 | const string matmul_output = base + "MatMul"; |
| 1500 | tensorflow::NodeDef* matmul_op = tensorflow_graph->add_node(); |
| 1501 | matmul_op->set_op("MatMul"); |
| 1502 | matmul_op->set_name(matmul_output); |
| 1503 | *matmul_op->add_input() = concat_output; |
| 1504 | *matmul_op->add_input() = weights_output; |
| 1505 | (*matmul_op->mutable_attr())["transpose_a"].set_b(false); |
| 1506 | (*matmul_op->mutable_attr())["transpose_b"].set_b(false); |
| 1507 | (*matmul_op->mutable_attr())["T"].set_type(DT_FLOAT); |
| 1508 | |
| 1509 | // Write biases |
no test coverage detected