在使用keras的时候自己构建了一个layer:
class Neural_Tensor_layer(Layer):
def __init__(self, output_dim, input_dim=None, **kwargs):
self.output_dim = output_dim
self.input_dim = input_dim
if self.input_dim:
kwargs['input_shape'] = (self.input_dim,)
super(Neural_Tensor_layer, self).__init__(**kwargs)
def build(self, input_shape):
mean = 0.0
std = 1.0
k = self.output_dim
d = self.input_dim
W = stats.truncnorm.rvs(-2 * std, 2 * std, loc=mean, scale=std, size=(k, d, d))
V = stats.truncnorm.rvs(-2 * std, 2 * std, loc=mean, scale=std, size=(2 * d, k))
self.W = K.variable(W)
self.V = K.variable(V)
self.b = K.zeros((self.input_dim,))
self.trainable_weights = [self.W, self.V, self.b]
def call(self, inputs, mask=None):
e1 = inputs[0]
e2 = inputs[1]
batch_size = K.shape(e1)[0]
k = self.output_dim
feed_forward = K.dot(K.concatenate([e1, e2]), self.V)
bilinear_tensor_products = [K.sum((e2 * K.dot(e1, self.W[0])) + self.b, axis=1)]
for i in range(k)[1:]:
btp = K.sum((e2 * K.dot(e1, self.W[i])) + self.b, axis=1)
bilinear_tensor_products.append(btp)
result = K.tanh(K.reshape(K.concatenate(bilinear_tensor_products, axis=0), (batch_size, k)) + feed_forward)
return result
def compute_output_shape(self, input_shape):
batch_size = input_shape[0][0]
return (batch_size, self.output_dim)
继承自keras的layer层,除了必要的build 和call,以及自己自定义的function之外,没有其他属性。
之后把这个层用来create model,训练好之后尝试把模型保存
model.save("best.h5")
直接用model.save保存为.h5文件,到这里都很顺
然后问题就来了,尝试将保存的model load过来进行预测:
keras.models.load_model("best.h5")
报了错:
ValueError: Unknown layer: Neural_Tensor_layer
原因很简单,保存模型的时候,由于model里面有自定义层,所以保存的h5文件里面有几层就是自己自定义的,config中自然就有以自定义层名为命名的字段,但是直接load model的时候,keras在常见层中找不到你这个自定义层,因此报错。
解决办法:
加customer layer属性,把config里面有些自定义层的对象传进去,告诉keras:“这几个层是我自定义的,你遇到了可别慌”
new_model = keras.models.load_model("best.h5", custom_objects={"Neural_Tensor_layer": Neural_Tensor_layer,"Temporal_Mean_Pooling":Temporal_Mean_Pooling})
至此,模型正常load…
然后——又有错:
TypeError: __init__() missing 1 required positional argument: 'output_dim'
问题出在哪里呢?顺着报错的提示我回溯到最近一行,也就是报错的根源:
from_config
,也就是keras将你保存的模型load过来的时候,运用这个方法将模型文件中的config字典传入,希望重新构建模型,但是我保存的模型的config文件中少了某些属性,也就是上述的output_dim。这样一来,虽然在model.load的时候,指定了customer layer,知道了这个neural_tensor_layer的结构和组成,但是模型文件在load进来的时候,调用自定义层的init,却缺少了指定参数,也就是下面的output_dim和input_dim,自然也就报错了。
解决办法:
参照官方文档,需要implement get_config
,因为model.save的时候,就是把每一层用get_config的方法获得他们的各自属性,然后把模型结构信息保存至config文件中,如果少了它,那么model.save的时候就会缺失属性。
所以,多加一个method返回一个config的dict就行了:
class Neural_Tensor_layer(Layer):
def __init__(self, output_dim, input_dim=None, **kwargs):
self.output_dim = output_dim
self.input_dim = input_dim
if self.input_dim:
kwargs['input_shape'] = (self.input_dim,)
super(Neural_Tensor_layer, self).__init__(**kwargs)
def build(self, input_shape):
mean = 0.0
std = 1.0
k = self.output_dim
d = self.input_dim
W = stats.truncnorm.rvs(-2 * std, 2 * std, loc=mean, scale=std, size=(k, d, d))
V = stats.truncnorm.rvs(-2 * std, 2 * std, loc=mean, scale=std, size=(2 * d, k))
self.W = K.variable(W)
self.V = K.variable(V)
self.b = K.zeros((self.input_dim,))
self.trainable_weights = [self.W, self.V, self.b]
def call(self, inputs, mask=None):
e1 = inputs[0]
e2 = inputs[1]
batch_size = K.shape(e1)[0]
k = self.output_dim
feed_forward = K.dot(K.concatenate([e1, e2]), self.V)
bilinear_tensor_products = [K.sum((e2 * K.dot(e1, self.W[0])) + self.b, axis=1)]
for i in range(k)[1:]:
btp = K.sum((e2 * K.dot(e1, self.W[i])) + self.b, axis=1)
bilinear_tensor_products.append(btp)
result = K.tanh(K.reshape(K.concatenate(bilinear_tensor_products, axis=0), (batch_size, k)) + feed_forward)
return result
def compute_output_shape(self, input_shape):
batch_size = input_shape[0][0]
return (batch_size, self.output_dim)
def get_config(self):
return {"output_dim": self.output_dim, "input_dim": self.input_dim}
很简单很基础的一个问题搞了半天,还是要反思自己解决问题的方式吧…
https://keras.io/guides/making_new_layers_and_models_via_subclassing/