今天讲解的内容是自定义层,和我们之前所学的构建层的方法相比,自定义层要复杂一些,而且要多一些注意事项,同时对python的要求也提高了不少,下边我们根据老师给出的案例代码进行讲解(注释)。
#首先说一下自定义层的三种方法
import tensorflow as tf
#自定义全连接层
class Linear(tf.keras.layers.Layer):
#在__init__中进行所有与输入无关的初始化,定义相关的层,这里我们定义一个神经元,维度是4的输入
def __init__(self, units=1, input_dim=4):
super(Linear, self).__init__()
#初始化权重w和偏差b,权重的维度是(输入维度,神经元个数),偏差的维度(神经元个数,)
w_init = tf.random_normal_initializer()
self.w = tf.Variable(initial_value=w_init(shape=(input_dim, units),
dtype='float32'),
trainable=True)
b_init = tf.zeros_initializer()
self.b = tf.Variable(initial_value=b_init(shape=(units,),dtype='float32'),trainable=True)
#call是前向传播,也就是y = w * x + b
def call(self, inputs):
#tf.matmul相当与矩阵的乘积 这里w * x的维度和b是不同的 但是因为tf封装了numpy的广播机制,
#所以可以相加,[广播](https://www.cnblogs.com/jiaxin359/p/9021726.html)
return tf.matmul(inputs, self.w) + self.b
x = tf.constant(data) #(150,4)
linear_layer = Linear(units = 1, input_dim=4) #()
y = linear_layer(x)
print(y.shape) #(150,1)
"""
Python中,如果在创建class的时候写了call()方法, 那么该class实例化出实例后, 实例名()就是调用call()方法,所以这里y = linear_layer(x)直接执行call函数。
"""
#方法二,相当于第一种方法的简化版,推荐!
class Linear(tf.keras.layers.Layer):
def __init__(self, units=1, input_dim=4):
super(Linear, self).__init__()
self.w = self.add_weight(shape=(input_dim, units),
initializer='random_normal',
trainable=True)
self.b = self.add_weight(shape=(units,),
initializer='zeros',
trainable=True)
def call(self, inputs):
return tf.matmul(inputs, self.w) + self.b
x = tf.constant(data)
linear_layer = Linear(units = 1, input_dim=4)
y = linear_layer(x)
print(y.shape)
#方法三:添加了build方法:指导输入张量的形状可以进行其余的初始化
class Linear(tf.keras.layers.Layer):
def __init__(self, units=32):
super(Linear, self).__init__()
self.units = units
def build(self, input_shape): #(150,4)
self.w = self.add_weight(shape=(input_shape[-1], self.units),
initializer='random_normal',
trainable=True,
name = 'w')
self.b = self.add_weight(shape=(self.units,),
initializer='random_normal',
trainable=True,
name = 'b')
#相当于设置self.built = True
super(Linear,self).build(input_shape)
def call(self, inputs):
return tf.matmul(inputs, self.w) + self.b
x = tf.constant(data) #150*4
linear_layer = Linear(units = 1)
y = linear_layer(x)
print(y.shape)
#可以打印出所有参数、可训练参数、不可训练参数
print('weight:', linear_layer.weights)
print('non-trainable weight:', linear_layer.non_trainable_weights)
print('trainable weight:', linear_layer.trainable_weights)
下边说一下自定义层的注意事项:
'''
1.如果要让自定义的Layer通过Functional API 组合成模型时可以序列化,需要自定义get_config方法。
get_config的作用是获取该层的参数配置,以便模型保存时候使用。
'''
def get_config(self):
config = super(MyDense, self).get_config()
config.update({'units': self.units})
return config
'''
2.使用model.save保存模型时候,如果自定义层中有build方法,一定要写name属性,否则会报错
'''
def build(self, input_shape):
self.w = self.add_weight(shape=(input_shape[-1], self.units),
initializer='random_normal',
trainable=True,
name='w')
'''
3.当我们自定义网络层并且有效保存模型后,希望使用tf.keras.models.load_model进行模型加载时建立一个字典,该字典的键是自定
义网络层时设定该层的名字,其值为该自定义网络层的类名,该字典将用于加载模型时使用!然后,在tf.keras.models.load_model内传入
custom_objects告知如何解析重建自定义网络层,其方法如下:
'''
_custom_objects = {
"MyDense" : MyDense,
}
new_model = tf.keras.models.load_model("keras_model_tf_version.h5",custom_objects=_custom_objects)
'''
4.自定义层的时候网络层的名字不能与默认的tf.keras网络层一样,否则会报错。
5.实现自定义网络层的时候,最好统一在初始化时传入可变参数**kwargs这是因为在model推理时,有时我们需要对所有构成该
模型的网络层进行统一的传参。
'''
class MyDense(tf.keras.layers.Layer):
def __init__(self, units=32, **kwargs):
self.units = units
super(MyDense, self).__init__(**kwargs)