keras 自定义层的使用和保存时,报missing 1 required positional argument解决方案

在使用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/

猜你喜欢

转载自blog.csdn.net/weixin_43301333/article/details/111996975