Узнайте, как настроить настраиваемую модель глубокого обучения для обнаружения объектов на основе собственного набора данных.
Обучение достойной модели глубокого обучения для обнаружения объектов требует больших усилий, которые усугубляются при развертывании и внедрении модели в веб-приложение для конечных пользователей. В этом руководстве мы намерены решить эту, казалось бы, сложную задачу, предоставив практический пример того, как разработать точную модель глубокого обучения с использованием Python и фреймворка Tensorflow, а также создать работающее веб-приложение, которое поддерживает обнаружение объектов на лету с помощью R Блестящий каркас. К концу руководства вы сможете создать полномасштабное приложение для распознавания объектов для таких продуктовых товаров, как это:
Обучение модели глубокого обучения обнаружению объектов
Обучение работающей модели глубокого обучения обнаружению объектов требует больших объемов данных и вычислительной мощности. Чтобы облегчить разработку, мы можем использовать трансферное обучение путем точной настройки моделей, предварительно обученных на основе других соответствующих наборов данных. В этом примере мы выбираем мобильную сеть sdd, предоставленную Tensorflow Hub, которая предлагает хороший баланс между скоростью и точностью.
Поскольку при обучении полномасштабной модели глубокого обучения необходимо учитывать несколько внутренних логистических процессов, таких как пути и гиперпараметры, мы можем создать центральный словарь для хранения этих параметров конфигурации, включая настройку различных путей, установку соответствующих библиотек и загрузка предварительно обученных моделей.
# Configuration parameters CUSTOM_MODEL_NAME = 'my_ssd_mobnet' # SSD has good tradeoff between speed and accuracy; can switch to other pretrained model PRETRAINED_MODEL_NAME = 'ssd_mobilenet_v2_fpnlite_320x320_coco17_tpu-8' PRETRAINED_MODEL_URL = 'http://download.tensorflow.org/models/object_detection/tf2/20200711/ssd_mobilenet_v2_fpnlite_320x320_coco17_tpu-8.tar.gz' # TF official script to encode training data to tf record format TF_RECORD_SCRIPT_NAME = 'generate_tfrecord.py' # Mapping dictionary between label and integer id LABEL_MAP_NAME = 'label_map.pbtxt' # Define a list of folder paths to be created (if needed) and used later paths = { 'WORKSPACE_PATH': os.path.join('Tensorflow', 'workspace'), 'SCRIPTS_PATH': os.path.join('Tensorflow','scripts'), 'APIMODEL_PATH': os.path.join('Tensorflow','models'), # bounding box annotation 'ANNOTATION_PATH': os.path.join('Tensorflow', 'workspace','annotations'), 'IMAGE_PATH': os.path.join('Tensorflow', 'workspace','images'), 'MODEL_PATH': os.path.join('Tensorflow', 'workspace','models'), 'PRETRAINED_MODEL_PATH': os.path.join('Tensorflow', 'workspace','pre-trained-models'), 'CHECKPOINT_PATH': os.path.join('Tensorflow', 'workspace','models',CUSTOM_MODEL_NAME), 'OUTPUT_PATH': os.path.join('Tensorflow', 'workspace','models',CUSTOM_MODEL_NAME, 'export'), 'PROTOC_PATH':os.path.join('Tensorflow','protoc') } files = { 'PIPELINE_CONFIG':os.path.join('Tensorflow', 'workspace','models', CUSTOM_MODEL_NAME, 'pipeline.config'), 'TF_RECORD_SCRIPT': os.path.join(paths['SCRIPTS_PATH'], TF_RECORD_SCRIPT_NAME), 'LABELMAP': os.path.join(paths['ANNOTATION_PATH'], LABEL_MAP_NAME) } # Download TF model training utility scripts from TF model zoo if not os.path.exists(os.path.join(paths['APIMODEL_PATH'], 'research', 'objection_detection')): !git clone https://github.com/tensorflow/models {paths['APIMODEL_PATH']} # Install TF object detection library if os.name=='posix': !apt-get install protobuf-compiler !cd Tensorflow/models/research && protoc object_detection/protos/*.proto --python_out=. && cp object_detection/packages/tf2/setup.py . && python -m pip install .
Нам также нужно будет предоставить обучающие изображения вместе с ограничивающими прямоугольниками для нашей конкретной задачи распознавания продуктов. Эти данные будут использоваться для точной настройки предварительно обученной модели путем изменения вывода по умолчанию на конкретную настройку, т. е. распознавание шести продуктов (яблоко, авокадо, банан, капуста, морковь и картофель), которые профилированы в словарь.
# Download training images import shutil if os.path.exists('object_detection_using_tensorflow'): shutil.rmtree('object_detection_using_tensorflow') !git clone https://github.com/jackliu333/object_detection_using_tensorflow.git # Create label map labels = [{'name':'Apple', 'id':1}, {'name':'Avocado', 'id':2}, {'name':'Banana', 'id':3}, {'name':'Cabbage', 'id':4}, {'name':'Carrot', 'id':5}, {'name':'Potato', 'id':6}] with open(files['LABELMAP'], 'w') as f: for label in labels: f.write('item { \n') f.write('\tname:\'{}\'\n'.format(label['name'])) f.write('\tid:{}\n'.format(label['id'])) f.write('}\n')
Мы также разделим данные на обучающую и тестовую выборки. Обратите внимание, что разделение должно проходить по индексу категории, чтобы элементы определенной категории не были полностью распределены ни в обучающем, ни в тестовом наборе.
# Split into train test folders tmp_folders = ['train', 'test'] for i in tmp_folders: if os.path.exists(os.path.join(paths['IMAGE_PATH'], i)): shutil.rmtree(os.path.join(paths['IMAGE_PATH'], i)) !mkdir -p {os.path.join(paths['IMAGE_PATH'], i)} else: !mkdir -p {os.path.join(paths['IMAGE_PATH'], i)} import shutil for i in range(len(labels)): # print(labels[i]['name']) from_path = os.path.join('object_detection_using_tensorflow','images',labels[i]['name']) # print(from_path) # get unique file names tmp_files = os.listdir(from_path) tmp_names = [] tmp_file_types = [] for tmp_file in tmp_files: tmp_name = os.path.splitext(tmp_file)[0] tmp_file_type = os.path.splitext(tmp_file)[1] tmp_names.append(tmp_name) tmp_file_types.append(tmp_file_type) tmp_names = list(set(tmp_names)) tmp_names = [i for i in tmp_names if i != '.DS_Store'] tmp_file_types = list(set(tmp_file_types)) tmp_file_types = [i for i in tmp_file_types if len(i) != 0] # random shuffle the files random.shuffle(tmp_names) # training and test files tmp_names_train = tmp_names[0:int(len(tmp_names)*0.9)] tmp_names_test = [i for i in tmp_names if i not in tmp_names_train] # move into respective target folders for tmp_name in tmp_names_train: for tmp_file_type in tmp_file_types: tmp_name_full = tmp_name + tmp_file_type shutil.copy(os.path.join(from_path, tmp_name_full), \ os.path.join(paths['IMAGE_PATH'], "train")) for tmp_name in tmp_names_test: for tmp_file_type in tmp_file_types: tmp_name_full = tmp_name + tmp_file_type shutil.copy(os.path.join(from_path, tmp_name_full), \ os.path.join(paths['IMAGE_PATH'], "test"))
Затем полученные данные изображения преобразуются в формат TF Record для более быстрой обработки.
# Create TF Record # download conversion script if not os.path.exists(files['TF_RECORD_SCRIPT']): !git clone https://github.com/nicknochnack/GenerateTFRecord {paths['SCRIPTS_PATH']} !python {files['TF_RECORD_SCRIPT']} -x {os.path.join(paths['IMAGE_PATH'], 'train')} -l {files['LABELMAP']} -o {os.path.join(paths['ANNOTATION_PATH'], 'train.record')} !python {files['TF_RECORD_SCRIPT']} -x {os.path.join(paths['IMAGE_PATH'], 'test')} -l {files['LABELMAP']} -o {os.path.join(paths['ANNOTATION_PATH'], 'test.record')}
Перед началом обучения модели нам нужно обновить несколько параметров конфигурации, которые будут отличаться от настроек по умолчанию, чтобы конвейер обучения знал, что мы выполняем распознавание объектов на основе шести категорий.
# Update configuration file for transfer learning import tensorflow as tf from object_detection.utils import config_util from object_detection.protos import pipeline_pb2 from google.protobuf import text_format # Read current configuration file pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() with tf.io.gfile.GFile(files['PIPELINE_CONFIG'], "r") as f: proto_str = f.read() text_format.Merge(proto_str, pipeline_config) # Update based on new labels pipeline_config.model.ssd.num_classes = len(labels) pipeline_config.train_config.batch_size = 4 pipeline_config.train_config.fine_tune_checkpoint = os.path.join(paths['PRETRAINED_MODEL_PATH'], PRETRAINED_MODEL_NAME, 'checkpoint', 'ckpt-0') pipeline_config.train_config.fine_tune_checkpoint_type = "detection" pipeline_config.train_input_reader.label_map_path= files['LABELMAP'] pipeline_config.train_input_reader.tf_record_input_reader.input_path[:] = [os.path.join(paths['ANNOTATION_PATH'], 'train.record')] pipeline_config.eval_input_reader[0].label_map_path = files['LABELMAP'] pipeline_config.eval_input_reader[0].tf_record_input_reader.input_path[:] = [os.path.join(paths['ANNOTATION_PATH'], 'test.record')] # Write to configuration file config_text = text_format.MessageToString(pipeline_config) with tf.io.gfile.GFile(files['PIPELINE_CONFIG'], "wb") as f: f.write(config_text)
Обучение модели упрощается с помощью обучающего сценария, предоставленного Tensorflow.
TRAINING_SCRIPT = os.path.join(paths['APIMODEL_PATH'], 'research', 'object_detection', 'model_main_tf2.py') command = "python {} --model_dir={} --pipeline_config_path={} --num_train_steps=2000".format(TRAINING_SCRIPT, paths['CHECKPOINT_PATH'],files['PIPELINE_CONFIG']) !{command}
Поскольку процедура обучения будет сохранять промежуточные контрольные точки, т. е. веса модели, мы можем выбрать, какую контрольную точку модели загрузить и использовать для обнаружения.
# Load trained model from checkpoint import os import tensorflow as tf from object_detection.utils import label_map_util from object_detection.utils import visualization_utils as viz_utils from object_detection.builders import model_builder from object_detection.utils import config_util # Load pipeline config and build a detection model configs = config_util.get_configs_from_pipeline_file(files['PIPELINE_CONFIG']) detection_model = model_builder.build(model_config=configs['model'], is_training=False) # Restore checkpoint ckpt = tf.compat.v2.train.Checkpoint(model=detection_model) ckpt.restore(os.path.join(paths['CHECKPOINT_PATH'], 'ckpt-3')).expect_partial() # @tf.function def detect_fn(image): image, shapes = detection_model.preprocess(image) prediction_dict = detection_model.predict(image, shapes) detections = detection_model.postprocess(prediction_dict, shapes) return detections
Теперь мы можем протестировать точно настроенную модель, передав изображение, случайно выбранное из папки с изображениями, определенной ранее.
import cv2 from matplotlib import pyplot as plt %matplotlib inline # Randomly select an image to be detected tmp_img = random.choice([file for file in os.listdir(os.path.join(paths['IMAGE_PATH'], 'test')) if file.endswith(".jpg")]) IMAGE_PATH = os.path.join(paths['IMAGE_PATH'], 'test', tmp_img) category_index = label_map_util.create_category_index_from_labelmap(files['LABELMAP']) img = cv2.imread(IMAGE_PATH) image_np = np.array(img) input_tensor = tf.convert_to_tensor(np.expand_dims(image_np, 0), dtype=tf.float32) detections = detect_fn(input_tensor) num_detections = int(detections.pop('num_detections')) detections = {key: value[0, :num_detections].numpy() for key, value in detections.items()} detections['num_detections'] = num_detections # detection_classes should be ints. detections['detection_classes'] = detections['detection_classes'].astype(np.int64) label_id_offset = 1 image_np_with_detections = image_np.copy() viz_utils.visualize_boxes_and_labels_on_image_array( image_np_with_detections, detections['detection_boxes'], detections['detection_classes']+label_id_offset, detections['detection_scores'], category_index, use_normalized_coordinates=True, max_boxes_to_draw=5, min_score_thresh=.5, agnostic_mode=False) plt.imshow(cv2.cvtColor(image_np_with_detections, cv2.COLOR_BGR2RGB)) plt.show()
Создание приложения веб-приложения с использованием R Shiny
R Shiny — отличный инструмент для создания современных веб-приложений без глубоких знаний HTML, CSS или Javascript. Мы можем быстро развернуть приложение, выбрав конкретную структуру приложения и заполнив сценарии внешнего интерфейса (ui.R) и внутреннего интерфейса (server.R), а также необязательный глобальный файл (global.R) для обработки среды. через приложение.
Дизайн пользовательского интерфейса в R Shiny может следовать системе сетки следующим образом, что позволяет легко решить, какие компоненты добавить в определенную строку или столбец.
ui <- dashboardPage( skin=”blue”, #(1) Header dashboardHeader(title=”Object Recognition App”,#,style=”font-size: 120%; font-weight: bold; color: white”), titleWidth = 250, tags$li(class = “dropdown”), dropdownMenu( type = “notifications”, icon = icon(“question-circle”), badgeStatus = NULL, headerText = “Feedback”, notificationItem(“Send email to developer”, icon = icon(“file”), href = “[email protected]”) )), #(2) Sidebar dashboardSidebar( width=250, fileInput(“input_image_upload”,”Upload image”, accept = c(‘.jpg’,’.jpeg’)), tags$br(), sliderInput(“min_score_threshold”,”Confidence threshold”,0,1,0.5), # tags$p(“Upload the image here.”) selectInput(inputId = “product_type”,label = “Choose product”, choices = c(“Flour”,”Baby Food”), selected = NA), selectInput(inputId = “halal_status”,label = “Halal status”, choices = c(“H”,”NH”), selected = NA), selectInput(inputId = “weight”,label = “Choose weight”, choices = c(“50g”,”100g”), selected = NA), actionButton(“submit”,”Submit”,icon(“paper-plane”), style=”color: #fff; background-color: #337ab7; border-color: #2e6da4") ), #(3) Body dashboardBody( box( title = “Object Recognition”, width = 12, solidHeader = TRUE, status = “primary”, collapsible = T, collapsed = F, fluidRow( column(6, h4(“Instruction:”), # tags$br(), tags$p(“1. Upload image to be classified and set confidence threshold.”), tags$p(“2. Check prediction results.”), tags$p(“3. Select specific product category.”), tags$p(“4. Click submit to record in the system.”) ), column(6, h4(“Predicted Category:”), tableOutput(“text”) ) ), fluidRow( column(h4(“Image:”),imageOutput(“output_image”), width=6), column(h4(“Predicted Image:”),imageOutput(“output_image2”), width=6) ) ), box( title = “Image Gallery”, width = 12, solidHeader = TRUE, status = “success”, collapsible = T, collapsed = F, fluidRow( column(3, h3(“All categories”), verbatimTextOutput(“all_cats”) ), column(3, selectInput(“input_image_select”, “Select image”,c(“”,ALL_IMGS),selected = “”), ), column(6, column(h4(“Image:”),imageOutput(“output_image_selected”), width=6), ) ) ) # box( # title = “Product Recording”, width = 12, solidHeader = TRUE, status = “success”, # collapsible = T, collapsed = T, # “test” # ) ))
Сервер обрабатывает всю внутреннюю обработку, такую как создание прогнозов для загруженного изображения с использованием предварительно обученной модели и возврат результатов прогноза в пользовательский интерфейс внешнего интерфейса.
Мы также определяем несколько служебных функций, а также параметры среды в глобальном файле. Обратите внимание, что R использует библиотеку reticulate для обработки скриптов Python. В этом случае мы сначала создадим новую виртуальную среду, чтобы настроить необходимую фоновую среду для обнаружения объектов conda, а затем загрузим основные функции Python для распознавания изображений, которые будут преобразованы в виде API для взаимодействия с экосистемой R Shiny.
Заключение
В этом руководстве мы рассмотрели, как настроить предварительно обученные модели глубокого обучения в Tensorflow с помощью трансферного обучения для распознавания продуктов, а также как создать современное веб-приложение для размещения модели для конечных пользователей с помощью R Shiny. Конечный продукт представляет собой комбинацию сценариев R и Python, в которой основные функции Python, такие как обнаружение объектов, органично обернуты и представлены в виде API для R, который занимается разработкой приложений. Мы надеемся, что это руководство послужит вам хорошей отправной точкой для развертывания вашей собственной модели и обмена ею с другими эффективным и увлекательным способом.
Все вспомогательные данные и коды доступны в сопроводительном github, а также в пошаговом руководстве на YouTube ниже.