4.模型的自定义
4.1.自定义层
使用的主要数据结构是Layer
实现自定义层的最佳方法是扩展tf.keras.layers.Layer类并实现:
• __init__ :可以在其中进行所有与输入无关的初始化,定义相关的层
• build: 知道输入张量的形状并可以进行其余的初始化
• call: 在这里进行前向传播
注意:不一定需要在build中创建变量时,也可以在__init__中创建它们。
tf.keras.Model和tf.keras.layers.Layer有什么区别和联系?
• 通过继承 tf.keras.Model 编写自己的模型类
• 通过继承 tf.keras.layers.Layer 编写自己的层
• tf.keras中的模型和层都是继承tf.Module实现的
• tf.keras.Model继承tf.keras.layers.Layer实现的
tf.Module: 定位为一个轻量级的状态容器,因为可以收集变量,所以这个类型可以用来建模,配合tf.GradientTape使用。
自定义一个线性回归模型
使用鸢尾花数据集
from sklearn import datasets
iris = datasets.load_iris()
data = iris.data
target = iris.target
data.shape # x
(150, 4)
target.shape #y
(150,)
方法1:最基础的方法
import tensorflow as tf
#自定义全连接层
class Linear(tf.keras.layers.Layer):
def __init__(self, units=1, input_dim=4):
super(Linear, self).__init__() #
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)
def call(self, inputs):
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)
(150, 1)
方法2:使用self.add_weight创建变量
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)
(150, 1)
方法三: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)
self.b = self.add_weight(shape=(self.units,),
initializer='random_normal',
trainable=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)
(150, 1)
添加不可训练的参数
class Linear(tf.keras.layers.Layer):
def __init__(self, units=32):
super(Linear, self).__init__()
self.units = units
def build(self, input_shape):
self.w = self.add_weight(shape=(input_shape[-1], self.units),
initializer='random_normal',
trainable=True)
self.b = self.add_weight(shape=(self.units,),
initializer='random_normal',
trainable=False)
super(Linear,self).build(input_shape)
def call(self, inputs):
return tf.matmul(inputs, self.w) + self.b
x = tf.constant(data)
linear_layer = Linear(units = 1)
y = linear_layer(x)
print(y.shape)
(150, 1)
# 打印所有参数、不可训练参数、可训练参数
print('weight:', linear_layer.weights)
print('non-trainable weight:', linear_layer.non_trainable_weights)
print('trainable weight:', linear_layer.trainable_weights)
weight: [<tf.Variable 'linear_4/Variable:0' shape=(4, 1) dtype=float32, numpy=
array([[ 0.00276536],
[-0.07950259],
[ 0.01646506],
[ 0.00197834]], dtype=float32)>, <tf.Variable 'linear_4/Variable:0' shape=(1,) dtype=float32, numpy=array([0.00255805], dtype=float32)>]
non-trainable weight: [<tf.Variable 'linear_4/Variable:0' shape=(1,) dtype=float32, numpy=array([0.00255805], dtype=float32)>]
trainable weight: [<tf.Variable 'linear_4/Variable:0' shape=(4, 1) dtype=float32, numpy=
array([[ 0.00276536],
[-0.07950259],
[ 0.01646506],
[ 0.00197834]], dtype=float32)>]
自定义层的注意事项
如果需要保存模型,则在自定义网络层时需要重写get_config 方法
我们主要看传入__init__接口时有哪些配置参数,然后在get_config内一一的将它们转为字典键值并且返回使用
get_config的作用:获取该层的参数配置,以便模型保存时使用
自定义层的biuld 中创建初始矩阵时, 需要添加name属性
我们在实现自定义网络层时,最好统一在初始化时传入可变参数**kwargs,这是因为在model推理时,有时我们需要对所有构成该模型的网络层进行统一的传参。
import tensorflow as tf
#Dense
class MyDense(tf.keras.layers.Layer):
def __init__(self, units=32, **kwargs):
self.units = units
super(MyDense, self).__init__(**kwargs)
#build方法一般定义Layer需要被训练的参数。
def build(self, input_shape):
self.w = self.add_weight(shape=(input_shape[-1], self.units),
initializer='random_normal',
trainable=True,