avatar


14.Keras

TensorFlow和Keras,在之前我们都用过了。
现在,我们专门用两章分别讨论TensorFlow和Keras,是归纳总结,也是查漏补缺。

在之前的章节中,我们是这么导入Keras的。

1
from tensorflow import  keras

即,我们都是从TensorFlow中导入Keras,导入的其实是TensorFlow中的的一个子模块,就像tf.math,tf.data。
但,也有一个独立的包,也叫Keras。

那么Keras与tf.keras有什么区别与联系呢?
Keras包是在TensorFlow、CNTK等的基础上,再封装了一层。我们可以通过调用Keras,更方便更快捷的调用TensorFlow、CNTK等。而tf.keras只对TensorFlow进行了封装。当然,因为专注,所以专业。tf.keras是深度融合,更完美的支持。

这里,除非特别说明,否则我们所讨论的Keras都是TensorFlow中的Keras模块

题外话:
Keras这个包的官方的介绍是"Deep Learning for humans"。或许,这就是程序员的幽默。
Deep Learning for humans

常见方法

我们以最经典的一个神经网络LeNet-5为例,来讨论几个常见的方法。
LeNet-5的结构如下:
修改后的LeNet-5

网络层

layers中提供了大量的常见网络层,如:全连接层、激活函数层、池化层、卷积层、循环神经网络层等。
对于这些网络层类,只需要在创建时指定网络层的相关参数,并调用__call__()方法即可完成前向计算。
示例代码:

1
2
3
4
5
6
7
8
9
import tensorflow as tf
from tensorflow.keras import layers

# 两个样本,高宽为2,1个通道。
x = tf.random.normal([2,2,2,1])
print(x.numpy())
layer = layers.MaxPooling2D(pool_size=3,strides=2,padding='same')
print('\n' * 3)
print(layer(x).numpy())

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[[[[ 0.7679682 ]
[-0.5394121 ]]

[[-1.256376 ]
[-0.6682031 ]]]


[[[-1.1004809 ]
[ 1.2481769 ]]

[[-0.05369474]
[ 0.7209857 ]]]]




[[[[0.7679682]]]


[[[1.2481769]]]]

在LeNet-5中,准备网络层的代码如下:

1
2
3
4
5
6
7
8
9
# 网络层
layers.Conv2D(filters=6, kernel_size=(5, 5), activation="relu", padding="same"),
layers.MaxPool2D(pool_size=(2, 2), strides=2),
layers.Conv2D(filters=16, kernel_size=(5, 5), activation="relu", padding="same"),
layers.MaxPool2D(pool_size=2, strides=2),
layers.Conv2D(filters=32, kernel_size=(5, 5), activation="relu", padding="same"),
layers.Flatten(),
layers.Dense(200, activation="relu"),
layers.Dense(10, activation="softmax")

网络容器

Sequential用来把几个网络层组装成一个网络模型,可以通过Sequential对象直接进行前向运算。
示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import tensorflow as tf
from tensorflow.keras import layers, Sequential

network = Sequential([
layers.Dense(3),
layers.ReLU(),
layers.Dense(2),
layers.ReLU()
])
x = tf.random.normal([4, 3])
print(x)

print('\n' * 3)

out = network(x)
print(out)

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
tf.Tensor(
[[-0.78891283 -0.33460605 0.06515974]
[ 0.67153883 -1.4354606 -0.04982349]
[-0.15533394 0.0416547 -1.5677795 ]
[-0.7553246 1.7706865 -0.39527965]], shape=(4, 3), dtype=float32)




tf.Tensor(
[[0.3278713 0.02968266]
[0. 0. ]
[0. 0.09546646]
[0.21385244 1.1902449 ]], shape=(4, 2), dtype=float32)

Sequential对象还有add()方法追加网络层,summary()方法查看网络信息,trainable_variables属性查看可训练的变量。
示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from tensorflow.keras import layers, Sequential

network = Sequential([
layers.Dense(3)
])
network.build(input_shape=(4, 4))
print(network.summary())

print('\n' * 3)

network.add(layers.ReLU())
print(network.summary())

print('\n' * 3)

for p in network.trainable_variables:
print(p.name, p.shape)

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
dense (Dense) (4, 3) 15
=================================================================
Total params: 15
Trainable params: 15
Non-trainable params: 0
_________________________________________________________________
None




Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
dense (Dense) (4, 3) 15
_________________________________________________________________
re_lu (ReLU) (4, 3) 0
=================================================================
Total params: 15
Trainable params: 15
Non-trainable params: 0
_________________________________________________________________
None




dense/kernel:0 (4, 3)
dense/bias:0 (3,)

在LeNet-5中,组建网络模型的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
# 定义一个网络容器
network = Sequential([
# 网络层
layers.Conv2D(filters=6, kernel_size=(5, 5), activation="relu", padding="same"),
layers.MaxPool2D(pool_size=(2, 2), strides=2),
layers.Conv2D(filters=16, kernel_size=(5, 5), activation="relu", padding="same"),
layers.MaxPool2D(pool_size=2, strides=2),
layers.Conv2D(filters=32, kernel_size=(5, 5), activation="relu", padding="same"),
layers.Flatten(),
layers.Dense(200, activation="relu"),
layers.Dense(10, activation="softmax")
])

模型装配

我们通过compile方法指定网络使用的优化器对象、损失函数类型,评价指标等,这一步称为模型装配。
其中需要注意的是我们传入的参数optimizerloss,可以是实例化的对象,也可以是字符串。当传入字符串时,将使用优化器或损失函数的默认设置。
示例代码:

1
network.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])

在LeNet-5中,模型装配如下:

1
2
3
4
5
6
# 优化器
adam = optimizers.Adam()
# 损失
sparse_categorical_crossentropy = losses.SparseCategoricalCrossentropy()
# 装配
network.compile(optimizer=adam, loss=sparse_categorical_crossentropy, metrics=['accuracy'])

模型训练

模型装配完成后,可以通过fit()传入训练集,进行训练,这一步称为模型训练。
fit()方法会返回训练过程的记录history,包含了训练过程中的损失和准确率等。
示例代码:

1
2
3
history = network.fit(db_train, epochs=3)
print(history.epoch)
print(history.history)

运行结果:

1
2
[0, 1, 2]
{'loss': [0.6105563640594482, 0.06397475302219391, 0.03701183199882507], 'accuracy': [0.9253166913986206, 0.9803833365440369, 0.98826664686203]}

在LeNet-5中,模型训练如下:

1
2
# 训练
network.fit(db_train, epochs=10)

模型评估

我们可以通过evaluate()方法进行模型评估。
在LeNet-5中,模型评估如下:

1
2
3
# 评估
loss, accuracy = network.evaluate(db_test)
print(loss, accuracy)

模型预测

我们可以通过predict()获得输入数据对应的输出,进行模型预测。
在LeNet-5中,模型预测如下:

1
2
3
4
# 预测
result = network.predict(x_test)[0:25]
pred = tf.argmax(result, axis=1)
print(pred)

模型测量

在网络的训练过程中,经常需要统计准确率、召回率等测量指标。Keras提供了常用的测量工具:keras.metrics
使用方法一般有4个主要步骤:

  1. 新建测量器
  2. 写入数据
  3. 读取统计数据
  4. 清零测量器

新建测量器
示例代码:

1
loss_meter = metrics.Mean()

写入数据
示例代码:

1
loss_meter.update_state(float(loss))

读取统计数据
示例代码:

1
print(step, 'loss:', loss_meter.result())

清零测量器
示例代码:

1
loss_meter.reset_states()

在LeNet-5中,模型测量如下:

1
2
3
4
5
6
7
8
9
10
11
12
# 测量器
# 创建准确率测量器
acc_meter = metrics.Accuracy()
result = network.predict(x_test)
pred = tf.argmax(result, axis=1)
for item, y in zip(pred, y_test):
acc_meter.update_state(y, item.numpy())

# 读取统计结果
print('Evaluate Acc:', acc_meter.result().numpy())
# 清零测量器
acc_meter.reset_states()

模型保存和加载

一共有三种保存和加载方式:

  1. 权重方式
  2. 模型方式
  3. SavedModel方式

权重方式

我们知道一个模型,是由结构和权重两部分组成的。第一种方式,我们只保存权重,然后我们再构建一个结构相同的模型,把权重加载回来。

在LetNet-5中,权重方式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 保存权重
network.save_weights('weights.ckpt')
print('saved weights.')
# 删除网络对象
del network
# 重新创建相同的网络结构
network = Sequential([
# 网络层
layers.Conv2D(filters=6, kernel_size=(5, 5), activation="relu", padding="same"),
layers.MaxPool2D(pool_size=(2, 2), strides=2),
layers.Conv2D(filters=16, kernel_size=(5, 5), activation="relu", padding="same"),
layers.MaxPool2D(pool_size=2, strides=2),
layers.Conv2D(filters=32, kernel_size=(5, 5), activation="relu", padding="same"),
layers.Flatten(),
layers.Dense(200, activation="relu"),
layers.Dense(10, activation="softmax")
])
network.build(input_shape=(None, 28, 28, 1))
network.compile(optimizer=adam, loss=sparse_categorical_crossentropy, metrics=['accuracy'])
# 把权重加载回来
network.load_weights('weights.ckpt')
print('loaded weights!')
# 看看第一种方法的对不对
print(network.evaluate(db_test))

模型方式

第二种方法,我们同时保存结构和权重,这个称为模型方式。
在LetNet-5中,模型方式如下:

1
2
3
4
5
6
7
8
9
# 第二种模型保存和加载方法
# 保存模型
network.save('model.h5')
print('saved total model.')
del network
# 加载模型
network = keras.models.load_model('model.h5')
# 看看第二种对不对
print(network.evaluate(db_test))

SavedModel方式

SavedModel是一个协议,可以用于部署到生产环境,也可以用于其他深度学习框架读取。
在LetNet-5中,SavedModel方式如下:

1
2
3
4
5
6
7
8
# 第三种模型保存和加载方法
tf.saved_model.save(network, 'model-savedmodel')
print('saving savedmodel.')
del network
print('load savedmodel from file.')
network = tf.saved_model.load('model-savedmodel')
f = network.signatures['serving_default']
print(f(conv2d_3_input=tf.ones([1, 28, 28, 1])))

自定义

通过Keras,我们还可以非常方便的自定义各种网络,用类似继承的方法。

自定义网络层

继承Layer基类。
建议实现:

  • __init__()
  • build(self, input_shape)
  • call(self, *args, **kwargs)

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class SimpleDense(layers.Layer):

def __init__(self, units=32):
super(SimpleDense, self).__init__()
self.units = units

# Create the state of the layer (weights)
def build(self, input_shape):
w_init = tf.random_normal_initializer()
self.w = tf.Variable(initial_value=w_init(shape=(input_shape[-1], self.units),dtype='float32'),trainable=True)

b_init = tf.zeros_initializer()
self.b = tf.Variable(initial_value=b_init(shape=(self.units,), dtype='float32'),trainable=True)

# Defines the computation from inputs to outputs
def call(self, inputs):
return tf.matmul(inputs, self.w) + self.b

自定义网络容器

继承Model基类。
建议实现:

  • __init__()
  • call(self, *args, **kwargs)

示例代码:

1
2
3
4
5
6
7
8
9
10
11
import tensorflow as tf

class MyModel(tf.keras.Model):
def __init__(self):
super(MyModel, self).__init__()
self.dense1 = tf.keras.layers.Dense(4, activation=tf.nn.relu)
self.dense2 = tf.keras.layers.Dense(5, activation=tf.nn.softmax)

def call(self, inputs):
x = self.dense1(inputs)
return self.dense2(x)

如果想参考一个完整的用法,可以参考《深度学习初步及其Python实现:7.经典卷积神经网络》中关于"ResNet"部分。

applications

对于一些常见的模型,我们不需要手动创建,Keras中自带了,我们可以直接引用。

例如,我们直接引用已经训练好的DenseNet201,并通过include_top=False去除DenseNet201的最上一层,然后拼接上其他的网络层,组成一个新的网络模型。

示例代码:

1
2
3
4
5
6
7
8
9
10
from tensorflow.keras import applications, layers, Sequential

densenet201_without_top = applications.DenseNet201(weights='imagenet', include_top=False)
global_average_layer = layers.GlobalAveragePooling2D()
fc512 = layers.Dense(512)
fc256 = layers.Dense(256)
fc128 = layers.Dense(128)
fc10 = layers.Dense(10)
new_net = Sequential([densenet201_without_top, global_average_layer, fc512, fc256, fc128, fc10])
new_net.summary()

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
densenet201 (Functional) (None, None, None, 1920) 18321984
_________________________________________________________________
global_average_pooling2d (Gl (None, 1920) 0
_________________________________________________________________
dense (Dense) (None, 512) 983552
_________________________________________________________________
dense_1 (Dense) (None, 256) 131328
_________________________________________________________________
dense_2 (Dense) (None, 128) 32896
_________________________________________________________________
dense_3 (Dense) (None, 10) 1290
=================================================================
Total params: 19,471,050
Trainable params: 19,241,994
Non-trainable params: 229,056
_________________________________________________________________
文章作者: Kaka Wan Yifan
文章链接: https://kakawanyifan.com/10414
版权声明: 本博客所有文章版权为文章作者所有,未经书面许可,任何机构和个人不得以任何形式转载、摘编或复制。

评论区