Возможно ли использовать пользовательский tf.op
вместо оператора tf.backend
в пользовательском слое keras? более конкретно, пользовательский tf.op
, который позволяет pyopencl
logi c.
До сих пор я достиг создания и проверки пользовательского tf.op
отдельно и в одиночку с помощью tf.py_func()
, а также создал библиотеку .so и вызывал его. Код для tf.py_fun c ():
def matmul(a, b, dim_a, dim_b, bias=None):
return tf.py_func(func=kernel.run_args,
inp=[a, b, dim_a, dim_b, 0, bias],
Tout=tf.float32)
def matmul_relu(a, b, dim_a, dim_b, bias=None):
return tf.py_func(func=kernel.run_args,
inp=[a, b, dim_a, dim_b, 1, bias],
Tout=tf.float32)
def matmul_sigmoid(a, b, dim_a, dim_b, bias=None):
return tf.py_func(func=kernel.run_args,
inp=[a, b, dim_a, dim_b, 2, bias],
Tout=tf.float32)
def matmul_tanh(a, b, dim_a, dim_b, bias=None):
return tf.py_func(func=kernel.run_args,
inp=[a, b, dim_a, dim_b, 3, bias],
Tout=tf.float32)
def matmul_softmax(a, b, dim_a, dim_b, bias=None):
return tf.py_func(func=kernel.run_args,
inp=[a, b, dim_a, dim_b, 4, bias],
Tout=tf.float32)
Функция kernel.run_args(a, b, dim_a, dim_b, 0, bias)
позволяет всем логам c вычислять матричный продукт, добавлять смещение (или нет) и применять функцию активации (или не). Используя tf.py_func
, я получаю работы с тензорами вместо ndarrays. Тест выглядит нормально, когда переменные tf.variables известны. Тест и вывод:
# now declare the weights connecting the input to the hidden layer
W1 = tf.Variable(tf.random_normal([dims[1], dims[2]], stddev=0.03), name='W1')
W2 = tf.Variable(tf.random_normal([dims[2], dims[3]], stddev=0.03), name='W2')
b = tf.Variable(tf.random_normal([dims[3]]), name='b1')
y1_custom = tf_custom.matmul_relu(W1, W2, [dims[1], dims[2]], [dims[2], dims[3]], b)
y1_tf = tf.nn.relu(tf.add(tf.matmul(W1, W2), b))
init_op = tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init_op)
res_custom = sess.run(y1_custom)
res_tf = sess.run(y1_tf)
sess.close()
# A quickly way to compute the error. Not determinist, check it further on!
error = np.sum(abs(res_custom - res_tf)) / (res_custom.shape[0] * res_custom.shape[1])
if error >= 1e-6:
print(" -- Tests Failed: Error = " + str(round(error, 9)))
else:
print(" -- Tests Ok: Error = " + str(round(error, 12)))
Полученная ошибка:
-- Se ha entrado en <run_args>
-- Tests Ok: Error = 2.302e-09
Как это все увидеть, все нормально. Пользовательский слой keras, реализованный с теми же логами ядра c через pyopencl
и tf.py_func()
, также проходит тестирование. Код для реализации пользовательского слоя keras использует исходный код core.py
, и он модифицируется для использования пользовательского tf.py_func()
в class Dense(Layer)
, более конкретно в методе call(self, inputs)
. Создание весов и смещение по слоям одинаково в обоих методах. build()
метод:
def build(self, input_shape):
assert len(input_shape) >= 2
input_dim = input_shape[-1]
self.kernel = self.add_weight(shape=(input_dim, self.units),
initializer=self.kernel_initializer,
name='kernel',
regularizer=self.kernel_regularizer,
constraint=self.kernel_constraint)
if self.use_bias:
self.bias = self.add_weight(shape=(self.units,),
initializer=self.bias_initializer,
name='bias',
regularizer=self.bias_regularizer,
constraint=self.bias_constraint)
else:
self.bias = None
self.input_spec = InputSpec(min_ndim=2, axes={-1: input_dim})
self.built = True
Оригинальный метод использует K.ops()
, а новый метод использует tf.py_func()
с pyopencl
logi c изменения в коде:
Оригинал Метод:
output = K.dot(inputs, self.kernel)
if self.use_bias:
output = K.bias_add(output, self.bias, data_format='channels_last')
if self.activation is not None:
output = self.activation(output)
Пользовательский метод:
def call(self, inputs):
weights_shape = (self.kernel).shape.as_list()
output = functions.get(self.act)(inputs,
self.kernel,
[inputs.shape.as_list()[0],inputs.shape.as_list()[1]],
weights_shape,
self.bias)
return output
Тест si (я предположил, что это нормально, потому что вывод выглядит как relu):
x = tf.ones((dims, dims))
linear_layer = keras_custom.CustomDense(dims)
y = linear_layer(x)
init_op = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init_op)
sess.run(y)
sess.close()
Я пытаюсь использовать свой пользовательский слой keras в следующем сценарии:
# load the dataset
dataset = loadtxt('pima-indians-diabetes.csv', delimiter=',')
# split into input (X) and output (y) variables
X = dataset[:,0:8]
y = dataset[:,8]
# define the keras model
model = Sequential()
model.add(CustomDense(12, input_dim=8, activation='relu'))
model.add(CustomDense(8, activation='relu'))
model.add(CustomDense(1, activation='sigmoid'))
# compile the keras model
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
# fit the keras model on the dataset
model.fit(X, y, epochs=150, batch_size=10)
Различия между тензором, сгенерированным K.op и моим пользовательским оператором:
# Tensor generated with K.dot()
<tf.Tensor 'custom_dense_37/Relu:0' shape=(?, 12) dtype=float32>
# Tensor generated with tf.custom op
<tf.Tensor 'custom_dense_38/PyFunc:0' shape=<unknown> dtype=float32>
Очевидно, разница в форме, от (?, 12)
до <unknown>
. Изменив форму тензора с помощью tf.reshape(tensor[1,weights_shape[1]])
и снова запустив скрипт, я получаю следующую ошибку:
ValueError: An operation has None` for gradient. Please make sure that all of your ops have a gradient defined (i.e. are differentiable). Common ops without gradient: K.argmax, K.round, K.eval.
Есть идеи? Я следовал официальной документации для создания пользовательского оператора и пользовательского слоя.
Спасибо!