tf2.0真正有用的——Keras高层接口

前边介绍了TensorFlow的基本操作:
tf2.0学习(一)——基础知识
tf2.0学习(二)——进阶知识
tf2.0学习(三)——神经网络
tf2.0学习(四)——反向传播算法

下面介绍一个高度模块化、易扩展的高层接口Keras。

5.0 简介

Keras是一个有python开发的神经网络计算库。是一个高度模块化和易扩展的高层神经网络接口。它分为前端和后端,其中后端一般是用现有的神经网络框架实现底层计算,前端接口是经过Keras抽象后的统一的函数接口。用户使用Keras框架轻松实现不同底层框架的切换,灵活性较高。

Tensorflow2.0开始,Keras被认定为唯一的高层接口,取代了原来Tensorflow1中的tf.layers等高层接口。

5.1 常见功能模块

Keras提供了很多高层的神经网络相关类和函数。包括数据集加载、网络层类、模型容器、损失函数类、优化器类、经典模型类等等。

5.1.1 常见网络层

对于常见的神经网络层,可以使用张量形式的底层接口函数来实现,这些接口函数一般在tf.nn模块中。更常用的是用层的方式实现,在tf.keras.layers模块下提供了大量的神经网层,如全连接层、激活函数层、卷积层、池化层、循环神经网络层等。对于这些网络层类,只需要在创建时指定网络层的相关参数,并调用__call__方法即可完成前向计算。再调用__call__函数是,会自动调用没层的前向传播逻辑,前向传播逻辑一般在实现类的call函数里。

以softmax层为例,它既可以使用tf.nn.softmax函数在前向传播中完成Softmax计算,也可以通过tf.keras.layers.Softmax(axis)来搭建神经网络层。

import tensorflow as tf 

x = tf.constant([2,1,4,2,5], dtype=tf.float16)
layer = tf.keras.layers.Softmax(axis=-1)
layer(x)

<tf.Tensor: shape=(5,), dtype=float32, numpy=
array([0.03350928, 0.01232738, 0.247602  , 0.03350928, 0.673052  ],
      dtype=float32)>

5.1.2 网络容器

对于常见的神经网络,往往都会有很多层。前向传播的过程需要数据通过每个层来实现,这时候如果手动调用每一层的类的话,代码会变的非常臃肿。因此Keras提供了网络容器Sequential,他可以将多个网络层封装成一个大的网络模型,从而只需要调用一次网络模型的前向传播,就完成了从第一层到最后一层的全部计算。

import tensorflow as tf 
model = tf.keras.Sequential([
    tf.keras.layers.Dense(128),
    tf.keras.layers.ReLU(),
    tf.keras.layers.Dense(10),
    tf.keras.layers.Softmax()
])

x = tf.random.uniform([10, 50])


model(x)
<tf.Tensor: shape=(10, 10), dtype=float32, numpy=
array([[0.05376878, 0.17156708, 0.16427904, 0.05504481, 0.04971287,
        0.09335194, 0.10940206, 0.11748007, 0.11103256, 0.07436065],
       [0.09636729, 0.14340015, 0.10765289, 0.06640282, 0.04153357,
        0.08176894, 0.10070642, 0.14419483, 0.13028036, 0.0876927 ],
       [0.10896047, 0.12469008, 0.13271534, 0.05778752, 0.03720576,
        0.10271669, 0.10986929, 0.14858323, 0.10459764, 0.07287396],
       [0.0665472 , 0.15090697, 0.18803081, 0.06644785, 0.04090692,
        0.09305567, 0.0954863 , 0.1445031 , 0.08430488, 0.06981032],
       [0.09652282, 0.08426037, 0.12721734, 0.0804379 , 0.07161396,
        0.10512593, 0.11527853, 0.17215529, 0.08922189, 0.05816597],
       [0.07605492, 0.18164131, 0.16475034, 0.06142522, 0.04285333,
        0.09868649, 0.10680319, 0.11706878, 0.09897676, 0.0517397 ],
       [0.10844021, 0.13180628, 0.14759877, 0.0680831 , 0.04360767,
        0.07573178, 0.11283645, 0.14127326, 0.08165637, 0.08896605],
       [0.09385677, 0.14408283, 0.17832078, 0.07523569, 0.04950608,
        0.07810812, 0.10581949, 0.13110453, 0.08575373, 0.0582121 ],
       [0.07820774, 0.1099428 , 0.17537114, 0.04206308, 0.05659465,
        0.08852449, 0.10071502, 0.18280421, 0.09930225, 0.0664746 ],
       [0.07563203, 0.08918475, 0.13077222, 0.06964436, 0.04555722,
        0.13870038, 0.09354863, 0.15792203, 0.11947975, 0.0795586 ]],
      dtype=float32)>

model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #
=================================================================
dense (Dense)                (10, 128)                 6528
_________________________________________________________________
re_lu (ReLU)                 (10, 128)                 0
_________________________________________________________________
dense_1 (Dense)              (10, 10)                  1290
_________________________________________________________________
softmax (Softmax)            (10, 10)                  0
=================================================================
Total params: 7,818
Trainable params: 7,818
Non-trainable params: 0
_________________________________________________________________

5.2 模型装配、训练与测试

在训练网络时,一般的流程是先通过前向计算获得网络的输出值,在通过损失函数计算网络误差,然后通过自动求导工具计算梯度并利用梯度下降法更新惨呼,同时间隔性的测试网络性能。这些训练逻辑,一般都可以通过Keras提供的高层借口,简介清晰的实现。

5.2.1 模型装配

Keras中有两个比较特殊的类,分别是tf.keras.layers.Layer 和 tf.keras.layers.Model。其中tf.keras.layers.Layer是所有网络层的母类,它定义了网络层的一些常用功能,包括添加权值,管理权值列表等工程。Model是所有网络的母类,除了包含Layer类的工程外,还包括保存模型、加载模型、训练模型等便捷功能。其中Sequential也是Model的子类。

我们以Sequential为例,介绍一下模型的装配,首先创建一个5层的神经网络。

model = tf.keras.Sequential([
    tf.keras.layers.Dense(256, activation='relu'),
    tf.keras.layers.Dense(125, activation='relu'),
    tf.keras.layers.Dense(64, activation='relu'),
    tf.keras.layers.Dense(32, activation='relu'),
    tf.keras.layers.Dense(10)
])

model.build(input_shape=[4, 784])
model.summary()

Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #
=================================================================
dense (Dense)                (4, 256)                  262400
_________________________________________________________________
dense_1 (Dense)              (4, 125)                  32125
_________________________________________________________________
dense_2 (Dense)              (4, 64)                   8064
_________________________________________________________________
dense_3 (Dense)              (4, 32)                   2080
_________________________________________________________________
dense_4 (Dense)              (4, 10)                   330
=================================================================
Total params: 304,999
Trainable params: 304,999
Non-trainable params: 0
_________________________________________________________________

创建模型之后,正常流程就是对数据集迭代多个Epoch,每次按batch_size分批进行训练,对数据前向计算,计算误差,反向传播自动求导,更新参数。由于这部分的逻辑高度通用,所以在Keras中提供了compile()和fit()函数方便实现上述逻辑。

compile()函数用于指定优化器,损失函数,评价指标等,这一步成为装配。

optim = tf.keras.optimizers.Adam(lr=0.001)
loss = tf.keras.losses.CategoricalCrossentropy(from_logits=True)
model.compile(optimizer = optim, loss=loss, metrics=['accurary'])

5.2.2 模型训练

模型装配完成之后,还要通过fit()函数传入指定的训练集,验证集,这一步被称为模型的训练。

def preprocess(x, y):
	y= tf.cast(y, dtype=tf.int32)
	y= tf.one_hot(y, depth=10)
        y = tf.reshape(y, [-1, 10])
	x= tf.cast(x, dtype=tf.float32) / 255
        x = tf.reshape(x, [-1, 784])
	return x, y


(x, y), (x_test, y_test) = tf.keras.datasets.mnist.load_data()
train_db = tf.data.Dataset.from_tensor_slices((x, y))
val_db=tf.data.Dataset.from_tensor_slices((x_test, y_test))

train_db = train_db.map(preprocess)
val_db = val_db.map(preprocess)

history = model.fit(train_db, epochs=5, validation_data=val_db, validation_freq=2)

之后可以调用history.history,打印训练记录。

5.2.3 模型测试

模型测试只需要调用model.predict(x)即可。

还可以使用model.evaluate(db)循环测试db数据集上所有的样本,并打印性能指标。

5.3 模型的保存与加载

模型训练完成后一般需要将其保存起来,一遍后续的模型测试和部署。并且,由于大规模神经网络的训练,往往耗费数天或数周的时间,一旦训练过程中发生意外,为了避免导致之前的训练成果丢失,间隔性的保存模型是个非常好的习惯。如果训练过程中发生死机等意外,可以从最近保存的模型开始重新训练,从而避免大量浪费资源和时间。
Keras提供了三种模型的保存和加载方法。

5.3.1 张量方式

网络的状态主要体现在网络的机构和网络内部的张量上,因此在拥有网络结构的前提下,直接保存网络张量是最轻量级的保存方式。通过调用model.save_weights(path)的方法既可以将当前的网络参数保存到指定path目录下。

model.save_weights('weights.ckpt')
del model
model = tf.keras.Sequential([
    tf.keras.layers.Dense(256, activation='relu'),
    tf.keras.layers.Dense(125, activation='relu'),
    tf.keras.layers.Dense(64, activation='relu'),
    tf.keras.layers.Dense(32, activation='relu'),
    tf.keras.layers.Dense(10)
])
optim = tf.keras.optimizers.Adam(lr=0.001)
loss = tf.keras.losses.CategoricalCrossentropy(from_logits=True)
model.compile(optimizer = optim, loss=loss, metrics=['accurary'])

model.load_weights('weights.ckpt')

这种方式保存、加载网络最为轻量级,保存的文件中只保留了网络参数的数值,并没有保存网络的机构信息。但是只有有用相同的网络结构才能将保存的模型恢复成原来的网络状态。

5.3.2 网络方式

下面介绍一种不需要网络结构也能恢复成原网络的方式。通过调用model.save(path)方法,可以将网络结构和模型参数保存到path上。

model.save('model.h5')
model = tf.keras.models.load_model('model.h5')

model.h5文件,不仅保存了网络的参数信息,还保存另外网络的结构,因此在加载该网络的时候,并不需要提前创建网络模型。

5.3.3 SaveModel方式

TensorFlow之所以被产业界青睐,除了优秀的神经网络高层接口外,还得益于其强大的生态系统,包括网页端和移动端的支持。当我们需要将模型部署到其他平台式,采用TensorFlow提出来的SaveModel方式可以保证平台无关性。

tf.saved_model.save(model, 'model-savedmodel')
model = tf.saved_model.load('model-savedmodel')

5.4 自定义网络

尽管Keras提供了很多常用网络,但深度学习领域可以使用的网络层远远不止这些,科研工作者一般是先自行实现较为新颖的网络层,再通过大量的实验验证有效后,框架才会跟进,内置这些网络层。
对于需要自定义的网络层,可以通过自定义类实现。自定义的网络层,需要继承tf.keras.layers.Layer,自定义的网络需要继承自tf.keras.Model。这样定义出来的网络和网络层,能更方便的使用Layer/Model提供的参数管理功能,也更方便和其他已经定义好的网络进行交互。

5.4.1 自定义网络层

对于自定义的网络层,除了要继承自tf.keras.layers.Layer外,至少还需要实现初始化__init__方法和前向传播call方法。下面以全连接网络为例(没有骗置)。

class MyDense(tf.keras.layers.Layer):
    def __init__(self, input_dim, output_dim):
        super(MyDense, self).__init__()
        self.w = tf.Variable(tf.random.normal((input_dim, output_dim)), trainable=True)
    def call(self, inputs):
        out = inputs @ self.w 
        out = tf.nn.relu(out)
        return out

以上代码为,实现了一个没有偏置的全连接层,并加入了relu激活函数。

5.4.2 自定义网络

在完成了自定义网络层之后,我们通过Sequential实现一个网络模型:

model = tf.keras.Sequential([
    MyDense(784, 256),
    MyDense(256, 128),
    MyDense(128, 64),
    MyDense(64, 32),
    MyDense(32, 10),
])

model.build(input_shape=(None, 784))

model.summary()

Model: "sequential_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #
=================================================================
my_dense_5 (MyDense)         (None, 256)               200704
_________________________________________________________________
my_dense_6 (MyDense)         (None, 128)               32768
_________________________________________________________________
my_dense_7 (MyDense)         (None, 64)                8192
_________________________________________________________________
my_dense_8 (MyDense)         (None, 32)                2048
_________________________________________________________________
my_dense_9 (MyDense)         (None, 10)                320
=================================================================
Total params: 244,032
Trainable params: 244,032
Non-trainable params: 0
_________________________________________________________________

Sequential容器适合数据顺序的从第一层到最后一层进行传播的网络。然而对于复杂网络,例如第三层的输入不仅是第二层的输出,还有第一层的输出,此时用Sequential容器就不能实现了。需要自定义更加灵活的网络。

class MyModel(tf.keras.Model):
    def __init__(self):
        self.fc1 = MyDense(784, 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):
        x = self.fc1(inputs)
        x = self.fc2(x)
        x = self.fc3(x)
        x = self.fc4(x)
        x = self.fc5(x)
        return x

5.5 模型乐园

对于一些经典的网络模型,譬如ResNet,VGG等,不需要手动创建网络,可以直接从tf.keras.applications子模块中,通过一行代码即可创建并使用这些经典模型,同事还可以设置weights参数加载预训练的网络模型,非常方便。

5.5.1 加载模型

以ResNet50为例,一般讲ResNet50去除最后一层之后的网络作为新任务的特征提取网络,即用在ImageNet数据集上训练好的网络参数初始化,并根据自定义的任务,在后边追加一个分类器或子网络。
首先加载ResNet50:

resnet = tf.keras.applications.ResNet50(weights='imagenet', include_top=False)
resnet.summary()

include_top=False,就是去掉该网络的最后一层,此时的网络输出为(b, 7, 7, 2048),常备拿来当做特征提取器。对于某个特定的任务,可以在其之后加上子网络。

新建一个池化层(高、宽维度的下采样),将特征从(b, 7, 7, 2048)变为(b, 2048),再接一个全连接层,将数据映射成100维。

global_average_layer = tf.keras.layers.GlobalAveragePooling2D()
fc = tf.keras.layers.Dense(100)

然后我们把以上子网络风女装成一个全新的网络。

mynet = tf.keras.Sequential([resnet, global_average_layer, fc])

 mynet.summary()
Model: "sequential_2"
_________________________________________________________________
Layer (type)                 Output Shape              Param #
=================================================================
resnet50 (Functional)        (None, None, None, 2048)  23587712
_________________________________________________________________
global_average_pooling2d (Gl (None, 2048)              0
_________________________________________________________________
dense_1 (Dense)              (None, 100)               204900
=================================================================
Total params: 23,792,612
Trainable params: 23,739,492
Non-trainable params: 53,120
_________________________________________________________________

通过resnet.trainable = False可以冻结ResNet部分的网络参数,在训练的时候,只更新后边的网络层。

5.6 测量工具

在网络训练过程中,我们经常会关心准确率、召回率等指标。除了可以手动计算外,Keras提供了一些常用工具,位于tf.keras.metrics模块中。

5.6.1 新建测量工具

tf.keras.metrics中提供了很多测量器类,包括Mean,Accuracy,ConsineSimilarity等。

loss_meter = tf.keras.metrics.Mean()

5.6.2 写入数据

loss_meter.update_state(float(loss))

5.6.3 读入统计信息

loss_meter.result()

5.6.4 清除状态

loss_meter.reset_status()

5.6.5 准确率统计实战

acc_meter = tf.keras.metrics.Accuracy()

x = tf.random.uniform((4, 223, 223, 3))
out = mynet(x)

out.shape
TensorShape([4, 100])

y = [61, 61, 0, 61]
pred = tf.argmax(out, axis=1)

acc_meter.update_state(y, pred)
acc_meter.result().numpy()
0.75

acc_meter.reset_states()

5.7 可视化

在训练过程中,通过web端远程监控网络的训练进度,可视化网络的训练结果,是非常重要的。TensorFlow提供了专门的可视化工具,叫TensorBoard。它通过TensorFlow将监控数据写入文件系统,并利用web端监控对应的文件系统。

5.7.1 模型端

在模型端,通过tf.summary.create_file_writer创建监控数据的Summary类。

summary_writer = tf.summary.create_file_writer(log_dir)
with summary_writer.as_default():
    tf.summary.scalar('train-loss', float(loss), step=step)

TensorBoard通过字符串id监控不同类别的数据,因此对于loss,我们将它命名为train-loss,其他类型的数据最好不要写入,防止数据污染。

5.7.2 浏览器端

如何开启web后端查看呢,需要在终端运行 tensorboard --logdir path指定监控文件的path,即可打开web浏览器进行监控了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值