В этой статье в основном рассказывается, как использовать собственный набор данных для обучения алгоритму сегментации 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
форматировать данные изображения
Процесс производства набора данных:
- Подпишите данные и сделайте необходимые
mask
изображение - Разделите набор данных на обучающие, проверочные и тестовые наборы.
- генерировать
TFRecord
форматированный набор данных
3.1 Маркировка данных
Данные обучающего набора состоят из двух частей: одна представляет собой исходное изображение, а другая представляет собой значение метки соответствующей классификации (называемой в этой статьеmask
изображение).
Как задается значение изображения маски?Маска изображения, соответствующая исходному изображению, составляется по количеству классификаций сегментации изображения. Если всего имеется N категорий (фон как одна категория), диапазон значений изображения маски составляет[0~N)
.0
значение используется в качестве фонового значения, а значения других категорий сегментации задаются как1, 2, ..., N-1
.
Уведомление:
-
ignore_label
: Буквально игнорируемые теги, т.е.ignore_label
Относится к непомеченному пикселю, то есть к значению пикселя, которое не нужно предсказывать, поэтому он не участвует вloss
стоимость рассчитывается вmask
На изображении его значение записано как255
. -
mask
Изображение представляет собой одноканальное изображение в градациях серого. -
mask
Формат изображения не ограничен, но все изображения маски используют один и тот же формат изображения, что удобно для чтения данных.
краткое содержание
mask
Значения изображения делятся на три категории:
- фон: с
0
выражать - Категория Категория: Использование
1, 2, ....., N-1
выражать -
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
: путь, по которому хранится набор данных.
Что касается параметров тренировки, необходимо обратить внимание на следующие моменты:
-
Вопрос о том, следует ли нагружать веса предварительно обученной сети Если вы хотите настроить эту сеть на других наборах данных, вам необходимо обратить внимание на следующие параметры:
- Используя веса предварительно обученной сети, установите
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
- Используя веса предварительно обученной сети, установите
-
Вот несколько советов по обучению собственного набора данных, если у вас ограниченные ресурсы:
- настраивать
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
- настраивать
-
О тонкой настройке
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结果图像
На этом весь тренировочный процесс закончен! ! !