Могу ли я использовать сигмовидную активацию для моего выходного слоя, даже если моя модель CNN выполняет регрессию?

Конечная цель: расчет средней точки объекта.

У меня есть небольшой набор данных (около 120 изображений), в котором есть объект (одинаковый во всех случаях), а метки - это нормализованные координаты x, y средней точки объекта на изображении (всегда между 0 и 1)

например x = image_005; y = (0,1, 0,15) для изображения с объектом, расположенным в нижнем левом углу

Я пытаюсь использовать архитектуру ResNet, но настроенную под размер моего изображения (все изображения идентичны). Поскольку выходные значения всегда находятся в диапазоне от 0 до 1 для обеих координат, мне было интересно, можно ли использовать сигмовидную активацию в моем последнем слое:

 X = Dense(2, activation='sigmoid', name='fc', kernel_initializer = glorot_uniform(seed=0))(X)

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

Для функции потерь я использую MSE с оптимизатором rmsprop, и в дополнение к точности и MSE я написал настраиваемую метрику, которая сообщает мне, отклоняются ли прогнозируемые точки от меток более чем на 5%.

model.compile(optimizer='rmsprop', loss='mean_squared_error', metrics=['mse','acc',perc_midpoint_err])

Я не получаю хороших результатов после обучения модели примерно на 150 эпохах (я тоже экспериментировал с разными размерами пакетов)

Стоит ли менять слой активации на линейный? Или я могу сделать другую модификацию своей модели? Или ResNet совершенно не подходит для этой задачи?


person Ambareesh    schedule 21.10.2018    source источник
comment
Вы можете делать все, что работает. Вы должны внимательно оценивать   -  person CAFEBABE    schedule 21.10.2018
comment
Не могли бы вы подробнее рассказать об оценке?   -  person Ambareesh    schedule 21.10.2018
comment
Во-первых, вы должны, очевидно, сравнить качество сигмовидной и линейной формы при перекрестной проверке. Однако в этом случае я бы также сделал визуальный осмотр результатов. Я предполагаю, что активация сигмовидной кишки приведет к предсказаниям, направленным к центру. Это проблема? Только знаток предметной области может сказать.   -  person CAFEBABE    schedule 22.10.2018


Ответы (2)


Ваша задача связана с обнаружением объектов. Разница в том, что у вас, кажется, есть только один объект на каждом из ваших изображений, тогда как при обнаружении может быть несколько объектов или объект не присутствует. Для обнаружения объектов существуют такие сети, как YOLOv3 (https://pjreddie.com/media/files/papers/YOLOv3.pdf) или детектор Single Shot Multibox - SSD (https://arxiv.org/pdf/1512.02325.pdf), но также ResNet можно обучить как сеть обнаружения объектов (как в этой статье: https://arxiv.org/pdf/1506.01497.pdf)

Я кратко опишу, как YOLO решает проблему регрессии для координат x, y ограничивающего прямоугольника:

  • YOLO использует сигмовидную функцию активации для x, y
  • Он разделяет изображение на ячейки сетки и прогнозирует смещения для потенциального объекта в каждой ячейке сетки. Это может быть полезно, если у вас есть большие изображения или объекты в нескольких местах.
  • В исходной статье MSE используется как функция потерь, но в моей любимой повторной реализации keras они используют потерю кроссэнтропии с оптимизатором Adam.

В принципе, ваша установка мне нравится. Но есть много вещей, которые могут привести к снижению производительности, поскольку вы не сообщаете о домене вашего набора данных: используете ли вы предварительно обученную сеть или тренируетесь с нуля? Это новая категория, которую вы должны изучить, или категория объектов, которую сеть видела раньше? и т.п.

Вот несколько идей, которые вы можете попробовать:

  • смени оптимизатор (на SGD или Adam)
  • изменить скорость обучения (лучше меньше, чем слишком много)
  • увеличьте размер набора данных. Для переобучения сети для новой категории объектов мое практическое правило - использовать около 500–1000 изображений. Для переобучения с нуля нужно на порядки больше.
  • вы можете проверить YOLO или SSD и изменить эти сети для вашего случая

Надеюсь, вы найдете вдохновение для своего решения.

person brbr    schedule 21.10.2018

Помимо того, что вы сделали, вы можете сделать еще много:

  1. Используйте метод ImageAugmentation для создания дополнительных данных. Также нормализуйте изображения.
  2. Сделайте более глубокую модель с еще несколькими сверточными слоями.
  3. Используйте правильный инициализатор весов, возможно, He-normal для сверточных слоев.
  4. Используйте BatchNormalization между слоями, чтобы получить среднее и std значений вашего фильтра равны 0 и 1 соответственно.
  5. Попробуйте использовать потерю кроссэнтропии, поскольку это помогает лучше вычислять градиенты. В MSE градиенты со временем становятся очень маленькими, хотя это предпочтительнее для задач регрессионного типа. Вы также можете попробовать MeanSquaredLogarithmicError.
  6. Попробуйте изменить оптимизатор на Адам или Стохастический градиентный спуск с импульсом нестров (работает лучше, чем Адам на проверочном наборе).
  7. В случае, если у вас есть еще несколько классов в вашем наборе данных, и у вас есть проблема с дисбалансом классов, вы можете использовать Focal loss, вариант потери кроссэнтропии, который наказывает неправильно классифицированные метки больше, чем правильно классифицированные метки. Кроме того, могут помочь уменьшение размера пакета и передискретизации.
  8. Используйте методы байесовской оптимизации для настройки гиперпараметров вашей модели.

Вот простая реализация Resnet:

def unit(x, filters, pool=False):
    res = x
    if pool:
        x = MaxPooling2D(pool_size=(2, 2))(x)
        res = Conv2D(filters=filters, kernel_size=(1, 1), strides=(2, 2), padding='same', kernel_initializer='he_normal')(res)
    out = BatchNormalization()(x)
    out = Activation('relu')(out)
    out = Conv2D(filters=filters, kernel_size=(3, 3), strides=(1, 1), padding='same', kernel_initializer='he_normal')(out)

    out = BatchNormalization()(out)
    out = Activation('relu')(out)
    out = Conv2D(filters=filters, kernel_size=(3, 3), strides=(1, 1), padding='same', kernel_initializer='he_normal')(out)

    x = keras.layers.add([res, out])
    return x

def model(inputs):
    inp = Input(inputs)
    x = Conv2D(32, (3, 3), padding='same', kernel_initializer='he_uniform')(inp)
    x = unit(x, 32)
    x = unit(x, 32)
    x = unit(x, 32)

    x = unit(x, 64, pool=True)
    x = unit(x, 64)
    x = unit(x, 64)

    x = unit(x, 128, pool=True)
    x = unit(x, 128)
    x = unit(x, 128)

    x = unit(x, 256, pool=True)
    x = unit(x, 256)
    x = unit(x, 256)

    x = BatchNormalization()(x)
    x = Activation('relu')(x)
    x = Dropout(0.25)(x)

    x = AveragePooling2D((3, 3))(x)
    x = Flatten()(x)
    x = Dense(2, activation='sigmoid')(x)

    model = Model(inputs=inp, outputs=x)
    optimizer = Adam(lr=0.001)
    # model.compile(optimizer=optimizer, loss='categorical_crossentropy', metrics=['accuracy'])
    model.compile(optimizer=optimizer, loss=keras.losses.MeanSquaredLogarithmicError(), metrics=['accuracy'])
    return model    
person Rishab P.    schedule 11.09.2019