Обслуживание вашей модели обнаружения объектов TensorFlow по сети

Этот блокнот является продолжением Обслуживание моделей глубокого обучения на основе изображений с помощью RESTful API TensorFlow-Serving. Обязательно прочтите эту статью, чтобы понять основы TensorFlow-Serving и библиотеки TensorFlow Distributed Image Serving (Tendies). Настоятельно рекомендуется клонировать репозиторий Tendies, чтобы следовать этому руководству, так как я сосредоточусь на важных отрывках кода, а не на всем файле. Если вы хотите просмотреть эту статью в блокноте Jupyter, нажмите здесь.

Здесь мы расширим функциональность базовых классов Tendies, чтобы интегрировать глубокую нейронную сеть Faster R-CNN, которая использует API обнаружения объектов TensorFlow. Это позволит нам обслуживать Faster R-CNN для REST-совместимого удаленного вывода, во многом как CycleGAN в предыдущей статье.

CycleGAN был довольно простым, потому что он принимал изображение и выводил изображение; однако Faster R-CNN принимает изображение и выводит словарь тензоров. Более того, API обнаружения объектов заставляет нас строить нашу модель из pipeline.config и переопределять функцию вывода, что делает обслуживание Faster R-CNN более сложной задачей. Шаги по интеграции новой модели с Tendies следующие:

  1. Определите функции предварительной и постобработки в LayerInjector.
  2. Создайте или импортируйте функцию вывода модели в ServerBuilder.
  3. Создайте или импортируйте клиента.

В то время как я буду демонстрировать с Faster R-CNN, эти шаги остаются такими же для любой произвольной модели, поэтому не стесняйтесь следовать вместе с вашим конкретным вариантом использования.

Инъекция слоев

Каждая функция предварительной обработки должна принимать в качестве аргументов строку битов изображения, размер изображения и * args (где * args может использоваться для представления любого количества настраиваемых позиционных аргументов), а затем возвращает входные данные модели в виде тензора. И наоборот, каждая функция постобработки должна принимать в качестве аргументов выходные данные модели и * args, а затем возвращать список имен выходных узлов и указывать, следует ли передавать выходные данные в виде изображения. Эти выходные данные затем будут использоваться в ServerBuilder при экспорте модели.

Сначала мы определим нашу функцию предварительной обработки, которая преобразует битовую строку изображения в тензор uint8, подходящий для вывода.

import tensorflow as tf
def bitstring_to_uint8_tensor(self, input_bytes, image_size, *args):
    input_bytes = tf.reshape(input_bytes, [])
# Transforms bitstring to uint8 tensor
    input_tensor = tf.image.decode_png(input_bytes, channels=3)
# Ensures tensor has correct shape
    input_tensor = tf.reshape(input_tensor, [image_size, image_size, 3])
# Expands the single tensor into a batch of 1
    input_tensor = tf.expand_dims(input_tensor, 0)
    return input_tensor

Модели, совместимые с API обнаружения объектов, возвращают словарь полезных тензоров, таких как num_detections, detect_boxes и т. Д. В нашей функции постобработки мы будем перебирать эти тензоры и присваивать им имена, чтобы мы могли извлечь их в ServerBuilder. Мы также должны учитывать 1-индексацию тензора detect_classes. Наконец, мы возвращаем список имен выходных узлов и устанавливаем output_as_image в False, поскольку мы будем отправлять выходные тензоры (а не визуализированное изображение) обратно клиенту через JSON.

def object_detection_dict_to_tensor_dict(self, object_detection_tensor_dict, *args):
    # Sets output to a non-image
    OUTPUT_AS_IMAGE = False
    # Class labels are 1-indexed
    LABEL_ID_OFFSET = 1
    # Assigns names to tensors and adds them to output list
    output_node_names = []
    for name, tensor in object_detection_tensor_dict.items():
        if name == "detection_classes":
            tensor += LABEL_ID_OFFSET
        tensor = tf.identity(tensor, name)
        output_node_names.append(name)
    # Returns output list and image boolean
    return output_node_names, OUTPUT_AS_IMAGE

Если вы следуете своей собственной модели, не стесняйтесь использовать * args, чтобы принять столько параметров, сколько вам нужно для обработки. Tendies довольно разборчив в отношении формы тензора и набора текста, поэтому убедитесь, что протоколы вывода вашего препроцессора и ввода вашего постпроцессора эквивалентны входным и выходным данным вашей модели, соответственно.

Функция вывода

Затем мы должны построить Faster R-CNN из pipeline.config и определить нашу функцию вывода. Код для этого находится в ServerBuilder.py в разделе example_usage (), где происходит экспорт нашей модели. Считывая файл конфигурации в API обнаружения объектов model_builder, мы можем создать экземпляр Faster R-CNN, даже не видя кода модели. Подразумевается, что следующие несколько ячеек находятся в области example_usage ().

from object_detection.protos import pipeline_pb2
from object_detection.builders import model_builder
from google.protobuf import text_format
# Builds object detection model from config file
pipeline_config = pipeline_pb2.TrainEvalPipelineConfig()
with tf.gfile.GFile(config_file_path, 'r') as config:
    text_format.Merge(config.read(), pipeline_config)
detection_model = model_builder.build(pipeline_config.model, is_training=False)

Поскольку export_graph ожидает единственной функции вывода, но API обнаружения объектов имеет свою собственную предварительную и постобработку, мы должны объединить их сами. Это отличное место для использования замыкания, потому что мы хотим сохранить область, в которой мы создали экземпляр Faster R-CNN при передаче функции вывода. Укупорки самые лучшие.

# Creates inference function, encapsulating object detection requirements
def object_detection_inference(input_tensors):
    # Converts uint8 inputs to float tensors
    inputs = tf.to_float(input_tensors)
    # Object detection preprocessing
    preprocessed_inputs, true_image_shapes = detection_model.preprocess(inputs)
    # Object detection inference
    output_tensors = detection_model.predict(preprocessed_inputs, true_image_shapes)
    # Object detection postprocessing
    postprocessed_tensors = detection_model.postprocess(output_tensors, true_image_shapes)
    return postprocessed_tensors

Наконец, мы создадим экземпляры ServerBuilder и LayerInjector, а затем экспортируем модель. Обратите внимание, что мы передаем нашу функцию вывода, препроцессор и постпроцессор в export_graph ().

# Instantiates a ServerBuilder
server_builder = ServerBuilder()
# Instantiates a LayerInjector
layer_injector = LayerInjector()
# Exports model
print("Exporting model to ProtoBuf...")
output_node_names, output_as_image = server_builder.export_graph(
                            object_detection_inference,
                            layer_injector.bitstring_to_uint8_tensor,
                            layer_injector.object_detection_dict_to_tensor_dict,
                            FLAGS.model_name,
                            FLAGS.model_version,
                            FLAGS.checkpoint_dir,
                            FLAGS.protobuf_dir,
                            FLAGS.image_size)
print("Wrapping ProtoBuf in SavedModel...")
server_builder.build_saved_model(output_node_names,
                                 output_as_image,
                                 FLAGS.model_name,
                                 FLAGS.model_version,
                                 FLAGS.protobuf_dir,
                                 FLAGS.serve_dir)
print("Exported successfully!")

Клиент

Лучший способ создания настраиваемых клиентов Tendies - это наследование от Client, которое обеспечивает основу для удаленного вывода. В таком дочернем классе нужно только создать visualize () и связанные с ним вспомогательные функции, а затем вызвать client.inference (), чтобы начать процесс оценки.

Нам понадобится пара этих вспомогательных функций; первая почти такая же, как наша функция предварительной обработки, за исключением добавления пакетной обработки.

def bitstring_to_uint8_tensor(self, input_bytes):
    input_bytes = tf.reshape(input_bytes, [])
    # Transforms bitstring to uint8 tensor
    input_tensor = tf.image.decode_jpeg(input_bytes, channels=3)
    # Ensures tensor has correct shape
    input_tensor = tf.reshape(input_tensor, [self.image_size, self.image_size, 3])
    return input_tensor

Наша вторая вспомогательная функция будет использоваться для создания нашего словаря индекса категории из API обнаружения объектов из предоставленной карты меток; эта конкретная реализация Faster R-CNN имеет только один класс, так что это просто:

from object_detection.utils import label_map_util
def get_category_index(self):
    # Loads label map
    label_map = label_map_util.load_labelmap(self.label_path)
    
    # Builds category index from label map
    categories = label_map_util.convert_label_map_to_categories(label_map, max_num_classes=1, use_display_name=True)
    category_index = label_map_util.create_category_index(categories)
    return category_index

С использованием наших помощников наша функция визуализации не так уж и плоха. Мы декодируем данные JSON и преобразуем их в ограничивающие прямоугольники, а затем наложим их на наше входное изображение с помощью Visualization_utils API обнаружения объектов. Обратите внимание, что мы конвертируем входное изображение в тензор, поэтому перед визуализацией мы должны использовать его .eval ().

from object_detection.utils import visualization_utils
def visualize(self, input_image, response, i):
    # Processes response for visualization
    detection_boxes = response["detection_boxes"]
    detection_classes = response["detection_classes"]
    detection_scores = response["detection_scores"]
    image = self.bitstring_to_uint8_tensor(input_image)
    with tf.Session() as sess:
        image = image.eval()
    # Overlays bounding boxes and labels on image
    visualization_utils.visualize_boxes_and_labels_on_image_array(
        image,
        np.asarray(detection_boxes, dtype=np.float32),
        np.asarray(detection_classes, dtype=np.uint8),
        scores=np.asarray(detection_scores, dtype=np.float32),
        category_index=self.get_category_index(),
        instance_masks=None,
        use_normalized_coordinates=True,
        line_thickness=2)
    # Saves image
    output_file = self.output_dir + "/images/" + self.output_filename + str(i) + self.output_extension
    visualization_utils.save_image_array_as_png(image, output_file)

Использование сервера

Теперь, когда мы закончили интеграцию Faster R-CNN с Tendies, давайте запустим сервер. Во-первых, нам нужно экспортировать нашу модель:

python serverbuilder.py --checkpoint_dir $(path) --image_size 512

По состоянию на июль 2018 года Python 3 официально не поддерживается с помощью TensorFlow Serving, но кто-то нашел решение. Установите Python 3 TensorFlow Serving API с помощью:

pip install tensorflow-serving-api-python3

Теперь мы можем запустить этот сервер моделей TensorFlow из bash с помощью команды:

tensorflow_model_server --rest_api_port=8501 --model_name=saved_model --model_base_path=$(path)

Где $ (путь) - это путь к каталогу обслуживания. В моем случае это / mnt / c / Users / Tyler / Desktop / tenies / full_functionality / serve.

Наконец, мы можем запустить удаленный вывод, вызвав нашего клиента в папке с входными изображениями:

python objectdetectionclient.py

Заключение

Спасибо, что следуете этому руководству; Надеюсь, это вам помогло! Этот блокнот был создан с использованием моей библиотеки распределенного обслуживания изображений TensorFlow, которую вы можете скачать здесь. Чтобы увидеть больше сообщений в блоге и информацию обо мне, посетите мой сайт.