Tensorflow 2.0 使用流程详解
前言 :明确神经网络搭建流程,列举了过程中所有实现方法。
絮叨几句:
自己最初就是想借 tensorflow 架构一个简单网络,但看了网上诸多教程,依旧对 tensorflow 如何去实现感到糊涂,官方文档教程和指南也感觉逻辑搞得相当混乱和复杂,各种方法混用,看了反而更莫名其妙,获取到的知识碎片化严重,还记不牢。更有些教程知识点反而集中到了感知机、线性回归、各类神经网络上。我只是想搞清架构网络的方法而已,比如怎么架构,最重要的有几种实现方法,毕竟tensorflow只是工具而已,没有整体概念,知识无法宏观掌控,就会觉得这玩意的学习乱七八糟、没有尽头似的。摸不清工具的底细是很难受的。
索性写笔记,记录获取到的知识点。也做总结归纳用。夹杂自己的理解,难免有错,仅供参考。
文章目录
0. 写在前面
Tensorflow 模型架构比较成熟,系统复杂庞大,学习代价相对高些,但优点是稳定,可这一点就足以受到工业青睐。事实上,目前工业上大多都是使用这一架构部署人工智能网络。
至于其它的一些人工智能架构,一般特点是易用性强,易上手。多用于科研。如PyTorch,还有曾经的 Keras,但随着谷歌对Keras的收购,其单独的 Keras 库不再更新,取而代之的是,Keras 融入到了 tensorflow 中,变成其一个子模块tf.keras
,tf.keras
的所有实现都是以 tensorflow 为基础。(注:Keras
和 tf.keras
有些地方会有稍微的差别)
自己感觉学习 tensorflow 比较好,因为它就是神经网络架构的大杂烩,啥都有,啥都不缺,底层的基础函数、中层API、高层API,应有尽有。实现某一功能,可以借助不同层级的 API ,从而达到多种实现,这可能也是看完诸多教程知识混乱的原因。
贴一个比较重要的图,Tensorflow 的系统架构,对tensorflow整体情况有个了解,如下
其中,作为新手,只把它当做人工智能研究的工具,只需关注客户端部分就可以了。我们用python编写 tensorflow 代码就是客户端层面。
客户端层面,编写代码本质上就是函数组合调用,或者说是使用tensorflow提供的 API。但这也分情况:
-
一种是直接调用底层函数
如变量常量声明、一些数学运算等。对应于
tf.Variable
,tf.constant
,tf.nn.conv2d
,tf.GradientTape
等等 -
一种是调用 tensorflow 已经把底层函数组合封装好以实现某一常用功能的函数(其实自己用底层函数也可以实现)。
如封装好一些卷积运算、激活函数的层,封装好的网络评估函数等。对应于
tf.keras.layers
,tf.keras.losses
,tf.keras.optimizers
等等,这些函数可能存在优化,可能比自己实现计算效率要高。在未收购 keras 前,tensorflow 也有
tf.layers
,tf.losses
等,现在仍旧存在。应该跟tf.keras.layers
,tf.keras.losses
差别不到哪去。
事实上,还有第三种,更高层的 API。就是
-
把第二种得到的如
tf.keras.layers
的网络层组合起来,直接组建一个神经网络模型,即tf.keras.model
。该模型是作为类出现,使用者自定义模型时,可以基于这个基本类(继承)进行修改。添加自己想要的一些功能。
记得最初接触 tensorflow.v1 的时候,编写网络还都是青一色的自己手写各种层的实现(第一种API),手写网络性能评估。可能源于收购 Keras 的原因,现在更方便更普遍的是直接调用更高一层封装好的功能函数(第二种API)。Pytorch等其它网络的易用性也体现在此处,开发人员已经把常用功能函数封装,提供API接口,因此实现某个新网络搭建更快捷,使用者直接调用即可。
所以综上所述,可以把 tensorflow 提供的函数,或者说 API 分为三类:低阶API、中阶API、高阶API。
用一张图表示更清晰明了些:
所以,搭建神经网络时,就不止一种方式可以实现之。
不仅如此,训练网络也不止一种方法可以实现之。
更有甚者,你想要用 tensorflow 实现某一功能,都总有很多种方法可以实现之。
1. 数据准备
先明确一个目标吧。因为自己多处理图像,就实现用一个简单神经网络实现一个简单功能。确切说是
用经典的 LeNet 网络实现 手写数字识别。
1.1 数据获取
最通用方法就是下载数据集,现在热点人工智能方面的问题,网上都会有对应的公开数据集。当然也可以自己制作,比如借助网络和爬虫的力量 【使用爬虫构建新闻文本数据集】 。
至于手写数据集,神经网络的hello world
,网络上有。当然,使用起来有两种方法,
现在也有个 Fasion-MNIST 数据集,其外在特征跟 MNIST 完全一致,只是内容变成了10类商品,克服手写数字集太简单问题,更复杂、具象化些。下载从这:【Fasion-MNIST 数据集】
-
使用代码,自动下载
from tensorflow.keras.datasets import mnist # 下载并加载数据 (x_train, y_train), (x_test, y_test) = mnist.load_data()
-
下载至本地,直接使用
- 可以下载 .npz 格式,使用 numpy 加载。下载从这:【npz 格式 MNIST(11Mb)】
import numpy as np MNIST = np.load('./mnist.npz') x_train, y_train = MNIST['x_train'], MNIST['y_train'] x_test, y_test = MNIST['x_test'], MNIST['x_test'] # 或者使用 tensorflow,注:下面路径要填写绝对路径!不然依旧会从网络上下载! (x_train, y_train), (x_test, y_test) = mnist.load_data(r'd:\mnist-npz\mnist.npz')
- 也可以下载源文件,使用 tensorflow 加载。从这:【gz 格式 MNIST】
#------ tensorflow 2.0 中下面方法已不可用 --------# # from tensorflow.examples.tutorials.mnist import input_data # MNIST = input_data.read_data_sets('./MNIST_data', one_hot = True) # 可以自己解压,自己定义函数加载,这里牵扯到 *.gz 格式文件认知和解码。不想研究。
1.2 数据处理
本例数据比较简单,已经划分好训练集和测试集,数据量也小,不用怎么处理。
只要想办法载入就可以了,遵循五步走即可,直接上代码
# 自定义一些特殊处理
def preprocess(x, y):
# 将图像归一化的 [0,1]
x = tf.cast(x, tf.float32)/ 255
# 将标签转成 one-hot 格式
y = tf.cast(y, tf.int32)
y = tf.one_hot(y, depth=10)
return x, y
# 1.另样加载
train_data = tf.data.Dataset.from_tensor_slices((x_train, y_train))
# 2.特殊处理
train_data = train_data.map(preprocess)
# 3.复制几份
train_data = train_data.repeat(3)
# 4.打乱顺序
train_data = train_data.shuffle(5000)
# 5.分批训练
batch_size = 500 # 一次喂 1000 个数据
train_data = train_data.repeat(3).shuffle(5000).batch(batch_size)
# 说明:事实上,这些功能后面 model.fit() 就可以实现了,因此后面使用model搭建的模型进行训练时并没用到。
另外值得强调的是!真实项目中数据处理是极其极其极其重要的一部分,并且这一重要部分牵扯到至少三个重要知识点:
(1)数据集划分
包括训练集、验证集、测试集等。
听起来似乎很简单,但这是个技术活,好的数据集划分和使用可以大大提高网络性能。
(2)数据增广
当训练集中数据很少时,通过一些手段来增加训练数据数量。见【深度学习之数据增强方式和实现总结】。
(3)数据读取
当数据量很少时,可以直接读取,加载到内存中,导入到网络中,进行训练。很推荐这么做,这种方法很高效、快捷。
当数据量很大时,不能一次性全部加载入内存,此时就需要一些手段来处理这些数据。此时会牵扯到 标准tensorflow数据格式(TFRecords) , **数据输入方法(tf.data API, Queue API, PreloadedData, Feeding)**等一系列”复杂“内容。
此外还可能包括数据归一化处理等,每个都是一个值得研究的知识点。现不详述,现也不深究。(曾痛苦地深究过,但都已忘记)
但先有概念、框架,了解其在整个流程的大概位置,具体内容后续再详细了解,再向框架中定点填充实物。
2. 网络搭建
经典 LeNet 网络,结构如下:
Layer | Description | output_shape | params |
---|---|---|---|
INPUT | Input_data | 32 x 32 | 0 |
C1 | Conv 5x5 , strides= 1, filter_number = 6 | 6, 28 x 28 | 156 |
S2 | Pooling 2x2, strides = 2 / sigmoid | 6, 14 x 14 | 12 |
C3 | Conv 5x5, strides = 1, filter_number = 16 | 16, 10 x 10 | 24164 |
S4 | Pooling 2x2, strides = 2 / sigmoid | 16, 5 x 5 | 32 |
C5 | FC 120 | 120 | 48120 |
F6 | FC 84 /sigmoid | 84 | 10164 |
OUTPUT | FC 10 | 10 | 840 |
下面使用前述三种层次的 API 进行搭建。这里用到了四种方法