Обслуживание вашей модели обнаружения объектов 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 следующие:
- Определите функции предварительной и постобработки в LayerInjector.
- Создайте или импортируйте функцию вывода модели в ServerBuilder.
- Создайте или импортируйте клиента.
В то время как я буду демонстрировать с 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, которую вы можете скачать здесь. Чтобы увидеть больше сообщений в блоге и информацию обо мне, посетите мой сайт.