Использование модели Tensorflow BI LSTM CTC в Android
TL;DR, я хочу знать, как использовать модель тензорного потока bi-lstm-ctc в приложении для Android.
Мне удалось обучить мою модель тензорного потока bi-lstm-ctc, и теперь я хочу использовать ее для своего приложения Android для распознавания рукописного ввода. Вот часть кода, которая определяет график, который я использовал:
self.inputs = tf.placeholder(tf.float32, [None, None, network_config.num_features], name="input")
self.labels = tf.sparse_placeholder(tf.int32, name="label")
self.seq_len = tf.placeholder(tf.int32, [None], name="seq_len_input")
logits = self._bidirectional_lstm_layers(
network_config.num_hidden_units,
network_config.num_layers,
network_config.num_classes
)
self.global_step = tf.Variable(0, trainable=False)
self.loss = tf.nn.ctc_loss(labels=self.labels, inputs=logits, sequence_length=self.seq_len)
self.cost = tf.reduce_mean(self.loss)
self.optimizer = tf.train.AdamOptimizer(network_config.learning_rate).minimize(self.cost)
self.decoded, self.log_prob = tf.nn.ctc_beam_search_decoder(inputs=logits, sequence_length=self.seq_len, merge_repeated=False)
self.dense_decoded = tf.sparse_tensor_to_dense(self.decoded[0], default_value=-1, name="output")
Мне также удалось заморозить и оптимизировать график после замораживания и оптимизировать коды графиков в этом руководстве. Вот часть кода, которая должна запускать модель:
bitmap = Bitmap.createScaledBitmap(bitmap, 1024, 128, true);
int[] intValues = new int[bitmap.getWidth() * bitmap.getHeight()];
bitmap.getPixels(intValues, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
float[] floatValues = new float[bitmap.getWidth() * bitmap.getHeight()];
for (int i = 0; i < intValues.length; ++i) {
final int val = intValues[i];
floatValues[i] = (((val >> 16) & 0xFF));
}
float[] result = new float[80];
long[] INPUT_SIZE = new long[]{1, bitmap.getHeight(), bitmap.getWidth()};
inferenceInterface.feed(config.getInputName(), floatValues, INPUT_SIZE);
inferenceInterface.feed("seq_len_input", new int[]{bitmap.getWidth()}, 1);
inferenceInterface.run(config.getOutputNames());
inferenceInterface.fetch(config.getOutputNames()[0], result);
return result.toString();
Тем не менее, я сталкиваюсь с этими проблемами в зависимости от модели, которую я использую. Если я использую замороженный график, я сталкиваюсь с этой ошибкой:
Caused by: java.lang.IllegalArgumentException: No OpKernel was registered to support
Op 'SparseToDense' with these attrs. Registered devices: [CPU], Registered kernels:
device='CPU'; T in [DT_STRING]; Tindices in [DT_INT64]
device='CPU'; T in [DT_STRING]; Tindices in [DT_INT32]
device='CPU'; T in [DT_BOOL]; Tindices in [DT_INT64]
device='CPU'; T in [DT_BOOL]; Tindices in [DT_INT32]
device='CPU'; T in [DT_FLOAT]; Tindices in [DT_INT64]
device='CPU'; T in [DT_FLOAT]; Tindices in [DT_INT32]
device='CPU'; T in [DT_INT32]; Tindices in [DT_INT64]
device='CPU'; T in [DT_INT32]; Tindices in [DT_INT32]
[[Node: output = SparseToDense[T=DT_INT64, Tindices=DT_INT64, validate_indices=true](CTCBeamSearchDecoder, CTCBeamSearchDecoder:2, CTCBeamSearchDecoder:1, output/default_value)]]
Если я использую оптимизированный замороженный график, я сталкиваюсь с этой ошибкой:
java.io.IOException: Not a valid TensorFlow Graph serialization: NodeDef expected inputs '' do not match 1 inputs
specified; Op<name=Const; signature= -> output:dtype; attr=value:tensor; attr=dtype:type>;
NodeDef: stack_bidirectional_rnn/cell_0/bidirectional_rnn/bw/bw/while/add/y = Const[dtype=DT_INT32,
value=Tensor<type: int32 shape: [] values: 1>](stack_bidirectional_rnn/cell_0/bidirectional_rnn/bw/bw/while/Switch:1)
Помимо способов устранения этих ошибок, у меня есть другие вопросы / разъяснения:
Как мне исправить эти ошибки?
1 ответ
Я уже заставил это работать. Решение также можно найти в этом выпуске github.
Видимо, проблема была в используемых типах. Я проходил int64, где принимается только int32.
self.dense_decoded = tf.sparse_tensor_to_dense(self.decoded[0], default_value=-1, name="output")
Чтобы решить эту проблему, я привел редкие тензорные элементы к int32:
self.dense_decoded = tf.sparse_to_dense(tf.to_int32(self.decoded[0].indices),
tf.to_int32(self.decoded[0].dense_shape),
tf.to_int32(self.decoded[0].values),
name="output")
Запуск приложения после этого дал мне эту ошибку:
java.lang.IllegalArgumentException: Matrix size-incompatible: In[0]: [1,1056], In[1]: [160,128]
[[Node:stack_bidirectional_rnn/cell_0/bidirectional_rnn/bw/bw/while/bw/basic_lstm_cell/basic_lstm_cell/
MatMul = MatMul[T=DT_FLOAT, transpose_a=false, transpose_b=false, _device="/job:localhost/replica:0/task:0/cpu:0"]
(stack_bidirectional_rnn/cell_0/bidirectional_rnn/bw/bw/while/bw/basic_lstm_cell/basic_lstm_cell/concat,
stack_bidirectional_rnn/cell_0/bidirectional_rnn/bw/bw/while/bw/basic_lstm_cell/basic_lstm_cell/MatMul/Enter)]]
По какой-то странной причине изменение ширины изображения с 1024 до 128 в java-коде исправило эту ошибку. Запуск приложения снова дал мне эту ошибку:
java.lang.IllegalArgumentException: cannot use java.nio.FloatArrayBuffer with Tensor of type INT32
Проблема возникла при получении вывода. При этом я знал, что модель работает успешно, но приложение просто не может получить результаты.
inferenceInterface.run(outputs);
inferenceInterface.fetch(outputs[0], result); //where the error happens
Глупо, забыл, что выходные данные - это целочисленный массив, а не массив с плавающей точкой Итак, я изменил тип результирующего массива на массив int:
//float[] result = new float[80];
int[] result = new int[80];
Таким образом, приложение работает. Точность модели не очень хорошая, так как она не была обучена должным образом. Я просто пытался заставить его работать в приложении. Пришло время для серьезных тренировок!