В этом руководстве я покажу, как я преобразовал предварительно подготовленную сеть, написанную на TF 2.0, с немного продвинутой архитектурой, в оптимизированную сеть, подходящую для вывода. Моей целью было запустить предтренированную модель в выводе, используя C ++ и модуль Dnn OpenCV . Однако это руководство поможет вам создать оптимизированную модель, которая может использоваться на многих платформах, включая Python и TensorRT.
После обучения модели в TF 2.0 я искал простой способ оптимизировать модель для вывода. Однако я не нашел ни одного места, которое помогло бы мне сделать это в TF 2.0, поскольку TF удалила их поддержку в замороженных оптимизированных графиках, как видно из всех жалоб, собранных в StackOverflow (например, 1 , 2 ).
Есть много решений, написанных на разных интернет-форумах. Я пишу руководство по самому простому способу, который я нашел для этого.
Технические характеристики системы
Anaconda3 4.4+,
Tensorflow 2.0,
OpenCV 4.1.2+,
Вам также может понравиться:
Введение в TensorFlow .
Начиная
В моей сетевой реализации я использовал:
- Набор данных и итераторы ( 
tf.data.dataset). - Заполнители ( 
tf.placeholder). - Свертки ( 
tf.layers.conv2d). - Глубинные свертки ( 
tf.nn.depthwise_conv2dиtd.nn.bias_add). - Relu6 ( 
tf.nn.relu6). - Пакетная нормализация ( 
tf.layers.batch_normalization). - Свести ( 
tf.layers.flatten). - Softmax ( 
tf.nn.softmax). - Глобальное среднее ( 
tf.layers.average_pooling2d). 
После обучения мы хотели бы экспортировать нашу модель в файл .pb, чтобы запустить его на логическом этапе. Tensorflow имеет простой API кода для публикации модели:
 tf.saved_model.simple_save(sess,path,inputs,outputs) 
Однако это приведет к экспорту полной модели во многие файлы, включая определение графика, переменные, и не удалит сводки, которые не нужны во время вывода. Это ограничивает производительность использования графического процессора, поскольку ненужные сетевые компоненты часто работают на процессоре.
До TF 2.0 наилучшей практикой было:
- Заморозить модель (взять график def в сочетании с контрольной точкой и преобразовать все переменные в константы только в одном файле pb)
 - Оптимизируйте замороженный график для вывода, в том числе:
- Удалить неиспользуемые узлы.
 - Сложите арифметику и выражения для констант.
 - Удалить личность и никаких операций.
 - Сложите пакетную нормализацию в простые умножения.
 - Сортировать по порядку выполнения, который необходим для запуска в OpenCV.
 
 
Этот код в основном поддерживался Graph Transform Tool и был интегрирован в библиотеку инструментов TensorFlow. Это было до TF2.0, где TF удалил свою поддержку и свой граф использует код, поскольку tf.compact1.graph_utilв основном он пуст. Вместо этого они ожидают, что разработчики будут использовать API SavedModels  . Однако другие библиотеки и модули логического вывода не поддерживают его, и пользователям TF нужно сломать голову, чтобы найти способ оптимизировать свою модель.
Я подробно опишу, что я сделал, чтобы заморозить мою модель:
Напишите свой код заранее, чтобы соответствовать выводу
Используйте флаг с именем Inference. 
Для итераторов используйте:
питон
xxxxxxxxxx
1
if not inference:
2
    X,y=iterator.get_next() 
3
    is_training=tf.placeholder(tf.bool, name=”is_training”)
4
else:
5
    X=tf.placeholder(tf.float32,shape=…)
6
    y=tf.placeholder(tf.float32,shape=…)
7
    is_training=tf.constant(tf.bool, name=”is_training”)
8
 
Я рекомендую этот метод вместо пост-редактирования графика. Это помогает рассмотреть вывод с самого начала разработки. Например, я изменил свою реализацию среднего пула во время обучения, от использования average_pooling2dк reduce_sumпростому использованию , что проще и будет работать на большем количестве платформ. 
Вы также можете добавить обработку под этим флагом для сводок и автора файлов. Вместо того, чтобы убирать их на пост-тренинге, просто не добавляйте их 🙂
Тренируй модель в TF 2.0
Сохранить контрольную точку модели, используя:
tf.train.Saver(tf.all_varibles()). save(sess,path) 
Сохраните графическое определение модели, используя:
 Tf.train.write_graph(sess.graph,path) 
Создайте новую среду с tenorflow 1.15 и запустите следующий код:
питон
xxxxxxxxxx
1
From tf.python,tools import freeze_graph,optimize_for_inference_lib
2
freeze_graph.freeze_graph(input_graph_path,input_saver_def_path=””, 
4
                          input binary =False,checkpoint_path=checkpoint_path,
5
                          output_nodes=[“predictions”],restore_op_name = “”, 
6
                          filename tensor_ name=””, clear_devices=False,””)
7
inputGraph = tf.GraphDef() 
9
with tf.gfile.Open(frozen graph path, “rb”) as f: 
10
    data2read = f.read() 
11
    inputGraph.ParseFromString(data2read) 
12
outputGraph = optimize_for_inference_lib.
14
                    optimize_for_inference(inputGraph, [input node name],
15
                                           [output node names], 
16
                                           tf.float32.as datatype enum)
17
with tf.gfile.FastGFile(optimized graph path, ‘w’) as f :
18
    f.write(outputGraph.SerializeToString())
Этот код должен работать отлично, но не работал на моей конкретной модели. Я получил исключение:
 “Didn’t find expected Conv2D input to bn” 
Это было странно, и я начал исследовать свой код и выяснить, была ли проблема в моей сети или изменилась среда.
Наконец, я обнаружил, что это не было моим предположением. Я обнаружил, что код TF1.15 не обновляется последним кодом инструмента преобразования графов inference_lib. Проблема с кодом заключалась в том, что при пакетной нормализации в качестве входных данных принималась операция Conv2D. Но в моей сети я использовал смещение, используя результирующую BiasAddоперацию перед применением нормализации партии
Прежде чем пытаться исправить ошибку, я искал в Интернете и увидел, что об этом уже позаботились в GitHub, но он не обновлялся в TF и не будет, так как он был удален в TF2.0.
Я изменил код TF и исправил файл в:
%installation python folder%/tensorflow/python/tools/optimjzed_for_inference_lib.py 
с прикрепленным кодом . После этих исправлений мой код не возвращал никаких ошибок.
Я надеюсь, что это руководство поможет кому-то конвертировать модель, написанную на TF2.0, для запуска на выводе.
Удачи 🙂
В следующем посте я покажу, как запустить оптимизированный граф при выводе на OpenCV в C ++ и на TF в Python. Я сравню их (подсказка: на процессоре они довольно равны).