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

  • 下载离线包:https://pypi.org/project/tensorflow/#files执行安装

  • 修改源进行安装。如果安装包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()

QQ截图20230714144544.jpg

张量相加

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))

QQ截图20230714144807.jpg

Tensorflow体系结构

体系结构概述

  • TensorFlow属于“定义”与“运行”相分离的运行机制。从操作层面可以抽象成两种:模型构建和模型运行
  1. 客户端:用户编程、执行使用
  2. master:用来与客户端交互,并进行任务调度
  3. worker process:工作节点,每个worker process可以访问一到多个device
  4. device:TF的计算核心,执行计算

单机模式与分布式模式

  • TF的实现分为“单机实现”和“分布式实现”

QQ截图20230714150733.jpg

后端逻辑层次

QQ截图20230714150749.jpg

基本概念

张量

  • 张量(Tensor): 多维数组或向量,张量是数据的载体,包含名字、形状、数据类型等属性

QQ截图20230714150824.jpg

数据流

  • 数据流图(Data Flow Graph)用“结点”(nodes)和“线”(edges)的有向图来描述数学计算
  • “节点” 一般用来表示数学操作,也可以表示数据输入(feed in)的起点/输出(push out)的终点,或者是读取/写入持久变量(persistent variable)的终点
  • “线”表示“节点”之间的输入/输出关系。这些数据“线”可以输运多维数据数组,即“张量”(tensor)
  • 一旦输入端的所有张量准备好,节点将被分配到各种计算设备完成异步并行地执行运算

QQ截图20230714150901.jpg

操作

  • 操作(Operation,简称op)指专门执行计算的节点,tensorflow函数或API定义的都是操作。常用操作包括:
  1. 标量运算,向量运算,矩阵运算
  2. 带状态的运算
  3. 神经网络组建
  4. 存储、恢复
  5. 控制流
  6. 队列及同步运算

图和会话

  • 图(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)用来执行图中的计算,并且保存了计算张量对象的上下文信息。会话的作用主要有:
  1. 运行图结构
  2. 分配资源
  3. 掌握资源(如变量、队列、线程)
  • 一个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()方法时,可能会出现的错误及原因
  1. RuntimeError:Session处于无效(如关闭)
  2. TypeError:fetches或feed_dict的键是不合适的值
  3. ValueError:fetches或feed_dict的键无效或引用的值不存在

张量及基本运算

张量的阶与形状

  • 阶:张量的维度(数方括号的层数)
  1. 0维:( )
  2. 1维:(5),1行5个元素
  3. 2维:(2,3),2行3列
  4. 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}))

张量形状改变

  • 静态形状:在创建一个张量,初始状态的形状
  1. tf.Tensor.get_shape():获取Tensor对象的静态形状
  2. tf.Tensor.set_shape():更新Tensor对象的静态形状
  • 注意:转换静态形状的时候,1-D到1-D,2-D到2-D,不能跨阶数改变形状;对于已经固定或者设置静态形状的张量/变量,不能再次设置静态形状
  • 动态形状:在运行图时,动态形状才是真正用到的,这种形状是一种描述原始张量在执行过程中的一种张量
  1. 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) 计算张量片段总和
  • 矩阵乘法说明
  1. 当矩阵A的列数(column)等于矩阵B的行数(row)时,A与B可以相乘
  2. 矩阵C的行数等于矩阵A的行数,C的列数等于B的列数
  3. 乘积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

  • 使用以下命令启动tensorboardtensorboard --logdir="PycharmProjects/tensorflow_study/summary/"

  • 其中,logdir参数的值为事件文件存储目录,启动成功后可以看到如下信息,使用提示的URL地址和端口进行访问:

QQ截图20230719104124.jpg

tensorboard主页说明

QQ截图20230719104147.jpg

为操作添加可视化

# 变量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]))
  • 注:张量如果未使用默认情况下不显示

QQ截图20230719104300.jpg

摘要与事件文件操作

  • 如果需要将变量/张量在tensorboard中显示,需要执行以下两步:
  1. 收集变量
tf.summary.scalar(name, tensor) # 收集标量,name为名字,tensor为值
tf.summary.histogram(name, tensor) # 收集高维度变量参数
tf.summary.image(name, tensor) # 收集图片张量
  1. 合并变量并写入事件文件
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/")
  • 从执行结果可以看出,如果模型之前经过训练,直接从之前的参数值开始执行迭代,而不是从第一次给的初始值开始

QQ截图20230719143526.jpg

数据读取

数据读取

文件读取机制

  • TensorFlow文件读取分为三个步骤:
  1. 将要读取的文件放入文件名队列
  2. 读取文件内容,并实行解码
  3. 批处理,按照指定笔数构建成一个批次取出

QQ截图20230719143935.jpg

  • 文件队列构造:生成一个先入先出的队列,文件阅读器会需要它来读取数据
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()

图像识别

手写体识别

QQ截图20230719150915.jpg

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/

网络结构

QQ截图20230719150211.jpg

相关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()

输出结果

QQ截图20230719150751.jpg

服饰识别

数据集介绍

QQ截图20230719150953.jpg

  • 是来自 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 短靴

网络结构

8676d88e96d828be96018f4ca3da929.png

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()