Ввиду необходимости, которое я выставил в этом вопросе, я решил создать подкласс слоя Conv2D, чтобы он правильно обернул входной тензор первым.
Но я не мог заставить это работать. В частности, я не смог даже выполнить модульный тест, который инициализирует слой с данным ядром, затем передает ему заданный входной тензор и проверяет, что вычисление, определенное в методе .call()
, дает ожидаемый результат .
Итак, я делаю шаг назад, и, чтобы избежать проблем с моим кодом, я концентрируюсь на том, как сначала разобраться с уже существующим родительским уровнем Keras Conv2D
.
import unittest
from numpy import asarray, arange
import keras.backend as K
from keras.models import Sequential #, Model,
from keras.layers import Conv2D #, Input, MaxPooling2D
from keras.initializers import Constant
from keras.optimizers import SGD
class Test(unittest.TestCase):
def testConv2D(self):
''' verify that I can initialize, invoke, and test a given Keras layer
'''
# kernel to make convolution constant except at borders
kern = asarray([[ 1, 0, -1]]).T + asarray([[ 0.5, 1, 0, -1, -0.5 ]])
print(kern)
kern = Constant(kern)
input_shape = (10, 10, 3)
sgd = SGD(lr=0.01, decay=1e-6, momentum=0.9, nesterov=True)
base_model = Sequential()
base_layer = Conv2D(10, (3, 5),
input_shape=input_shape,
kernel_initializer=kern,
use_bias=False)
base_model.add(base_layer)
base_model.compile(loss='mean_squared_error', optimizer=sgd)
x = arange(5 * 10 * 10 * 3).reshape((5, 10, 10, 3)) # 5 samples, 3 channels
x = K.variable(x)
# the print() statements to be replaced by asserts of course
y = base_layer(x)
print(y) # this just prints Tensor("conv2d_1_1/convolution:0", shape=(5, 8, 6, 10), dtype=float32)
yp = base_model.predict(x) # this throws a ValueError about specifying the `steps` argument
print(yp)
pass
# once this works I'll replace Conv2D with a subclass of it, and
# verify that I can preprocess the input tensor; and will be able
# to verify that the convolution gives the right result
pass
# once also this works, I'll replace partial tests with a single test that verifies only the result
pass
if __name__ == "__main__":
#import sys;sys.argv = ['', 'Test.testName']
unittest.main()
Попытка выполнить это показывает, что я даже не могу вызвать существующий слой Keras для данных. Понятия не имею, как это сделать лучше (может, мне лучше использовать функциональный API?)
Я подозреваю, что я полностью сбился с пути. Если есть какой-нибудь пример или ссылка, где я могу получить вдохновение, или любой намек о правильном способе выполнения вышеизложенного, я был бы очень признателен, чтобы прочитать его
PS. Я также проверил эти вопросы, которые пролили некоторый свет на то, как реализовать то, что мне нужно, но на самом деле сейчас Я сосредоточен на том, как протестировать то, что я реализую :
Обновление 1: два вывода
Прежде чем кто-то еще это укажет, я нашел несколько первых ответов и могу сказать, в каком направлении я следую:
y = base_layer(x)
; Непосредственно не подходит для тестирования , так как не дает никакого значения (в Functional API он просто объявляет, как должна быть преобразована переменная x, чтобы получить выходные данные слоя)
- Вместо этого
yp = base_model.predict(x)
является правильным , но x должен быть массивом numpy
. Гораздо проще, чем любой Keras variable
или Constant
вообще (что я считаю более декларативным объектами)
- явная инициализация ядра с помощью объекта
keras.initializers.Constant
, созданного из numpy
массива , работает, если предоставленное ядро имеет правильный размер! В этом случае Я хотел использовать ядро [3,5]
, но сделал это очень плохо: если мы работаем с изображениями с C
цветными слоями, один фильтр подразумевает ядро [3,5,C]
. Наконец, поскольку слой Conv2D имеет F=10
фильтры, правильный инициализатор ядра должен быть из четырехмерного массива с формой [3,5,C,F]
. Обратите внимание, что размер фильтра является последним (а не первым).
- с точки зрения тестирования, я мог бы реализовать это, вызвав метод layer
get_weights()
и проверив, что форма инициализатора является конгруэнтной (описано здесь ) (может быть специальная проверка и предупреждающие сообщения вставляется в код Keras, когда инициализатор содержит явный массив, чтобы предотвратить эту ошибку).
Так что теперь я знаю, как двигаться дальше. Я буду держать вас в курсе, надеюсь, с полным ответом с рабочим решением.