如何从 [c_api.h] 向 TF_SessionRun 提供训练数据

How to provide training data to TF_SessionRun from [c_api.h]

我正在尝试使用 TF 2.0beta C api(c_api.h).

实现 fashion-mnist 教程的训练部分

简而言之,模型是按照前面提到的教程使用 python 创建的。然后将模型保存为 .pb 文件,然后将其与训练数据(28*28 灰度图像)和训练标签(标量 运行ging from [0-9])一起加载到我的 C 程序中。

你们中的一些人可能知道,TF C api 的文档很差(插入 运行t 充满挫败感 here) and after much reading and searching on the internet here, here, here, and here 在许多其他地方,我仍然无法实现此任务(下面有更多详细信息)。

让我们开始吧:

我不想在代码中淹没这个问题,但我希望您能够访问我已经完成的工作。

生成模型的pythonscript通过以下方式保存: keras.experimental.export_saved_model.

简单的执行代码: python3 createModel.py.

保存的模型将存储在名为“saved_model”的新文件夹中。 这很重要,因为这个名称是硬编码在 C 代码中的

Data 用于训练模型也是通过 python TF 获得的。

再次:python getData.py.

将数据和相应的标签保存为 data.txt 和 labels.txt 同样,这些名称很重要,因为它们已硬编码在我的 C 代码中

现在我们可以进入 C 代码了!!

所有代码都驻留在一个 single 文件中,其中包含我想象的尽可能多的注释。只要你有 libtensorflow.so.2 和 libtensorflow_framework.so.2.0.0

编译就应该很简单

gcc -Wall -I /path/to/TFlib/include -L /path/to/TFlib/lib -o trainMnist trainMnist.c

在x86_64系统中应该没有警告(至少我试过的两台电脑是这样)

编译后执行:

LD_LIBRARY_PATH=/path/to/TFlib/lib ./trainMnist

如果 .so 文件已经可以访问,您可以跳过 LD_LIBRARY_PATH。

如果您没有 TF 共享对象文件,如果您敢于相信来自 运行dom 人的编译代码,我很乐意与任何人共享它们。您可以自己生成它:

git clone https://github.com/tensorflow/tensorflow.git
cd tensorflow
git checkout r2.0
./configure
bazel build -c opt //tensorflow/tools/lib_package:libtensorflow 

header 和 .so 文件将在 bazel-bin/tensorflow/tensorflow/tools/lib_package/libtensorflow.tar.gz

假设 saved_modeldata.txtlabels.txt 与 ./trainMnist 所在的目录相同 运行,应该只打印 TF 警告和一些冗长的警告来自代码本身的消息。

但感兴趣的函数是:

int Belly_ModelTrain(model_t *model,
                     float **train_data,
                     float **label_data,
                     int numPoints)

model 是一个包含重要成员以启动 TF 会话的结构

train_data 包含 28*28=784 个浮点数(图像的像素值)的 numPoints 数组

label_data 包含 10 个浮点数的 numPoints 数组(除该图像的真实标签外均为 0)。

numPoints 是加载的图像数量。 numPoints 的值在 main.

中硬编码

在此函数中,训练按以下方式进行:

  // The actual tensors that will hold the data and be passed to TF_SessionRun
  TF_Tensor *x, *y;
  
  // Establish the dimentions of the input data
  const int64_t dimTrain[] = {numPoints, 784}; //pixel values
  const int64_t dimLabel[] = {numPoints, 10};  //labels
  
  {
  //Boilerplate allocation code happens here 
  size_t nbytesT = (numPoints*784) * sizeof(float);
  x = TF_AllocateTensor(TF_FLOAT, dimTrain, 2, nbytesT);

  size_t nbytesL = (numPoints*10) * sizeof(float);
  y = TF_AllocateTensor(TF_FLOAT, dimLabel, 2, nbytesL);
  }
  
  //Copy the data in my arrays to the tensors
  memcpy(TF_TensorData(x), train_data, nbytesT);
  memcpy(TF_TensorData(y), label_data, nbytesL);

  //TBH I really don't know what is going on in these two lines
  //Establish model inputs and model targets(labels)
  TF_Output inputs[2] = {model->input, model->target};
  TF_Tensor* input_values[2] = {x, y};
  //training operation defined in model
  const TF_Operation* train_op[1] = {model->train_op};

  //Run the model with the data
  TF_SessionRun(model->session,
                NULL,
                inputs, input_values, 2,
                NULL, NULL, 0,
                train_op, 1,
                NULL, model->status);
 
  TF_DeleteTensor(x);
  TF_DeleteTensor(y);
  //Return 0 on success
  return Belly_CheckStatus(model->status);

我希望这个函数 运行 很好并且 return 0 基于模型的状态应该是 TF_OK。这当然失败了,我得到:

错误:节点 'dense_2_target'(类型:'Placeholder',输出数量:1)没有输出 [一些疯狂的 batshit 数字]

根据错误消息,我了解到我的 target 输入格式错误。我已经尝试更改我通过 label_data 的方式以及更改 dimL 但没有任何成功。

原来我在使用 C api 时还在思考 python。

trainMnist.c中,数据被读入数组数组(float **points):

int Belly_ReadData(float **points, int numSamples) {
  ...
  ...
  ...
  for(int i = 0; i < numSamples; i++) {
    //Allocate memory for datapoint
    points[i] = (float *)malloc(784*sizeof(float));
  ...
  ...
  ...
    //Read other 783 pixels
    for(int j = 0; j < 783; j++) {
      point = strtok(NULL,"\t");
      points[i][j+1] = strtof(point,&err);
      if(*err != 0) return -1;
    }
  ...
  }
}

之后这个数组数组被输入到输入张量"x"中:

int Belly_ModelTrain(model_t *model,
                     float **train_data,
                     float **label_data,
                     int numPoints) {
  ...
  ...
  ...

  // Allocate data for tensors
  x = TF_AllocateTensor(TF_FLOAT, dimTrain, 2, nbytesT);
  if(x==NULL) {
    printf("ERROR allocate x\n");
    return -1;
  }

  y = TF_AllocateTensor(TF_FLOAT, dimLabel, 2, nbytesL);
  if(y==NULL) {
    printf("ERROR allocate y\n");
    return -1;
  }

  // Copy data from arrays into tensors
  memcpy(TF_TensorData(x), train_data, nbytesT); //<===== "train_data should be an array of floats"
  memcpy(TF_TensorData(y), label_data, nbytesL); //<===== "label_data should be an array of floats"
  ...
  ...
  ...

按照我的理解,我将垃圾数据传递给了张量 x 和 y。

Will post 为任何想尝试的人更正代码。