1. 模型构造
相比于先前代码示例中的 tf.keras.Sequential
类,tf.keras.Model
的模型构造方法更加灵活。
1.1 build model from block
tf.keras.Model
类是 tf.keras
模块里提供的一个模型构造类,可以继承它来定义需要的模型。
1.1.1 构造
构造多层感知机,代码示例如下:
import tensorflow as tf
class MLP(tf.keras.Model):
def __init__(self):
# Initialize self
super().__init__()
self.flatten = tf.keras.layers.Flatten()
self.dense1 = tf.keras.layers.Dense(units=256, activation=tf.nn.relu)
self.dense2 = tf.keras.layers.Dense(units=10)
def call(self, inputs):
x = self.flatten(inputs)
x = self.dense1(x)
output = self.dense2(x)
return output
以上的MLP
类中无须定义反向传播函数,系统将通过自动求梯度而自动生成反向传播所需的backward
函数。
1.1.2 调用及输出
实例化MLP
类得到模型变量net
,代码示例如下:
# Outputs random values from a uniform distribution.
X = tf.random.uniform((2,20))
net = MLP()
net(X)
代码初始化net
并传入输入数据X
做一次前向计算。
其中,net(X)
调用MLP
类定义的call
函数完成前向计算。
输出:
<tf.Tensor: id=62, shape=(2, 10), dtype=float32, numpy=
array([[ 0.2037605 , 0.49271488, 0.23174636, -0.16000518, 0.29224387,
0.28200507, -0.18656507, -0.13471593, -0.00501121, -0.2199152 ],
[ 0.18346256, 0.2195911 , 0.05488815, -0.07237642, 0.40799558,
0.3671595 , -0.36161992, -0.05363751, -0.1569711 , -0.14399342]],
dtype=float32)>
1.2 Sequential
事实上,Sequential
类继承自tf.keras.Model
类。当模型的前向计算为串联各层的简单计算时,可以通过这种更加简便的方式来定义模型。
Sequential
类的目的:提供add
函数来逐一添加串联的Block
子类实例,而模型的前向计算就是将这些实例按添加的顺序逐一计算。
用Sequential
类来实现1.1小节的MLP
类,并用随机初始化的模型做一次前向计算,代码示例如下:
model = tf.keras.models.Sequential([
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(units=256, activation=tf.nn.relu),
tf.keras.layers.Dense(units=10)
])
model(X)
输出:
<tf.Tensor: id=200, shape=(2, 10), dtype=float32, numpy=
array([[-0.05212587, -0.02922452, -0.08287363, 0.14752318, 0.07725549,
-0.2653394 , -0.15807061, -0.07166141, -0.1762014 , -0.1454198 ],
[ 0.03376006, 0.08956617, -0.01811027, -0.04343057, -0.04205497,
-0.26812813, -0.23462416, 0.008449 , -0.16048843, -0.22188428]],
dtype=float32)>
1.3 build complex model
虽然Sequential
类使模型构造更简单,但继承tf.keras.Model
类可以极大拓展模型构造的灵活性。
1.3.1 FancyMLP
构造一个略复杂的网络FancyMLP
,代码示例如下:
class FancyMLP(tf.keras.Model):
def __init__(self):
super().__init__()
self.flatten = tf.keras.layers.Flatten()
self.rand_weight = tf.constant(tf.random.normal((20,20)))
self.dense = tf.keras.layers.Dense(units=20, activation=tf.nn.relu)
def call(self, inputs):
x = self.flatten(inputs)
# hidden layer
x = tf.nn.relu(tf.matmul(x, self.rand_weight) + 1)
x = self.dense(x)
# control l2 norm
"""
Signature: tf.norm(tensor, ord='euclidean', axis=None, keepdims=None, name=None)
Docstring: Computes the norm of vectors, matrices, and tensors.
"""
while tf.norm(x) > 1:
x /= 2
if tf.norm(x) < 0.8:
x *= 10
return tf.reduce_sum(x)
在该网络中,通过constant
函数创建训练中不被迭代的参数,即常数参数。
使用了常数权重rand_weight
(并非模型参数)、做了矩阵乘法操作(tf.matmul
),并重复使用了相同的Dense
层。
该模型进行随机初始化和前向计算:
net = FancyMLP()
net(X)
输出:
<tf.Tensor: id=689, shape=(), dtype=float32, numpy=3.374749>
1.3.2 NestMLP
FancyMLP
和Sequential
类都是tf.keras.Model
类的子类,嵌套调用示例如下:
class NestMLP(tf.keras.Model):
def __init__(self):
super().__init__()
self.net = tf.keras.models.Sequential()
self.net.add(tf.keras.layers.Flatten())
self.net.add(tf.keras.layers.Dense(units=64, activation=tf.nn.relu))
self.net.add(tf.keras.layers.Dense(units=32, activation=tf.nn.relu))
self.dense = tf.keras.layers.Dense(units=16, activation=tf.nn.relu)
def call(self, inputs):
return self.dense(self.net(inputs))
net = tf.keras.models.Sequential()
net.add(NestMLP())
net.add(tf.keras.layers.Dense(20))
net.add(FancyMLP())
net(X)
输出:
<tf.Tensor: id=2267, shape=(), dtype=float32, numpy=18.456535>