保存包含自定义图层的tf.keras自定义模型的模型或权重时出错

时间:2020-07-05 14:16:14

标签: python tensorflow keras tensorflow2.0

注意:下面给出的所有代码都可以正常工作,并且给出很好的结果,但是问题是我无法保存模型。您可以运行下面给出的所有代码来在保存模型的同时纠正错误

我最近学习了如何构建自定义图层和模型。我将自定义图层构建为:

class FullyConnected(Layer):
    '''
    Fully Connected or Dense layer 
    '''
    def __init__(self,units=16,w_init='he_uniform',b_init='zeros',activation=None,**kwargs):
        '''
        Constructor of the class
        args:
            units: {int} number of neurons to use
            w_init = {string/callable} weight initializer
            b_init = {string/callable/None} bias initializer. None if no bias is included
            activation: {string} activation function to use
            **kwargs: {dict} keyword arg for the parent class
        '''
        super(FullyConnected,self).__init__(**kwargs)
        self.units = units
        self.w_init = w_init
        self.b_init = b_init
        self.activation = tf.keras.activations.get(activation) # gives a respective callable. None gives linear
        
        
    def build(self,input_shape):
        '''
        Assign weights to the layer dynamically. Base layer method
        '''
        self.w = self.add_weight(shape=(input_shape[-1],self.units),initializer=self.w_init,trainable=True)
        if self.b_init:
            self.b = self.add_weight(shape=(self.units,),initializer=self.b_init,trainable=True)
        
    
    def call(self,input_tensor):
        '''
        Forward Pass. Part of Base layer
        '''
        result = tf.matmul(input_tensor,self.w)
        if self.b_init:
            result = result + self.b
            
        if self.activation:
            result = self.activation(result)
        return result
    
    
    def compute_output_shape(self,input_shape):
        '''
        Method of base class which computes the shape of output. 
        compute_output_shape is not needed unless the Layer is Dynamic
        args:
            input_shape: (tuple) shape of incoming tensor
        out:
            out_shape: (tuple)  shape of resulting tensor
        '''
        out_shape = list(input_shape) # because we can not append to tuple
        out_shape[-1] = self.units # replace the incoming feature dimension to outgoing
        return tuple(out_shape) # a tuple is needed for shape
    
    
    def get_config(self):
        config = super(FullyConnected,self).get_config() # get config of the base Layer class
        
        config.update({'units':self.units,'activation':tf.keras.activations.serialize(self.activation)})
        # you need to serialise the callable activation function
        
        return config  

我创建了一个自定义模型:

class ClassificationModel(Model):
    '''
    A model that performs simple classification where each input can belong to Only 1 class
    '''
    def __init__(self,input_shape,layers_units=[8,],classes=2,activation='relu',**kwargs):
        '''
        Constructor of the class to get initial arguments
        args:
            input_shape = {tuple} shape of incoming data
            layer_units: {list} units to use for each layer
            classes: {int} Number of classes either binary or multiclass
            activation = {string/callable} activation function to use
        '''
        super(ClassificationModel,self).__init__(**kwargs)
        
        assert len(layers_units)>=1 , "units must be >=1"
        assert classes>=2, "classes must be >=2"
        
        self.in_shape = input_shape
        self.units = layers_units
        self.classes = classes
        self.activation = activation
        
        
        self.in_layer = FullyConnected(self.units[0],activation=self.activation,
                                       name='input_layer',
                                       input_shape=self.in_shape)
        # input_shape is a parameter of base class
        
        
        self.middle_layers = [] # middle layers do not have Input_shape
        for i in range(1,len(self.units)):
            self.middle_layers.append(FullyConnected(self.units[i],activation=self.activation))
            
            
        if self.classes == 2:
            self.out_layer = FullyConnected(1,activation='sigmoid',name='output_layer')
        else:
            self.out_layer = FullyConnected(self.classes,activation='softmax',name='output_layer')
            
        
    def call(self,tensor):
        '''
        Perform a forward pass operation
        '''
        x = self.in_layer(tensor)

        for layer in self.middle_layers:
            x = layer(x)

        probs = self.out_layer(x)

        return probs

我训练了模型并表现良好。

wine = load_wine() # sklearn dataset
X = wine['data']
y = wine['target']

X_train, X_test, y_train, y_test = train_test_split(X,y,test_size=0.2,random_state=13)

model = ClassificationModel(input_shape=X_train.shape,layers_units=[64,32,32],classes=3,activation='relu')


model.compile(optimizer=tf.keras.optimizers.Adam(lr=0.0001),
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

history = model.fit(X_train,y_train,epochs=300,validation_data=(X_test,y_test),batch_size=32)

但是当我尝试使用model.save('model.tf',save_format='tf')保存模型时,它给我一个错误:

AttributeError: 'NoneType' object has no attribute 'replace'

以及当我使用model.save_weights('model_weight',save_format='h5')时 它会抛出:

OSError: Unable to create link (name already exists)

我已多次检查是否存在不存在该名称的文件。

0 个答案:

没有答案