Я построил модель для раскрашивания изображения в оттенках серого, на этапе обучения я загружаю в сеть 100 изображений леса в формате RGB, а затем конвертирую изображения в цветовое пространство LAB, чтобы разделить обучающий набор на L и AB, на основе обученных Данные AB, модель будет прогнозировать эти два канала для входного изображения в градациях серого на этапе тестирования. Теперь у меня проблема, я обучил модель с другой архитектурой, чем эта, с 10 изображениями, потери уменьшились до 0,0035, и она сработала хорошо, для этого я хотел увеличить размер набора данных, чтобы получить лучший результат, но взамен потери и точность оставались постоянными, а на выходе модели - беспорядок. Мой код следующий, я хочу, чтобы кто-нибудь мог указать мне, что я делаю неправильно, это из-за оптимизатора? функция потерь? размер партии? или что-то еще, о чем я не знаю, заранее спасибо.

# Import images
MODEL_NAME = 'forest'

X = []
Y = []
for filename in os.listdir('forest/'):
    if (filename != '.DS_Store'):
        image = img_to_array(load_img("/Users/moos/Desktop/Project-Master/forest/" + filename))
        image = np.array(image, dtype=float)
        imL = rgb2lab(1.0 / 255 * image)[:, :,0]
        X.append(imL)
        imAB = rgb2lab(1.0 / 255 * image)[:, :,1:]
        imAB = imAB/128
        Y.append(imAB)

X = np.array(X)
Y = np.array(Y)

X = X.reshape(1, 256 , np.size(X)/256, 1)
Y = Y.reshape(1, 256, np.size(Y)/256/2, 2)

# Building the neural network
model = Sequential()
model.add(InputLayer(input_shape=(256, np.size(X)/256, 1)))
model.add(Conv2D(8, (3, 3), activation='relu', padding='same', strides=2))
model.add(Conv2D(32, (3, 3), activation='relu', padding='same'))
model.add(Conv2D(32, (3, 3), activation='relu', padding='same', strides=2))
model.add(Conv2D(64, (3, 3), activation='relu', padding='same', strides=1))
model.add(Conv2D(64, (3, 3), activation='relu', padding='same', strides=2))
model.add(Conv2D(128, (3, 3), activation='relu', padding='same', strides=1))
model.add(Conv2D(128, (3, 3), activation='relu', padding='same', strides=1))
model.add(Conv2D(64, (3, 3), activation='relu', padding='same', strides=1))
model.add(Conv2D(32, (3, 3), activation='relu', padding='same', strides=1))
model.add(UpSampling2D((2, 2)))
model.add(Conv2D(8, (3, 3), activation='relu', padding='same'))
model.add(Conv2D(8, (3, 3), activation='relu', padding='same'))
model.add(UpSampling2D((2, 2)))
model.add(Conv2D(2, (3, 3), activation='relu', padding='same'))
model.add(Conv2D(2, (3, 3), activation='tanh', padding='same'))
model.add(UpSampling2D((2, 2)))

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

#Train the neural network
model.fit(x=X, y=Y, batch_size=100, epochs=1000)
print(model.evaluate(X, Y, batch_size=100))

Выход

Эпоха 1/1000 1/1 [==============================] - 7 с 7 с / шаг - потеря: 0,0214 - соотв. : 0,4987 Эпоха 2/1000 1/1 [==============================] - 7 с 7 с / шаг - потеря: 0,0214 - согласно: 0.4987 Эпоха 3/1000 1/1 [==============================] - 9 с 9 с / шаг - потеря : 0,0214 - согласно: 0,4987 эпоха 4/1000 1/1 [==============================] - 8 с 8 с / шаг - убыток: 0,0214 - согласно: 0,4987. . . .

1
Moos Khaldi 13 Апр 2018 в 00:57

1 ответ

Лучший ответ

Прежде всего, я упростил код загрузки изображения, а также нормализовал (вычесть среднее, разделить на стандартное отклонение) все каналы (L, A, B) отдельно, также переименовал переменные, что обычно очень помогает. (5-минутное бесплатное видео Coursera о нормализации ввода (будет ошибка вы можете подписаться, но просто щелкните по нему.).) Итак, часть загрузки теперь выглядит так:

# Import images
MODEL_NAME = 'forest'

imgLABs = []
for filename in os.listdir('./forest/'):
    if (filename != '.DS_Store'):
        image = img_to_array( load_img("./forest/" + filename) )
        imgLABs.append( rgb2lab( image / 255.0 ) )

imgLABs_arr = np.array( imgLABs )

L, A, B = imgLABs_arr[ :, :, :, 0 : 1 ], imgLABs_arr[ :, :, :, 1 : 2 ], imgLABs_arr[ :, :, :, 2 : 3 ]

L_mean, L_std = np.mean( L ), np.std( L )
A_mean, A_std = np.mean( A ), np.std( A )
B_mean, B_std = np.mean( B ), np.std( B )
L, A, B = ( L - L_mean ) / L_std, ( A - A_mean ) / A_std, ( B - B_mean ) / B_std
AB = np.concatenate( ( A, B ), axis = 3)

Также изменилась модель, добавлена ​​дополнительная глубина объектов и несколько максимальных слоев пула (не забудьте включить их в импорт, не показано). Обратите внимание, что функция активации на последних нескольких слоях установлена ​​на None, чтобы разрешить отрицательные значения, поскольку мы ожидаем нормализованных результатов:

# Building the neural network
model = Sequential()
model.add(InputLayer( input_shape = L.shape[ 1: ] ) )
model.add(Conv2D(64, (3, 3), activation='relu', padding='same', strides=2,
                 kernel_initializer='truncated_normal'))
model.add(MaxPooling2D( (3, 3), strides = 1, padding='same' ) )
model.add(Conv2D(64, (3, 3), activation='relu', padding='same',
                 kernel_initializer='truncated_normal'))
model.add(Conv2D(64, (3, 3), activation='relu', padding='same', strides=2,
                 kernel_initializer='truncated_normal'))
model.add(Conv2D(64, (3, 3), activation='relu', padding='same', strides=1,
                 kernel_initializer='truncated_normal'))
model.add(Conv2D(64, (3, 3), activation='relu', padding='same', strides=2,
                 kernel_initializer='truncated_normal'))
model.add(MaxPooling2D( (3, 3), strides = 1, padding='same' ) )
model.add(Conv2D(64, (3, 3), activation='relu', padding='same', strides=1,
                 kernel_initializer='truncated_normal'))
model.add(Conv2D(64, (3, 3), activation='relu', padding='same', strides=1,
                 kernel_initializer='truncated_normal'))
model.add(Conv2D(64, (3, 3), activation='relu', padding='same', strides=1,
                 kernel_initializer='truncated_normal'))
model.add(Conv2D(64, (3, 3), activation='relu', padding='same', strides=1,
                 kernel_initializer='truncated_normal'))
model.add(UpSampling2D((2, 2)))
model.add(Conv2D(32, (3, 3), activation='relu', padding='same',
                 kernel_initializer='truncated_normal'))
model.add(Conv2D(32, (3, 3), activation='relu', padding='same',
                 kernel_initializer='truncated_normal'))
model.add(UpSampling2D((2, 2)))
model.add(Conv2D(32, (3, 3), activation=None, padding='same',
                 kernel_initializer='truncated_normal'))
model.add(Conv2D(2, (3, 3), activation=None, padding='same',
                 kernel_initializer='truncated_normal'))
model.add(UpSampling2D((2, 2)))

# Finish model
optimizer = optimizers.RMSprop( lr = 0.0005, decay = 1e-5 )
model.compile( optimizer=optimizer, loss='mse', metrics=['acc'] )

#Train the neural network
model.fit( x=L, y=AB, batch_size=1, epochs=1800 )
model.save("forest-model-v2.h5")

Обратите внимание на скорость обучения 0,0005, я экспериментировал с некоторыми значениями, это выглядело лучше всего. Тогда снижение скорости обучения может помочь на более поздних этапах обучения, снижая скорость обучения по мере продвижения. Кроме того, я изменил batch_size на 1 - это очень специфично для данной сети и обычно не рекомендуется. Но здесь у вас в основном прямые свертки, поэтому имеет смысл обновлять ядра после каждого образца, поскольку каждый образец сам по себе влияет на веса каждого пикселя. Но если вы измените архитектуру, это может потерять смысл, и вам следует снова изменить размер пакета. Я также увеличил количество эпох до 1800, потому что он довольно быстро работает на моей машине, и у меня было время запустить его. Однако он достигает своего максимума около 1000.

При этом вот результат обучения (только первые и последние несколько строк):

Эпоха 1/1800
100/100 [==============================] - 6 с 63 мс / шаг - потеря: 1.0554 - точность: 0.5217
Эпоха 2/1800
100/100 [==============================] - 1 с 13 мс / шаг - потеря: 1,1097 - точность: 0,5703
...
Эпоха 1000/1800
100/100 [==============================] - 1 с 13 мс / шаг - потеря: 0,0533 - точность: 0,9338
...
Эпоха 1800/1800
100/100 [==============================] - 1 с 13 мс / шаг - потеря: 0,0404 - точность: 0,9422

Чтобы напечатать перекрашенное изображение, я использовал следующий код, обратите внимание, что 5 - это просто произвольный индекс изображения, которое я выбрал из 100; также нам нужно добавить обратно средние и стандартные отклонения для L, A и B (вы должны рассматривать эти шесть чисел как часть своей сети, когда вы хотите использовать их для фактического перекраски - вам необходимо предварительно обработать ввод с помощью L_std, L_mean, а затем обработать вывод с помощью A, B means и std-s):

predicted = model.predict( x = L[ 5 : 6 ], batch_size = 1, verbose = 1 )
plt.imshow( lab2rgb( np.concatenate(
    ( ( L[ 5 ] * L_std ) + L_mean,
     ( predicted[ 0, :, :, 0 : 1 ] * A_std ) + A_mean,
     ( predicted[ 0, :, :, 1 : 2 ] * B_std ) + B_mean),
    axis = 2 ) ) )

img_pred = lab2rgb( np.concatenate(
    ( ( L[ 5 ] * L_std ) + L_mean,
     ( predicted[ 0, :, :, 0 : 1 ] * A_std ) + A_mean,
     ( predicted[ 0, :, :, 1 : 2 ] * B_std ) + B_mean),
    axis = 2 ) ) 
img_orig = lab2rgb( np.concatenate(
    ( ( L[ 5 ] * L_std ) + L_mean,
      ( A[ 5 ] * A_std ) + A_mean,
      ( B[ 5 ] * B_std ) + B_mean ),
    axis = 2 ) ) 
diff = img_orig - img_pred
plt.imshow( diff * 10 )

И при всем при этом изображения (оригинал; сетевой вход в оттенках серого; сетевой выход (цвета восстановлены); разница между исходным и восстановленным):

original rgb image greyscale negative image, the input to the network enter image description here enter image description here

Довольно аккуратно! :) В основном некоторые детали о горах, что потеряно. Поскольку это всего лишь 100 обучающих изображений, он может быть серьезно переобучен. Тем не менее, я надеюсь, что это хорошее начало!

3
Peter Szoldan 14 Апр 2018 в 19:12