这个小问题为难了自己一天,所以fix后决定记录以下。
这篇博客是按照自己发现问题到定位问题再到解决问题的顺序来记录的,如果各位大神有更好的解决办法请指出~
如果想直接上手大家可以直接看最后代码。
在我的项目中需要一个可训练的vector(1,hidden_num)来对一个(batch_size,hidden_num)的矩阵每一维完成点乘
在代码中看,目标是完成以下操作
semantic_output_layer=Multiply()([w_batch,semantic_input_layer])
Multiply()([inputs])是点乘操作,inputs是tensor的列表,但是我直接用tf.Variable()创建的变量w_batch会报错。
刚开始以为是keras.layer.Multiply的调用方式不对,查了一下发现没有问题。
关于Multiply()的API及其常见错误和正确的调用方法可以参考:https://stackoverflow.com/questions/48309322/keras-multiply-layer-in-functional-api
看了Multiply()源码:https://github.com/tensorflow/tensorflow/blob/r1.13/tensorflow/python/keras/layers/merge.py
发现调用方式没有问题,遂把问题定位到w_batch并不是一个layer,也就是说Keras构的图都是以Layer为单位的,和tf定义的变量无法混着用。
重点:构图时Keras和Tensorflow混着用会报错。
于是查了下如何用Kears定义一个可训练的权重,发现只能通过构建自己的自定义Layer中的add_weight可以完成该操作。
自定义Layer官方源码及API:https://keras.io/layers/writing-your-own-keras-layers/
但是从上述网站看并非可以简单明了直接get到如何自定义并在项目中调用,自己又查了点资料
自定义 Layer分为是否包含可训练的变量两种,如果不包含可训练变量可直接利用Lambda,如果包含则需要编写Layer继承类。
大家可以在这个网页上理解:https://blog.youkuaiyun.com/u013084616/article/details/79295857
为了方便大家直接上手,我给大家展示下我自己项目中的自定义的Layer可能会更能直接上手。
如何利用自定义Layer创建一个可训练变量。
class MyLayer(Layer):
def __init__(self, output_dim,**kwargs):
self.output_dim = output_dim
super(MyLayer, self).__init__(**kwargs)
def build(self, input_layer):
# 为该层创建一个可训练的权重
self.kernel = self.add_weight(name='kernel',
shape=(1,self.output_dim),
initializer='uniform',
trainable=True)
super(MyLayer, self).build(input_layer) # 一定要在最后调用它
def call(self,input_layer):
return tf.tile(self.kernel, [tf.shape(input_layer)[0], 1])
前面提到我的目标是生成一个可训练的变量(1,hidden_num),然后扩展到(batch_size,hidden_num),为什么不直接生成一个可训练变量(batch_size,hidden_num)是因为我想在一个batch中每条数据加权用的权重都是一致的,所以采用先定义一个可训练向量,然扩展到batch_size个。
看代码中的_init_(self, output_dim,**kwargs)中的output_dim其实就是下一层的hidden_size,在调用的时候传入即可。
而build(input_layer)函数则是为MyLayer创建可训练权重的函数,.input_layer是上一层Layer。在build中的add_weight()函数中自己定义shape,将他的参数trainable=True
而call(input_layer)函数中则返回该Layer的结果。
如何调用自定义Layer,使得该Layer中返回的weight正常使用。
w_batch=MyLayer(output_dim=7)(output_no_semantic_layer)
semantic_output_layer=Multiply()([w_batch,semantic_input_layer])
可以看到在调用MyLayer后面两个括号的参数分别对应着_init_中的output_dim和build()/call()中的input_layer,这样即可得到以Layer形式出现的一个可训练变量,即可和上一层的semantic_input_layer一起操作了。
写得有点乱,Keras新手,要是有不合适的地方或者有更好的方法欢迎大家指出~