创建一个自定义的全连接层(Dense)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Linear(keras.layers.Layer):
def __init__(self,units=32,input_dim=32):
super(Linear,self).__init__()
w_init = tf.random_normal_initializer()#标准正态分布进行初始化(提供初始值)
self.w = tf.Variable(
w_init(shape=(input_dim,units),dtype='float32'),
trainable=True
)
b_init = tf.zeros_initializer()#根据形状全0初始化
self.b = tf.Variable(
b_init(shape=(units,),dtype='float32'),#shape=(units,) shape要是元组','是元组标志
trainable=True
)
def call(self,inputs):#调用
return tf.matmul(inputs,self.w) + self.b#输入和权重进行矩阵乘法 + 偏值

x = tf.ones((2,2))

1
2
3
<tf.Tensor: shape=(2, 2), dtype=float32, numpy=
array([[1., 1.],
[1., 1.]], dtype=float32)>

my_linear = Linear(4,2)

y = my_linear(x)

1
2
3
4
<tf.Tensor: shape=(2, 4), dtype=float32, numpy=
array([[ 0.00905943, 0.10692596, -0.07619047, 0.04650879],
[ 0.00905943, 0.10692596, -0.07619047, 0.04650879]],
dtype=float32)>

my_linear.weights

1
2
3
4
5
[<tf.Variable 'Variable:0' shape=(2, 4) dtype=float32, numpy=
array([[ 0.0006441 , 0.11491605, -0.07707106, -0.01418747],
[ 0.00841848, -0.00800045, 0.00089693, 0.06069225]],
dtype=float32)>,
<tf.Variable 'Variable:0' shape=(4,) dtype=float32, numpy=array([0., 0., 0., 0.], dtype=float32)>]

使用更快捷的方法为层添加权重:add_weight()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Linear(keras.layers.Layer):
def __init__(self,units=32,input_dim=32):
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#输入和权重进行矩阵乘法 + 偏值

an_later = Linear()

an_later.weights

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[<tf.Variable 'Variable:0' shape=(32, 32) dtype=float32, numpy=
array([[-0.02809566, 0.00103881, 0.05861744, ..., 0.06338453,
0.0538682 , -0.03630157],
[-0.00649852, -0.05861601, -0.01545036, ..., 0.0073656 ,
-0.03227425, -0.08600676],
[ 0.08083364, 0.03803931, 0.07166076, ..., -0.03101794,
0.01622724, -0.07307748],
...,
[ 0.08931817, 0.00320732, 0.03871277, ..., -0.019708 ,
-0.00081164, 0.00713481],
[-0.03456153, -0.075034 , 0.04615847, ..., -0.0993882 ,
-0.04810502, 0.06305873],
[ 0.09911603, -0.01343805, -0.02753673, ..., 0.01256844,
-0.030287 , -0.01860095]], dtype=float32)>,
<tf.Variable 'Variable:0' shape=(32,) dtype=float32, numpy=
array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
dtype=float32)>]

与内置层一样在初始化时不初始化权重:build(self,inputs_shape)方法中创建层的权重

==注意trainable参数==

==将权重创建推迟到得知输入的形状之后==

==在层的build(self,inputs_shape)方法中创建层的权重==

my_linear(inputs) __call__ 层的 __call__() 方法将在首次调用时自动运行构建训练参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Linear(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='zeros',
trainable=True
)

def call(self,inputs):#调用
return tf.matmul(inputs,self.w) + self.b#输入和权重进行矩阵乘法 + 偏值

my_linear = Linear(4)

my_linear.weights

1
[]

x = tf.ones((2,2))

my_linear(x)

1
2
3
4
<tf.Tensor: shape=(2, 4), dtype=float32, numpy=
array([[ 0.09677124, -0.05200195, 0.01205444, -0.06474304],
[ 0.09677124, -0.05200195, 0.01205444, -0.06474304]],
dtype=float32)>

my_linear.weights

1
2
3
4
5
[<tf.Variable 'linear_4/Variable:0' shape=(2, 4) dtype=float32, numpy=
array([[ 0.06728531, -0.01315262, -0.00903699, 0.0252159 ],
[ 0.02951044, -0.03885547, 0.02108227, -0.08997723]],
dtype=float32)>,
<tf.Variable 'linear_4/Variable:0' shape=(4,) dtype=float32, numpy=array([0., 0., 0., 0.], dtype=float32)>]

层可递归组合

==将一个层实例分配为另一个层的特性,则外部层将开始跟踪内部层的权重。==

==建议在 init() 方法中创建此类子层(由于子层通常具有构建方法,它们将与外部层同时构建)。==

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Linear(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='zeros',
trainable=True
)

def call(self,inputs):#调用
return tf.matmul(inputs,self.w) + self.b#输入和权重进行矩阵乘法 + 偏值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class MLPBlock(keras.layers.Layer):
def __init__(self):
super(MLPBlock,self).__init__()
self.lin_1 = Linear(32)
self.lin_2 = Linear(64)
self.lin_3 = Linear(1)

def call(self,inputs):
x = self.lin_1(inputs)
x = tf.nn.relu(x)#relu激活
x = self.lin_2(x)
x = tf.nn.relu(x)#relu激活
x = self.lin_3(x)#不激活直接返回
return x
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class MLPBlock_2(keras.layers.Layer):
def __init__(self):
super(MLPBlock,self).__init__()
self.lin_1 = tf.keras.layers.Dense(32)
self.lin_2 = tf.keras.layers.Dense(64)
self.lin_3 = tf.keras.layers.Dense(32)

def call(self,inputs):
x1 = self.lin_1(inputs)
x1 = tf.nn.relu(x1)#relu激活
x2 = self.lin_2(x1)
x2 = tf.nn.relu(x2)#relu激活
x3 = self.lin_3(x2)#不激活直接返回
return tf.concat([x1,x3])

tf.concat(concat_dim, values, name=’concat’)

除去name参数用以指定该操作的name,与方法有关的一共两个参数:

第一个参数concat_dim:必须是一个数,表明在哪一维上连接

t1 = [[1, 2, 3], [4, 5, 6]]

t2 = [[7, 8, 9], [10, 11, 12]]

tf.concat(0, [t1, t2]) == > [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]

tf.concat(1, [t1, t2]) ==> [[1, 2, 3, 7, 8, 9], [4, 5, 6, 10, 11, 12]]

自定义模型

==通常使用 Layer 类来定义内部计算块,并使用 Model 类来定义外部模型,即将训练的对象。==

Model 类具有与 Layer 相同的 API,但有如下区别: 它会公开内置训练、评估和预测循环(model.fit()、model.evaluate()、model.predict())。 它会通过 model.layers 属性公开其内部层的列表。 它会公开保存和序列化 API(save()、save_weights()…)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class MLPB_model(keras.Model):
def __init__(self):
super(MLPB_model,self).__init__()
self.lin_1 = tf.keras.layers.Dense(32)
self.lin_2 = tf.keras.layers.Dense(64)
self.lin_3 = tf.keras.layers.Dense(32)

def call(self,inputs):
x1 = self.lin_1(inputs)
x1 = tf.nn.relu(x1)#relu激活
x2 = self.lin_2(x1)
x2 = tf.nn.relu(x2)#relu激活
x3 = self.lin_3(x2)#不激活直接返回
return tf.concat([x1,x3])

因此,如果您想知道“我应该用 Layer 类还是 Model 类?”,

请问自己:我是否需要在它上面调用 fit()?

我是否需要在它上面调用 save()?

如果是,则使用 Model。如果不是(要么因为您的类只是更大系统中的一个块,要么因为您正在自己编写训练和保存代码),则使用 Layer。