前言

之前在《Tensorflow笔记:高级封装——tf.Estimator》中介绍了Tensorflow的一种高级封装,本文介绍另一种高级封装Keras。Keras的特点就是两个字——简单,不用花时间和脑子去研究各种细节问题。

1. 贯序结构

最简单的情况就是贯序模型,就是将网络层一层一层堆叠起来,比如DNN、LeNet等,与之相对的非贯序模型的层和层之间可能存在分叉、合并等复杂结构。下面通过一个LeNet的例子来展示Keras如何实现贯序模型,我们依然采用MNIST数据集举例:

LeNet-5模型结构

首先假设我们已经读到了数据,对于MNIST数据可以通过官方API直接获取,如果是其他数据可以自行进行数据预处理,由于数据读取内容不是本篇介绍重点,所以不做介绍。

(train_images, train_labels), (valid_images, valid_labels) = tf.keras.datasets.mnist.load_data()
train_images = train_images.reshape(-1, 28, 28, 1)
valid_images = valid_images.reshape(-1, 28, 28, 1)
train_images, valid_images = train_images / 255.0, valid_images / 255.0

最后数据的格式为 (n, height, width, channel) ,数据和标签的dtype分别为float和int,Keras相比与原生和tf.Estimator相比对于数据type的要求比较友好

print(train_images.shape)
# (60000, 28, 28)
print(train_labels.shape)
# (60000,)
print(train_images.dtype)
# float64 / float32 都可以
print(train_labels.dtype)
# uint8 / int16 / int32 / int64 等都可以

接下来开始构建模型

import tensorflow as tf
from tensorflow.keras.optimizers import SGD# 构建模型结构
model = tf.keras.models.Sequential([tf.keras.layers.Conv2D(6, (5,5), activation='tanh', input_shape=(28,28,1)),tf.keras.layers.MaxPooling2D(2,2),tf.keras.layers.Conv2D(16, (5,5), activation='tanh'),tf.keras.layers.MaxPooling2D(2,2),tf.keras.layers.Flatten(),tf.keras.layers.Dense(120, activation='tanh'),tf.keras.layers.Dense(84, activation='tanh'),tf.keras.layers.Dense(10, activation='softmax')
])# 模型编译(告诉模型怎么优化)
model.compile(loss='sparse_categorical_crossentropy',    # 损失函数optimizer=SGD(lr=0.05, decay=1e-6, momentum=0.9, nesterov=True),     # 优化器metrics=['acc'])    # 评估指标

对于贯序模型,只需要调用tf.keras.models.Sequential(),他的参数是一个由tf.keras.layers组成的列表,就可以确定一个模型的结构,然后再简单通过model.compile()就可以确定模型关于“如何优化”方面的信息。很像sklearn的那样简单易用,没有原生tensorflow那种结构和对话的分离,没有必要维护tensor的name。下面看一些怎么开始训练:

history = model.fit(train_images, train_labels, batch_size=32, epochs=1, verbose=1, shuffle=True, validation_data=(valid_images, valid_labels))

就一句fit就解决了!很sklearn。对于evaluate任务也超简单

model.evaluate(test_images, test_labels, verbose=2)
# [0.06203492795133497, 0.9811]

最后对于predict任务,也和sklearn一样

model.predict(test_images)

可见Keras的另一个优势就是,不需要人为的去考虑每一个batch,只需要指定一个batch_size即可,即使是在predict时也可以直接吧全部数据集喂进去。相比之下在原生Tensorflow中要通过一个for循环一个batch一个batch的去sess.run(train_op),就比较麻烦。

2. 复杂结构

贯序模型对于结构复杂的模型,比如层之间出现了分叉、拼接等操作就无法表示了(比如Inception家族)。但是Keras并没有因此放弃,依然是可以很容易的构建复杂结构的网络的。下面来实现一个下图所示的多塔Inception块(该Inception块及其改进是在各种Inception网络的基础结构):

Inception块结构

假设我们在Previous layer处的输入数据的shape为(256, 256, 3),该结构用Keras这样实现:

import tensorflow as tf# input数据接口
input_img = tf.keras.layers.Input(shape=(256, 256, 3))# 分支0
tower0 = tf.keras.layers.Conv2D(64, (1,1), padding="same", activation="relu")(input_img)
# 分支1
tower1 = tf.keras.layers.Conv2D(64, (1,1), padding="same", activation="relu")(input_img)
tower1 = tf.keras.layers.Conv2D(64, (3,3), padding="same", activation="relu")(tower1)
# 分支2
tower2 = tf.keras.layers.Conv2D(64, (1,1), padding="same", activation="relu")(input_img)
tower2 = tf.keras.layers.Conv2D(64, (5,5), padding="same", activation="relu")(tower2)
# 分支3
tower3 = tf.keras.layers.MaxPooling2D((3,3), strides=(1,1), padding="same")(input_img)
tower3 = tf.keras.layers.Conv2D(64, (1,1), padding="same", activation="relu")(tower3)# 拼接output
output = tf.keras.layers.concatenate([tower0, tower1, tower2, tower3], axis=1)# 把前面的计算逻辑,分别指定input和output,并构建成网络
model = tf.keras.models.Model(inputs=input_img, outputs=output)

这个过程与Tensorflow原生或tf.Estimator中构建网络结构在本质上是类似的,都是需要表示根据xxx计算xxx,只不过在这里不需要维护name,以及只需要考虑每一层的输入输出即可,十分节省精力。最后构建网络的步骤中也只需要指定inputs和outouts两格参数,在计算时也依然是从后到前追溯计算的。我们来看一下搭建这个网络结构的结果:

print(model.summary())
# 下面为输出
Model: "model_1"
__________________________________________________________________________________________________
Layer (type)                    Output Shape         Param #     Connected to
==================================================================================================
input_1 (InputLayer)            [(None, 256, 256, 3) 0
__________________________________________________________________________________________________
conv2d_8 (Conv2D)               (None, 256, 256, 64) 256         input_1[0][0]
__________________________________________________________________________________________________
conv2d_10 (Conv2D)              (None, 256, 256, 64) 256         input_1[0][0]
__________________________________________________________________________________________________
max_pooling2d_3 (MaxPooling2D)  (None, 256, 256, 3)  0           input_1[0][0]
__________________________________________________________________________________________________
conv2d_7 (Conv2D)               (None, 256, 256, 64) 256         input_1[0][0]
__________________________________________________________________________________________________
conv2d_9 (Conv2D)               (None, 256, 256, 64) 36928       conv2d_8[0][0]
__________________________________________________________________________________________________
conv2d_11 (Conv2D)              (None, 256, 256, 64) 102464      conv2d_10[0][0]
__________________________________________________________________________________________________
conv2d_12 (Conv2D)              (None, 256, 256, 64) 256         max_pooling2d_3[0][0]
__________________________________________________________________________________________________
concatenate (Concatenate)       (None, 1024, 256, 64 0           conv2d_7[0][0]                   conv2d_9[0][0]                   conv2d_11[0][0]                  conv2d_12[0][0]
==================================================================================================
Total params: 140,416
Trainable params: 140,416
Non-trainable params: 0
__________________________________________________________________________________________________

在tf.keras.layers中定义了很多封装好的层,在复杂的网络,只要用到的都是其中的层,就能通过tf.keras实现。对于那些这里面没有包含的层结构,就只能通过原生Tensorflow或tf.Estimator来手动搭建了,不过对于99.9%的人来说,能把现有的层灵活运用就已经很好了,研究新的层结构的工作,还是交给搞学术研究的科学家吧。

3. 保存与加载

我在《Tensorflow笔记:模型保存、加载和Fine-tune》中详细讲述了原生Tensorflow保存加载模型的过程,可谓是复杂繁琐。相比之下tf.keras的模型保存加载是非常简单的。先来看看模型的保存:

model.save("./model_h5/test-model.h5")

一行代码搞定,没有网络结构和变量的分离,保存起来非常容易。如果要加载这个模型也同样是一行代码:

new_model = tf.keras.models.load_model("./model_h5/test-model.h5")

这样加载进来的模型就可以和原模型一模一样,可以直接predict和evaluate,当然也可以看作是热启动继续进行增量训练。

new_model.summary() == model.summary()  # True
new_model.predict(test_images)    # predict
new_model.evaluate(test_images, test_labels, verbose=2)    # evaluate
# 热启动 + 增量训练
new_model.fit(training_images, training_labels, batch_size=32, epochs=1, verbose=1, shuffle=True, validation_data=(test_images, test_labels))

4. 迁移学习

除了可以将预训练好的模型加载进来,进行增量学习以外,tf.keras还可以灵活的选取模型中的部分层组成新的模型,并且冻结模型的部分层,以达到迁移学习的目的。比如我们将刚刚保存好的模型"./model_h5/test-model.h5”作为预训练模型,选取它的卷积层+池化层并冻结,然后在后面拼接上新的全链接层。

# 加载 base 模型
base_model = tf.keras.models.load_model("./model_h5/test-model.h5")
# base_model.summary()
# 构建新的网络结构
flatten = base_model.get_layer("flatten").output    # 这个layer的name可以通过 base_model.summary()获取
dense = tf.keras.layers.Dense(256, activation='relu')(flatten)
dense = tf.keras.layers.Dense(128, activation='relu')(dense)
pred = tf.keras.layers.Dense(10, activation='softmax')(dense)
# 将 base_model的输入 -> pred 这段网络结构看作是新的模型
fine_tune_model = tf.keras.models.Model(inputs=base_model.input, outputs=pred)
fine_tune_model.summary()

目前为止,我们就得到了一个新的网络结构,他长这个样子

Model: "model"
_________________________________________________________________
Layer (type)                 Output Shape              Param #
=================================================================
conv2d_input (InputLayer)    [(None, 28, 28, 1)]       0
_________________________________________________________________
conv2d (Conv2D)              (None, 24, 24, 6)         156
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 12, 12, 6)         0
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 8, 8, 16)          2416
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 4, 4, 16)          0
_________________________________________________________________
flatten (Flatten)            (None, 256)               0
_________________________________________________________________
dense_4 (Dense)              (None, 256)               65792
_________________________________________________________________
dense_5 (Dense)              (None, 128)               32896
_________________________________________________________________
dense_6 (Dense)              (None, 10)                1290
=================================================================
Total params: 102,550
Trainable params: 102,550
Non-trainable params: 0
_________________________________________________________________

然后我们来将前面的卷积层冻结,具体冻结哪几层需要我们来手动指定。

# 冻结前面的卷积层
for i, layer in enumerate(fine_tune_model.layers): # 打印各卷积层的名字print(i, layer.name)
for layer in fine_tune_model.layers[:6]:layer.trainable = False

现在一切准备就绪,最后就是编译网络结构,并且进行Fine-tune:

# 新模型编译
fine_tune_model.compile(loss='sparse_categorical_crossentropy', optimizer=SGD(lr=0.05, decay=1e-6, momentum=0.9, nesterov=True), metrics=['acc'])# Fine-tune
fine_tune_model.fit(training_images, training_labels, batch_size=32, epochs=1, verbose=1, shuffle=True, validation_data=(test_images, test_labels))

tf.keras的Fine-tune也十分简单,把预训练模型加载进来之后,可以根据每一层的名字来将该层的output提取出来(即base_model.get_layer("flatten").output),并且可以直接作为新一层的输入。另外tf.keras会自动管理每一层的名字以及整个网络的inputs和outouts,而且可以直接通过model.summary()或model.layers()获取,避免了原生Tensorflow中原模型作者没有很好管理节点名导致无法获取tensor的情况。

5. 部署

5.1 利用tf.Serving+Docker

前面说了那么多,tf.keras又方便又强大,就差最后一步——部署了。我在《Tensorflow笔记:通过tf.Serving+Docker部署》中介绍了如何将已经导出为saved_model格式的模型,通过tf.Serving+Docker进行部署。对于tf.keras模型来说当然也可以这样,只需要将模型保存为saved_model形式就可以通过tf.Serving+Docker进行部署了,保存为saved_model模式的方法也很简单:

# 保存为 h5 模型
# model.save("./model_h5/test-model.h5")# 保存为 saved_model
tf.saved_model.save(model, './saved_model_keras/1')

与原生Tensorflow同理,保存为saved_model的时候手动指定保存到版本号路径下,在服务时只需指定./saved_model_keras路径即可,tf.Serving会自动在该路径下挑选最新版本进行服务。

5.2 利用flask搭建服务

当然因为tf.keras模型的加载和预测都非常方便,也可以不用tf.Serving来进行部署。可以直接写一个基于flask的后台脚本,接收请求后调用model.predict(),然后返回就可以了。

import numpy as np
import tensorflow as tf
from flask import Flask
from flask import request
import jsonapp = Flask(__name__)# 配置tf运行环境 + 加载模型
graph = tf.get_default_graph()
sess = tf.Session()
set_session(sess)
model = tf.keras.models.load_model("./model_h5/test-model.h5")# 用于服务的接口
@app.route("/predict", methods=["GET","POST"])
def predict():data = request.get_json()if 'values' not in data:return {'result': 'no input'}arr = data['values']global sessglobal graphwith graph.as_default():set_session(sess)res = np.argmax(model.predict(np.array(arr)), axis=1)return {'result':res.tolist()}if __name__ == '__main__':app.run()

这期间会有一些坑,比如要设置graph,否则会出现graph重复导入的问题;以及要设置Session,否则会出现FailedPreconditionError错误。接下来我们来请求一下试试:

import json
import numpy as np
import tensorflow as tf
from urllib import request# 通过 urllib.request 进行请求
HOST = "http://127.0.0.1:5000/"
image = training_images[0:5].tolist()    # 这里的training_images的格式就是前面训练时training_images的格式
data = json.dumps({'values': image})     # 构造json格式的数据,这里的"values"作为key必须和服务端的"values"相同
req = request.Request(HOST + "predict", headers={"Content-Type": "application/json"})    # 请求
res = request.urlopen(req, data=bytes(data, encoding="utf-8"))
result = json.loads(res.read())          # 字符串转化为字典
print(result)
# 打印结果: {'result': [5, 0, 4, 1, 9]}

关于flask和web服务的内容不是本文重点,所以就不介绍了。至此,tf.keras的内容就介绍完了。

6. 并行训练

6.1 单机多卡

6.1.1 结构并行

所谓结构并行,就是对不同的GPU分别计算网络中不同的结构。只需要通过with tf.device_scope('/gpu:0')的方法来手动控制设备,以实现并行。比如对于前面的Inception块的例子中,完全可以改写成

with tf.device_scope('/gpu:0'):tower0 = tf.keras.layers.Conv2D(64, (1,1), padding="same", activation="relu")(input_img)
with tf.device_scope('/gpu:1'):tower1 = tf.keras.layers.Conv2D(64, (1,1), padding="same", activation="relu")(input_img)tower1 = tf.keras.layers.Conv2D(64, (3,3), padding="same", activation="relu")(tower1)
with tf.device_scope('/gpu:2'):tower2 = tf.keras.layers.Conv2D(64, (1,1), padding="same", activation="relu")(input_img)tower2 = tf.keras.layers.Conv2D(64, (5,5), padding="same", activation="relu")(tower2)
with tf.device_scope('/gpu:3'):tower3 = tf.keras.layers.MaxPooling2D((3,3), strides=(1,1), padding="same")(input_img)tower3 = tf.keras.layers.Conv2D(64, (1,1), padding="same", activation="relu")(tower3)

这样就可以使用CPU与多个GPU进行并行计算网络的不同结构部分。

6.1.2 数据并行

所谓数据并行,就是不同的GPU都是计算整个网络的每一个节点,只是处理的数据不同(不同的batch)。这种并行方式不需要手动控制设备,只需要加上一行代码就可以

model = ...           # 和前面一样
model = tf.keras.utils.multi_gpu_model(model, gpus=2)    # 只需要加上这句
model.compile(...)    # 和前面一样
model.fit(...)        # 和前面一样

只要在model.compile之前加上multi_gpu_model一行就可以将模型转化为数据并行模式。十分简单,其中gpus指用几个GPU。另外除了这种方法也可以直接指定哪一块GPU进行计算

import os
os.environ["CUDA_VISIBLE_DEVICES"] = "3,5"

上面就是指定第三块和第五块显卡参与训练。

6.2 集群并行

在集群并行中,和原生Tensorflow类似,都需要提供一份“集群名单”以及告诉该机器是集群中的谁。并且在集群中的每一台机器上都运行一次脚本,以启动分布式训练。下面看一个例子

import os
import json
import tensorflow as tftf.app.flags.DEFINE_string("job_name", "worker", "ps/worker")
tf.app.flags.DEFINE_integer("task_id", 0, "Task ID of the worker running the train")os.environ['TF_CONFIG'] = json.dumps({'cluster': {‘ps’: ["localhost:2222"],'worker': ["localhost:2223", "localhost:2224"]},'task': {'type': FLAGS.job_name, 'index': FLAGS.task_id}
})

本例采用本地机的两个端口模拟集群中的两个机器,"cluster"表示集群的“名单”信息。"task"表示该机器的信息,"index"表示该机器是"worker"列表中的第几个。

接下来就是训练代码部分了,与单机代码不同的是,只需先声明一个strategy,并且将构造模型部分放在with strategy.scope():中就可以了。下面来看一个例子

# 准备数据
(training_images, training_labels), (test_images, test_labels) = tf.keras.datasets.mnist.load_data(path="./mnist.npz")
training_images = training_images.reshape(-1, 28, 28, 1)
test_images = test_images.reshape(-1, 28, 28, 1)
training_images, test_images = training_images / 255.0, test_images / 255.0# 声明分布式 strategy
strategy = tf.distribute.experimental.ParameterServerStrategy()# 在分布式strategy下构造网络模型
with strategy.scope():model = tf.keras.models.Sequential([tf.keras.layers.Conv2D(6, (5,5), activation='tanh', input_shape=(28,28,1)),tf.keras.layers.MaxPooling2D(2,2),tf.keras.layers.Conv2D(16, (5,5), activation='tanh'),tf.keras.layers.MaxPooling2D(2,2),tf.keras.layers.Flatten(),tf.keras.layers.Dense(120, activation='tanh'),tf.keras.layers.Dense(84, activation='tanh'),tf.keras.layers.Dense(10, activation='softmax')])# 模型编译model.compile(loss='sparse_categorical_crossentropy',optimizer=SGD(lr=0.05, decay=1e-6, momentum=0.9, nesterov=True), metrics=['acc'])# 模型训练
history = model.fit(training_images, training_labels, batch_size=32, epochs=3)
# 保存模型
model.save("./model_h5/dist_model.h5")

以上就是分布式keras训练的方法。实际上可以声明不同的strategy,来实现不同的并行策略:

  • tf.distribute.MirroredStrategy:单机多卡情况,每一个GPU都保存变量副本。
  • tf.distribute.experimental.CentralStorageStrategy:单机多卡情况,GPU不保存变量副本,变量都保存在CPU上。
  • tf.distribute.experimental.MultiWorkerMirroredStrategy :在所有机器的每台设备上创建模型层中所有变量的副本。它使用CollectiveOps,一个用于集体通信的 TensorFlow 操作,来聚合梯度并使变量保持同步。
  • tf.distribute.experimental.TPUStrategy:在TPU上训练模型
  • tf.distribute.experimental.ParameterServerStrategy:本例中采用的策略,有专门的ps机负责处理变量和梯度,worker机专门负责训练,计算梯度。所以只有在这种策略下,才需要在os.environ['TF_CONFIG']中设置ps机
  • tf.distribute.OneDeviceStrategy:用单独的设备来训练。

keras提取模型中的某一层_Tensorflow笔记:高级封装——Keras相关推荐

  1. keras提取模型中的某一层_Keras做图片分类(四):迁移学习--猫狗大战实战

    本项目数据集来自kaggle竞赛,地址: https://www.kaggle.com/c/dogs-vs-cats-redux-kernels-edition/data 数据的训练集放在train文 ...

  2. keras提取模型中的某一层_keras获得某一层或者某层权重的输出实例

    一个例子: print("Loading vgg19 weights...") vgg_model = VGG19(include_top=False, weights='imag ...

  3. keras提取模型中的某一层_keras K.function获取某层的输出操作

    如下所示: from keras import backend as K from keras.models import load_model models = load_model('models ...

  4. python模型保存save_浅谈keras保存模型中的save()和save_weights()区别

    今天做了一个关于keras保存模型的实验,希望有助于大家了解keras保存模型的区别. 我们知道keras的模型一般保存为后缀名为h5的文件,比如final_model.h5.同样是h5文件用save ...

  5. OSI的七层模型,网线,网卡,集线器,交换机,路由器分别工作在七层模型中的哪一层?

    OSI七层网络模型由下至上为1至7层,分别为物理层(Physical layer),数据链路层(Data link layer),网络层(Network layer),传输层(Transport la ...

  6. python如何提取数据中的年月_Python数据处理笔记——Pandas时间数据提取处理

    做数据分析时,对于有时间数据的数据来源,在时间维度上的剖析必不可少 比如: 在一天的销售时间内,哪些时间段是高峰 是否与星期有关 RFM模型中的R怎么快速计算距离天数 如何找出某个时间点或时间段的数据 ...

  7. SSL协议工作在OSI模型中的哪一层?

    首先我们来看看什么是SSL协议(引申出TLS): SSL(Secure Sockets Layer 安全套接层),及其继任者传输层安全(Transport Layer Security,TLS)是为网 ...

  8. Keras | 计算模型的FLOPs、MACCs

    FLOPs全称是floating point operations的缩写,翻译过来是浮点运算数,理解为计算量,常用来衡量算法或深度学习模型的计算复杂度. 关于计算FLOPs值的函数,网上相关的博客很多 ...

  9. python输入数据的维度_keras分类模型中的输入数据与标签的维度实例

    在<python深度学习>这本书中. 一.21页mnist十分类 导入数据集 from keras.datasets import mnist (train_images, train_l ...

最新文章

  1. expand yourself
  2. python变量类型-python 变量类型 number
  3. 团队博客 一 需求分析
  4. Python通过WMI读取主板BIOS信息
  5. ArcGIS中文注记图层发布服务后变乱码(方框乱码)/如何有效修改注记要素类文字样式
  6. php把amr转换成mp3,php 微信amr转mp3的方法
  7. WordCloud 中英文词云图绘制,看这一篇就够了
  8. win10安装小爱同学 UWP
  9. Python 如何反方向迭代一个序列
  10. 和block循环引用说再见
  11. 能让3D打印自动支撑和减少压力积累的云软件
  12. java生成csr_使用Keytool工具生成CSR
  13. 鸿蒙系统摄像头,内置鸿蒙系统,华为生态产品海雀智能摄像头Pro体验
  14. 圆桌实录:技术无感化成为 2023 年最值得开发者和企业用户关注的技术趋势丨PingCAP DevCon 2022
  15. 4399手游事业部,诚聘AS开发主程、C++前端开发工程师、erlang后端开发工程师
  16. Xilinx zc706 Si5324
  17. CHROME扩展笔记之拒绝unsafe-eval求值
  18. 曙光计算机能玩游戏吗,怎么用手机模拟器玩曙光英雄,曙光英雄手游电脑版教程...
  19. 怎么连接本地其他计算机设备,win7电脑本地连接连不上怎么办?详解如何设置本地连接...
  20. length()和strlen()

热门文章

  1. 杂志html转换服务平台,Verypdf HTML Converter(网页转换器)
  2. java信号量源代码_Java信号量Semaphore原理及代码实例
  3. Intelli IDEA 快捷键操作,咱来点不一样的
  4. Oracle(二)单行函数
  5. okhttp框架学习
  6. 基于JAVA+SpringBoot+Mybatis+MYSQL的记账本管理系统
  7. Spark SQL join的三种实现方式
  8. Webservice学习之——即时发布与定制发布
  9. 如何自建appender扩展Log4j框架
  10. 缺陷管理系统mantisbt环境搭建