简体   繁体   中英

Tensorflow C API - Problem with the Input Tensor

I'm working with the Tensorflow API for C to load a pre-trained model in python and run predictions in an embedded compiled program. The model I'm using takes a string as input, which is converted to a tensor, and gives a single float as output.

The API loads the model just fine and runs sessions without complaining.

The issue I'm facing is that no matter what data I feed into the C API session, I always get the exact same output tensor. So I'm guessing that I'm doing something wrong and I just can't see what it is. But I'm assuming that I am not formatting the input data in the way the C API expects.

Here is the output of saved_model_cli :

The given SavedModel SignatureDef contains the following input(s):
  inputs['lstm_input'] tensor_info:
      dtype: DT_FLOAT
      shape: (-1, 64, 88)
      name: serving_default_lstm_input:0
The given SavedModel SignatureDef contains the following output(s):
  outputs['dense'] tensor_info:
      dtype: DT_FLOAT
      shape: (-1, 1)
      name: StatefulPartitionedCall:0
Method name is: tensorflow/serving/predict

In python, here is how I transform the input strings to tensors: (The variables X and X_val hold the training strings and validation strings respectively)

from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences
from tensorflow.keras.utils import to_categorical

alphabet_size = len(set(all_lines))

tokenizer = Tokenizer(char_level=True)
tokenizer.fit_on_texts(all_lines)
seq_X = tokenizer.texts_to_sequences(X)
seq_X_val = tokenizer.texts_to_sequences(X_val)

seq_X = pad_sequences(seq_X, maxlen=64, padding='post')
seq_X_val = pad_sequences(seq_X_val, maxlen=64, padding='post')

one_hot_X = [to_categorical(x, num_classes=alphabet_size) for x in seq_X]
one_hot_X_val = [to_categorical(x_val, num_classes=alphabet_size) for x_val in seq_X_val]
one_hot_X = np.array(one_hot_X)
one_hot_X_val = np.array(one_hot_X_val)

Which finally gives me the following tokenizer.word_index :

{'%': 1, '2': 2, 'e': 3, 'l': 4, 'c': 5, 'n': 6, 'i': 7, 'a': 8, '0': 9, 's': 10, 'r': 11, 't': 12, '/': 13, '1': 14, 'o': 15, 'u': 16, ',': 17, 'd': 18, '7': 19, 'h': 20, '8': 21, ' ': 22, '6': 23, '9': 24, '=': 25, '\n': 26, '(': 27, ')': 28, 'p': 29, 'b': 30, '5': 31, 'm': 32, 'f': 33, 'w': 34, '3': 35, 'g': 36, 'v': 37, '?': 38, 'x': 39, "'": 40, '-': 41, '4': 42, '&': 43, '.': 44, '+': 45, '_': 46, 'y': 47, '|': 48, 'k': 49, 'j': 50, '@': 51, 'z': 52, '#': 53, '"': 54, 'q': 55, '>': 56, '*': 57, '~': 58, '!': 59, '^': 60, ':': 61, '<': 62}

So when I want to use this model in C, I load it using the method described here: https://github.com/AmirulOm/tensorflow_capi_sample

And here is how I setup the Session:

First, I have a C array that plays the same role as the tokenizer.word_index above:

int     dictionary[] = {
...
    14,//1 - 49
    2,//2 - 50
    35,//3 - 51
    42,//4 - 52
    31,//5 - 53
    23,//6 - 54
    19,//7 - 55
    21,//8 - 56
    24,//9 - 57
    61,//: - 58
    0,//59
    62,//< - 60
    25,//= - 61
    56,//> - 62
    38,//? - 63
    51,//@ - 64
...
    5,//c - 99
    18,//d - 100
    3,//e - 101
    33,//f - 102
    36,//g - 103
    20,//h - 104
    7,//i - 105
    50,//j - 106
    49,//k - 107
    4,//l - 108
    32,//m - 109
    6,//n - 110
    15,//o - 111
    29,//p - 112
    55,//q - 113
    11,//r - 114
    10,//s - 115
    12,//t - 116
    16,//u - 117
    37,//v - 118
    34,//w - 119
...
};  

The following function is used to fill a C float array in the same fashion that I do in the python model:

float   *get_input_tensor(char *text)
{
    int     i;
    float   *result;
    size_t  tensor_size;
    char    *current;

    i = 0;
    current = text;
    tensor_size = sizeof(float) * 1 * 64 * 88;
    result = (float*)malloc(sizeof(float) * tensor_size);
    memset(result, 0, tensor_size);
    while (*current)
    {
        *(result + 88 * i + dictionary[(int)*current]) = 1.00f;
        current++;
        i++;
    }
    return (result);
}

Finally, set up the Session:

    int     ndims = 3;
    int64_t dims[] = {1, 64, 88};
    float   *data = get_input_tensor("test_string");
    int     ndata = sizeof(float) * 1 * 64 * 88;

    TF_Tensor   *float_tensor = TF_NewTensor(TF_FLOAT, dims, ndims, data, ndata, &NoOpDeallocator, 0);
    TF_SessionRun(Session, NULL, Input, InputValues, NumInputs, Output, OutputValues, NumOutputs, NULL, 0, NULL, Status);0);

Running the program always give the following output:

[*] TF_NewTensor OK
[*] Starting session
[*] Session OK
[*] Result tensor: 0.999864
[*] Tensorflow Data memory cleared and freed

I'm quite lost here. So here's my question: given the input tensor shape from the model, how is the data supposed to be formatted in the C API before being thrown into TF_NewTensor and then into TF_SessionRun? Or is there a documentation online that I didn't find? Or even a general approach when filling the input tensor?

Data in API C of TF are stored in row-major order.

If you have 3D data layout, like [dim1,dim2,dim3] ( 1/64/88 ), and you want to access (d1,d2,d3) item you should use formula:

item = d1 * dim2 * dim3 + d2 * dim3 + d3

So you could write helper function like:

float* AccessResult(float* result, 
    int x, 
    int dim1, // 1
    int y, 
    int dim2, // 64
    int z,
    int dim3)  // 88
{
    float* item = result + x * dim2 * dim3 + y * dim3 + z;
    return item;
}

and the code filling out your array could be:

for (int i = 0; i < 64; ++i) {
    for (int j = 0; j < 88; ++j) {
        float* res = AccessResult(result,0,1,i,64,j,88);
        *res = 1.0f;
    }
}

i or j (when calling AccessResult ) should be replaced by dictionary[..] , but I could't infer which one should be replaced.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM