神经网络:Keras 高层接口基础学习

1、Keras 简介

   Keras 是一个主要用于Python 语言开发的开源神经网络计算库,被设计为高度模块化和易扩展的高层神经网络接口,方便用户简洁、快速完成模型的搭建与训练。在 TensorFlow 中,Keras 被实现在 tf.keras 子模块中。

2、常见网络层类

   对于常见的神经网络层,可以用张量方式的底层接口函数来实现,一般直接使用层方式来完成模型的搭建。在 tf.keras.layers 命名空间(下文使用 layers 指代 tf.keras.layers)中提供了大量常见网络层的类,如全连接层、激活函数层、池化层、卷积层、循环神经网络层等。对于这些网络层类,只需要在创建时指定网络层的相关参数,并调用__call__方法即可完成前向计算。在调用__call__方法时,Keras 会自动调用每个层的前向传播逻辑,这些逻辑一般实现在类的call 函数中。
  以 Softmax 层为例,它既可以用 tf.nn.softmax 函数在前向传播逻辑中完成 Softmax运算,也可以通过 layers.Softmax(axis)类搭建 Softmax 网络层,其中 axis 参数指定进行softmax 运算的维度。

# 导入 keras 模型,不能使用 import keras,它导入的是标准的 Keras 库
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers # 导入常见网络层类

# 创建 Softmax 层,并调用__call__方法完成前向计算
x = tf.constant([2.,1.,0.1]) # 创建输入张量
layer = layers.Softmax(axis=-1) # 创建 Softmax 层
out = layer(x) # 调用 softmax 前向计算,输出概率分布 out

#也可以直接通过 tf.nn.softmax()函数完成计算
out1 = tf.nn.softmax(x) # 调用 softmax 函数完成前向计算

print(out)
print(out1)

tf.Tensor([0.6590012 0.24243298 0.09856589], shape=(3,), dtype=float32)
tf.Tensor([0.6590012 0.24243298 0.09856589], shape=(3,), dtype=float32)

3、网络容器

   对于常见的网络,需要手动调用每一层的类实例完成前向传播运算,当网络层数变得较深时,这一部分代码显得非常臃肿。可以通过 Keras 提供的网络容器 Sequential 将多个网络层封装成一个大网络模型,只需要调用网络模型的实例一次即可完成数据从第一层到最末层的顺序传播运算。
   例如,2 层的全连接层加上单独的激活函数层,可以通过Sequential 容器封装为一个网络。

# 导入 Sequential 容器
from tensorflow.keras import layers, Sequential
network = Sequential([ # 封装为一个网络
 layers.Dense(3, activation=None), # 全连接层,此处不使用激活函数
 layers.ReLU(),#激活函数层
 layers.Dense(2, activation=None), # 全连接层,此处不使用激活函数
 layers.ReLU() #激活函数层
])

x = tf.random.normal([4,3])
out = network(x) # 输入从第一层开始,逐层传播至输出层,并返回输出层的输出
print(out)
print('----------------------------------------')

# Sequential 容器也可以通过 add()方法继续追加新的网络层,实现动态创建网络的功能
    
#     指定任意的 layers_num 参数即可创建对应层数的网络结构,
#     通过调用类的 build 方法并指定输入大小,即可自动创建所有层的内部张量
#     通过 summary()函数可以方便打印出网络结构和参数量
  
layers_num = 2 # 堆叠 2 次
network = Sequential([]) # 先创建空的网络容器
for _ in range(layers_num):
    network.add(layers.Dense(3)) # 添加全连接层
    network.add(layers.ReLU())# 添加激活函数层
network.build(input_shape=(4, 4)) # 创建网络参数
network.summary()
print('=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=')

# Sequential 对象的 trainable_variables 和 variables 包含了所有层的待优化张量列表和全部张量列表
for p in network.trainable_variables:
    print(p.name, p.shape) # 参数名和形状

神经网络:Keras 高层接口基础学习_第1张图片
Param#列为层的参数个数
Total params 项统计出了总的参数量
Trainable params为总的待优化参数量
Non-trainable params 为总的不需要优化的参数量
.

4、模型装配、训练和测试

模型装配

   在 Keras 中,keras.Model 和 keras.layers.Layer 类比较特殊。 Layer类是网络层的母类,定义了网络层的一些常见功能,如添加权值、管理权值列表等。Model 类是网络的母类,除了具有 Layer 类的功能,还添加了保存模型、加载模型、训练与测试模型等便捷功能。Sequential 也是 Model 的子类,因此具有 Model 类的所有功能。
   以 Sequential 容器封装的网络为例,介绍 Model 及其子类的模型装配与训练功能。通过compile 函数指定网络使用的优化器对象、损失函数类型,评价指标等设定,这一步称为装配。

# 创建 5 层的全连接网络
network = Sequential([layers.Dense(256, activation='relu'),
                      layers.Dense(128, activation='relu'),
                      layers.Dense(64, activation='relu'),
                      layers.Dense(32, activation='relu'),
                      layers.Dense(10)])

network.build(input_shape=(4, 28*28))
network.summary()

# 导入优化器,损失函数模块
from tensorflow.keras import optimizers,losses
# 模型装配
# 采用 Adam 优化器,学习率为 0.01;采用交叉熵损失函数,包含 Softmax
network.compile(optimizer=optimizers.Adam(lr=0.01),
                loss=losses.CategoricalCrossentropy(from_logits=True),
                metrics=['accuracy'] # 设置测量指标为准确率
               )

.

模型训练

  模型装配完成后,即可通过 fit()函数送入待训练的数据集和验证用的数据集,这一步称为模型训练。
  其中 train_db 为 tf.data.Dataset 对象,也可以传入 Numpy Array 类型的数据;epochs 参数指定训练迭代的 Epoch 数量;validation_data 参数指定用于验证(测试)的数据集和验证的频率
validation_freq。fit 函数会返回训练过程的数据记录history,其中 history.history 为字典对象,包含了训练过程中的 loss、测量指标等记录项,可以直接查看这些训练数据。

# 指定训练集为 train_db,验证集为 val_db,训练 5 个 epochs,每 2 个 epoch 验证一次
# 返回训练轨迹信息保存在 history 对象中
history = network.fit(tf.data.Dataset.train_db, epochs=5, validation_data=val_db, 
                      validation_freq=2)

history.history # 打印训练记录

  fit()函数的运行代表了网络的训练过程,因此会消耗相当的训练时间,并在训练结束后才返回,训练中产生的历史数据可以通过返回值对象取得。可以看到通过 compile&fit 方式实现的代码非常简洁和高效,大大缩减了开发时间。但是因为接口非常高层,灵活性也降低了,是否使用需要用户自行判断。
.

模型测试

  Model 基类除了可以便捷地完成网络的装配与训练、验证,还可以非常方便的预测和测试。通过 Model.predict(x)方法即可完成模型的预测。

# 加载一个 batch 的测试数据
x,y = next(iter(db_test))
print('predict x:', x.shape) # 打印当前 batch 的形状
out = network.predict(x) # 模型预测,预测结果保存在 out 中
print(out)

# 如果只是简单的测试模型的性能,可以通过 Model.evaluate(db)循环测试完 db 数据集上所有样本,并打印出性能指标
network.evaluate(db_test) # 模型测试,测试在 db_test 上的性能表现

.

5、模型保存与加载

  模型训练完成后,需要将模型保存到文件系统上,从而方便后续的模型相关操作。实际上,在训练时间隔性地保存模型状态也是非常好的习惯,这一点对于训练大规模的网络尤其重要。在 Keras 中,有三种常用的模型保存与加载方法。
.

5.1 张量方式

  网络的状态主要体现在网络的结构以及网络层内部张量数据上,因此在拥有网络结构源文件的条件下,直接保存网络张量参数到文件系统上是最轻量级的一种方式。以MNIST 手写数字图片识别模型为例,通过调用 Model.save_weights(path)方法即可将当前的网络参数保存到 path 文件上。这种保存与加载网络的方式最为轻量级,文件中保存的仅仅是张量参数的数值,并没有其它额外的结构参数。但是它需要使用相同的网络结构才能够正确恢复网络状态,因此一般在拥有网络源文件的情况下使用。

# 保存模型的所有张量数据到文件上
network.save_weights('weights.ckpt')
print('saved weights.')

del network # 删除网络对象

# 重新创建相同的网络结构
network = Sequential([layers.Dense(256, activation='relu'),
                      layers.Dense(128, activation='relu'),
                      layers.Dense(64, activation='relu'),
                      layers.Dense(32, activation='relu'),
                      layers.Dense(10)])
network.compile(optimizer=optimizers.Adam(lr=0.01),
                loss=tf.losses.CategoricalCrossentropy(from_logits=True),
                metrics=['accuracy']
               ) 

# 从参数文件中读取数据并写入当前网络
network.load_weights('weights.ckpt')
print('loaded weights!')

.

5.2 网络方式

  通过 Model.save(path)函数可以将模型的结构以及模型的参数保存到 path 文件上,在不需要网络源文件的条件下,通过keras.models.load_model(path)即可恢复网络结构和网络参数。

# 保存模型结构与模型参数到文件
network.save('model.h5')
print('saved total model.')
del network # 删除网络对象

# 通过 model.h5 文件即可恢复出网络的结构和状态,不需要提前创建网络对象
# 从文件恢复网络结构与网络参数
network = keras.models.load_model('model.h5')

5.3 SavedModel 方式

  通过 tf.saved_model.save (network, path)即可将模型以 SavedModel 方式保存到 path 目录中,代码如下:

# 保存模型结构与模型参数到文件
tf.saved_model.save(network, 'model-savedmodel')
print('saving savedmodel.')
del network # 删除网络对象

# 无需关心文件的保存格式,只需通过 tf.saved_model.load 函数即可恢复出模型对象,并完成测试准确率的计算
print('load savedmodel from file.')
# 从文件恢复网络结构与网络参数
network = tf.saved_model.load('model-savedmodel')

# 准确率计量器
acc_meter = metrics.CategoricalAccuracy()
for x,y in ds_val: # 遍历测试集
    pred = network(x) # 前向计算
    acc_meter.update_state(y_true=y, y_pred=pred) # 更新准确率统计

    # 打印准确率
print("Test Accuracy:%f" % acc_meter.result())

.

6、自定义网络

  对于需要创建自定义逻辑的网络层,可以通过自定义类来实现。在创建自定义网络层类时,需要继承自 layers.Layer 基类;创建自定义的网络类时,需要继承自 keras.Model 基类,这样建立的自定义类才能够方便的利用 Layer/Model 基类提供的参数管理等功能,同时也能够与其他的标准网络层类交互使用。

6.1 自定义网络层

  对于自定义的网络层,至少需要实现初始化__init__方法和前向传播逻辑 call 方法。首先创建类,并继承自 Layer 基类。创建初始化方法,并调用母类的初始化函数,由于是全连接层,因此需要设置两个参数:输入特征的长度 inp_dim 和输出特征的长度outp_dim,并通过 self.add_variable(name, shape)创建 shape 大小,名字为 name 的张量,并设置为需要优化。代码如下:

class MyDense(layers.Layer):
    # 自定义网络层
    def __init__(self, inp_dim, outp_dim):
        super(MyDense, self).__init__()
        # 创建权值张量并添加到类管理列表中,设置为需要优化
        self.kernel = self.add_variable('w', [inp_dim, outp_dim],trainable=True)
        

net = MyDense(4,3) # 创建输入为 4,输出为 3 节点的自定义层
net.variables,net.trainable_variables # 查看自定义层的参数列表

  完成自定义类的初始化工作后,我们来设计自定义类的前向运算逻辑,对于这个例子,只需要完成 = @矩阵运算,并通过固定的 ReLU 激活函数即可,代码如下:

def call(self, inputs, training=None):
    # 实现自定义类的前向计算逻辑
    # X@W
    out = inputs @ self.kernel
    
    # 执行激活函数运算
    out = tf.nn.relu(out)
    return out

  自定义类的前向运算逻辑实现在 call(inputs, training=None)函数中,其中 inputs代表输入,由用户在调用时传入;training 参数用于指定模型的状态:training 为 True 时执行训练模式,training 为 False 时执行测试模式,默认参数为 None。
.

6.2 自定义网络

  自定义网络类可以和其他标准类一样,通过 Sequential 容器方便地封装成一个网络模型:

network = Sequential([MyDense(784, 256), # 使用自定义的层
                      MyDense(256, 128),
                      MyDense(128, 64),
                      MyDense(64, 32),
                      MyDense(32, 10)])

network.build(input_shape=(None, 28*28))
network.summary()

class MyModel(keras.Model):
    # 自定义网络类,继承自 Model 基类
    def __init__(self):
        super(MyModel, self).__init__()
        
        # 完成网络内需要的网络层的创建工作
        self.fc1 = MyDense(28*28, 256)
        self.fc2 = MyDense(256, 128)
        self.fc3 = MyDense(128, 64)
        self.fc4 = MyDense(64, 32)
        self.fc5 = MyDense(32, 10)
        
    def call(self, inputs, training=None):
        # 自定义前向运算逻辑
        x = self.fc1(inputs) 
        x = self.fc2(x) 
        x = self.fc3(x) 
        x = self.fc4(x) 
        x = self.fc5(x) 
        return x

.

7、测量工具

  在网络的训练过程中,经常需要统计准确率、召回率等测量指标,除了可以通过手动计算的方式获取这些统计数据外,Keras 提供了一些常用的测量工具,位于 keras.metrics 模块中,专门用于统计训练过程中常用的指标数据。
  Keras 的测量工具的使用方法一般有 4 个主要步骤:新建测量器,写入数据,读取统计数据和清零测量器。

7.1 新建测量器

  在 keras.metrics 模块中,提供了较多的常用测量器类,如统计平均值的 Mean 类,统计准确率的 Accuracy 类,统计余弦相似度的 CosineSimilarity 类等。以统计误差值为例。在前向运算时,我们会得到每一个 Batch 的平均误差,但是我们希望统计每个Step 的平均误差,因此选择使用 Mean 测量器。新建一个平均测量器,代码如下:

# 新建平均测量器,适合 Loss 数据
loss_meter = metrics.Mean()

.

7.2 写入数据

  通过测量器的 update_state 函数可以写入新的数据,测量器会根据自身逻辑记录并处理采样数据。例如,在每个 Step 结束时采集一次 loss 值,代码如下:

# 记录采样的数据,通过 float()函数将张量转换为普通数值
 loss_meter.update_state(float(loss))

  上述采样代码放置在每个 Batch 运算结束后,测量器会自动根据采样的数据来统计平均值。
.

7.3 读取统计信息

  在采样多次数据后,可以选择在需要的地方调用测量器的 result()函数,来获取统计值。例如,间隔性统计 loss 均值,代码如下:

# 打印统计期间的平均 loss
print(step, 'loss:', loss_meter.result()) 

7.4 清除状态

  由于测量器会统计所有历史记录的数据,因此在启动新一轮统计时,有必要清除历史状态。通过 reset_states()即可实现清除状态功能。例如,在每次读取完平均误差后,清零统计信息,以便下一轮统计的开始,代码如下:

 if step % 100 == 0:
 # 打印统计的平均 loss
 print(step, 'loss:', loss_meter.result()) 
 loss_meter.reset_states() # 打印完后,清零测量器

.

你可能感兴趣的:(神经网络,keras,神经网络,学习)