在自定义keras图层中使用pyopencl

时间:2020-02-13 19:17:18

标签: python tensorflow keras pyopencl

是否可以在自定义keras图层中使用自定义tf.op而不是tf.backend运算符?更具体地说,是一个自定义tf.op,它允许pyopencl逻辑。

到目前为止,我已经实现了分别通过tf.op单独创建和验证自定义tf.py_func()以及创建.so库并调用他的功能。 tf.py_func()的代码为:

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)允许所有逻辑计算矩阵乘积,加(或不加)偏差和应用(或不加)激活函数。使用tf.py_func可以实现使用张量而不是ndarray。当知道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

一切正常。通过pyopencltf.py_func()用相同的内核逻辑实现的定制keras层也通过了测试。实现自定义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逻辑,代码更改如下:

原始方法:

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和我的自定义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.

有什么主意吗?我已经按照官方文档创建了自定义运算符和自定义层。

谢谢!

0 个答案:

没有答案