Сегментация изображения: Tensorflow Deeplabv3+ набор данных для сегментации тренировочного портрета

глубокое обучение

В этой статье в основном рассказывается, как использовать собственный набор данных для обучения алгоритму сегментации DeepLabv3+.Код использует официальный исходный код.

1. Введение в код

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

Кодовый адрес:【гитхаб】модели/исследования/глубокая лаборатория в мастере · тензорный поток/модели

Далее я дам краткое введение в это хранилище кода, потому что, когда я использую это хранилище кода, я забочусь только о реализации обучающего кода и игнорирую другой контент, который уже находится на складе (==).

В текущей реализации мы поддерживаем использование следующих сетевых магистралей:

  • MobileNetv2иMobileNetv3: быстрая сетевая структура, разработанная для мобильных устройств
  • Xception: Мощная сетевая структура для развертывания на стороне сервера.
  • ResNet-v1-{50, 101}: Мы предоставляем оригиналResNet-v1и“ beta”вариант, где“ stem”Модифицировано для семантической сегментации.
  • PNASNet: мощная сетевая структура, обнаруженная с помощью поиска по нейронной архитектуре.
  • Auto-Deeplab(вызывается в кодеHNASNet): Магистральная сеть, специфичная для сегмента, найденная с помощью поиска нейронной архитектуры.

Этот каталог содержит реализацию TensorFlow. Код, который мы предоставляем, позволяет пользователям обучать модели в соответствии сmIOU(Сумма средних пересечений) Оценка результатов и визуализация результатов сегментации. мы начинаем сPASCAL VOC 2012иCityscapesВозьмем в качестве примера тесты семантической сегментации.

Несколько важных файлов в коде:

  • datasets/: Эта папка содержит код обработки для набора обучающих данных, в основном дляPASCAL VOC 2012иCityscapesОбработка наборов данных.
  • g3doc/: эта папка содержит несколькоMarkdownДокументация, очень полезная, как установить, FAQ и т.д.
  • deeplab_demo.ipynb: в этом файле показана демонстрация семантической сегментации изображения и отображения результата.
  • export_model.py: этот файл предоставляетcheckpointмодель для.pbКодовая реализация файла.
  • train.py: файл кода обучения.При обучении необходимо указать предоставленные параметры обучения.
  • eval.py: проверьте код и выведите mIOU, чтобы оценить качество модели.
  • vis.py: Визуализируйте код.

2. Установка

DeeplabЗависимые библиотеки:

  • Numpy
  • Pillow 1.0
  • tf Slim (which is included in the "tensorflow/models/research/" checkout)
  • Jupyter notebook
  • Matplotlib
  • Tensorflow

2.1 Добавить библиотеку вPYTHONPATH

При локальном запускеtensorflow/models/research/Каталог должен быть добавлен кPYTHONPATH, следующее:

# From tensorflow/models/research/
export PYTHONPATH=$PYTHONPATH:`pwd`:`pwd`/slim

# [Optional] for panoptic evaluation, you might need panopticapi:
# https://github.com/cocodataset/panopticapi
# Please clone it to a local directory ${PANOPTICAPI_DIR}
touch ${PANOPTICAPI_DIR}/panopticapi/__init__.py
export PYTHONPATH=$PYTHONPATH:${PANOPTICAPI_DIR}/panopticapi

Примечание. Эту команду необходимо запускать при каждом запуске нового терминала. Если вы хотите избежать запуска этой команды вручную, вы можете добавить ее как новую строку в〜/ .bashrcконец файла.

2.2 Проверьте, прошла ли установка успешно

запустивmodel_test.pyБыстрый тест:

# From tensorflow/models/research/
python deeplab/model_test.py

существуетPASCAL VOC 2012Быстро запустите весь код в наборе данных:

# From tensorflow/models/research/deeplab
sh local_test.sh

3. Подготовка набора данных

Конечная цель: создатьTFRecordформатированные данные

Структура каталога набора данных выглядит следующим образом:

+dataset #数据集名称
	+image
	+mask
	+index
		- train.txt
		- trainval.txt
		- val.txt
	+tfrecord
  • image: Исходное изображение, цветное изображение RGB
  • mask: Значение пикселя изображения маски метки категории, один канал, то же, что и имя исходного изображения, суффикс.jpgи.pngВы можете, если вы последовательно читаете код. Исходное изображение набора данных VOC по умолчанию:.jpg, изображение маски.png.
  • index: сохранить имя файла изображенияtxtфайл (без суффикса)
  • tfrecord: сохранить вtfrecordформатировать данные изображения

Процесс производства набора данных:

  1. Подпишите данные и сделайте необходимыеmaskизображение
  2. Разделите набор данных на обучающие, проверочные и тестовые наборы.
  3. генерироватьTFRecordформатированный набор данных

3.1 Маркировка данных

Данные обучающего набора состоят из двух частей: одна представляет собой исходное изображение, а другая представляет собой значение метки соответствующей классификации (называемой в этой статьеmaskизображение).

Как задается значение изображения маски?Маска изображения, соответствующая исходному изображению, составляется по количеству классификаций сегментации изображения. Если всего имеется N категорий (фон как одна категория), диапазон значений изображения маски составляет[0~N).0значение используется в качестве фонового значения, а значения других категорий сегментации задаются как1, 2, ..., N-1.

Уведомление:

  • ignore_label: Буквально игнорируемые теги, т.е.ignore_labelОтносится к непомеченному пикселю, то есть к значению пикселя, которое не нужно предсказывать, поэтому он не участвует вlossстоимость рассчитывается вmaskНа изображении его значение записано как255.
  • maskИзображение представляет собой одноканальное изображение в градациях серого.
  • maskФормат изображения не ограничен, но все изображения маски используют один и тот же формат изображения, что удобно для чтения данных.

краткое содержание maskЗначения изображения делятся на три категории:

  1. фон: с0выражать
  2. Категория Категория: Использование1, 2, ....., N-1выражать
  3. ignore_labelзначение: с255выражать

Если сегментируемых классов меньше, результирующийmaskИзображение кажется черным, потому что все значения классификации маленькие, в0~255диапазон не легко отображается.

3.2 Разделение набора данных

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

Предположим, что пути хранения исходного изображения и образа маски следующие:

  • Исходное изображение:./dataset/images
  • maskизображение:./dataset/mask: Здесь хранитсяРаздел 2.1требуемый формат

оригинальное изображение сmaskИзображения находятся во взаимно однозначном соответствии, включая размер изображения, имя изображения (суффикс может быть другим)

Путь хранения файла индекса:./dataset/index, который генерируется по этому пути:

  • train.txt
  • trainval.txt
  • val.txt

В файле индекса просто запишите имя файла (без суффикса), в зависимости от того, как набор данных загружается в код.

3.3 Данные упаковки какTFRecordФормат

TFRecordЭто рекомендуемый Google формат двоичного файла, который теоретически может сохранять информацию в любом формате.TFRecordиспользуется внутри“Protocol Buffer”Схема кодирования двоичных данных, она занимает только один блок памяти и требует загрузки только одного двоичного файла за раз.Это просто, быстро и особенно удобно для больших обучающих данных. И когда объем наших обучающих данных относительно велик, данные можно разделить на несколько файлов TFRecord для повышения эффективности обработки.

Итак, как генерировать данныеTFRecordЧто насчет формата?

Здесь мы можем использовать код проекта в./datasets/build_voc2012_data.pyфайл для реализации. Учитывая, что файл представляет собой код для обработки набора данных VOC2012, нам нужно только изменить входные параметры.

параметр:

  • image_folder: имя исходной папки изображения,./dataset/image
  • semantic_segmentation_folder: имя разделенной папки,./dataset/mask
  • list_folder: имя индексной папки,./dataset/index
  • output_dir: выходной путь, т.е. сгенерированныйtfrecordрасположение файла,./dataset/tfrecord

Запустите команду:

python ./datasets/build_voc2012_data.py --image_folder=./dataset/image
										--semantic_segmentation_folder=./dataset/mask
										--list_folder=./dataset/index
										--output_dir=./dataset/tfrecord

Сгенерированные файлы выглядят следующим образом:

在这里插入图片描述
Примечание. Параметры можно настроить в коде._NUM_SHARDS(по умолчанию4), чтобы изменить количество фрагментов данных. (Некоторые файловые системы имеют максимальный размер одного файла, если набор данных очень большой, увеличьте_NUM_SHARDSможно уменьшить размер одного файла)

Основной код файла выглядит следующим образом:

# dataset_split指的是train.txt, val.txt等
dataset = os.path.basename(dataset_split)[:-4]
filenames = [x.strip('\n') for x in open(dataset_split, 'r')] # 文件名列表

# 输出tfrecord文件名
output_filename = os.path.join(
            FLAGS.output_dir,
            '%s-%05d-of-%05d.tfrecord' % (dataset, shard_id, _NUM_SHARDS))
with tf.python_io.TFRecordWriter(output_filename) as tfrecord_writer:
	for i in range(start_idx, end_idx): 
		image_filename = os.path.join(iamge_folder, filenames[i]+'.'+image_format)# 原图路径
		image_data = tf.gfile.GFile(image_filename, 'rb').read() #读取原图文件
	    height, width = image_reader.read_image_dims(image_data)
	    
	    seg_filename = os.path.join(semantic_segmentation_folder,
                    filenames[i] + '.' + label_format) # mask图像路径
	    seg_data = tf.gfile.GFile(seg_filename, 'rb').read() # 读取分割图像
	    seg_height, seg_width = label_reader.read_image_dims(seg_data)
	    
	    # 判断原图与mask图像尺寸是否匹配
	    if height != seg_height or width != seg_width:
	        raise RuntimeError('Shape mismatched between image and label.')
	    # Convert to tf example.
	    example = build_data.image_seg_to_tfexample(
	        image_data, filenames[i], height, width, seg_data)
	    tfrecord_writer.write(example.SerializeToString())

На данный момент производственная часть набора данных завершена! ! !

4. Обучение

4.1 Модификация кода

Чтобы обучить собственный набор данных, вам необходимо изменить следующие файлы:

1 datasets/data_generator.py: увеличить регистрацию набора данных

Этот файл предоставляет оболочки для семантически сегментированных данных.

В этом файле вы можете увидетьPASCAL_VOC,CITYSCAPESа такжеADE20KОписание данных набора данных выглядит следующим образом:

_PASCAL_VOC_SEG_INFORMATION = DatasetDescriptor(
    splits_to_sizes={
        'train': 1464,
        'train_aug': 10582,
        'trainval': 2913,
        'val': 1449,
    },
    num_classes=21,
    ignore_label=255,
)

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

_PORTRAIT_INFORMATION = DatasetDescriptor(
    splits_to_sizes={
        'train': 17116,
        'trainval': 21395,
        'val': 4279,
    },
    num_classes=2,  # 类别数目,包括背景
    ignore_label=255,  # 忽略像素值
)

Если взять в качестве примера задачу портретной сегментации, то есть только две категории, а именно передний план (портрет) и фон (непортрет).

После добавления информации описания вам необходимо зарегистрировать информацию набора данных следующим образом:

_DATASETS_INFORMATION = {
    'cityscapes': _CITYSCAPES_INFORMATION,
    'pascal_voc_seg': _PASCAL_VOC_SEG_INFORMATION,
    'ade20k': _ADE20K_INFORMATION,
    'portrait_seg': _PORTRAIT_INFORMATION, #增加此句
}

Примечание. Имя набора данных здесь должно соответствовать предыдущему!

2 ./utils/train_utils.pyИсправлять

в функцииget_model_init_fn, измените его на следующий код, добавьтеlogitsСлой не загружает предварительно обученные веса модели:

	# Variables that will not be restored.
    exclude_list = ['global_step', 'logits']
    if not initialize_last_layer:
        exclude_list.extend(last_layers)

4.2 Основные параметры тренировки

учебный файлtrain.pyиcommon.pyФайл содержит все параметры, необходимые для обучения сети сегментации.

  • model_variant:DeeplabПеременные модели, видны необязательные значенияcore/feature_extractor.py.
    • когда используешьmobilenet_v2, установите переменнуюstrous_rates=decoder_output_stride=None;
    • когда используешьxception_65илиresnet_v1, наборstrous_rates=[6,12,18](output stride 16), decoder_output_stride=4.
  • label_weights: эта переменная может установить значение веса метки.Если категории в наборе данных несбалансированы, эту переменную можно использовать для указания значения веса каждой метки категории, напримерlabel_weights=[0.1, 0.5]Это означает, что вес метки 0 равен 0,1, а вес метки 1 равен 0,5. Если значениеNone, то все метки имеют одинаковый вес1.0.
  • train_logdir: место храненияcheckpointиlogsмаршрут из.
  • log_steps: это значение указывает, сколько шагов необходимо для вывода информации журнала.
  • save_interval_secs: Это значение указывает, как часто файл модели сохраняется на жесткий диск в секундах.
  • optimizer: оптимизатор, необязательный['momentum', 'adam'].
  • learning_policy: политика скорости обучения, необязательное значение['poly', 'step'].
  • base_learning_rate: базовая скорость обучения, по умолчанию0.0001.
  • training_number_of_steps: количество итераций для обучения модели.
  • train_batch_size: количество пакетных изображений для обучения модели.
  • train_crop_size: Размер изображения, используемый во время обучения модели, по умолчанию.'513, 513'.
  • tf_initial_checkpoint: предварительно обученная модель.
  • initialize_last_layer: Инициализировать ли последний слой.
  • last_layers_contain_logits_only: считать ли последним слоем только логический уровень.
  • fine_tune_batch_norm: Должна ли быть тонкая настройкаbatch normпараметр.
  • atrous_rates: По умолчанию[6, 12, 18].
  • output_stride: По умолчанию16, отношение входного и выходного пространственного разрешения
    • заxception_65, еслиoutput_stride=8, затем используйтеatrous_rates=[12, 24, 36]
    • еслиoutput_stride=16,ноatrous_rates=[6, 12, 18]
    • заmobilenet_v2,использоватьNone
    • Примечание. Вы можете использовать различныеstrous_ratesиoutput_stride.
  • dataset: Используемый набор данных сегментации, здесь то же имя, что и регистрация набора данных.
  • train_split: какой набор данных использовать для обучения, необязательное значение — это значение при регистрации набора данных, напримерtrain, trainval.
  • dataset_dir: путь, по которому хранится набор данных.

Что касается параметров тренировки, необходимо обратить внимание на следующие моменты:

  1. Вопрос о том, следует ли нагружать веса предварительно обученной сети Если вы хотите настроить эту сеть на других наборах данных, вам необходимо обратить внимание на следующие параметры:

    • Используя веса предварительно обученной сети, установитеinitialize_last_layer=True
    • только сетьbackbone,настраиватьinitialize_last_layer=Falseиlast_layers_contain_logits_only=False
    • Используйте все предварительно подготовленные веса, кромеlogits,настраиватьinitialize_last_layer=Falseиlast_layers_contain_logits_only=True

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

    --initialize_last_layer=false
    --last_layers_contain_logits_only=true
    
  2. Вот несколько советов по обучению собственного набора данных, если у вас ограниченные ресурсы:

    • настраиватьoutput_stride=16или даже32(также необходимо изменитьatrous_ratesпеременная, например, дляoutput_stride=32,atrous_rates=[3, 6, 9])
    • использовать как можно большеGPU,Изменятьnum_cloneподпишусь и будуtrain_batch_sizeустановить как можно больше
    • регулироватьtrain_crop_size, вы можете сделать его меньше, например.513x513(четное321x321), так что большееbatch_size
    • Используйте меньшую магистральную сеть, такую ​​какmobilenet_v2
  3. О тонкой настройкеbatch_normразмер партии, используемый при обученииtrain_batch_sizeЕсли больше 12 (предпочтительно больше 16), установитеfine_tune_batch_norm=True. В противном случае установитеfine_tune_batch_norm=False.

4.3 Предварительно обученная модель

Ссылку на модель можно посмотреть в деталях:models/model_zoo.md в главном тензорном потоке/модели

Предоставляет предварительно обученные модели для нескольких наборов данных, в том числе (1)PASCAL VOC 2012, (2)Cityscapes, (3)ADE20K

Несжатые файлы включают в себя:

  • Одинfrozen inference graph(forzen_inference_graph.pb). По умолчанию все замороженные графы логического вывода имеют выходной шаг 8, единую оценочную шкалу 1,0 и отсутствие переключения влево-вправо, если не указано иное. на основеMobileNet-v2Модель не включает модуль декодера.
  • Одинcheckpoint(model.ckpt.data-00000-of-00001, model.ckpt.index)

также предусмотрено вImageNetпредварительно обученныйcheckpoints

Среди распакованных файлов:Одинmodel checkpoint (model.ckpt.data-00000-of-00001, model.ckpt.index)

Загрузите в соответствии с вашей собственной ситуацией!

4.4 Обучение модели

python train.py \
    --logtostderr \
    --training_number_of_steps=20000 \
    --train_split="train" \
    --model_variant="xception_65" \
    --train_crop_size="513,513" \
    --atrous_rates=6 \
    --atrous_rates=12 \
    --atrous_rates=18 \
    --output_stride=16 \
    --decoder_output_stride=4 \
    --train_batch_size=2 \
    --save_interval_secs=240 \
    --optimizer="momentum" \
    --leraning_policy="poly" \
    --fine_tune_batch_norm=false \
    --initialize_last_layer=false \
    --last_layers_contain_logits_only=true \
    --dataset="portrait_seg" \
    --tf_initial_checkpoint="./checkpoint/deeplabv3_pascal_trainval/model.ckpt" \
    --train_logdir="./train_logs" \
    --dataset_dir="./dataset/tfrecord"

4.5 Проверка модели

Проверочный код:./eval.py

# From tensorflow/models/research/
python deeplab/eval.py \
    --logtostderr \
    --eval_split="val" \
    --model_variant="xception_65" \
    --atrous_rates=6 \
    --atrous_rates=12 \
    --atrous_rates=18 \
    --output_stride=16 \
    --decoder_output_stride=4 \
    --eval_crop_size="513,513" \
    --dataset="portrait_seg" \ # 数据集名称
    --checkpoint_dir=${PATH_TO_CHECKPOINT} \ # 预训练模型
    --eval_logdir=${PATH_TO_EVAL_DIR} \ 
    --dataset_dir="./dataset/tfrecord" # 数据集路径

Полученный результат выглядит следующим образом:

在这里插入图片描述

4.6 Визуализация тренировочного процесса

можно использоватьTensorboardПроверить ход обучения и оценочной работы. При использовании рекомендуемой структуры каталогов Tensorboard можно запустить с помощью следующей команды:

tensorboard --logdir=${PATH_TO_LOG_DIRECTORY}
# 文中log地址
tensorboard --logdir="./train_logs"

5. Рассуждение

5.1 Экспорт модели

В процессе обучения файл модели будет сохранен на жесткий диск следующим образом:

Его формаTensorFlowизcheckpointформат, в коде предусмотрен скрипт (export_model.py)можетcheckpointпреобразовать в.pbФормат.

export_model.pyОсновные параметры:

  • checkpoint_path: тренировочный сохраненный файл контрольной точки
  • export_path: Путь экспорта модели
  • num_classes: Категория классификации
  • crop_size: Размер изображения,[513, 513]
  • atrous_rates:12, 24, 36
  • output_stride:8

Сгенерировано.pbФайлы следующие:

在这里插入图片描述

5.2 Вывод по одному изображению

class DeepLabModel(object):
    """class to load deeplab model and run inference"""

    INPUT_TENSOR_NAME = 'ImageTensor:0'
    OUTPUT_TENSOR_NAME='SemanticPredictions:0'
    INPUT_SIZE = 513
    FROZEN_GRAPH_NAME= 'frozen_inference_graph'

    def __init__(self, pretrained_weights):
        """Creates and loads pretrained deeplab model."""
        self.graph = tf.Graph()
        graph_def = None
        # Extract frozen graph from tar archive
        if pretrained_weights.endswith('.tar.gz'):
            tar_file = tarfile.open(pretrained_weights)
            for tar_info in tar_file.getmembers():
                if self.FROZEN_GRAPH_NAME in os.path.basename(tar_info.name):
                    file_handle = tar_file.extractfile(tar_info)
                    graph_def = tf.GraphDef.FromString(file_handle.read())
                    break
            tar_file.close()
        else:
            with open(pretrained_weights, 'rb') as fd:
                graph_def = tf.GraphDef.FromString(fd.read())

        if graph_def is None:
            raise RuntimeError('Cannot find inference graph in tar archive.')

        with self.graph.as_default():
            tf.import_graph_def(graph_def, name='')

        gpu_options = tf.GPUOptions(allow_growth=True)
        config = tf.ConfigProto(gpu_options=gpu_options, log_device_placement=False)
        self.sess = tf.Session(graph=self.graph, config=config)

    def run(self, image):
        """Runs inference on a single image.
        Args:
            image: A PIL.Image object, raw input image.
        Returns:
            resized_image:RGB image resized from original input image.
            seg_map:Segmentation map of 'resized_iamge'.
        """
        width, height = image.size
        resize_ratio = 1.0 * self.INPUT_SIZE/max(width, height)
        target_size = (int(resize_ratio*width), int(resize_ratio * height))
        resized_image = image.convert('RGB').resize(target_size, Image.ANTIALIAS)
        batch_seg_map = self.sess.run(
            self.OUTPUT_TENSOR_NAME,
            feed_dict={self.INPUT_TENSOR_NAME:[np.asarray(resized_image)]}
        )
        seg_map = batch_seg_map[0]

        return resized_image, seg_map
        
        
if __name__ == '__main__':
    pretrained_weights = './train_logs/frozen_inference_graph_20000.pb'
    MODEL = DeepLabModel(pretrained_weights) # 加载模型
    
    img_name = 'test.jpg'
    img = Image.open(img_name)
    resized_im, seg_map = MODEL.run(original_im) #获取结果
    seg_map[seg_map==1]=255 #将人像的像素值置为255
    seg_map.save('output.jpg') # 保存mask结果图像

На этом весь тренировочный процесс закончен! ! !