Tensorflow概述
Tensorflow简介
什么是Tensorflow
- TensorFlow由谷歌人工智能团队谷歌大脑(Google Brain)开发和维护的开源深度学习平台,是目前人工智能领域主流的开发平台,在全世界有着广泛的用户群体
Tensorflow的特点
- 优秀的构架设计,通过“张量流”进行数据传递和计算,用户可以很容易地、可视化地看到张量流动的每一个环节
- 可轻松地在CPU/GPU上部署,进行分布式计算,为大数据分析提供计算能力的支撑
- 跨平台性好,灵活性强。TensorFlow不仅可在Linux、Mac和Windows系统中运行,甚至还可在移动终端下工作
Tensorflow发展历史
- 2011年,Google公司开发了它的第一代分布式机器学习系统DistBelief。著名计算机科学家杰夫·迪恩(Jeff Dean)和深度学习专家吴恩达(Andrew Y.Ng)都是这个项目的核心成员
- 2015年11月,Google将它的升级版实现正式开源,协议遵循Apache2.0并更名为TensorFlow
- 目前,TensorFlow最新版为2.X,教学使用1.14.0
Tensorflow安装
-
安装纯净包:
pip install tensorflow
-
安装GPU版本:
pip install tf-nightly-gpu
-
修改源进行安装。如果安装包time out错误,则可以修改pip源,重新进行安装,修改方式
#编辑或新建pip配置文件(~/.pip/pip.conf),在配置文件下加入:
[global]
index-url = http://mirrors.aliyun.com/pypi/simple/
[install]
trusted-host = mirrors.aliyun.com
- 安装时将timeout时间设置长一点
sudo pip3 --timeout 600 install tensorflow-1.14.0-cp35-cp35m-manylinux1_x86_64.whl
- 也可使用如下完整命令安装:
pip3 install --user tensorflow==1.14.0 --index-url https://pypi.tuna.tsinghua.edu.cn/simple/ --trusted-host https://pypi.tuna.tsinghua.edu.cn --timeout 600
快速开始
import tensorflow as tf
hello = tf.constant('hello wrold') # 定义一个常量
sess = tf.Session() # 创建一个session
print(sess.run(hello)) # 计算
sess.close()
张量相加
import tensorflow as tf
#定义
conts1 = tf.constant(100.0)
conts2 = tf.constant(200.0)
res = tf.add(conts1,conts2)
#执行
with tf.Session() as sess:
print(sess.run(res))
Tensorflow体系结构
体系结构概述
- TensorFlow属于“定义”与“运行”相分离的运行机制。从操作层面可以抽象成两种:模型构建和模型运行
- 客户端:用户编程、执行使用
- master:用来与客户端交互,并进行任务调度
- worker process:工作节点,每个worker process可以访问一到多个device
- device:TF的计算核心,执行计算
单机模式与分布式模式
- TF的实现分为“单机实现”和“分布式实现”
后端逻辑层次
基本概念
张量
- 张量(Tensor): 多维数组或向量,张量是数据的载体,包含名字、形状、数据类型等属性
数据流
- 数据流图(Data Flow Graph)用“结点”(nodes)和“线”(edges)的有向图来描述数学计算
- “节点” 一般用来表示数学操作,也可以表示数据输入(feed in)的起点/输出(push out)的终点,或者是读取/写入持久变量(persistent variable)的终点
- “线”表示“节点”之间的输入/输出关系。这些数据“线”可以输运多维数据数组,即“张量”(tensor)
- 一旦输入端的所有张量准备好,节点将被分配到各种计算设备完成异步并行地执行运算
操作
- 操作(Operation,简称op)指专门执行计算的节点,tensorflow函数或API定义的都是操作。常用操作包括:
- 标量运算,向量运算,矩阵运算
- 带状态的运算
- 神经网络组建
- 存储、恢复
- 控制流
- 队列及同步运算
图和会话
- 图(Graph)描述整个程序结构,Tensorflow中所有的计算都构建在图中
- 会话(Session)用来执行图的运算
变量和占位符
- 在Tensorflow中,变量(Variable)是一种操作,变量是一种特殊的张量,能够进行存储持久化(张量不能进行持久化),它的值是张量
- 占位符(placeholder)是变量占位符,当不能确定变量的值时,可以先声明一个占位符,真正执行时再传入变量
Tensorflow基本使用
图和会话操作
什么是图
- 图(Graph)描述了计算的过程。TensorFlow 程序通常被组织成一个构建阶段和一个执行阶段。在构建阶段, op 的执行步骤被描述成一个图. 在执行阶段, 使用会话执行执行图中的 op.
- TensorFlow Python 库有一个默认图 (default graph), op构造器可以为其增加节点. 这个默认图对 许多程序来说已经足够用了,也可以创建新的图来描述计算过程
- 在Tensorflow中,op/session/tensor都有graph属性
查看图对象
# 常量加法运算示例
import tensorflow as tf
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' # 调整警告级别
a = tf.constant(5.0) # 定义常量a
b = tf.constant(1.0) # 定义常量a
c = tf.add(a, b)
print("c:", c)
graph = tf.get_default_graph() # 获取缺省图
print(graph)
with tf.Session() as sess:
print(sess.run(c)) # 执行计算
print(a.graph) # 通过tensor获取graph对象
print(c.graph) # 通过op获取graph对象
print(sess.graph) # 通过session获取graph对象
会话及相关操作
- 会话(session)用来执行图中的计算,并且保存了计算张量对象的上下文信息。会话的作用主要有:
- 运行图结构
- 分配资源
- 掌握资源(如变量、队列、线程)
- 一个session只能执行一个图的运算。可以在会话对象创建时,指定运行的图。如果在构造会话时未指定图形参数,则将在会话中使用默认图。如果在同一进程中使用多个图(使用tf.graph( )创建),则必须为每个图使用不同的会话,但每个图可以在多个会话中使用。
- 使用默认图创建会话
tf.Session()
- 运行
session.run(fetches, feed_dict=None)
,fetches
图中的单个操作,或多个操作的列表,feed_dict
运行传入的参数构成的字典,可以覆盖之前的值 - 关闭
session.close()
指定会话运行某个图
# 创建多个图,指定图运行
import tensorflow as tf
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' # 调整警告级别
a = tf.constant(5.0) # 定义常量a
b = tf.constant(1.0) # 定义常量b
c = tf.add(a, b)
graph = tf.get_default_graph() # 获取缺省图
print(graph)
graph2 = tf.Graph()
print(graph2)
with graph2.as_default(): # 在指定图上创建op
d = tf.constant(11.0)
with tf.Session(graph=graph2) as sess:
print(sess.run(d)) # 执行计算
# print(sess.run(c)) # 报错
会话常见的错误及原因
- 调用run()方法时,可能会出现的错误及原因
- RuntimeError:Session处于无效(如关闭)
- TypeError:fetches或feed_dict的键是不合适的值
- ValueError:fetches或feed_dict的键无效或引用的值不存在
张量及基本运算
张量的阶与形状
- 阶:张量的维度(数方括号的层数)
- 0维:( )
- 1维:(5),1行5个元素
- 2维:(2,3),2行3列
- 3维:(2,3,4), 两个3行4列的矩阵
张量的数据类型
数据类型 | Python 类型 | 描述 |
---|---|---|
DT_FLOAT | tf.float32 | 32位浮点数 |
DT_DOUBLE | tf.float64 | 64位浮点数 |
DT_INT64 | tf.int64 | 64位有符号整型 |
DT_INT32 | tf.int32 | 32位有符号整型 |
DT_INT16 | tf.int16 | 16位有符号整型 |
DT_INT8 | tf.int8 | 8位有符号整型 |
DT_UINT8 | tf.uint8 | 8位无符号整型 |
DT_STRING | tf.string | 可变长度的字节数组.每一个张量元素都是一个字节数组 |
DT_BOOL | tf.bool | 布尔型 |
DT COMPLEX64 | tf.complex64 | 由两个32位浮点数组成的复数:实数和虚数 |
DT_QINT32 | tf.qint32 | 用于量化Ops的32位有符号整型 |
DT_QINT8 | tf.qint8 | 用于量化Ops的8位有符号整型 |
DT_QUINT8 | tf.quint8 | 用于量化Ops的8位无符号整型 |
张量常用属性
属性名称 | 说明 |
---|---|
graph | 所属的默认图 |
op | 张量的操作名 |
name | 名称 |
shape | 形状 |
dtype | 元素类型 |
查看张量属性
# 创建多个图,指定图运行
import tensorflow as tf
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' # 调整警告级别
# a = tf.constant(5.0) # 定义常量a
# a = tf.constant([1,2,3])
a = tf.constant([[1,2,3],[4,5,6]])
with tf.Session() as sess:
print(sess.run(a)) # 执行计算
print("name:", a.name)
print("dtype:", a.dtype)
print("shape:", a.shape)
print("op:", a.op)
print("graph:", a.graph)
生成张量
# 创建张量操作
import tensorflow as tf
# 生成值全为0的张量
tensor_zeros = tf.zeros(shape=[2, 3], dtype="float32")
# 生成值全为1的张量
tensor_ones = tf.ones(shape=[2, 3], dtype="float32")
# 创建正态分布张量
tensor_nd = tf.random_normal(shape=[10],
mean=1.7,
stddev=0.2,
dtype="float32")
# 生成和输入张量形状一样的张量,值全为1
tensor_zeros_like = tf.zeros_like(tensor_ones)
with tf.Session() as sess:
print(tensor_zeros.eval()) # eval表示在session中计算该张量
print(tensor_ones.eval())
print(tensor_nd.eval())
print(tensor_zeros_like.eval())
张量类型转换
函数名称 | 说明 |
---|---|
tf.string_to_number(string_tensor) | 字符串转换为数字 |
tf.to_double(x) | 转换为64位浮点型 |
tf.to_float(x) | 转换为32位浮点型 |
tf.to_int32(x) | 转换为32位整型 |
tf.to_int64(x) | 转换为64位整型 |
tf.cast(x, dtype) | 将x转换为dtype所指定的类型 |
张量类型转换
# 张量类型转换
import tensorflow as tf
tensor_ones = tf.ones(shape=[2, 3], dtype="int32")
tensor_float = tf.constant([1.1, 2.2, 3.3])
with tf.Session() as sess:
print(tf.cast(tensor_ones, tf.float32).eval())
# print(tf.cast(tensor_float, tf.string).eval()) #不支持浮点数到字符串直接转换
占位符
- 不确定张量内容情况下,可以使用占位符先占个位置,然后执行计算时,通过参数传入具体数据执行计算(通过feed_dict参数指定)。placeholder节点被声明的时候是未初始化的, 也不包含数据,如果没有为它供给数据,则TensorFlow运算的时候会产生错误
# 占位符定义
name = placeholder(dtype, shape=None, name=None)
占位符使用
# 占位符示例
import tensorflow as tf
# 不确定数据,先使用占位符占个位置
plhd = tf.placeholder(tf.float32, [2, 3]) # 2行3列的tensor
plhd2 = tf.placeholder(tf.float32, [None, 3]) # N行3列的tensor
with tf.Session() as sess:
d = [[1, 2, 3],
[4, 5, 6]]
print(sess.run(plhd, feed_dict={plhd: d}))
print("shape:", plhd.shape)
print("name:", plhd.name)
print("graph:", plhd.graph)
print("op:", plhd.op)
print(sess.run(plhd2, feed_dict={plhd2: d}))
张量形状改变
- 静态形状:在创建一个张量,初始状态的形状
- tf.Tensor.get_shape():获取Tensor对象的静态形状
- tf.Tensor.set_shape():更新Tensor对象的静态形状
- 注意:转换静态形状的时候,1-D到1-D,2-D到2-D,不能跨阶数改变形状;对于已经固定或者设置静态形状的张量/变量,不能再次设置静态形状
- 动态形状:在运行图时,动态形状才是真正用到的,这种形状是一种描述原始张量在执行过程中的一种张量
- tf.reshape(tf.Tensor, shape) :创建一个具有不同动态形状的新张量,可以跨纬度转换,如1D-->2D, 1D-->3D
修改张量形状
# 改变张量形状示例(重点)
import tensorflow as tf
pld = tf.placeholder(tf.float32, [None, 3])
print(pld)
pld.set_shape([4, 3])
print(pld)
# pld.set_shape([3, 3]) #报错,静态形状一旦固定就不能再设置静态形状
# 动态形状可以创建一个新的张量,改变时候一定要注意元素的数量要匹配
new_pld = tf.reshape(pld, [3, 4])
print(new_pld)
# new_pld = tf.reshape(pld, [2, 4]) # 报错,元素的数量不匹配
with tf.Session() as sess:
pass
张量数学计算
函数名称 | 说明 |
---|---|
tf.add(x, y) | 张量相加 |
tf.matmul(x, y) | 张量相乘 |
tf.log(x) | 求张量的自然对数 |
tf.reduce_sum(x, axis) | 计算张量指定维度上的总和 |
tf.segment_sum(data, segment_ids) | 计算张量片段总和 |
- 矩阵乘法说明
- 当矩阵A的列数(column)等于矩阵B的行数(row)时,A与B可以相乘
- 矩阵C的行数等于矩阵A的行数,C的列数等于B的列数
- 乘积C的第m行第n列的元素等于矩阵A的第m行的元素与矩阵B的第n列对应元素乘积之和
$$C=AB=\begin{pmatrix}1 & 2 & 3 \\4 & 5 & 6\end{pmatrix}\begin{pmatrix}1 & 4 \\2 & 5 \\3 & 6\end{pmatrix}=\begin{pmatrix}1*1+2*2+3*3 & 1*4+2*5+3*6 \\4*1+5*2+6*3 & 4*4+5*5+6*6\end{pmatrix}=\begin{pmatrix}14 & 32\\32 & 77\end{pmatrix}$$
张量数学计算
# 数学计算示例
import tensorflow as tf
x = tf.constant([[1, 2], [3, 4]], dtype=tf.float32)
y = tf.constant([[4, 3], [3, 2]], dtype=tf.float32)
x_add_y = tf.add(x, y) # 张量相加
x_mul_y = tf.matmul(x, y) # 张量相乘
log_x = tf.log(x) # log(x)
# reduce_sum: 此函数计算一个张量的各个维度上元素的总和
x_sum_1 = tf.reduce_sum(x, axis=[1]) #0-列方向 1-行方向
# segment_sum: 沿张量的片段计算总和
# 函数返回的是一个Tensor,它与data有相同的类型,与data具有相同的形状
# 但大小为 k(段的数目)的维度0除外
data = tf.constant([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], dtype=tf.float32)
segment_ids = tf.constant([0, 0, 0, 1, 1, 2, 2, 2, 2, 2], dtype=tf.int32)
x_seg_sum = tf.segment_sum(data, segment_ids) # [6, 9, 40]
with tf.Session() as sess:
print(x_add_y.eval())
print(x_mul_y.eval())
print(log_x.eval())
print(x_sum_1.eval())
print(x_seg_sum.eval())
变量
- 变量是一种op,它的值是张量
- 变量能够持久化保存,普通张量则不可
- 当定义一个变量时,需要在会话中进行初始化
- 变量创建
tf.Variable(initial_value=None, name=None)
变量使用
# 变量OP示例
import tensorflow as tf
# 创建普通张量
a = tf.constant([1, 2, 3, 4, 5])
# 创建变量
var = tf.Variable(tf.random_normal([2, 3], mean=0.0, stddev=1.0),
name="variable")
# 变量必须显式初始化, 这里定义的是初始化操作,并没有运行
init_op = tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init_op)
print(sess.run([a, var]))
Tensorboard可视化
Tensorboard工具
什么是可视化
- 可视化是用来查看在Tensorflow平台下程序运行的过程,包括:张量/变量,操作,数据流,学习过程等,从而方便TensorFlow程序的理解、调试与优化
- Tensorflow提供了专门的可视化工具tensorboard,它将tensorflow执行的数据、模型、过程用图形方式进行显示。tensorflow在执行过程中,可以通过某些操作,将模型、数据、graph等信息,保存到磁盘中的Events文件中去,从而提供给tensorboard进行可视化
启动tensorboard
-
使用以下命令启动tensorboard
tensorboard --logdir="PycharmProjects/tensorflow_study/summary/"
-
其中,logdir参数的值为事件文件存储目录,启动成功后可以看到如下信息,使用提示的URL地址和端口进行访问:
tensorboard主页说明
为操作添加可视化
# 变量OP示例
import tensorflow as tf
''' 变量OP
1. 变量OP能够持久化保存,普通张量则不可
2. 当定义一个变量OP时,在会话中进行初始化
3. name参数:在tensorboard使用的时候显示名字,可以让相同的OP进行区分
'''
# 创建普通张量
a = tf.constant([1, 2, 3, 4, 5])
# 创建变量
var = tf.Variable(tf.random_normal([2, 3], mean=0.0, stddev=1.0),
name="variable")
b = tf.constant(3.0, name="a")
c = tf.constant(4.0, name="b")
d = tf.add(b, c, name="add")
# 变量必须显式初始化, 这里定义的是初始化操作,并没有运行
init_op = tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init_op)
# 将程序图结构写入事件文件
fw = tf.summary.FileWriter("../summary/", graph=sess.graph)
print(sess.run([a, var]))
- 注:张量如果未使用默认情况下不显示
摘要与事件文件操作
- 如果需要将变量/张量在tensorboard中显示,需要执行以下两步:
- 收集变量
tf.summary.scalar(name, tensor) # 收集标量,name为名字,tensor为值
tf.summary.histogram(name, tensor) # 收集高维度变量参数
tf.summary.image(name, tensor) # 收集图片张量
- 合并变量并写入事件文件
merged = tf.summary.merge_all() # 合并所有变量
summary = sess.run(merged) # 运行合并,每次迭代训练都需要运行
FileWriter.add_summary(summary, i) # 添加摘要,i表示第几次的值
综合案例:实现线性回归
- 给定一组输入、输出作为样本
- 定义线性模型,并进行训练
- 将训练过程可视化
# 线性回归示例
import tensorflow as tf
# 第一步:创建数据
x = tf.random_normal([100, 1], mean=1.75, stddev=0.5, name="x_data")
y_true = tf.matmul(x, [[2.0]]) + 5.0 # 矩阵相乘必须是二维的
# 第二步:建立线性回归模型
# 建立模型时,随机建立权重、偏置 y = wx + b
# 权重需要不断更新,所以必须是变量类型. trainable指定该变量是否能随梯度下降一起变化
weight = tf.Variable(tf.random_normal([1, 1], name="w"),
trainable=True) # 训练过程中值是否允许变化
bias = tf.Variable(0.0, name="b", trainable=True) # 偏置
y_predict = tf.matmul(x, weight) + bias # 计算 wx + b
# # 第三步:求损失函数,误差(均方差)
loss = tf.reduce_mean(tf.square(y_true - y_predict))
# # 第四步:使用梯度下降法优化损失
# 学习率是比价敏感的参数,过小会导致收敛慢,过大可能导致梯度爆炸
train_op = tf.train.GradientDescentOptimizer(0.1).minimize(loss)
#### 收集损失值
tf.summary.scalar("losses", loss)
merged = tf.summary.merge_all() #将所有的摘要信息保存到磁盘
init_op = tf.global_variables_initializer()
with tf.Session() as sess: # 通过Session运行op
sess.run(init_op)
# 打印初始权重、偏移值
print("weight:", weight.eval(), " bias:", bias.eval())
#### 指定事件文件
fw = tf.summary.FileWriter("../summary/", graph=sess.graph)
for i in range(500): # 循环执行训练
sess.run(train_op) # 执行训练
summary = sess.run(merged) # 运行合并摘要op
fw.add_summary(summary, i) # 写入文件
print(i, ":", i, "weight:", weight.eval(), " bias:", bias.eval())
模型保存与加载
模型保存与加载
什么是模型保存与加载
- 模型训练可能是一个很长的过程,如果每次执行预测之前都重新训练,会非常耗时,所以几乎所有人工智能框架都提供了模型保存与加载功能,使得模型训练完成后,可以保存到文件中,供其它程序使用或继续训练
模型保存与加载API
- 模型保存与加载通过tf.train.Saver对象完成,实例化对象:
saver = tf.train.Saver(var_list=None, max_to_keep=5)
# var_list: 要保存和还原的变量,可以是一个dict或一个列表
# max_to_keep: 要保留的最近检查点文件的最大数量。创建新文件时,会删除较旧的文件(如max_to_keep=5表示保留5个检查点文件)
- 保存:
saver.save(sess, '/tmp/ckpt/model')
- 加载:
saver.restore(sess, '/tmp/ckpt/model')
模型保存/加载
# 模型保存示例
import tensorflow as tf
import os
# 第一步:创建数据
x = tf.random_normal([100, 1], mean=1.75, stddev=0.5, name="x_data")
y_true = tf.matmul(x, [[2.0]]) + 5.0 # 矩阵相乘必须是二维的
# 第二步:建立线性回归模型
# 建立模型时,随机建立权重、偏置 y = wx + b
# 权重需要不断更新,所以必须是变量类型. trainable指定该变量是否能随梯度下降一起变化
weight = tf.Variable(tf.random_normal([1, 1], name="w"),
trainable=True) # 训练过程中值是否允许变化
bias = tf.Variable(0.0, name="b", trainable=True) # 偏置
y_predict = tf.matmul(x, weight) + bias # 计算 wx + b
# # 第三步:求损失函数,误差(均方差)
loss = tf.reduce_mean(tf.square(y_true - y_predict))
# # 第四步:使用梯度下降法优化损失
# 学习率是比价敏感的参数,过小会导致收敛慢,过大可能导致梯度爆炸
train_op = tf.train.GradientDescentOptimizer(0.1).minimize(loss)
# 收集损失值
tf.summary.scalar("losses", loss)
merged = tf.summary.merge_all() #将所有的摘要信息保存到磁盘
init_op = tf.global_variables_initializer()
# 保存与加载
saver = tf.train.Saver() #实例化Saver
with tf.Session() as sess: # 通过Session运行op
sess.run(init_op)
print("weight:", weight.eval(), " bias:", bias.eval()) # 打印初始权重、偏移值
fw = tf.summary.FileWriter("../summary/", graph=sess.graph) # 指定事件文件
# 保存与加载
# 训练之前,加载之前训练的模型,覆盖之前的参数
if os.path.exists("../model/linear_model/checkpoint"):
saver.restore(sess, "../model/linear_model/")
for i in range(500): # 循环执行训练
sess.run(train_op) # 执行训练
summary = sess.run(merged) # 运行合并后的tensor
fw.add_summary(summary, i)
print(i, ":", i, "weight:", weight.eval(), " bias:", bias.eval())
# 保存与加载
saver.save(sess, "../model/linear_model/")
- 从执行结果可以看出,如果模型之前经过训练,直接从之前的参数值开始执行迭代,而不是从第一次给的初始值开始
数据读取
数据读取
文件读取机制
- TensorFlow文件读取分为三个步骤:
- 将要读取的文件放入文件名队列
- 读取文件内容,并实行解码
- 批处理,按照指定笔数构建成一个批次取出
- 文件队列构造:生成一个先入先出的队列,文件阅读器会需要它来读取数据
file_queue = tf.train.string_input_producer(string_tensor, shuffle=True)
# string_tensor: 含有文件名的一阶张量
# shuffle: 是否打乱文件顺序
# return:文件队列
- 文件读取
# 文本文件读取
# 读取CSV文件,默认按行读取
tf.TextLineReader()
# 二进制文件读取
# 读取每个记录是固定字节的二进制文件
tf.FixedLengthRecordReader(record_bytes)
# record_bytes: 每次读取的字节数
# 通用读取方法
# 从队列中读取指定数量(行,字节)的内容
read(file_queue)
# 返回值:一个tensor元组(filename, value)
- 文件内容解码
# 解码文本文件
# 将CSV文件内容转换为张量,与tf.TextLineReader搭配使用
tf.decode_csv(records, record_defaults)
# records: 字符串,对应文件中的一行
# record_defaults: 类型
# 返回值:tensor对象列表
# 解码二进制文件
# 将字节转换为由数字表示的张量,与tf.FixedLengthRecordReader搭配使用
tf.decode_raw(input_bytes, out_type)
# input_bytes: 待转换字节
# out_type: 输出类型
# 返回值:转换结果
CSV文件读取
# csv文件读取示例
import tensorflow as tf
import os
def csv_read(filelist):
# 2. 构建文件队列
file_queue = tf.train.string_input_producer(filelist)
# 3. 构建csv reader,读取队列内容(一行)
reader = tf.TextLineReader()
k, v = reader.read(file_queue)
# 4. 对每行内容进行解码
## record_defaults:指定每一个样本每一列的类型,指定默认值
records = [["None"], ["None"]]
example, label = tf.decode_csv(v, record_defaults=records) # 每行两个值
# 5. 批处理
# batch_size: 跟队列大小无关,只决定本批次取多少数据
example_bat, label_bat = tf.train.batch([example, label],batch_size=9,num_threads=1,capacity=9)
return example_bat, label_bat
if __name__ == "__main__":
# 1. 找到文件,构造一个列表
dir_name = "./test_data/"
file_names = os.listdir(dir_name)
file_list = []
for f in file_names:
file_list.append(os.path.join(dir_name, f)) # 拼接目录和文件名
example, label = csv_read(file_list)
# 开启session运行结果
with tf.Session() as sess:
coord = tf.train.Coordinator() # 定义线程协调器
# 开启读取文件线程
# 调用 tf.train.start_queue_runners 之后,才会真正把tensor推入内存序列中
# 供计算单元调用,否则会由于内存序列为空,数据流图会处于一直等待状态
# 返回一组线程
threads = tf.train.start_queue_runners(sess, coord=coord)
print(sess.run([example, label])) # 打印读取的内容
# 回收线程
coord.request_stop()
coord.join(threads)
图片文件读取API
# 图像读取器
# 将文件的全部内容作为值输出的reader
reader = tf.WholeFileReader()
# read():读取文件内容,返回文件名和文件内容
k,v = reader.read(file_queue)#k 文件名 v 数据
# 图像解码器
# 解码jpeg格式
tf.image.decode_jpeg(constants)
#返回值:3-D张量[height, width, channels]
# 解码png格式
tf.image.decode_png(constants)
#返回值:3-D张量[height, width, channels]
# 修改图像大小
tf.image.resize(images, size)
# images:图片数据,3-D或4-D张量
# 3-D:[长,宽,通道]
# 4-D:[数量, 长,宽,通道]
# size:1-D int32张量,[长、宽] (不需要传通道数)
图片文件读取
# 图片文件读取示例
import tensorflow as tf
import os
def img_read(filelist):
# 1. 构建文件队列
file_queue = tf.train.string_input_producer(filelist)
# 2. 构建reader读取文件内容,默认读取一张图片
reader = tf.WholeFileReader()
k, v = reader.read(file_queue)
# 3. 对图片数据进行解码
img = tf.image.decode_jpeg(v)
# 4. 批处理, 图片需要处理成统一大小
img_resized = tf.image.resize(img, [200, 200]) # 200*200
img_resized.set_shape([200, 200, 3]) # 固定样本形状,批处理时对数据形状有要求
img_bat = tf.train.batch([img_resized],batch_size=10,num_threads=1)
return img_bat
if __name__ == "__main__":
# 1. 找到文件,构造一个列表
dir_name = "../data/test_img/"
file_names = os.listdir(dir_name)
file_list = []
for f in file_names:
file_list.append(os.path.join(dir_name, f)) # 拼接目录和文件名
imgs = img_read(file_list)
# 开启session运行结果
with tf.Session() as sess:
coord = tf.train.Coordinator() # 定义线程协调器
# 开启读取文件线程
# 调用 tf.train.start_queue_runners 之后,才会真正把tensor推入内存序列中
# 供计算单元调用,否则会由于内存序列为空,数据流图会处于一直等待状态
# 返回一组线程
threads = tf.train.start_queue_runners(sess, coord=coord)
# print(sess.run([imgs])) # 打印读取的内容
imgs = imgs.eval()
# 回收线程
coord.request_stop()
coord.join(threads)
## 显示图片
print(imgs.shape)
import matplotlib.pyplot as plt
plt.figure("Img Show", facecolor="lightgray")
for i in range(10):
plt.subplot(2, 5, i+1)
plt.xticks([])
plt.yticks([])
plt.imshow(imgs[i].astype("int32"))
plt.tight_layout()
plt.show()
图像识别
手写体识别
MNIST数据集
- 手写数字的数据集,来自美国国家标准与技术研究所(National Institute of Standards and Technology,NIST),发布与1998年
- 样本来自250个不同人的手写数字,50%高中学生,50%是人口普查局的工作人员
- 数字从0~9,图片大小是28×28像素,训练数据集包含60000个样本,测试数据集包含10000个样本。数据集的标签是长度为 10 的一维数组,数组中每个元素索引号表示对应数字出现的概率
- 下载地址:http://yann.lecun.com/exdb/mnist/
网络结构
相关API
- tf.matmul():执行矩阵乘法计算
- tf.nn.softmax():softmax激活函数
- tf.reduce_sum():指定维度上求张量和
- tf.train.GradientDescentOptimizer():优化器,执行梯度下降
- tf.argmax():返回张量最大元素的索引值
# 手写体识别
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
import pylab
# 读入数据集(如果没有则在线下载),并转换成独热编码
# 如果不能下载,则到http://yann.lecun.com/exdb/mnist/进行手工下载,下载后拷贝到当前MNIST_data目录下
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
x = tf.placeholder(tf.float32, [None, 784]) # 占位符,输入
y = tf.placeholder(tf.float32, [None, 10]) # 占位符,输出
# 28*28的图片一共有784列
W = tf.Variable(tf.random_normal([784, 10])) # 权重
b = tf.Variable(tf.zeros([10])) # 偏置值
# 构建模型
pred_y = tf.nn.softmax(tf.matmul(x, W) + b) # softmax分类
print("pred_y.shape:", pred_y.shape)
# 损失函数
# reduction_indices=1 水平方向
cross_entropy = -tf.reduce_sum(y * tf.log(pred_y),reduction_indices=1) # 求交叉熵
cost = tf.reduce_mean(cross_entropy) # 求损失函数平均值
# 参数设置
lr = 0.01
# 梯度下降优化器
optimizer = tf.train.GradientDescentOptimizer(lr).minimize(cost)
training_epochs = 200
batch_size = 100
saver = tf.train.Saver()
model_path = "../model/mnist/mnist_model.ckpt" # 模型路径
# 启动session
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
# 循环开始训练
for epoch in range(training_epochs):
avg_cost = 0.0
total_batch = int(mnist.train.num_examples / batch_size) # 计算总批次
# 遍历全数据集
for i in range(total_batch):
batch_xs, batch_ys = mnist.train.next_batch(batch_size) # 读取一个批次样本
params = {x: batch_xs, y: batch_ys} # 训练参数
o, c = sess.run([optimizer, cost], feed_dict=params) # 执行训练
avg_cost += (c / total_batch) # 求平均损失值
print("epoch: %d, cost=%.9f" % (epoch + 1, avg_cost))
print("Finished!")
# 模型评估
correct_pred = tf.equal(tf.argmax(pred_y, 1), tf.argmax(y, 1))
# 计算准确率
accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))
print("accuracy:", accuracy.eval({x: mnist.test.images,
y: mnist.test.labels}))
# 将模型保存到文件
save_path = saver.save(sess, model_path)
print("Model saved:", save_path)
# 测试模型
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
saver.restore(sess, model_path) # 加载模型
batch_xs, batch_ys = mnist.test.next_batch(2) # 读取2个测试样本
output = tf.argmax(pred_y, 1) # 预测结果值
output_val, predv = sess.run([output, pred_y], # 操作
feed_dict={x: batch_xs}) # 参数
print("预测结论:\n", output_val, "\n")
print("实际结果:\n", batch_ys, "\n")
print("预测概率:\n", predv, "\n")
# 显示图片
im = batch_xs[0] # 第1个测试样本数据
im = im.reshape(-1, 28)
pylab.imshow(im)
pylab.show()
im = batch_xs[1] # 第2个测试样本数据
im = im.reshape(-1, 28)
pylab.imshow(im)
pylab.show()
输出结果
服饰识别
数据集介绍
- 是来自 Zalando 文章的数据集,是时尚版的 MNIST。包括 60,000 个训练集数据,10,000 个测试集数据, 每个数据为 28x28 灰度图像,一共有 10 类
- fashion_mnist.zip
序号 | 名称 | 对应结果 |
---|---|---|
0 | T-shirt/top | T恤 |
1 | Trouser | 裤子 |
2 | Pullover | 套衫 |
3 | Dress | 衣服 |
4 | Coat | 外套 |
5 | Sandal | 凉鞋 |
6 | Shirt | 衬衫 |
7 | Sneaker | 运动鞋 |
8 | Bag | 包 |
9 | Ankle boot | 短靴 |
网络结构
import tensorflow as tf
from tensorflow.contrib.learn.python.learn.datasets.mnist import read_data_sets
class FashionMnist():
flt_num1 = 12 # 第一个卷积层卷积核数量
flt_num2 = 24 # 第一个卷积层卷积核数量
con_neurons = 512 # 第一个fc层神经元数量
def __init__(self, path):
"""
构造方法
:param path: 数据集目录
"""
self.sess = tf.Session() # Session对象
self.data = read_data_sets(
path, # 数据库路径
one_hot=True) # 标签采用独热编码 [0 0 0 0 1 0 0 0]只有1个参数为1的数据
def init_weight(self, shape):
"""
初始化权重(初始化成随机值)
:param shape: 指定权重矩阵/张量形状
:return: 经过初始化的权重张量
"""
inital = tf.truncated_normal( # 截尾正态分布
shape, # 形状
stddev=0.1) # 标准差
return tf.Variable(inital)
def init_bias(self, shape):
"""
初始化偏置(初始化成小常数)
:param shape: 形状
:return: 经过初始化的偏置张量
"""
inital = tf.constant(1.0, shape=shape)
return tf.Variable(inital)
def conv2d(self, x, w):
"""
卷积运算
:param x: 输入
:param w: 卷积核
:return: 卷积运算结果
"""
# 卷积核:[h,w,in_channels,output_channels]
return tf.nn.conv2d(
x, # 输入
w, # 卷积核
strides=[1, 1, 1, 1], # 四个维度上的步长
padding="SAME") # 填充,输出和输入矩阵一样大
def max_pool_2x2(self, x):
"""
池化计算
:param x: 输入
:return: 返回池化计算结果
"""
return tf.nn.max_pool(
x, # 输入
ksize=[1, 2, 2, 1], # 池化区域大小
strides=[1, 2, 2, 1], # 步长
padding="SAME")
def create_conv_pool(self, x,
in_chn, out_chn):
"""
卷积、激活、池化计算
:param x: 输入
:param in_chn: 输入张量的通道数量
:param out_chn: 输出张量的通道数量
:return: 返回卷积、激活、池化计算结果
"""
w = self.init_weight(
[5, 5, in_chn, out_chn]) # 初始化卷积核
b = self.init_bias([out_chn]) # 偏置
# 卷积、激活运算
h_conv = tf.nn.relu(self.conv2d(x, w) + b)
# 池化运算
h_pool = self.max_pool_2x2(h_conv)
return h_pool
def create_fc(self, x,
in_features, out_features):
"""
全连接层
:param x: 输入
:param in_features: 输入特征数量
:param out_features: 输出特征数量
:return: 全连接计算结果
"""
w = self.init_weight(
[in_features, out_features]) # 权重
b = self.init_bias([out_features]) # 偏置
h_fc = tf.nn.relu(
tf.matmul(x, w) + b) # 计算xw + b
return h_fc
def build(self):
"""
组建CNN
:return: 返回模型结算结果(预测值)
"""
self.x = tf.placeholder(tf.float32, shape=[None, 784])
# 读取到的数据是一个二维数组,单个图片数据为[1,784]要转化为28*28的图片进行卷积运算所以需要变维
# -1代表自适应 因为不确定的样本数量 28代表张量的高度(行数) 28代表张量的宽度(列数) 1表示通道数为1
x_image = tf.reshape(self.x,[-1, 28, 28, 1]) # 变维 [样本数量 -1代表自适应,宽,高,通道数为1]
self.y = tf.placeholder(tf.float32, shape=[None, 10])
# 第一层卷积池化
h_pool1 = self.create_conv_pool(
x_image, # 输入:图像样本
1, # 输入通道数
self.flt_num1) # 输出通道数(等于卷积核数量)
# 第二层卷积池化
h_pool2 = self.create_conv_pool(
h_pool1, # 输入:上层输出结果
self.flt_num1, # 输入通道数(上层输出通道数)
self.flt_num2) # 输出通道数(等于本层卷积核数量)
# 第一个fc
featues_num = 7 * 7 * self.flt_num2 # 特征值数量
# 经过卷积处理后的特征图,进入全连接层之前,需要把数据转化为一维向量,即每张图片为[1,1176]
h_flat = tf.reshape(
h_pool2, # 要变维的张量
[-1, featues_num]) # 形状 变维成二位数组 n个1维数组 每个一位数组是一张图片的特征值
# 第一个fc
h_fc1 = self.create_fc(
h_flat, # 输入:扁平化处理的张量
featues_num, # 输入特征数量
self.con_neurons) # 输出特征数量(这层神经元的数量)
# dropout层 (丢弃学习 随机丢弃部分参数防止过拟合)
self.keep_prob = tf.placeholder("float") # 保持率
h_drop = tf.nn.dropout(h_fc1, self.keep_prob)
# fc层(输出层)
w = self.init_weight([self.con_neurons, 10]) # 权重
b = self.init_bias([10]) # 偏置
y_conv = tf.matmul(h_drop, w) + b # wx+b
# 损失函数
cross_entroy = tf.nn.softmax_cross_entropy_with_logits(
labels=self.y, # 标签
logits=y_conv) # 预测值
# 求均值
avg_cost = tf.reduce_mean(cross_entroy)
# 优化器
optimizer = tf.train.AdamOptimizer(0.001)
# 指定优化的目标函数
self.train_step = optimizer.minimize(avg_cost)
# # 改动代码
# # 尝试使用均方误差替换交叉熵计算准确率
# mes_loss = tf.reduce_mean(tf.square(self.y - y_conv))
# # 梯度下降
# train_op = tf.train.AdamOptimizer(0.001)
# # 指定优化的目标函数
# self.train_step = train_op.minimize(mes_loss)
# 计算准确率
correct_pred = tf.equal(
tf.argmax(y_conv, 1), # 返回预测结果中概率最大的索引
tf.argmax(self.y, 1)) # 标签值中概率最大的索引
# 计算准确率
self.accurcy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))
# 训练
def train(self):
# 变量初始化
self.sess.run(tf.global_variables_initializer())
batch_size = 100 # 批次大小(每次从样本集中随机读取100个样本)
print('train start')
# mnist.train.num_examples 获取训练集的数量
# mnist.test.num_examples 获取测试集的数量
# 获取训练集并且分次进行训练
# print("样本数:%d"%self.data.train.num_examples) 55000个样本
total = int(self.data.train.num_examples / batch_size)
for epoch in range(20): # 外层循环控制轮次 一共训练20次
for batch in range(total):
# 读取批次样本
xs, ys = self.data.train.next_batch(batch_size)
# 参数字典
params = {self.x: xs, # 输入
self.y: ys, # 标签
self.keep_prob: 0.5} # 保持率
# 训练(执行对损失函数最小化的过程)
t, acc = self.sess.run([self.train_step, self.accurcy], # 执行的操作
feed_dict=params) # 喂入的参数
# 打印
if batch % 100 == 0:
print("epoch:%d,batch:%d,acc:%f" % (epoch, batch, acc))
print('train end')
def eval(self, x, y, keep_prob):
"""
测试数据进行准确率评估
:param x: 输入
:param y: 标签
:param keep_prob: 保持率
:return: 准确率
"""
params = {self.x: x, # 输入
self.y: y, # 标签
self.keep_prob: 1} # 保持率
test_acc = self.sess.run([self.accurcy], # 执行准确率的计算
feed_dict=params) # 喂入的参数
print("测试集准确率:",test_acc)
return test_acc
def close(self):
self.sess.close()
if __name__ == '__main__':
f_mnist = FashionMnist("fashion_mnist/") # 实例化对象
f_mnist.build() # 组建CNN
f_mnist.train() # 训练
print("\n--------------\n")
xs, ys = f_mnist.data.test.next_batch(100) # 读取测试样本
acc = f_mnist.eval(xs, ys, 1.0) # 测试时保持率为1.0
f_mnist.close()
0 条评论