Обучение полностью сверточной нейронной сети со входами переменной величины требует неоправданно долгого времени в Keras/TensorFlow

Я пытаюсь реализовать FCNN для классификации изображений, которые могут принимать входы переменного размера. Модель построена в Keras с поддержкой TensorFlow.

Рассмотрим следующий пример игрушки:

model = Sequential()

# width and height are None because we want to process images of variable size 
# nb_channels is either 1 (grayscale) or 3 (rgb)
model.add(Convolution2D(32, 3, 3, input_shape=(nb_channels, None, None), border_mode='same'))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))

model.add(Convolution2D(32, 3, 3, border_mode='same'))
model.add(MaxPooling2D(pool_size=(2, 2)))

model.add(Convolution2D(16, 1, 1))
model.add(Activation('relu'))

model.add(Convolution2D(8, 1, 1))
model.add(Activation('relu'))

# reduce the number of dimensions to the number of classes
model.add(Convolution2D(nb_classses, 1, 1))
model.add(Activation('relu'))

# do global pooling to yield one value per class
model.add(GlobalAveragePooling2D())

model.add(Activation('softmax'))

Эта модель работает нормально, но я столкнулся с проблемой производительности. Обучение изображениям переменной величины требует неоправданно долгого времени по сравнению с обучением на входах фиксированного размера. Если я изменю размер всех изображений на максимальный размер в наборе данных, для обучения модели по-прежнему требуется гораздо меньше времени, чем обучение на входе с переменным размером. Так что input_shape=(nb_channels, None, None) правильный способ указать ввод переменных размера? И есть ли способ смягчить эту проблему производительности?

Обновить

model.summary() для модели с 3 классами и полутоновыми изображениями:

Layer (type)                     Output Shape          Param #     Connected to                     
====================================================================================================
convolution2d_1 (Convolution2D)  (None, 32, None, None 320         convolution2d_input_1[0][0]      
____________________________________________________________________________________________________
activation_1 (Activation)        (None, 32, None, None 0           convolution2d_1[0][0]            
____________________________________________________________________________________________________
maxpooling2d_1 (MaxPooling2D)    (None, 32, None, None 0           activation_1[0][0]               
____________________________________________________________________________________________________
convolution2d_2 (Convolution2D)  (None, 32, None, None 9248        maxpooling2d_1[0][0]             
____________________________________________________________________________________________________
maxpooling2d_2 (MaxPooling2D)    (None, 32, None, None 0           convolution2d_2[0][0]            
____________________________________________________________________________________________________
convolution2d_3 (Convolution2D)  (None, 16, None, None 528         maxpooling2d_2[0][0]             
____________________________________________________________________________________________________
activation_2 (Activation)        (None, 16, None, None 0           convolution2d_3[0][0]            
____________________________________________________________________________________________________
convolution2d_4 (Convolution2D)  (None, 8, None, None) 136         activation_2[0][0]               
____________________________________________________________________________________________________
activation_3 (Activation)        (None, 8, None, None) 0           convolution2d_4[0][0]            
____________________________________________________________________________________________________
convolution2d_5 (Convolution2D)  (None, 3, None, None) 27          activation_3[0][0]               
____________________________________________________________________________________________________
activation_4 (Activation)        (None, 3, None, None) 0           convolution2d_5[0][0]            
____________________________________________________________________________________________________
globalaveragepooling2d_1 (Global (None, 3)             0           activation_4[0][0]               
____________________________________________________________________________________________________
activation_5 (Activation)        (None, 3)             0           globalaveragepooling2d_1[0][0]   
====================================================================================================
Total params: 10,259
Trainable params: 10,259
Non-trainable params: 0

Ответ 1

Я думаю, что @marcin-możejko может иметь правильный ответ в своем комментарии. Это может быть связано с этой ошибкой, которая была только что исправлена. И этот патч может предупредить вас, если что-то компилируется слишком часто.

Так что обновление до пакета tf-nightly-gpu-2.0-preview может исправить это. Также вы получаете эту проблему с tf.keras.

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

Обратите внимание, что для базовых сверток с "одинаковым" заполнением дополнение к нулю не должно влиять на вывод, кроме выравнивания по пикселям.

Таким образом, один из подходов состоит в том, чтобы тренироваться с фиксированным списком размеров и изображениями нулевых площадок до этих размеров. Например и тренироваться партиями 128х128, 256х256, 512х512. Если вы не можете исправить динамическую компиляцию, это по крайней мере скомпилирует ее только 3 раза. Это было бы немного похоже на двумерный подход "последовательность по длине последовательности", иногда встречающийся в моделях последовательностей.

Ответ 2

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

Определенная степень сдвига, поворота, зеркалирования поможет модели обобщить, но снова масштабируется до согласованного размера. Таким образом, при изменении размера вы исправляете проблему масштабирования и делаете входные данные пространственно согласованными.

Короче говоря, я думаю, что эта сетевая архитектура не подходит/способна для задачи, которую вы ей даете, т.е. различных масштабов.