2018年10月21日星期日

Tensorflow's tensor

A simple program that use tensorflow's C API interface.

#include <stdio.h>
#include <stdlib.h>
#include <string>
#include "tensorflow/c/c_api.h"

/*
 * Super basic example of using google tensorflow directly from C
 *
 */

// Using stack input data nothing to free
void tensor_free_none(void * data, size_t len, void* arg) {
}

TF_Operation * PlaceHolder(TF_Graph * graph, TF_Status * status, TF_DataType dtype, const char * name) {
        TF_OperationDescription * desc = TF_NewOperation(graph, "Placeholder", name);
        TF_SetAttrType(desc, "dtype", TF_FLOAT);
        return TF_FinishOperation(desc, status);
}

TF_Operation * Const(TF_Graph * graph, TF_Status * status, TF_Tensor * tensor, const char * name) {
        TF_OperationDescription * desc = TF_NewOperation(graph, "Const", name);
        TF_SetAttrTensor(desc, "value", tensor, status);
        TF_SetAttrType(desc, "dtype", TF_TensorType(tensor));
        return TF_FinishOperation(desc, status);
}

TF_Operation * Add(TF_Graph * graph, TF_Status * status, TF_Operation * one, TF_Operation * two, const char * name) {
        TF_OperationDescription * desc = TF_NewOperation(graph, "AddN", name);
        TF_Output add_inputs[2] = {{one, 0}, {two, 0}};
        TF_AddInputList(desc, add_inputs, 2);
        return TF_FinishOperation(desc, status);
}


int main() {
        printf("TensorFlow C library version: %s\n", TF_Version());

        //test();

        TF_Graph * graph = TF_NewGraph();
        TF_SessionOptions * options = TF_NewSessionOptions();
        TF_Status * status = TF_NewStatus();
        TF_Session * session = TF_NewSession(graph, options, status);

        float in_val_one = 4.0f;
        float const_two = 2.0f;

        TF_Tensor * tensor_in = TF_NewTensor(TF_FLOAT, NULL, 0, &in_val_one, sizeof(float), tensor_free_none, NULL);
        TF_Tensor * tensor_out = NULL; // easy access after this is allocated by TF_SessionRun
        TF_Tensor * tensor_const_two = TF_NewTensor(TF_FLOAT, NULL, 0, &const_two, sizeof(float), tensor_free_none, NULL);

        // Operations
        TF_Operation * feed = PlaceHolder(graph, status, TF_FLOAT, "feed");
        TF_Operation * two = Const(graph, status, tensor_const_two, "const");
        TF_Operation * add = Add(graph, status, feed, two, "add");

        // Session Inputs
        TF_Output input_operations[] = { feed, 0 };
        TF_Tensor ** input_tensors = {&tensor_in};

        // Session Outputs
        TF_Output output_operations[] = { add, 0 };
        TF_Tensor ** output_tensors = {&tensor_out};

        TF_SessionRun(session, NULL,
                        // Inputs
                        input_operations, input_tensors, 1,
                        // Outputs
                        output_operations, output_tensors, 1,
                        // Target operations
                        NULL, 0, NULL,
                        status);

        printf("Session Run Status: %d - %s\n", TF_GetCode(status), TF_Message(status) );
        printf("Output Tensor Type: %d\n", TF_TensorType(tensor_out));
        float * outval = (float*)TF_TensorData(tensor_out);
        printf("Output Tensor Value: %.2f\n", *outval);

        TF_CloseSession(session, status);
        TF_DeleteSession(session, status);

        TF_DeleteSessionOptions(options);

        TF_DeleteGraph(graph);

        TF_DeleteTensor(tensor_in);
        TF_DeleteTensor(tensor_out);
        TF_DeleteTensor(tensor_const_two);

        TF_DeleteStatus(status);
        return 0;
}


I am trying to set a special flag in the "const" tensor.

The program allocates a TF_Tensor for this const_two float variable.

TF_Tensor * tensor_const_two = TF_NewTensor(TF_FLOAT, NULL, 0, &const_two, sizeof(float), tensor_free_none, NULL);

But is this TF_Tensor the one that linked with the graph?


Similar to C++ interface, C code also needs to build operation (add node to the graph).

TF_Operation * Const(TF_Graph * graph, TF_Status * status, TF_Tensor * tensor, const char * name) {
        TF_OperationDescription * desc = TF_NewOperation(graph, "Const", name);
        TF_SetAttrTensor(desc, "value", tensor, status);
        TF_SetAttrType(desc, "dtype", TF_TensorType(tensor));
        return TF_FinishOperation(desc, status);
}

TF_SetAttrTensor(desc, "value", tensor, status); is where sends the tensor value into the operation.

In tensorflow/c/c_api.cc:

void TF_SetAttrTensor(TF_OperationDescription* desc, const char* attr_name,
                      TF_Tensor* value, TF_Status* status) {
  Tensor t;
  status->status = TF_TensorToTensor(value, &t);
  if (status->status.ok()) desc->node_builder.Attr(attr_name, t);
}


Basically, TF_TensorToTensor is a copy function, allocate a new tensor, and add this newly allocated one into the graph.

So, if I set some flag in the tensor, here is where I need to add code.


Status TF_TensorToTensor(const TF_Tensor* src, Tensor* dst) {
  if (src->dtype == TF_RESOURCE) {
    if (src->shape.dims() != 0) {
      return InvalidArgument(
          "Malformed TF_RESOURCE tensor: expected a scalar, got a tensor with "
          "shape ",
          src->shape.DebugString());
    }
    *dst = Tensor(DT_RESOURCE, src->shape);
    if (!dst->scalar<ResourceHandle>()().ParseFromString(
            string(static_cast<const char*>(TF_TensorData(src)),
                   TF_TensorByteSize(src)))) {
      return InvalidArgument(
          "Malformed TF_RESOUCE tensor: unable to parse resource handle");
    }
    return Status::OK();
  }
  if (src->dtype != TF_STRING) {
    *dst = TensorCApi::MakeTensor(src->dtype, src->shape, src->buffer);
    return Status::OK();
  }
  // TF_STRING tensors require copying since Tensor class expects a sequence of
  // string objects.
  const tensorflow::int64 num_elements = src->shape.num_elements();
  const char* input = reinterpret_cast<const char*>(TF_TensorData(src));
  const size_t src_size = TF_TensorByteSize(src);
  if (static_cast<tensorflow::int64>(src_size / sizeof(tensorflow::uint64)) <
      num_elements) {
    return InvalidArgument(
        "Malformed TF_STRING tensor; too short to hold number of elements");
  }
  const char* data_start = input + sizeof(tensorflow::uint64) * num_elements;
  const char* limit = input + src_size;

  *dst = Tensor(static_cast<DataType>(src->dtype), src->shape);
  auto dstarray = dst->flat<string>();
  for (tensorflow::int64 i = 0; i < num_elements; ++i) {
    tensorflow::uint64 offset =
        reinterpret_cast<const tensorflow::uint64*>(input)[i];
    if (static_cast<ptrdiff_t>(offset) >= (limit - data_start)) {
      return InvalidArgument("Malformed TF_STRING tensor; element ", i,
                             " out of range");
    }
    size_t len;
    const char* p;
    const char* srcp = data_start + offset;
    Status status = TF_StringDecode_Impl(srcp, limit - srcp, &p, &len);
    if (!status.ok()) return status;
    dstarray(i).assign(p, len);
  }
  return Status::OK();
}




没有评论:

发表评论