tflite: get_tensor для тензоров без вывода дает случайные значения

Я пытаюсь отладить свою tflite модель, в которой используются специальные операции. Я обнаружил соответствие между именами операций (в *.pb) и идентификаторами операций (в *.tflite), и я провожу сравнение уровня на слой (чтобы убедиться, что разница выходов всегда находится в диапазоне 1e-4 (поскольку он взрывается в конце я хочу найти точное место, где мой пользовательский слой не работает) следующим образом:


Метод 1. Я использую get_tensor, чтобы получить следующий результат:

from tensorflow.contrib.lite.python import interpreter

# load the model
model = interpreter.Interpreter(model_path='model.tflite')
model.allocate_tensors()

# get tensors
for i in tensor_ids:
    tensor_output[i] = model.get_tensor(i)

Он показывает совершенно неадекватные случайные значения (по сравнению с выходными данными модели TensorFlow).


Метод 2. Преобразуйте *.pb только до определенного уровня, а затем повторите, в основном:

  1. Создайте *.pb, чтобы он содержал сеть только от input до layer_1.

  2. Преобразуйте в tflite (теперь результат будет layer_1) и проверьте выходные данные TF-Lite с помощью TensorFlow.

  3. Повторите шаги 1-2 для layer_2, layer_3, ... outputs.

Этот метод требует гораздо больше работы и выполнения, но он правильно показывает, что для встроенных операций выходы моделей tflite и pb были идентичны, и только начинает различаться в моих пользовательских операциях (в то время как в методе 1 выходы расходятся сразу от первых слоев).


Вопрос: Почему get_tensor так странно ведет себя? Может быть, это потому, что я использую tensorflow 1.9 (когда TF-Lite еще не был выпущен и был доступен только в предварительной версии для разработчиков)?

PS: Мне известно о выпуске TF-Lite, но я вручную скомпилировал TensorFlow 1.9 для своего проекта, и теперь трудно изменить управление версиями.


person Dmitrii    schedule 01.11.2018    source источник


Ответы (4)


У меня была такая же проблема несколько месяцев назад. Дело в том, что TF-Lite полностью отличается от TensorFlow: в нем используются статическая память и планы выполнения, файлы отображения памяти для более быстрой загрузки и предполагается, что он оптимизирует все возможное в конвейере прямого распространения в сети.

Я не разработчик TF-Lite, но полагаю, что он сохраняет очень низкий объем памяти за счет повторного использования областей памяти, которые использовались для ранее вычисленных операций. Давайте посмотрим на идею на следующей иллюстрации:


Шаг 1: сначала мы передаем входные данные в символьный тензор I (в скобках). Скажем, его значение хранится в буфере с именем buffer_1.

     op1       op2       op3
(I) ---->  A  ---->  B  ---->  O
_________________________________
^^^        ^^^^^^^^^^^^       ^^^
input      intermediate    output
tensor     tensors         tensor

Шаг 2: Теперь нам нужно вычислить op1 на символьном тензоре I, чтобы получить символьный тензор A. Мы вычисляем buffer_1 и сохраняем значение символьного тензора A в буфере с именем buffer_2.

    [op1]      op2       op3
(I) ----> (A) ---->  B  ---->  O

Шаг 3: Теперь мы вычисляем op2 на символьном тензоре A, чтобы получить символьный тензор B. Мы вычисляем buffer_2 и сохраняем значение символьного тензора B в буфере с именем _16 _...

     op1      [op2]      op3
 I  ----> (A) ----> (B) ---->  O

Но подождите! Зачем тратить нашу память на хранение в buffer_3, если теперь у нас есть buffer_1, который не используется, и значение которого теперь бесполезно для получения результата O? Таким образом, вместо сохранения в buffer_3, мы фактически сохраним результаты этой операции в buffer_1!

Это основная идея эффективного повторного использования памяти, которая, как мне кажется, реализована в TF-Lite, учитывая его встроенный в toco анализатор статических графиков и другие вещи. И поэтому вы не можете просто применить get_tensor к тензорам без вывода.


Более простой способ отладки?

Вы упомянули, что пишете нестандартную операцию, так что я полагаю, вы построили tflite с bazel, верно? Затем вы можете ввести код регистрации в Interpreter::Invoke() в файле tensorflow/lite/interpreter.cc. Уродливый хак, но он работает.

PS: Буду рад, если встретятся разработчики TensorFlow Lite и прокомментируют это :)

person FalconUA    schedule 01.11.2018

Да, промежуточные тензоры можно перезаписывать, если они не указаны как выходы.

Изменить: мне удалось решить проблему, сделав все операции в списке вывода во время преобразования. Затем они сохраняются во время выполнения, и значения могут быть правильно прочитаны.

Видеть:

Получение квантованных активаций в tenorflow lite

person dez    schedule 06.12.2018
comment
Хотя эта ссылка может дать ответ на вопрос, лучше включить сюда основные части ответа и предоставить ссылку для справки. Ответы, содержащие только ссылки, могут стать недействительными, если ссылка на страницу изменится. как ответить - person Agilanbu; 06.12.2018

Я столкнулся с аналогичной проблемой, когда хотел преобразовать файл TFLite в другую структуру без доступа к исходному графу TF, который использовался для создания файла TFLite. Поскольку результат моего преобразования отличался от вывода модели TFLite, я хотел посмотреть на вывод промежуточных слоев. Благодаря этой теме на SO я узнал, что get_tensor() ненадежный подход.

Самым простым решением было отредактировать файл TFLite в шестнадцатеричном редакторе!

Выходом модели является индекс одного из тензоров в модели. В моем случае это был тензор 175 (вы можете увидеть это с помощью get_tensor_details(). Он хранится как little-endian int32 где-то в файле TFLite. Для тензора 175 значение TFLite будет содержать значение 0xAF000000.

Я хотел, чтобы вместо этого в выходных данных модели использовался тензор 3, поэтому я открыл файл TFLite в шестнадцатеричном редакторе, выполнил поиск 0xAF000000 и заменил его на 0x03000000. Сохраните файл и снова загрузите его с помощью интерпретатора TFLite. Работает как шарм. Вам просто нужно быть осторожным, чтобы файл мог содержать более одного вхождения 0xAF000000 (или того, что вы ищете). В моем файле TFLite он хранился ближе к концу.

Надеюсь, этот совет кому-нибудь пригодится. :-)

person Matthijs Hollemans    schedule 21.12.2019

По умолчанию TFLite не сохраняет промежуточные тензоры, это потому, что он оптимизирует использование памяти и повторно использует выделенную память тензора на основе зависимости потока данных. Вы можете использовать недавно добавленную функцию отладки, чтобы сохранить все тензоры.

interpreter = tf.lite.Interpreter(
    model_path="test.tflite",
    experimental_preserve_all_tensors=True)

Теперь вы можете просматривать промежуточные тензоры в этом интерпретаторе.

person Karim Nosseir    schedule 02.06.2021