Keras提供Sequential API和Functional API来利用现有的layers和models构建模型,也可以构建自定义的layer和model。
以构建一个有三层隐藏层的MLP神经网络为例,hidden units为[64,32,16],使用mnist数据集,构建多分类模型,输出层unit为10。本文使用tensorflow框架。
一、Sequential API
Sequential API适合构造结构简单的神经网络,即每一层网络只有一个输入和一个输出。
import tensorflow as tf
from tensorflow.keras.layers import Layer, Dense
hidden_units = [64, 32, 16]
model = tf.keras.Sequential()
for unit in hidden_units:
model.add(Dense(unit, activation='relu'))
model.add(Dense(10, activation='softmax'))
model.build(input_shape=(None,784))
模型要built成功,需给定input_shape,除了上面使用的call build,还可以采用如下方法
model.add(tf.keras.Input(shape=(784,))) #加入Input层
model.add(Dense(64, activation='relu', input_shape=(784,))) # 构建第一层时指定input_shape
y = model(x_train) # call model on data
二、Functional API
Functional API可以构造网络拓扑结构复杂的模型,支持多输入和多输出。
inputs = tf.keras.Input(shape=(784,))
X = inputs
for unit in hidden_units:
X = Dense(unit, activation='relu')(X)
outputs = Dense(10, activation='softmax')(X)
model = tf.keras.Model(inputs=inputs, outputs=outputs)
三、自定义模型
Keras-Making new layers and models via subclassing 支持建立自定义的layers and models。
以下构建一个HappyLayer类和一个HappyModel类:
import tensorflow as tf
from tensorflow.keras.layers import Layer
class HappyLayer(Layer):
def __init__(self, units, num, initer="glorot_uniform", trainable=True):
super(HappyLayer, self).__init__()
self.units = units
self.initer = initer
self.num = num
print('HappyLayer',self.num, ' init')
def build(self, input_shape):
print('HappyLayer',self.num, ' build')
self.w = self.add_weight(shape=(input_shape[-1], self.units), initializer=self.initer, trainable=True)
self.b = self.add_weight(shape=(self.units,), initializer=self.initer, trainable=True)
def call(self, inputs):
print('HappyLayer',self.num, ' call')
return tf.nn.relu(tf.matmul(inputs, self.w) + self.b)
import tensorflow as tf
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Dense
from HappyLayer import HappyLayer
class HappyModel(Model):
def __init__(self, hidden_units, output_unit):
super(HappyModel, self).__init__()
print('HappyModel init start')
self.n_layers = len(hidden_units)
self.mylayers = []
for i in range(self.n_layers):
self.mylayers.append(HappyLayer(hidden_units[i],num=i+1))
self.mylayers.append(Dense(output_unit, activation='softmax'))
print('HappyModel init end')
def call(self, X):
print('HappyModel call')
for i in range(self.n_layers):
X = self.mylayers[i](X)
Y = self.mylayers[-1](X)
return Y
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()
x_train = x_train.reshape(60000, 784).astype("float32") / 255
y_train = tf.one_hot(y_train, 10)
hidden_units = [64, 32, 16]
output_unit = 10
model = HappyModel(hidden_units, output_unit)
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
model.fit(x_train, y_train, epochs=3, batch_size=32, verbose=2)
代码输出结果如下:
HappyModel init start
HappyLayer 1 init
HappyLayer 2 init
HappyLayer 3 init
HappyModel init end
Epoch 1/3
HappyModel call
HappyLayer 1 build
HappyLayer 1 call
HappyLayer 2 build
HappyLayer 2 call
HappyLayer 3 build
HappyLayer 3 call
HappyModel call
HappyLayer 1 call
HappyLayer 2 call
HappyLayer 3 call
1875/1875 - 2s - loss: 0.3072 - accuracy: 0.9105 - 2s/epoch - 1ms/step
Epoch 2/3
1875/1875 - 2s - loss: 0.1321 - accuracy: 0.9619 - 2s/epoch - 1ms/step
Epoch 3/3
1875/1875 - 2s - loss: 0.0990 - accuracy: 0.9702 - 2s/epoch - 971us/step
- 在第一次调用call方法时,系统会自动调用build方法,我们通常在build里才创建参数,因为在init模型时,还不知道输入数据的大小,build方法中的input_shape来自调用call时的传入数据。
若call有多个输入参数,会获得第一个参数的shape作为input_shape。 - Model class与Layer class的使用接口基本一样,区别就是Model提供如模型训练、模型预测、模型保存等方法。
- fit采用mini-batch=32,每个epoch内会调用模型的call方法1875次,可实际只输出print两次,原因在于tensorflow使用C++来加速训练,除了一次用来创建模型weights,一次开始训练并构建C++ object (graph),剩下的过程都在C++模式下进行,所以不会运行python print。