- Вопрос или проблема
- Ответ или решение
- Почему ваши предсказания нарушены при выполнении семантической сегментации изображений с использованием TensorFlow?
- 1. Проблема описания
- 2. Анализ причин проблемы
- 2.1. Неправильная активация выходного слоя
- 2.2. Проблема с настройками потерь
- 2.3. Отсутствие преобразования меток
- 3. Рекомендации по исправлению кода
- 3.1. Изменение активации вывода
- 3.2. Преобразование аннотаций
- 3.3. One-Hot кодирование меток
- 4. Заключение
Вопрос или проблема
Я пытаюсь выполнить семантическую сегментацию изображений с помощью TensorFlow. Чтобы просто заставить что-то работать, я беру одно учебное изображение, тренирую сеть на этом изображении некоторое время, а затем “тестирую” сеть на том же самом изображении, то есть я сильно переобучаюсь на этом одном изображении. Во время тестирования я ожидал бы, что сеть выдаст более или менее такие же сегментации, как и истинные данные, на которых она была обучена. Однако я получаю совершенно неверные прогнозы:
Я использую набор данных Synthia. Аннотации сегментации предоставляются в виде RGB изображений, где цвет каждого пикселя обозначает его класс:
Поскольку нет функции TensorFlow, которая автоматически берет RGB истинные данные и выводит их распределение классов (почему нет?), я просто использую значения цвета с плавающей точкой в качестве распределения классов истинных данных.
Почему мой код выдает такие произвольные карты прогнозирования, вместо того чтобы выдавать что-то, что более или менее напоминает соответствующее изображение аннотации истинных данных?
Мой код выглядит следующим образом:
from __future__ import absolute_import, division, print_function
import tensorflow as tf
import numpy as np
import os
tf.enable_eager_execution()
NUM_TRAINING_SAMPLES = 1
NUM_CLASSES = 3
BATCH_SIZE = 5
NUM_EPOCHS = 3
INPUT_SIZE = (256, 256, 3)
random_indices = np.random.choice(range(13000), NUM_TRAINING_SAMPLES)
directory_images = "C:/SYNTHIA/RGB"
directory_labels = "C:/SYNTHIA/GT"
train_images = np.array(os.listdir(directory_images))
train_labels = np.array(os.listdir(directory_labels))
train_images = train_images[random_indices]
train_labels = train_labels[random_indices]
train_images = [tf.read_file(os.path.join(directory_images, img)) for img in train_images]
train_labels = [tf.read_file(os.path.join(directory_labels, img)) for img in train_labels]
train_images = [tf.io.decode_image(img, channels=3) for img in train_images]
train_labels = [tf.io.decode_image(img, channels=3) for img in train_labels]
train_images = tf.image.resize_images(train_images, INPUT_SIZE[:2])
train_labels = tf.image.resize_images(train_labels, INPUT_SIZE[:2])
train_images = tf.image.convert_image_dtype(train_images, dtype=tf.uint8)
train_labels = train_labels / 256
train_dataset = tf.data.Dataset.from_tensor_slices((train_images, train_labels))
train_dataset = train_dataset.batch(1)
train_dataset = train_dataset.repeat()
def convolve(input_layer, num_filters):
layer = tf.keras.layers.Conv2D(num_filters, (3, 3), padding='same')(input_layer)
layer = tf.keras.layers.BatchNormalization()(layer)
layer = tf.keras.layers.Activation('relu')(layer)
layer = tf.keras.layers.Conv2D(num_filters, (3, 3), padding='same')(layer)
layer = tf.keras.layers.BatchNormalization()(layer)
layer = tf.keras.layers.Activation('relu')(layer)
return layer
def downsample(input_layer, num_filters):
layer = convolve(input_layer, num_filters)
layer = tf.keras.layers.MaxPooling2D((2, 2), strides=(2, 2))(layer)
return layer
def upsample(input_layer, num_filters):
layer = tf.keras.layers.Conv2DTranspose(num_filters, (2, 2), strides=(2, 2), padding='same')(input_layer)
layer = tf.keras.layers.BatchNormalization()(layer)
layer = tf.keras.layers.Activation('relu')(layer)
layer = tf.keras.layers.Conv2D(num_filters, (3, 3), padding='same')(layer)
layer = tf.keras.layers.BatchNormalization()(layer)
layer = tf.keras.layers.Activation('relu')(layer)
layer = tf.keras.layers.Conv2D(num_filters, (3, 3), padding='same')(layer)
layer = tf.keras.layers.BatchNormalization()(layer)
layer = tf.keras.layers.Activation('relu')(layer)
return layer
inputs = tf.keras.layers.Input(shape=INPUT_SIZE) # 256
encoder0 = downsample(inputs, 32) # 128
encoder1 = downsample(encoder0, 64) # 64
encoder2 = downsample(encoder1, 128) # 32
encoder3 = downsample(encoder2, 256) # 16
encoder4 = downsample(encoder3, 512) # 8
center = convolve(encoder4, 1024) # center
decoder4 = upsample(center, 512) # 16
decoder3 = upsample(decoder4, 256) # 32
decoder2 = upsample(decoder3, 128) # 64
decoder1 = upsample(decoder2, 64) # 128
decoder0 = upsample(decoder1, 32) # 256
outputs = tf.keras.layers.Conv2D(NUM_CLASSES, (1, 1), activation='sigmoid')(decoder0)
model = tf.keras.Model(inputs=[inputs], outputs=[outputs]) # model = tf.keras.Model(inputs=[images], outputs=[output])
model.compile(optimizer=tf.train.GradientDescentOptimizer(learning_rate=0.001), loss="categorical_crossentropy", metrics=['accuracy'])
#model.summary()
model.fit(train_dataset, batch_size=BATCH_SIZE, epochs=NUM_EPOCHS, steps_per_epoch=6, callbacks=[tf.keras.callbacks.TensorBoard(log_dir="./logs")])
#random_indices = np.random.choice(range(13000), NUM_TRAINING_SAMPLES)
test_images = np.array(os.listdir(directory_images))
test_images = test_images[random_indices]
test_images = [tf.read_file(os.path.join(directory_images, img)) for img in test_images]
test_images = [tf.io.decode_image(img, channels=3) for img in test_images]
test_images = tf.image.resize_images(test_images, INPUT_SIZE[:2])
test_images = tf.image.convert_image_dtype(test_images, dtype=tf.uint8)
test_dataset = tf.data.Dataset.from_tensors(test_images)
predictions = model.predict(test_dataset, batch_size=5, steps=1)
predictions = predictions * 256
predictions = tf.image.convert_image_dtype(predictions, dtype=tf.uint8)
jpg = tf.io.encode_jpeg(predictions[0])
tf.io.write_file("C:/SYNTHIA/prediction_map.jpg", jpg)
```
Вам следует использовать softmax в качестве активации выходного слоя, поскольку вы выполняете многоклассовую классификацию. Затем, для прогнозирования, вам нужно взять argmax выходного слоя
Также, так как вы используете категориальную кросс-энтропию, не забудьте преобразовать метки в one-hot кодирование
ИЗМЕНЕНИЕ ПОСЛЕ КОММЕНТАРИЕВ
Ваши метки предоставляются в виде RGB изображений, вы должны преобразовать их в подходящие метки, например, в скалярные целые значения.
Пример:
Оригинальные метки имеют размеры (H,W,3), а преобразованные метки имеют размеры (H,W,1). Если у вас 3 класса, у вас должно быть ровно 3 значения RGB, возьмем (255,0,0), (0,255,0) и (0,0,255). Таким образом, каждый пиксель с этими значениями должен быть преобразован в метку 0, 1 или 2 соответственно. Затем вы можете закодировать метки в виде матриц one-hot
Весь процесс выглядит следующим образом:
- (255,0,0) –> 0 –> (1,0,0)
- (0,255,0) –> 1 –> (0,1,0)
- (0,0,255) –> 2 –> (0,0,1)
Ответ или решение
Почему ваши предсказания нарушены при выполнении семантической сегментации изображений с использованием TensorFlow?
1. Проблема описания
Вы столкнулись с проблемой, когда ваша модель для семантической сегментации изображений на основе TensorFlow не выдает ожидаемые сегментации, несмотря на то что вы намеренно переобучали модель на одном изображении. Вместо более-менее точного результата вы наблюдаете "случайные" предсказания, которые не соответствуют ожидаемым значениям, представленным в аннотациях.
2. Анализ причин проблемы
2.1. Неправильная активация выходного слоя
Вы используете sigmoid
как функцию активации на выходном слое вашей модели, что подходит только для бинарной классификации, но не для многоклассовой семантической сегментации. Вместо этого необходимо использовать softmax
, который нормализует выходные предсказания в распределение вероятности между классами. Это критично для задачи многоклассовой классификации, так как ваша задача требует выбора одного класса из нескольких на каждом пикселе изображения.
2.2. Проблема с настройками потерь
Вы используете categorical_crossentropy
в качестве функции потерь, предполагая, что ваши метки представляют собой вектор в формате one-hot. Однако ваши метки находятся в RGB-формате, и их необходимо предварительно обработать для соответствия формату одного класса для каждого пикселя.
2.3. Отсутствие преобразования меток
Ваши данные аннотаций в RGB-формате должны быть преобразованы в соответствующие целочисленные метки классов. Например, если у вас есть три класса, и они представлены цветами RGB (255, 0, 0), (0, 255, 0) и (0, 0, 255), вам нужно преобразовать их в числовые метки 0, 1 и 2 соответственно. Это предполагает изменение формы аннотаций с (H, W, 3) на (H, W, 1).
3. Рекомендации по исправлению кода
Вот некоторые ключевые изменения, которые нужно внести в ваш код:
3.1. Изменение активации вывода
Измените выходной слой вашей модели на следующий:
outputs = tf.keras.layers.Conv2D(NUM_CLASSES, (1, 1), activation='softmax')(decoder0)
3.2. Преобразование аннотаций
Создайте функцию, которая преобразует ваши RGB-метки в целочисленные. Например:
def rgb_to_class(labels):
class_map = {
(255, 0, 0): 0, # Красный
(0, 255, 0): 1, # Зеленый
(0, 0, 255): 2 # Синий
}
# Преобразование в числовые метки
h, w, _ = labels.shape
class_labels = np.zeros((h, w), dtype=np.uint8)
for rgb, class_id in class_map.items():
mask = np.all(labels == rgb, axis=-1)
class_labels[mask] = class_id
return class_labels
Затем вы сможете применять эту функцию к вашим аннотациям перед обучением модели.
3.3. One-Hot кодирование меток
После того как ваши метки преобразованы в целые числа, вам необходимо сделать их one-hot кодирование. Это можно сделать с помощью TensorFlow:
train_labels = tf.keras.utils.to_categorical(train_labels, num_classes=NUM_CLASSES)
4. Заключение
Эти изменения помогут устранить проблемы, с которыми вы сталкиваетесь при выполнении сегментации изображений с использованием TensorFlow. Это обеспечит корректную интерпретацию выходных данных вашей модели и даст возможность вашей модели правильно обучаться и делать осмысленные предсказания.
Следуя этим рекомендациям, вы сможете скорректировать текущую настройку вашей модели и улучшить качество предсказаний на основе семантической сегментации.