ВВЕДЕНИЕ

В этом блоге мы проведем сегментацию изображений на очень ограниченном наборе данных, используя U-Net, популярную модель сегментации CNN. Также будут некоторые настраиваемые функции потерь, используемые для обучения, такие как потеря кубиков и метрики индекс Жаккара.

Сбор данных

Данные, с которыми мы будем работать, взяты из kaggle. Набор данных называется Семантическая сегментация аэрофотоснимков. Набор данных имеет два типа файлов: формат .jpg, который содержит наши настоящие аэрофотоснимки, сделанные со спутника, и формат .png, который содержит замаскированные изображения этих файлов .jpg, которые будут использоваться в качестве наземных данных для нашей модели. Каждый файл содержит около 72 изображений, причем один файл .json содержит информацию о различных классах данных.

Понимание данных

Каждое изображение в данных имеет разные пиксели. Первое, что нам нужно сделать, чтобы передать данные в нашу модель, — это сделать все данные одинакового размера. И для этого мы собираемся обрезать каждое изображение до размера 256X256 пикселей.

Обрезка фотографий вместо их масштабирования позволяет нам создавать больше данных для нашей модели, разбивая большие изображения на маленькие фрагменты, а также дает более точные прогнозы, чем изменение размера изображений. Patchify и Pillow(PIL) — это инструменты Python, которые очень полезны в нашем сценарии и просты в использовании.

Как мы будем делать патчи из большого изображения?

Например, возьмем изображение размером (1479 x 2149 x 3), где 1479 x 2149 — это размер изображения в пикселях, где 3 указывает на каналы RGB изображения. Итак, сначала мы прочитаем изображение как оно есть, а затем изменим его размер до размера, который ближе всего кратен нашему размеру патча, который в нашем случае равен 256. После изменения размера изображения мы получим изображение размера (1280 x 2048 x 3). После изменения размера изображения мы обрежем изображение до размера (256 x 256 x 3). После исправления этого большого изображения мы получим почти 5 разных изображений из одного изображения, а затем преобразуем эти изображения в массив Numpy, а после преобразования их в массив мы масштабируем их с помощью MinMaxScaler().

image_dataset = []
for y in range(1,19):
image = cv2.imread(f'path of your file with only 00 at end {y}.jpg',1)
SIZE_X = (image.shape[1]//patch_size)*patch_size
SIZE_Y = (image.shape[0]//patch_size)*patch_size
image = Image.fromarray(image)
image = image.crop((0 ,0, SIZE_X, SIZE_Y))
image = np.array(image)
patches_img = patchify(image, (patch_size, patch_size, 3), step=patch_size)
for i in range(patches_img.shape[0]):
for j in range(patches_img.shape[1]):
single_patch_img = patches_img[i,j,:,:]
single_patch_img = scaler.fit_transform(single_patch_img.reshape(-1, single_patch_img.shape[-1])).reshape(single_patch_img.shape)
single_patch_img = single_patch_img[0]
image_dataset.append(single_patch_img)

Точно так же мы сделаем то же самое для замаскированного набора данных. Единственное изменение, которое мы внесем, — это чтение изображений. Поскольку openCV считывает изображения в формате BGR, пока мы работаем в стиле RGB, мы просто добавим одну дополнительную строку кода для преобразования BGR в RGB при чтении изображения. После этого мы убедимся, что наши изображения и созданные изображения правильно выровнены, визуализируя оба изображения с помощью графиков matplotlib.

Шестнадцатеричный код

Данные замаскированные классы представлены в формате шестнадцатеричного кода, который нам необходимо преобразовать в формат RGB. Существуют различные веб-страницы для выполнения такой работы, но для этого нам нужно понять, что это за HEX-коды. В шестнадцатеричном коде числа 0–9 представляют числа 0–9, а 10–15 используются для алфавитов AF.

Возьмем, к примеру, здания только одного класса. В наших данных здания имеют шестнадцатеричный код 3C1098. Поскольку все шестнадцатеричные с основанием 16.

Обозначим красный цвет как R. Мы знаем, что R имеет значение 201, поэтому у нас будет R = 201/16 = 12 с остатком 9, поэтому шестнадцатеричный код красного цвета будет C9. Таким образом, для класса здания значение RGB будет следующим.

3C = 3 x 16 + 12 = 60

10 = 1 x 16 + 0 = 10

98 = 9 x 16 + 8 = 152

Поэтому значение RGB для 3C1098 равно (R=60 , G=10 , B=152), и мы применим тот же процесс для нахождения значений RGB других классов в данных.

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

def rgb_to_2D_label(label):
label_seg = np.zeros(label.shape,dtype=np.uint8)
label_seg [np.all(label == Building,axis=-1)] = 0
label_seg [np.all(label==Land,axis=-1)] = 1
label_seg [np.all(label==Road,axis=-1)] = 2
label_seg [np.all(label==Vegetation,axis=-1)] = 3
label_seg [np.all(label==Water,axis=-1)] = 4
label_seg [np.all(label==Unlabeled,axis=-1)] = 5
label_seg = label_seg[:,:,0]
return label_seg

После этого шага у нас будут данные, готовые для ввода в нашу модель CNN.

Архитектура U-net

Согласно документу U-Net: сверточные сети для сегментации биомедицинских изображений. Предполагаемая наша модель также будет состоять из двух частей: пути сокращения модели (кодировщик) и пути расширения (декодера). Но мы сделаем некоторые модификации, которых нет в приведенной выше модели, такие как добавление мягких сверток.

Контрактный путь (энкодер)

Он состоит из двух сверток 3x3, за каждой из которых следует выпрямленный линейный блок (ReLU) и операция максимального объединения 2x2. Основная цель части кодировщика — понять изображение, пока модель не достигнет дна, как показано на рисунке выше. Единственное, что мы добавили, — это заполнение сверток, которое помогает нам поддерживать размер входных данных на всем протяжении нашей сети U-net, добавляя дополнительные пиксели по краям изображений.

Расширенный путь (декодер)

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

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

В конце декодера мы будем использовать один слой CNN с softmax в качестве активации для получения выходного изображения из модели.

Оценка (метрики)

В целом, для большинства моделей глубокого обучения мы часто используем такие метрики, как точность, но в нашем случае, поскольку мы решаем задачи сегментации, мы не должны использовать точность в качестве нашей метрики. Вместо этого мы будем использовать IoU (Intersection over Union) в качестве нашей метрики.

Метрика Intersection over Union (IoU), также называемая индексом Жаккара. Эта метрика часто используется с коэффициентом кости, который является функцией потерь.

Дисбаланс в классах очень очевиден в проблеме сегментации изображения, и эта проблема не может быть решена метриками IoU. Площадь перекрытия — это не что иное, как истинные положительные значения, а объединение — это сложение (TP + FP + FN).

Функция потери

Функция потерь, которую мы будем использовать, — это фокальные потери. Итак, в двух словах, что такое потеря фокуса? По сути, фокальная потеря — это не что иное, как расширение кросс-энтропийной потери. Добавив всего лишь один параметр γ≥0. Из фокальных потерь мы получаем кросс-энтропийные потери. Формула для очаговой потери выглядит следующим образом.

FL (pt) = -αt(1-pt)^γ log log(pt).

Если мы внимательно понаблюдаем, то увидим, что если мы положим значение = 0, мы получим формулу кросс-энтропии.

ПРЕИМУЩЕСТВА ИСПОЛЬЗОВАНИЯ FOCAL LOSS

  1. Это улучшает классификацию меньшинств.
  2. Он уменьшает вес легко классифицируемых объектов и в основном фокусируется на трудноклассифицируемых объектах.
  3. По умолчанию он уменьшает веса хорошо классифицированных объектов, вероятность которых больше или равна 0,5, и увеличивает веса объектов, вероятность которых меньше 0,5.

После определения нашей модели, потерь и показателей мы можем начать строить нашу модель с помощью оптимизатора Adam.

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

Я провел обучение этой модели на графическом процессоре Google Colab, который работает быстро, но с ограниченным объемом данных. Вы можете использовать AWS или любого другого поставщика облачных вычислений для работы с большими объемами данных и получения результатов.

Нам также необходимо предоставить набор данных для проверки, чтобы понять переоснащение или недообучение модели. Я подогнал модель по 100 эпохам с размером пакета 16. Вы также можете добавить обратные вызовы, чтобы избежать переобучения модели.

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

После подгонки модели мы увидим потери нашей модели и точность модели.

Предсказания

Вот некоторые прогнозы, сгенерированные нашей моделью.

Ссылка на Git-хаб