是否可以在自定义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
一切正常。通过pyopencl
和tf.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.
有什么主意吗?我已经按照官方文档创建了自定义运算符和自定义层。
谢谢!