tensorflow介绍
前言
这篇文章既然放在技能库里,肯定就要讲 TensorFlow 的具体用法。但是这么大一个框架肯定不是这样一篇短短的博客就能介绍完的,所以这里只展示几个例子,通过代码熟悉框架以快速入门,如果真想学好建议在网络条件良好的情况下看官方文档,也可以看谷歌在中国服务器上给的文档。因为 TensorFlow 1.x和2均支持keras,所以本文先介绍 keras,再对 TensorFlow 2 和 TensorFlow 1.x 的差异部分做特殊说明
Keras 是一个基于 TensorFlow 等框架提供的底层运算而实现的高层框架,提供了大量方便快速训练,测试的高层接口,对于常见应用来说,使用Keras 开发效率更高。
Keras 可以理解为一套高层API 的设计规范,TensorFlow 实现了这套规范,称为tf.keras 模块,并且tf.keras 将作为TensorFlow 2 版本的唯一高层接口,避免出现接口重复冗余的问题
TensorFlow 是Google 于2015 年发布的深度学习框架。得益于发布时间较早,以及Google 在深度学习领域的影响力,TensorFlow 一度成为最流行的深度学习框架。但是由于TensorFlow 接口设计频繁变动,功能设计重复冗余,符号式编程开发和调试非常困难等问题,TensorFlow 1.x 版本一度被业界诟病。
2019年,Google 推出TensorFlow 2 正式版本,以动态图为优先模式运行,从而能够避免TensorFlow 1.x 版本的诸多缺陷。但因为TensorFlow 2完全不兼容TensorFlow 1.x的代码,接口变动依旧频繁、功能设计仍有重复、加上为时已晚, PyTorch 逐渐在深度学习框架的份额上坐稳了第一的位置。
Keras
TensorFlow 与 Keras 是两个东西,但都是谷歌开发的。Keras 用的人多了以后 Google 把 Keras 集成在了 TensorFlow 里面,作为tf.keras
模块。在使用 TensorFlow 2 的时候多半会用到tf.keras.layers
构建神经网络层,因为如果真的全部从头开始搭费时费力,不如直接用造好的轮子。
搭建模型
使用 Keras 高阶 API 搭建模型有3种方法,分别是序列式、函数式和Model子类化。
序列式(Sequential API)
序列式是layer-by-layer
的方式,适用于简单的层堆栈,但难以构建多输入、多输出的模型,不够灵活。
你可以通过将层列表传递给tf.keras.Sequential
来创建 Sequential 模型:
1 | import tensorflow as tf |
也可以通过add()
方法:
1 | import tensorflow as tf |
这两段代码作用相同,都创建了一个第一层有2个节点,第二层有3个节点,输出层有4个节点的神经网络。
上面的代码仅仅搭建了模型框架,并未指定输入的shape,所以没有参数,需要进行后续处理才可用于训练。
函数式(Functional API)
1 | import tensorflow as tf |
上面这个网络的结构是:
1 | Model: "encoder" |
Model子类化创建自定义模型
使用这种方式可以非常自由地自定义模型。下面是一个自定义残差层的例子
1 | import tensorflow as tf |
上面几种方式各有所长。灵活地混合使用各种方式可以很方便地实现我们的网络模型。
1 | class ResnetBlock(tf.keras.layers.Layer): |
模型训练
通过Model.compile()
装配模型,Model.fit()
训练模型,Model.evaluate()
评估模型,Model.predict()
使用模型。
compile()
方法指定损失、指标和优化器:
1 | model.compile( |
1 | Fit model on training data |
返回的history
对象保存训练期间的损失值和指标值记录:
1 | print(history.history) |
1 | {'loss': [0.3473339378833771, 0.16158732771873474], |
学习率衰减的实现可以参照下面修改optimizer
里的learning_rate
参数
1 | initial_learning_rate = 0.1 |
使用回调函数可以方便地实现模型检查点、早停和TensorBoard。只需要把model.fit()
改为:
1 | model = get_compiled_model() |
准备数据
TensorFlow 中没有像 PyTorch里torch.utils.data.DataLoader
这么一致的数据加载流程,虽然大多各写各的。虽然tensorflow有tf.data.Dataset
、tf.data.Dataset.from_tensor_slices
,keras有tf.keras.utils.image_dataset_from_directory
当然,你也可以直接使用torch.utils.data.DataLoader
进行数据的加载,只要最后的数据是Numpy array格式就没问题。
以花卉数据集为例演示如何加载数据。首先下载数据:
1 | import pathlib |
得到的文件夹结构如下:
1 | flowers_photos/ |
tf.keras.utils.image_dataset_from_directory
你可以使用tf.keras.utils.image_dataset_from_directory
加载数据集,该方法适用于分类任务且一个类别一个文件夹
1 | batch_size = 32 |
使用image-label对
1 | list_ds = tf.data.Dataset.list_files(str(data_dir/'*/*'), shuffle=False) |
加载.npy
文件
下面是一个从.npz
文件加载数据的例子:
1 | DATA_URL = 'https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz' |
数据预处理
数据预处理通过网络层实现。每种预处理方法都可视为网络中的一层。
1 | num_classes = 5 |
模型保存与加载
注意:这里说的都是keras而非 TensorFlow。通过 TensorFlow API保存的模型与通过 keras API保存的模型不互通
keras模型通过model.save('path/to/location')
保存。
加载模型代码如下:
1 | from tensorflow import keras |
TensorFlow 2
使用梯度磁带自定义训练过程
下面是不使用Keras API 的 TensorFlow 2 训练过程。TensorFlow 2 引入梯度磁带实现自动求导。
如果你不想使用model.fit()
,而是想更为灵活的训练,可以使用下面的代码:
1 | epochs = 50 |
使用tf.Module
搭建模型
从这一小节开始的内容基本上都用不上了
在搭建模型的过程中,TensorFlow 2 中提供了最底层的tf.Variable
和tf.Module
,但是过于底层,官方不推荐使用,实际代码中使用到这部分的代码也是特别少。
除了使用Keras外,还可以使用tf.Module
搭建模型。Keras正是在tf.Module
的基础上实现的。下面是一个由模块组成的两层线性层模型的示例。
注:
tf.Module
是tf.keras.layers.Layer
和tf.keras.Model
的基类,因此您在此处看到的一切内容也适用于 Keras。出于历史兼容性原因,Keras 层不会从模块收集变量,因此您的模型应仅使用模块或仅使用 Keras 层。不过,下面给出的用于检查变量的方法在这两种情况下相同。
首先是一个密集(线性)层。为了不预先指定输入大小,将变量创建推迟到第一次使用特定输入形状调用模块时:
1 | import tensorflow as tf |
随后是完整的模型,此模型将创建并应用两个层实例:
1 | class MySequentialModule(tf.Module): |
1 | Model results: tf.Tensor([[0. 0.]], shape=(1, 2), dtype=float32) |
1 | ``` |
这样,c=a+b
的计算图就创建好了。下面开始使用刚才创建的图计算2+4
:
1 | sess = tf.InteractiveSession() # 创建运行环境 |
TensorFlow 1.x将“定义”与“运行”相分离,一个TensorFlow图描述了计算的过程。为了进行计算,图必须在会话session
里启动,会话将图的op分发到诸如CPU或者GPU的设备上,同时提供执行op的方法,这些方法执行后,将产生的tensor返回,在python语言中,返回的tensor是numpy array对象,在C或者C++语言中,返回的tensor是tensorflow:Tensor
实例。
不使用 keras 构建 pipline
理解了静态计算图的运行机制以后,来看下面这个不使用 keras 构建 pipline 的例子。下面的代码在MNIST上训练了一个分类模型。
1 | import tensorflow as tf |