Для такого рода большой архитектуры я предлагаю вам строить из маленьких кусочков, а затем соединять их вместе. Во-первых, часть кодера. Он получает изображение размером (28,28,1)
и возвращает закодированное изображение формы (28,28,1)
.
from keras.layers import Input, Concatenate, GaussianNoise
from keras.layers import Conv2D
from keras.models import Model
def make_encoder():
image = Input((28, 28, 1))
x = Conv2D(8, (5, 5), activation='relu', padding='same')(image)
x = Conv2D(4, (3, 3), activation='relu', padding='same')(x)
x = Conv2D(2, (3, 3), activation='relu', padding='same')(x)
encoded = Conv2D(1, (3, 3), activation='relu', padding='same')(x)
return Model(inputs=image, outputs=encoded)
encoder = make_encoder()
encoder.summary()
#_________________________________________________________________
#Layer (type) Output Shape Param #
#=================================================================
#input_1 (InputLayer) (None, 28, 28, 1) 0
#_________________________________________________________________
#conv2d_1 (Conv2D) (None, 28, 28, 8) 208
_________________________________________________________________
#conv2d_2 (Conv2D) (None, 28, 28, 4) 292
#_________________________________________________________________
#conv2d_3 (Conv2D) (None, 28, 28, 2) 74
#_________________________________________________________________
#conv2d_4 (Conv2D) (None, 28, 28, 1) 19
#=================================================================
#Total params: 593
#Trainable params: 593
#Non-trainable params: 0
#_________________________________________________________________
Переход формы соответствует теории.
Затем часть декодера получает закодированный слитый с другим массивом shape (28, 28, 2)
и, наконец, восстанавливает исходное изображение shape (28, 28, 1).
def make_decoder():
encoded_merged = Input((28, 28, 2))
x = Conv2D(2, (5, 5), activation='relu', padding='same')(encoded_merged)
x = Conv2D(4, (3, 3), activation='relu', padding='same')(x)
x = Conv2D(8, (3, 3), activation='relu',padding='same')(x)
decoded = Conv2D(1, (3, 3), activation='sigmoid', padding='same')(x)
return Model(inputs=encoded_merged, outputs=decoded)
decoder = make_decoder()
decoder.summary()
#_________________________________________________________________
#Layer (type) Output Shape Param #
#=================================================================
#input_2 (InputLayer) (None, 28, 28, 2) 0
#_________________________________________________________________
#conv2d_5 (Conv2D) (None, 28, 28, 2) 102
#_________________________________________________________________
#conv2d_6 (Conv2D) (None, 28, 28, 4) 76
#_________________________________________________________________
#conv2d_7 (Conv2D) (None, 28, 28, 8) 296
#_________________________________________________________________
#conv2d_8 (Conv2D) (None, 28, 28, 1) 73
#=================================================================
#Total params: 547
#Trainable params: 547
#Non-trainable params: 0
#_________________________________________________________________
Затем модель пытается восстановить массив W. Ввод - восстановленное изображение плюс шум (форма (28, 28, 1)
).
def make_w_predictor():
decoded_noise = Input((28, 28, 1))
x = Conv2D(8, (5, 5), activation='relu', padding='same')(decoded_noise)
x = Conv2D(4, (3, 3), activation='relu', padding='same')(x)
pred_w = Conv2D(1, (3, 3), activation='relu', padding='same')(x)
# reconsider activation (is W positive?)
# should be filter=1 to match W
return Model(inputs=decoded_noise, outputs=pred_w)
w_predictor = make_w_predictor()
w_predictor.summary()
#_________________________________________________________________
#Layer (type) Output Shape Param #
#=================================================================
#input_3 (InputLayer) (None, 28, 28, 1) 0
#_________________________________________________________________
#conv2d_9 (Conv2D) (None, 28, 28, 8) 208
#_________________________________________________________________
#conv2d_10 (Conv2D) (None, 28, 28, 4) 292
#_________________________________________________________________
#conv2d_11 (Conv2D) (None, 28, 28, 1) 37
#=================================================================
#Total params: 537
#Trainable params: 537
#Non-trainable params: 0
#_________________________________________________________________
Имея все под рукой, собрать все вместе, чтобы собрать всю модель, не так сложно. Обратите внимание, что созданные вами модели можно использовать как слои.
def put_together(encoder, decoder, w_predictor):
image = Input((28, 28, 1))
w = Input((28, 28, 1))
encoded = encoder(image)
encoded_merged = Concatenate(axis=3)([encoded, w])
decoded = decoder(encoded_merged)
decoded_noise = GaussianNoise(0.5)(decoded)
pred_w = w_predictor(decoded_noise)
return Model(inputs=[image, w], outputs=[decoded, pred_w])
model = put_together(encoder, decoder, w_predictor)
model.summary()
#__________________________________________________________________________________________________
#Layer (type) Output Shape Param # Connected to
#==================================================================================================
#input_4 (InputLayer) (None, 28, 28, 1) 0
#__________________________________________________________________________________________________
#model_1 (Model) (None, 28, 28, 1) 593 input_4[0][0]
#__________________________________________________________________________________________________
#input_5 (InputLayer) (None, 28, 28, 1) 0
#__________________________________________________________________________________________________
#concatenate_1 (Concatenate) (None, 28, 28, 2) 0 model_1[1][0]
# input_5[0][0]
#__________________________________________________________________________________________________
#model_2 (Model) (None, 28, 28, 1) 547 concatenate_1[0][0]
#__________________________________________________________________________________________________
#gaussian_noise_1 (GaussianNoise (None, 28, 28, 1) 0 model_2[1][0]
#__________________________________________________________________________________________________
#model_3 (Model) (None, 28, 28, 1) 537 gaussian_noise_1[0][0]
#==================================================================================================
#Total params: 1,677
#Trainable params: 1,677
#Non-trainable params: 0
#__________________________________________________________________________________________________
Код ниже обучает модель фиктивным данным. Конечно, вы можете использовать свой собственный, если форма соответствует.
import numpy as np
# dummy data
images = np.random.random((1000, 28, 28, 1))
w = np.random.lognormal(size=(1000, 28, 28, 1))
# is accuracy sensible metric for this model?
model.compile(optimizer='adadelta', loss='mse', metrics=['accuracy'])
model.fit([images, w], [images, w], batch_size=64, epochs=5)
РЕДАКТИРОВАТЬ НИЖЕ
У меня есть несколько вопросов о коде, который вы здесь разместили. в предикторе make_w_ вы сказали: «# пересмотреть активацию (W положительный?) # должен быть filter = 1, чтобы соответствовать W», что это значит? W - это массив, содержащий 0 и 1. Что означает «пересмотреть активацию», если я изменю код этой части?
relu
Активация возвращает положительные числа в [0, + inf), поэтому это может быть неправильным выбором, если W
принимает другой набор значений. Типичный выбор будет следующим.
W
могут быть положительными и отрицательными числами: «линейная» активация.
W
в [0, 1]: «сигмовидная» активация.
W
в [-1, 1]: активация "tanh".
W
- положительное число: активация "relu".
В исходном коде у вас было:
w=np.random.random((1, 28, 28))
, который принимает значения от 0 до 1. Поэтому я предложил переключиться с «relu» на «sigmoid». Но я не изменил свой пример кода, потому что я не был уверен, что это было задумано.
Вы сказали, что фильтр должен быть 1, это означает изменение (3,3) на (1,1)? Мне очень жаль эти вопросы. но я новичок, и я не могу найти некоторые из них, что вы говорите. не могли бы вы помочь мне и объяснить мне полностью.
Я ссылаюсь на эту строку в оригинальном вопросе:
final_image_watermark = Conv2D(2, (3, 3), activation='relu', padding='same')(x)
Если я правильно понимаю, это определяет W'
на прилагаемом изображении, которое должно предсказывать W
, а его размер равен (28, 28, 1)
. Тогда первый аргумент Conv2D
должен быть один. В противном случае выходная форма становится (28, 28, 2)
. Я сделал это изменение в моем примере кода, потому что в противном случае он выдает ошибку несоответствия формы:
pred_w = Conv2D(1, (3, 3), activation='relu', padding='same')(x)
Я думаю, (3, 3)
часть, kernel size
в керасе, это нормально, как есть.