Используйте pyopencl в пользовательском слое keras - PullRequest
0 голосов
/ 13 февраля 2020

Возможно ли использовать пользовательский 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.

Есть идеи? Я следовал официальной документации для создания пользовательского оператора и пользовательского слоя.

Спасибо!

...