三天入门深度学习

1
2
3
4
5
6
7
8
9
10
11
12
TensorFlow框架使用 1天
数据读取、神经网络基础 1天
卷积神经网络、验证码识别 1天
1、深度学习介绍
2、TensorFlow框架的使用
1)TensorFlow的结构
2)TensorFlow的各个组件

会话
张量
变量
3)简单的线性回归案例 - 将TensorFlow用起来

1.1 深度学习与机器学习的区别

1.1.1 特征提取方面

  • 机器学习的特征工程步骤是需要靠手动完成的 需要大量领域专业知识
  • 深度学习通常由多个层组成 通常将更简单的模型组合在一起 将数据从一层传递到另一层来构建更复杂的模型 通过训练大量的数据自动得出的模型 不需要人工特征提取环节

深度学习算法试图从数据中学习高级功能,这是深度学习的一个非常独特的部分 因此 减少了每个问题开发新特征提取器的任务。适合用在难提取特征的图像、语音、自然语言处理领域。

1.1.2 数据量和计算性能要求

机器学习需要的执行时间远少于深度学习 深度学习参数往往很庞大 需要通过大量数据的多次优化来训练参数。

第一,深度学习需要大量的训练数据集

第二,训练深度神经网络需要大量的算力

可能需要花费数天,数周 才能训练出一个深度网络

  • 需要强大的GPU
  • 全面管理的分布式训练与预测服务——比如谷歌tensorflow云机器学习平台

1.1.3 算法代表

  • 机器学习
    • 朴素贝叶斯 决策树
  • 深度学习
    • 神经网络

1.2 深度学习的应用场景

  • 图像识别

    • 物体识别
    • 场景识别
    • 车型识别
    • 人脸检测跟踪
    • 人脸关键点定位
    • 人脸身份认证
  • 自然语言处理技术

    • 机器翻译
    • 文本识别
    • 聊天对话
  • 语音技术

    • 语音识别

1.3 深度学习框架介绍

1.3.1 常见深度学习框架对比

snipaste20230722_162340

1.3.2 TensorFlow 的特点

官网:https://www.tensorflow.org/

  • 高度灵活
  • 语言多样
  • 设备支持
  • Tensorboard可视化

1.3.3 Tensorflow的安装

1 CPU版本

1
2
3
4
5
6
7
8
CPU:诸葛亮
综合能力比较强
核芯的数量更少
更适用于处理连续性(sequential)任务。
GPU:臭皮匠
专做某一个事情很好
核芯的数量更多
更适用于并行(parallel)任务

2 GPU版本

  • 很奇怪能用 但是后面的课程再看看

2.2 TF数据流图

2.2.1 案例:Tensorflow实现一个加法运算

1 代码

1
2
3
4
5
6
7
8
9
10
import tensorflow as tf
def tensorflow_demo():
#Tensorflow实现加法运算 图
a_t = tf.constant(2)
b_t = tf.constant(3)
c_t = a_t+b_t
print("结果为:\n",c_t)
return None
if __name__ == "__main__":
tensorflow_demo()

结果:

1
2
3
4
#开启会话 这样才能看到值 新版中已经没有这个功能了
with tf.Session() as sess:
c_t_value = sess.run(c_t)
print('c_t_value:\n',c_t_value)
1
2
3
import tensorflow._api.v2.compat.v1 as tf
tf.compat.v1.disable_eager_execution()
#变成这两句话 可以使用之前版本的语法 但得考虑一个问题就是后面神经网络有可能有问题

2 Tensorflow 结构分析

一个构建图的阶段 一个执行图的阶段

在构建阶段 数据与操作的执行步骤被描述成一个图

执行阶段 使用会话执行构建好图中的操作

  • 图和会话:
    • 图:这是Tensorflow将计算表示为指令之间的依赖关系的一种表示法 定义数据(张量)和操作
    • 会话: Tensorflow 跨一个或多个本地或远程设备运行数据流图的机制
  • 张量:Tensorflow中的基本数据对象
  • 节点(op):提供图当中执行的操作

2.1.2 数据流图介绍

2.2.1 什么是图结构

图包含了一组tf.Operation代表的计算单元对象和tf.Tensor代表的计算单元之间流动的数据。

1
2
图结构:
数据(Tensor) + 操作(Operation)

2.2.2 图相关操作

1 默认图

通常Tensorflow会默认创建一张图

1
2
3
4
5
6
1 默认图
查看默认图的方法
1)调用方法
用tf.get_default_graph()
2)查看属性
.graph

查看默认图的两种方法:

  • 通过调用tf.get_default_graph()访问 要将操作添加到默认图形中 直接创建OP即可
  • op/sess都有graph属性 默认都在一张图中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def graph_demo():
a_t = tf.constant(2)
b_t = tf.constant(3)
c_t = a_t + b_t
print("TensorFlow加法运算的结果:\n", c_t)
#查看默认图
#1)方法一:调用方法
default_g = tf.get_default_graph()
print("default_g:\n",default_g)
# 1)方法二:调用属性
print("a_t的属性:\n",a_t.graph)
print("c_t的属性:\n", c_t.graph)
with tf.Session() as sess:
c_t_value = sess.run(c_t)
print("c_t_value:\n", c_t_value)
print("sess_t的属性:\n", sess.graph)
return None
#打印出来的都是图的地址

2 创建图

1
2
3
new_g = tf.Graph()
with new_g.as_default():
定义数据和操作
  • 通过tf.Graph()
  • 如果要在这张图中创建OP tf.Graph.as_default()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
def graph_demo():
a_t = tf.constant(2)
b_t = tf.constant(3)
c_t = a_t + b_t
print("TensorFlow加法运算的结果:\n", c_t)
#查看默认图
#1)方法一:调用方法
default_g = tf.get_default_graph()
print("default_g:\n",default_g)
# 1)方法二:调用属性
print("a_t的属性:\n",a_t.graph)
print("c_t的属性:\n", c_t.graph)
with tf.Session() as sess:
c_t_value = sess.run(c_t)
print("c_t_value:\n", c_t_value)
print("sess_t的属性:\n", sess.graph)
# 自定义图
new_g = tf.Graph()
with new_g.as_default():
a_new = tf.constant(20)
b_new = tf.constant(30)
c_new = a_new + b_new
print("c_new:\n", c_new)
print("a_new的图属性:\n", a_new.graph)
print("c_new的图属性:\n", c_new.graph)
#开启新的会话
with tf.Session(graph=new_g) as new_sess:
c_new_value = new_sess.run(c_new)
print("c_new_value:\n", c_new_value)
print("new_sess的图属性:\n", new_sess.graph)
return None

Tensorflow可以看到写的程序可视化效果 就是Tensorboard

2.2.3 TensorBoard:可视化学习

Tensorflow可用于训练大规模深度神经网络所需的计算 使用往往涉及的计算复杂而深奥 为了更方便Tensorflow程序的理解 调试和优化 提供了可视化工具

1 数据序列化-events文件

Tensorboard 通过读取Tensorflow的事件文件来运行 需要将数据生成一个序列化的Summary protobuf对象

1
2
#返回filewriter 写入事件文件到指定目录(最好用绝对路径),提供给tensorboard使用
tf.summary.FileWriter('./././',graph = sess.graph)

这将在指定目录中生成一个event文件 名称格式如下:

1
events.out.tfevents.{timestamp}.{hostname}

2 启动TensorBoard

1
tensorboard --logdir = "./././"

2.2.4 OP

1 常见OP

1
2
3
4
5
6
7
8
数据:Tensor对象
操作:Operation对象 - Op
1 常见OP
操作函数 & 操作对象
tf.constant(Tensor对象) 输入Tensor对象 -Const-输出 Tensor对象
tf.add(Tensor对象1, Tensor对象2) 输入Tensor对象1, Tensor对象2 - Add对象 - 输出 Tensor对象3
2 指令名称
一张图 - 一个命名空间

1
2
3
c = tf.matmul(a,b) 
#创建了一个operation对象 类型为MatMul 将张量a,b输入 c作为输出 打印的时候也是打印数据
#其中tf.matmul()是函数 通过这个类创建一个和它对应的对象

注意,打印出来的是张量值 理解成OP中包含了这个值 每一个OP指令都对应一个惟一的名称 如上面的Const:0

注意 tf.Tensor对象是以输出该张量的tf.Operation明确命名

张量名称的形式" <OP_NAME>:" 其中:

  • 前面这个OP_NAME是生成该张量的指令名称
  • 后面这个I 是这个张量在指令输出中的索引

2 指令名称

1
一张图 - 一个命名空间

tf.Graph对象为其包含的tf.Operation对象定义的一个命名空间。Tensorflow 会自动为图中的每个指令选择一个唯一名称 用户也可以指定描述性名称

改写指令名称:

  • 每个创建的tf.Operation或返回新的tf.Tensor的API函数可以接受可选的name参数

例如:tf.Operation(42.0,name=“answer”)创建了一个名为”answer"的新tf.Operation并返回一个名为“answer:0"的tf.Tensor. 如果默认图已包含名为”answer“的指令 则Tensorflow 会在名称上加”1“”2“等字符 让名称具有唯一性

  • a = tf.constant(3.0,name="a")
    b = tf.constant(3.0,name="b")
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25

    ## 2.3 会话

    ### 2.3.1 会话

    一个运行Tensorflow operation的类 会话包括以下两种开启方式

    - tf.Session: 用于完整程序当中

    - tf.InteractiveSession:用于交互式上下文中的Tensorflow 例如shell

    > 1 Tensorflow 使用tf.Session类表示客户端程序(通常为python)与C++运行时的连接
    >
    > 2 tf.Session对象使用分布式Tensorflow运行时提供对本地计算机中的设备和远程设备的访问权限

    ### 1 `_init_(target='',graph = None,config= None)`

    开启终端 找到views->tools->terminal 可以直接进入

    也可以是使用activate.bat那个路径从cmd直接进入

    会话可能拥有的资源 如tf.Variable, tf.QueueBase 和tf.ReaderBase 当这些资源不再需要时 释放这些资源很重要 因此 需要调用tf.Session.close会话中的方法 或将会话用作上下文管理器

    把c_t_value 经过session变换的值直接变成`c_t.eval()`

def session_demo():
“”"
会话的演示
:return:
“”"

# TensorFlow实现加法运算
a_t = tf.constant(10)
b_t = tf.constant(20)
# 不提倡直接使用这种符号运算进行计算
# 更经常使用tensorflow提供的函数进行计算
c_t = tf.add(a_t, b_t)
print("a_t:\n", a_t)
print("b_t:\n", b_t)
print("c_t:\n", c_t)
print("c_t.eval():\n", c_t.eval()
# 开启会话
with tf.Session() as sess:
   # sum_t = sess.run(c_t)
   #想同时执行多个tensor
   print(sess.run([a_t,b_t,c_t]))
   #方便获取张量的方法
return None
1

1)会话掌握资源,用完要回收 - 上下文管理器
2)初始化会话对象时的参数
graph=None 运行哪一张图
target:如果将此参数留空(默认设置),
会话将仅使用本地计算机中的设备。
可以指定 grpc:// 网址,以便指定 TensorFlow 服务器的地址,
这使得会话可以访问该服务器控制的计算机上的所有设备。
config:此参数允许您指定一个 tf.ConfigProto
以便控制会话的行为。例如,ConfigProto协议用于打印设备使用信息
3)run(fetches,feed_dict=None)
3 feed操作
a = tf.placeholder(tf.float32, shape=)
b = tf.placeholder(tf.float32, shape=)

1

with tf.Session(config=tf.ConfigProto(allow_soft_placement=True,
log_device_placement=True)) as sess:
# c_t_value = sess.run(c_t)
打印设备信息

1
2
3
4
5
6
7
8
9
10
11
12
13

### 2 会话的run()

- run(fetches,feed_dict=None,options = None,run_metadata= None)
- 通过使用session.run()运行operation
- fetches:单一的operation 或者列表 元祖(其他不属于tensorflow类型不行)
- feed_dict:参数允许调用者覆盖图中张良的值 运行时赋值
- 与tf.placeholder搭配使用 会检查值的形状是否与占位符兼容

> 使用 tf.operation.eval()也可以运行operation 但需要在会话中进行

![snipaste20230726_221500](https://cdn.jsdelivr.net/gh/paipai2001/img@main/blogs/snipaste20230726_221500.jpg)

a_t = tf.constant(1)
b_t = tf.constant(2)
# 不提倡直接使用这种符号运算进行计算
# 更经常使用tensorflow提供的函数进行计算
c_t = tf.mul(a_t, b_t)
sess = tf.Session()
print(sess.run©)
print(c.eval(session = sess))

1
2
3
4
5

### 3 feed操作

- placeholder提供占位符,run时候通过feed_dict指定参数

a = tf.placeholder(tf.float32, shape=)
b = tf.placeholder(tf.float32, shape=)
sum_ab = tf.add(a,b)
with tf.Session() as sess:
print(“”,sess.run(sum_ab,feed_dict = {a :3.0,b:5.0}))
return None

1
2
3

**代码:**

def session_demo():
“”"
会话的演示
:return:
“”"
# TensorFlow实现加法运算
a_t = tf.constant(2, name=“a_t”)
b_t = tf.constant(3, name=“a_t”)
c_t = tf.add(a_t, b_t, name=“c_t”)
print(“a_t:\n”, a_t)
print(“b_t:\n”, b_t)
print(“c_t:\n”, c_t)
# print(“c_t.eval():\n”, c_t.eval())

# 查看默认图
# 方法1:调用方法
default_g = tf.get_default_graph()
print("default_g:\n", default_g)

# 方法2:查看属性
print("a_t的图属性:\n", a_t.graph)
print("c_t的图属性:\n", c_t.graph)


# 开启会话
with tf.Session(config=tf.ConfigProto(allow_soft_placement=True,
                                      log_device_placement=True)) as sess:
    # c_t_value = sess.run(c_t)
    # 试图运行自定义图中的数据、操作
    # c_new_value = sess.run((c_new))
    # print("c_new_value:\n", c_new_value)
    a, b, c = sess.run([a_t, b_t, c_t])
    print("abc:\n", a, b, c)
    print("c_t_value:\n", c_t.eval())
    print("sess的图属性:\n", sess.graph)
    print("c_t_value:\n", c_t.eval())
    print("sess的图属性:\n", sess.graph)
    # 1)将图写入本地生成events文件
    tf.summary.FileWriter("./tmp/summary", graph=sess.graph)
return None
1
2
3
4
5
6
7
8
9
10
11



报错:

- RunTimeError:如果这Session是无效状态 已关闭
- TypeError:如果fetches或者feed_dict类型不合适
- ValueError:如果fetches或feed_dict键无效或引用Tensor不存在的键

## 2.4 张量

张量 在计算机当中如何存储?
标量 一个数字 0阶张量
向量 一维数组 [2, 3, 4] 1阶张量
矩阵 二维数组 [[2, 3, 4], 2阶张量
[2, 3, 4]]
……
张量 n维数组 n阶张量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

### 2.4.1 张量

n维数组 ndarray 类型为tf.Tensor

- type:数据类型

- shape:形状(阶)

### 1 张量的类型

![snipaste20230726_222626](https://cdn.jsdelivr.net/gh/paipai2001/img@main/blogs/snipaste20230726_222626.jpg)

### 2 张量的阶

tensor1 = tf.constant(4.0)
tensor2 = tf.constant([1,2,3,4])
linear_squares = tf.constant([[4],[9],[16],[25]],dtype = tf.int32)

1
2
3

输出:

tensor1:
Tensor(“Const:0”, shape=(), dtype=float32)
tensor2:
Tensor(“Const_1:0”, shape=(4,), dtype=int32)
linear_squares_before:
Tensor(“Const_2:0”, shape=(4, 1), dtype=int32)

1

创建张量的时候,如果不指定类型
默认 tf.float32
整型 tf.int32
浮点型 tf.float32

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

### 2.4.2 创建张量的指令

- 固定值张量

![snipaste20230726_223130](https://cdn.jsdelivr.net/gh/paipai2001/img@main/blogs/snipaste20230726_223130.jpg)

- 随机值张量

![snipaste20230726_223248](https://cdn.jsdelivr.net/gh/paipai2001/img@main/blogs/snipaste20230726_223248.jpg)

### 2.4.3 张量的变换

#### 1 类型改变

提供了如下一些改变张量中数值类型的函数

ndarray属性的修改
类型的修改
1)ndarray.astype(type)
tf.cast(tensor, dtype)
不会改变原始的tensor
返回新的改变类型后的tensor
2)ndarray.tostring()
形状的修改
1)ndarray.reshape(shape)
-1 自动计算形状
2)ndarray.resize(shape)

1
2
3
4
5

![snipaste20230726_223648](https://cdn.jsdelivr.net/gh/paipai2001/img@main/blogs/snipaste20230726_223648.jpg)

#### 2 形状修改

静态形状 - 初始创建张量时的形状
1)如何改变静态形状
什么情况下才可以改变/更新静态形状?
只有在形状没有完全固定下来的情况下
tensor.set_shape(shape)
2)如何改变动态形状
tf.reshape(tensor, shape)
不会改变原始的tensor
返回新的改变形状后的tensor
动态创建新张量时,张量的元素个数必须匹配

1
2
3
4

- tf.reshape
- tf.set_shape

a_p:
Tensor(“Placeholder:0”, shape=(None, None), dtype=float32)
b_p:
Tensor(“Placeholder_1:0”, shape=(None, 10), dtype=float32)
c_p:
Tensor(“Placeholder_2:0”, shape=(3, 2), dtype=float32)
None的部分是属于可以更新的部分 也就是静态形状可以更改的

1

def tensor_demo():
“”"
张量的演示
:return:
“”"
tensor1 = tf.constant(4.0)
tensor2 = tf.constant([1, 2, 3, 4])
linear_squares = tf.constant([[4], [9], [16], [25]], dtype=tf.int32)

print("tensor1:\n", tensor1)
print("tensor2:\n", tensor2)
print("linear_squares_before:\n", linear_squares)
# 张量类型的修改
l_cast =  tf.cast(linear_squares,dtype=tf.float32)
print("linear_squares_after:\n", linear_squares)
print("l_cast:\n", l_cast)
# 更新/改变静态形状
# 定义占位符
# 没有完全固定下来的静态形状
a_p = tf.placeholder(dtype=tf.float32, shape=[None, None])
b_p = tf.placeholder(dtype=tf.float32, shape=[None, 10])
c_p = tf.placeholder(dtype=tf.float32, shape=[3, 2])
print("a_p:\n", a_p)
print("b_p:\n", b_p)
print("c_p:\n", c_p)
#更新形状未确定的部分
a_p.set_shape([2, 3])
b_p.set_shape([2, 10])
print("a_p:\n", a_p)
print("b_p:\n", b_p)
# 动态形状修改
a_p_reshape = tf.reshape(a_p, shape=[2, 3, 1])
print("a_p:\n", a_p)
# print("b_p:\n", b_p)
print("a_p_reshape:\n", a_p_reshape)
c_p_reshape = tf.reshape(c_p, shape=[2, 3])
print("c_p:\n", c_p)
print("c_p_reshape:\n", c_p_reshape)
return None
1
2
3
4
5
6
7
8
9
10
11
12
13

### 2.4.4 张量的数学运算

- 算数运算符
- 基本数学函数
- 矩阵运算
- reduce操作
- 序列索引操作

连接:https://www.tensorflow.org/versions/r1.8/api_guides/python/math_ops

## 2.5 变量OP

TensorFlow - 变量
存储模型参数
存储持久化
可修改值
指定被训练

1
2
3
4
5
6
7
8
9
10
11
12
13

### 2.5.1 创建变量

- tf.Variable(initial_value = None,trainable = True,collections = None,name = None)

- initial_value: 初始化的值

- trainable:是否被训练

- collections:新变量将添加到列出的图的集合中collections,默认为[GraphKeys.GLOBAL_VARIABLES],如果trainable是True变量也被添加到图形集合GraphKeys.GLOBAL_VARIABLES

- 变量需要显式初始化 才能运行值

def variable_demo():
“”"
变量的演示
:return:
“”"
#创建变量
a = tf.Variable(initial_value=50)
b = tf.Variable(initial_value=40)
c= tf.add(a,b)
print(“a:\n”, a)
print(“b:\n”, b)
print(“c:\n”, c)
# 初始化变量 这一步非常重要
init = tf.global_variables_initializer()
# 开启会话
with tf.Session() as sess:
#这一步也很重要
sess.run(init)
a_value,b_value,c_value = sess.run([a,b,c])
print(“a_value:\n”, a_value)
print(“b_value:\n”, b_value)
print(“c_value:\n”, c_value)

  return None
1
2
3
4
5

### 2.5.2 使用tf.variable.scope()修改变量的命名空间

会在OP的名字前面增加命名空间的指定名字

with tf.variable_scope(“name”):
var = tf.Variable(name = ‘var’,initial_value = [4],dtype = tf.float32)
var_double = tf.Variable(name = ‘var’,initial_value = [4],dtype = tf.float32)
<tf.Variable ‘name/var:0’ shape=() dtype=float32_ref>
<tf.Variable ‘name/var_1:0’ shape=() dtype=float32_ref>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60




## 2.6 高级API

### 2.6.1 其他基础API

### 1 tf.app

这个模块相当于为Tensorflow进行的脚本提供一个main函数入口 可以定义脚本运行的flags

### 2 tf.image

图像处理操作 颜色变换 变形图像编码解码

### 3 tf.gfile

提供文件操作函数

### 4 tf.summary

用来生成TensorBoard可用的统计日志 四种类型:audio image histogram scalar

### 5 tf.python_io

读写TFRecords文件

### 6 tf.train

提供一些训练器 与tf.nn结合 实现网格优化计算

### 7 tf.nn

构建神经网络的底层函数 构建网络的核心模块 添加各种层的函数比如卷积层 池化层

### 2.6.2 高级API

### 1 tf.keras

独立的深度学习库 快速构建模型

### 2 tf.layers

以更高级的概念层定义一个模型

### 3 tf.contrib

提供将计算图中的网络层 正则化 摘要操作 是构建计算图的高级操作 但tf.contrib包含不稳定和实验代码

### 4 tf.estimator

一个Estimator相当于model+train+evaluate

实现了简单地分类器和回归器 dnn baseline learing dnn只是全连接网络

## 2.7 Tensorflow实现线性回归

### 2.7.1 线性回归原理复习

1)构建模型
y = w1x1 + w2x2 + …… + wnxn + b
2)构造损失函数
均方误差
3)优化损失
梯度下降

1
2
3
4
5
6
7
8

### 2.7.2 案例:实现线性回归的训练

### 1 案例确定

- 假设随机指定100个点 只有一个特征
- 数据本身分布为y = 0.8 *x +0.7

准备真实数据
100样本
x 特征值 形状 (100, 1)
y_true 目标值 (100, 1)
y_true = 0.8x + 0.7
假定x 和 y 之间的关系 满足
y = kx + b
k ≈ 0.8 b ≈ 0.7
流程分析:
(100, 1) * (1, 1) = (100, 1)
y_predict = x * weights(1, 1) + bias(1, 1)
1)构建模型
y_predict = tf.matmul(x, weights) + bias
2)构造损失函数
error = tf.reduce_mean(tf.square(y_predict - y_true))
3)优化损失
optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.01).minimize(error)
5 学习率的设置、步数的设置与梯度爆炸

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

### 2 API

运算

- 矩阵运算
- tf.matmul(x,w)

- 平方
- tf.square(error)

- 均值
- tf.reduce_mean(error)

**梯度下降优化**

- tf.train.GradientDescentOptimizer(learing_rate)

- 梯度下降优化

- learing_rate:学习率 一般为0~1之间较小的值

- method:

- minimize(loss)

- return: 梯度下降OP

### 3 步骤分析

### 学习率的设置 步数的设置以及梯度爆炸

学习率越大 训练到较好的结果步数越小 学习率越小 训练到较好的结果步数越大

但是学习过大会出现梯度爆炸现象

如何解决梯度爆炸
重新设计网络
调整学习率
使用梯度截断(在训练过程中检查和限制梯度大小)
使用激活函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

### 4 变量的trainable设置观察

trainable的参数作用 指定是否训练

`weight = tf.Variable(tf.random([1,1],mean = 0.0,stddev = 1.0),na)`

### 2.7.3 增加其他功能

- 变量Tensorboard显示
- 增加命名空间
- 模型保存与加载
- 命令行参数设置

### 1 增加变量显示

**目的:在Tensorboard中观察模型的参数 损失值等变量值的变化**

- 1、收集变量
- tf.summary.scalar(name = "",tensor)收集对于损失函数和准确率等单值变量,name为变量的名字,tensor为值 标量
- tf.summary.histogram(name = "",tensor)收集高纬度的变量参数 直方图分布状况
- tf.summary.image(name = "",tensor)收集输入的图片张量能显示图片
- 2、合并变量写入事件文件中
- merged = tf.summary.merge_all()
- 运行合并:summary = sess.run(merged) 每次迭代都需运行
- 添加:FileWriter.add_summary(summary,i),i表示第几次的值

1)创建事件文件
2)收集变量
3)合并变量
4)每次迭代运行一次合并变量
5)每次迭代将summary对象写入事件文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

### 2 增加命名空间

使代码结构更清晰 Tensorboard图结构清楚

`with tf.variable_scope("lr_model")`

### 3 模型的保存与加载

- tf.train.Saver(var_list = None,max_to_keep = 5)

- 保存和加载模型(保存文件格式:checkpoint文件)

- var_list:指定将要保存和还原的变量 可以作为一个dict 或者一个列表传递

- max_to_keep:指示要保留的最近检查点文件的最大数量 创建新文件时 会删除比较旧的文件 如果无或者0 就保留所有检查点的文件


saver = tf.train.Saver(var_list=None,max_to_keep=5)
1)实例化Saver
2)保存
saver.save(sess, path)
3)加载
saver.restore(sess, path)

1
2
3

使用

指定目录+模型名字
saver.save(sess,‘./tmp/ckpt/test/myregression.ckpt’)
saver.restore(sess,‘./tmp/ckpt/test/myregression.ckpt’)

1
2
3

如果要判断模型是否存在 直接指定目录

checkpoint = tf.train.latest_checkpoint(“./tmp/model/”)
saver.restore(sess.checkpoint)

1
2
3
4
5

### 4 命令行参数使用

![snipaste20230728_182253](https://cdn.jsdelivr.net/gh/paipai2001/img@main/blogs/snipaste20230728_182253.jpg)

1)tf.app.flags
tf.app.flags.DEFINE_integer(“max_step”, 0, “训练模型的步数”)
tf.app.flags.DEFINE_string(“model_dir”, " ", “模型保存的路径+模型名字”)
2)FLAGS = tf.app.flags.FLAGS
通过FLAGS.max_step调用命令行中传过来的参数
3、通过tf.app.run()启动main(argv)函数

1

#定义一些常用的命令行参数
#训练步数
tf.app.flags.DEFINE_integer(“max_step”,0,“训练模型的步数”)
#定义模型的路径
tf.app.flags.DEFINE_string(“model_dir”," ",“模型的保存路径+模型名字”)

#定义获取命令行参数
FLAGS = tf.app.flags.FLAG
#开启训练
#训练的步数(依据模型的大小而定)
for i in range(FLAGS.max_step):
sess.run(train_op)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97

### 完整代码:

```python
def linear_regression():
"""
自实现一个线性回归
:return:
"""
with tf.variable_scope("prepare_data"):
# 1)准备数据
X = tf.random_normal(shape=[100, 1], name="feature")
y_true = tf.matmul(X, [[0.8]]) + 0.7

with tf.variable_scope("create_model"):
# 2)构造模型
# 定义模型参数 用 变量
weights = tf.Variable(initial_value=tf.random_normal(shape=[1, 1]), name="Weights")
bias = tf.Variable(initial_value=tf.random_normal(shape=[1, 1]), name="Bias")
y_predict = tf.matmul(X, weights) + bias

with tf.variable_scope("loss_function"):
# 3)构造损失函数
error = tf.reduce_mean(tf.square(y_predict - y_true))

with tf.variable_scope("optimizer"):
# 4)优化损失
optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.01).minimize(error)

# 2_收集变量
tf.summary.scalar("error", error)
tf.summary.histogram("weights", weights)
tf.summary.histogram("bias", bias)

# 3_合并变量
merged = tf.summary.merge_all()

# 创建Saver对象
saver = tf.train.Saver()

# 显式地初始化变量
init = tf.global_variables_initializer()

# 开启会话
with tf.Session() as sess:
# 初始化变量
sess.run(init)

# 1_创建事件文件
file_writer = tf.summary.FileWriter("./tmp/linear", graph=sess.graph)

# 查看初始化模型参数之后的值
print("训练前模型参数为:权重%f,偏置%f,损失为%f" % (weights.eval(), bias.eval(), error.eval()))

# 开始训练
# for i in range(100):
# sess.run(optimizer)
# print("第%d次训练后模型参数为:权重%f,偏置%f,损失为%f" % (i+1, weights.eval(), bias.eval(), error.eval()))
#
# # 运行合并变量操作
# summary = sess.run(merged)
# # 将每次迭代后的变量写入事件文件
# file_writer.add_summary(summary, i)
#
# # 保存模型
# if i % 10 ==0:
# saver.save(sess, "./tmp/model/my_linear.ckpt")
# 加载模型
if os.path.exists("./tmp/model/checkpoint"):
saver.restore(sess, "./tmp/model/my_linear.ckpt")

print("训练后模型参数为:权重%f,偏置%f,损失为%f" % (weights.eval(), bias.eval(), error.eval()))


return None

# 1)定义命令行参数
tf.app.flags.DEFINE_integer("max_step", 100, "训练模型的步数")
tf.app.flags.DEFINE_string("model_dir", "Unknown", "模型保存的路径+模型名字")

# 2)简化变量名
FLAGS = tf.app.flags.FLAGS

def command_demo():
"""
命令行参数演示
:return:
"""
print("max_step:\n", FLAGS.max_step)
print("model_dir:\n", FLAGS.model_dir)

return None

def main(argv):
print("code start")
return None

3.1 文件读取流程

1
2
3
4
5
6
7
8
9
10
11
12
13
数据IO操作
三种
占位符 & feed_dict搭配使用
QueueRunner:基于队列的输入管道从Tensorflow图形开头的文件中读取数据
Feeding:运行每一步时 python提供数据
预加载数据:图中张量包含所有数据(小数据集)
通用文件读取流程
图片
二进制数据
TFRecords
神经网络基础
神经网络原理
手写数字识别案例
1
多线程 + 队列

3.1.1 文件读取流程

  • 第一阶段: 构造文件名队列
  • 第二阶段:读取与解码
  • 第三阶段:批处理

注:这些操作需要启动运行这些队列操作的线程 以便我们在进行文件读取的过程中能够顺利进行入队出队操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
1)构造文件名队列
file_queue = tf.train.string_input_producer(string_tensor,shuffle=True)
2)读取与解码
文本:
读取:tf.TextLineReader()
解码:tf.decode_csv()
图片:
读取:tf.WholeFileReader()
解码:
tf.image.decode_jpeg(contents)
tf.image.decode_png(contents)
二进制:
读取:tf.FixedLengthRecordReader(record_bytes)
解码:tf.decode_raw()
TFRecords
读取:tf.TFRecordReader()
key, value = 读取器.read(file_queue)
key:文件名
value:一个样本
3)批处理队列
tf.train.batch(tensors, batch_size, num_threads = 1, capacity = 32, name=None)
手动开启线程
tf.train.QueueRunner()
开启会话:
tf.train.start_queue_runners(sess=None, coord=None)

1 构造文件名队列

将需要读取的文件的文件名放入文件名队列

  • tf.train.string_input_producer(string_tensor,shuffle = True) 后面这个是顺序
    • string_tensor:含有文件名+路径的1阶张量 一般用列表
    • num_epochs:过几遍数据 默认无限过数据
    • return 文件队列

2 读取与解码

从队列当中读取文件内容 并进行解码操作

1) 读取文件内容

阅读器默认每次只读取一个样本

具体来说:

文本文件默认一次读取一行 图片文件默认一次读取一张照片 二进制文件默认一次读取指定字节数 TFRecords默认一次读取一个example

  • tf.TextLineReader()

    • 阅读文本文件逗号分隔值(CSV)格式 默认按行读取
    • return:读取器实例
  • tf.WholeFileReader()图片

    • return:读取器实例
  • tf.FixedLengthRecordReader(record_bytes)

    • record_bytes: 整型 每次读取一个样本的字节数
  • tf.TFRecordReader()

    • return:读取器实例

1 共同读取方法:read(file_queue)并返回一个Tensors元祖(key:文件名 value:一个样本)

2 由于默认只会读取一个样本 所以如果想要进行批处理 需要使用tf.train.batch或者tf.train.shuffle_batch进行批处理 便于之后指定每批次多个样本的训练

2) 内容解码

读取不同类型的文件 也应该对不同类型文件解码成统一的Tensor格式

  • tf.decode_csv()
  • tf.image.decode_jpeg(contents)
    • 解码为unit8张量
    • return unit8张量 3-D形状[height,width,channels]
  • tf.image.decode_png(contents)
    • 解码为unit8张量
    • return 张量类型 3-D形状[height,width,channels]
  • tf.image.decode_raw

后面可以用tf.cast转换

3 批处理

解码之后 可以直接获取一个样本内容 如果想获取多个样本 需要加入到新的队列进行批处理

  • tf.train_batch(tensors,batch_size,num_threads = 1,capacity = 32,name = None)

    • 读取指定大小(个数)的张量
    • tensors:可以使包含张量的列表
    • batch_size:从队列中读取的批处理大小
    • num_threads:进入队列的线程数
    • capacity:整数 队列中元素的最大数量
    • return :tensors
  • tf.train.shuffle_batch

3.1.2 线程操作

以上用到的队列都是QueueRunner对象

每个QueueRunner都负责一个阶段 tf.train.QueueRunner()函数会要求图中每个QueueRunner启动它的运行队列操作的线程

  • tf.train.start_queue_runners(sess=None, coord=None)

    • 收集图中所有的队列线程 默认同时启动线程
    • sess:所在的会话
    • coord:现成协调器
    • return 返回所有线程
  • tf.train.Coordinate()

    • 线程协调员 对线程进行管理和协调
    • request_stop():请求停止
    • should_stop():询问是否结束
    • join(threads = None,stop_grace_period_secs=120):回收线程
    • return:线程协调员实例

3.2 图片数据

3.2.1 图像基本知识

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
文本  特征词 -> 二维数组 shape n_samples m_features
字典 one-hot -> 二维数组 shape n_samples m_features
图片 最基本单位:像素值 shape n_samples m_features
1 图片三要素
黑白图、灰度图
一个通道
黑[0, 255]白
彩色图
三个通道
一个像素点 三个通道值构成
R [0, 255]
G [0, 255]
B [0, 255]
2 TensorFlow中表示图片
Tensor对象
指令名称、形状、类型
shape = [height, width, channel]
3 图片特征值处理
[samples, features]
为什么要缩放图片到统一大小?
1)每一个样本特征数量要一样多
2)缩小图片的大小
tf.image.resize_images(images, size)
4 数据格式
存储:uint8
训练:float32

1 图片三要素

图片长度 图片宽度 图片通道数

像素数量= 长 * 宽 * 通道数目

2 张量形状

  • 单张图片 [height, width, channel]
  • 多个图片 [batch, height,width,channel] batch表示一个批次的张量数目

3.2.2 图片特征值处理

在进行图像识别的时候 每个图片样本特征数量要保持相同 所以需要将所有图片张量大小进行统一转换

另一个方面 如果图片的像素量太大 通过这种方式适当减少像素的数量 减少训练计算开销

  • tf.image.resize_images(images,size)

    • 缩小放大图片

    • images:4-D形状 就上面说的单张图片和多张图片的张量形状

      • 单张图片 [height, width, channel]

      • 多个图片 [batch, height,width,channel] batch表示一个批次的张量数目

    • images:4-D形状 就上面说的单张图片和多张图片的张量形状

    • size:1-D int32张量 new_height new_width 图像新尺寸

    • return 4-D 或者3-D格式图片

3.2.3 数据格式

  • 存储:unit8节约空间
  • 矩阵计算 float32 提高精度

3.2.4 案例:狗图片读取案例

1 读取流程分析

1
2
3
4
5
6
7
8
9
10
11
12
1)构造文件名队列
file_queue = tf.train.string_input_producer(string_tensor,shuffle=True)
2)读取与解码
形状和类型保持统一
读取:
reader = tf.WholeFileReader()
key, value = reader.read(file_queue)
解码:
image_decoded = tf.image.decode_jpeg(value)
3)批处理队列
image_decoded = tf.train.batch([image_decoded], 100, num_threads = 2, capacity=100)
手动开启线程

2 代码

  • import tensorflow._api.v2.compat.v1 as tf
    tf.compat.v1.disable_eager_execution()
    import os
    def picture_demo(file_list):
        """
        狗图片读取案例
        :return:
        """
        # 1构造文件名队列
        file_queue = tf.train.string_input_producer(file_list)
        # 2读取与解码
        # 读取阶段
        reader = tf.WholeFileReader()
        #key 文件名 value 一张图片的原始编码形式
        key,value = reader.read(file_queue)
        print("key:\n",key)
        print("value:\n",value)
        # 解码阶段
        image = tf.image.decode_image(value)
        # 图像形状 类型修改
        image_resized = tf.image.resize_images(image, [200, 200])
        # 静态形状修改
        image_resized.set_shape(shape=[200,200,3])
        # 3批处理
        image_batch = tf.train.batch([image_resized],batch_size=100,num_threads=1,capacity=100)
        
        
        # 开启会话
        with tf.Session() as sess:
            # 开启线程
            # 线程协调员
            coord = tf.train.Coordinator()
            threads = tf.train.start_queue_runners(sess=sess,coord=coord)
            key_new,value_new = sess.run([key,value])
            # print("key_new:\n",key_new)
            # print("value_new\n",value_new)
        #     回收线程
            coord.request_stop()
            coord.join(threads)
        return None
    if __name__ == "__main__":
      #   构建文件名的列表
      file_name = os.listdir("./dog")
      # print(file_name)
      # 拼接路径和文件名
      file_list = [os.path.join("./dog/",file) for file in file_name]
      # print(file_list)
      picture_demo(file_list)
    
    1
    2
    3
    4
    5

    ## 3.3. 二进制数据

    ### 3.3.1 CIFAR10二进制数据集介绍

https://www.cs.toronto.edu/~kriz/cifar.html

1
2
3
4
5
6
7
8
9
10
11

- 二进制版本数据文件

每3073个字节为一个样本

1个目标值+3072像素(1024字节红色通道+1024字节绿色通道+1024字节蓝色通道)

### 3.3.2 CIFAR10二进制数据读取

#### 1 分析

1)构造文件名队列
2)读取与解码
reader = tf.FixedLengthRecordReader(3073)
key,value= reader.read(file_queue)
decoded = tf.decode_raw(value,tf.unit8)
对tensor对象进行切片
目标值label 特征值image(3072字节)
[r[32,32]],
[g[32,32]].
[b[32,32]]
shape = (3,32,32) = (channels,height,width)
–>tensorflow图像表示习惯
图片的形状 类型调整完毕
3)批处理队列
开启会话
手动开启线程

1
2
3
4
5

#### 2 代码

- 定义CIFAR类 设置图片相关属性

import tensorflow._api.v2.compat.v1 as tf
tf.compat.v1.disable_eager_execution()
import os
class Cifar(object):
def init(self):
# 初始化操作
self.height = 32
self.width = 32
self.channels = 3
# 字节数
self.image_bytes = self.height * self.width * self.channels
self.label_bytes = 1
self.all_bytes = self.label_bytes+self.image_bytes
def read_and_decode(self,file_list):
# 1、构建文件名队列
file_queue = tf.train.string_input_producer(file_list)
# 2、读取的解码
reader = tf.FixedLengthRecordReader(self.all_bytes)
# key 文件名 value 一个样本
key, value = reader.read(file_queue)
print(“key:\n”, key)
print(“value:\n”, value)
# 解码阶段
decoded = tf.decode_raw(value,tf.uint8)
print(“decoded:\n”,decoded)
# 将目标值和特征值切片切开
label = tf.slice(decoded,[0],[self.label_bytes])
image = tf.slice(decoded,[self.label_bytes],[self.image_bytes])
print(“lable:\n”,label)
print(“image:\n”,image)
# 调整图像形状 类型修改
image_reshaped = tf.reshape(image,shape=[self.channels,self.height,self.width])
print(“image_reshaped:\n”,image_reshaped)
# 转置 将图片的顺序变成height width channels
image_transposed = tf.transpose(image_reshaped,[1,2,0])
print(“image_transposed:\n”,image_transposed)
# 调整图像类型
image_cast = tf.cast(image_transposed,tf.float32)
# 3、批处理
label_batch, image_batch = tf.train.batch([label,image_cast], batch_size=100, num_threads=1, capacity=100)
print(“lable_batch:\n”,label_batch)
print(“image_batch:\n”,image_batch)
# 开启会话
with tf.Session() as sess:
# 开启线程
# 线程协调员
coord = tf.train.Coordinator()
threads = tf.train.start_queue_runners(sess=sess, coord=coord)
key_new, value_new, decoded_new, label_new, image_new,image_reshaped_new, image_transposed_new = sess.run([key, value,decoded,label,image,image_reshaped,image_transposed])
print(“key_new:\n”, key_new)
print(“value_new\n”,value_new)
print(“decoded_new\n”, decoded_new)
print(“label_new\n”, label_new)
print(“image_new\n”, image_new)
print(“image_reshaped_new:\n”, image_reshaped_new)
print(“image_transposed_new:\n”,image_transposed_new)
# 回收线程
coord.request_stop()
coord.join(threads)
return None
if name == “main”:
# 构建文件名的列表
file_name = os.listdir(“./cifar-10-batches-bin”)
print(“file_name:\n”,file_name)
file_list = [os.path.join(“./cifar-10-batches-bin/”,file) for file in file_name if file[-3:] == “bin”]
print(“file_list:\n”, file_list)
# 实例化Cifar
cifar = Cifar()
cifar.read_and_decode(file_list)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

## 3.4 TFRecords

### 3.4.1 什么是TFRecords文件

二进制文件 不需要单独的标签文件

使用步骤:1)获取数据

​ 2)将数据填入到Example协议内存块(protocol buffer)

3) 将协议内存块序列化为字符串 并通过tf.python_io.TFRecordWriter写入到TFRecords文件




*.tfrecords

### 3.4.2 Example结构解析

cifar10
特征值 - image - 3072个字节
目标值 - label - 1个字节
example = tf.train.Example(features=tf.train.Features(feature={
“image”:tf.train.Feature(bytes_list=tf.train. BytesList(value=[image])
“label”:tf.train.Feature(int64_list=tf.train. Int64List(value=[label]))
}))
example.SerializeToString()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

![](https://cdn.jsdelivr.net/gh/paipai2001/img@main/blogs/snipaste20230802_173557.jpg)

![snipaste20230802_173620](https://cdn.jsdelivr.net/gh/paipai2001/img@main/blogs/snipaste20230802_173620.jpg)

### 3.4.3 案例:CIFAR10数据存入TFRecords文件

#### 1 分析

- 构造存储实例,tf.python_io.TFRecordWriter(path)
- 写入tfrecords文件
- path:TFRecords文件的路径
- return:写文件
- method方法
- write(record):向文件中写入一个example
- close():关闭文件写入器

- 循环将数据填入到example协议内存块(protocol buffer)

#### 2 代码

return image_value,label_value
上面的代码案例返回值更改
def write_to_tfrecords(self,image_batch,label_batch):
“”"
将样本的特征值和目标值一起写入tfrecords文件
:param image:
:param label:
:return:
“”"
with tf.python_io.TFRecordWriter(“cifar-10-batches-bin”) as writer:
# 循环构造example对象 并序列化写入文件
for i in range(100):
image = image_batch[i].toString()
label = label_batch[i].toString()
print(“tfrecords_image:\n”, image)
print(“tfrecords_label:\n”, label)
example = tf.train.Example(features=tf.train.Features(feature={
“image”: tf.train.Feature(bytes_list=tf.train.BytesList(value=[image])),
“label”: tf.train.Feature(int64_list=tf.train.Int64List(value=[label])),
}))
# example.SerializeToString()
# 将序列化后的example写入文件
writer.write(example.SerializeToString())

    return None
1
2
3
4
5

### 3.4.4 读取TFRecords文件API

读取这种文件整个过程与其他文件一样 只不过需要有个解析Example步骤 可以使用tf.TFRecordWriter的tf.parse_single_example解析器 将协议内存块解析为张量

1)构造文件名队列
2)读取和解码
读取
解析example
feature = tf.parse_single_example(value, features={
“image”:tf.FixedLenFeature([], tf.string),
“label”:tf.FixedLenFeature([], tf.int64)
})
image = feature[“image”]
label = feature[“label”]
解码
tf.decode_raw()
3)构造批处理队列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

![](https://cdn.jsdelivr.net/gh/paipai2001/img@main/blogs/snipaste20230802_175756.jpg)

### 3.4.5 案例:读取CIFAR的TFRecords文件

#### 1 分析

- 使用tf.train.string_input_producer构造文件队列
- tf.TFRecordReader读取数据并解析
- tf.parse_single_example 进行解析
- tf.decode_raw 解码
- 类型是bytes需要解码
- 其他不需要
- 处理图片数据形状以及数据类型,加入批处理队列
- 开启会话线程进行

#### 2 代码

def read_tfrecords(self):
“”"
读取TFRecords文件
:return:
“”"
# 1、构造文件名队列
file_queue = tf.train.string_input_producer([“cifar10.tfrecords”])

    # 2、读取与解码
    # 读取
    reader = tf.TFRecordReader()
    key, value = reader.read(file_queue)

    # 解析example
    feature = tf.parse_single_example(value, features={
        "image": tf.FixedLenFeature([], tf.string),
        "label": tf.FixedLenFeature([], tf.int64)
    })
    image = feature["image"]
    label = feature["label"]
    print("read_tf_image:\n", image)
    print("read_tf_label:\n", label)

    # 解码
    image_decoded = tf.decode_raw(image, tf.uint8)
    print("image_decoded:\n", image_decoded)
    # 图像形状调整
    image_reshaped = tf.reshape(image_decoded, [self.height, self.width, self.channel])
    print("image_reshaped:\n", image_reshaped)

    # 3、构造批处理队列
    image_batch, label_batch = tf.train.batch([image_reshaped, label], batch_size=100, num_threads=2, capacity=100)
    print("image_batch:\n", image_batch)
    print("label_batch:\n", label_batch)

    # 开启会话
    with tf.Session() as sess:

        # 开启线程
        coord = tf.train.Coordinator()
        threads = tf.train.start_queue_runners(sess=sess, coord=coord)

        image_value, label_value = sess.run([image_batch, label_batch])
        print("image_value:\n", image_value)
        print("label_value:\n", label_value)

        # 回收资源
        coord.request_stop()
        coord.join(threads)

    return None

if name == “main”:
cifar = Cifar()
# image_value, label_value = cifar.read_binary()
# cifar.write_to_tfrecords(image_value, label_value)
cifar.read_tfrecords()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75

## 3.5 神经网络基础

### 3.5.1 神经网络

人工神经网络(Artificial Neural Network,简写为ANN)也简称为神经网络(NN)。**是一种模仿生物神经网络(动物的中枢神经系统,特别是大脑)结构和功能的计算模型。经典的神经网络结构包含三个层次的神经网络。**分别为输入层,输出层以及隐藏层。

![snipaste20230809_195045](https://cdn.jsdelivr.net/gh/paipai2001/img@main/blogs/snipaste20230809_195045.jpg)

**其中每层的圆圈代表一个神经元,隐藏层和输出层的神经元有输入的数据计算后输出,输入层的神经元只是输入**

- 神经网络的特点:
- 每个连接都有个权值

- 同一层神经元之间没有连接

- 最后的输出结构对应的层也称之为全连接层


​ 那么为什么设计这样的结构呢?首先从一个最基础的结构说起,神经元。以前也称之为感知机。神经元就是要模拟人的神经元结构。

![](https://cdn.jsdelivr.net/gh/paipai2001/img@main/blogs/snipaste20230809_195418.jpg)

> 一个神经元通常具有多个**树突**,主要用来接受传入信息;而轴突只有一条,轴突尾端有许多轴突末梢可以给其他多个神经元传递信息。轴突末梢跟其他神经元的树突产生连接,从而传递信号。这个连接的位置在生物学上叫做**"突触”**。

**感知机(PLA : Perceptron Learning Algorithm)**

感知机就是模拟这样的大脑神经网络处理数据的过程。感知机模型如下图:

![snipaste20230809_195501](D:\_xpl2022\Desktop\markdown\picture\snipaste20230809_195501.jpg)

感知机是一种最基础的分类模型,类似于逻辑回归,不同的是,**感知机的激活函数用的是sign,而逻辑回归用的sigmoid。**感知机也具有连接的权重和偏置

![snipaste20230809_195549](https://cdn.jsdelivr.net/gh/paipai2001/img@main/blogs/snipaste20230809_195549.jpg)

输入层
特征值和权重 线性加权
y = w1x1 + w2x2 + …… + wnxn + b
细胞核-激活函数
sigmoid
sign
隐藏层
输出层
单个神经元 - 感知机
感知机(PLA: Perceptron Learning Algorithm))
逻辑回归
线性加权 sigmoid 映射到0-1之间
x1, x2
w1x1 + w2x2 + b = 常数
w2x2 = -w1x1 - b + 常数
x2 = kx1 + b
x2 = kx1 + b
x1 x2
单个神经元不能解决一些复杂问题
1)多层神经元
2)增加激活函数

## 3.6 神经网络原理

![snipaste20230809_200008](https://cdn.jsdelivr.net/gh/paipai2001/img@main/blogs/snipaste20230809_200008.jpg)

**神经网络解决多分类问题最常用的方法是设置n个输出节点,其中n为类别的个数。**

任意事件发生的概率都在0和1之间,且总有某一个事件发生(概率的和为1)。如果将分类问题中“一个样例属于某一个类别”看成一个概率事件,那么训练数据的正确答案就符合一个概率分布。如何将神经网络前向传播得到的结果也变成概率分布呢? Softmax回归就是一个非常常用的方法。

### 3.6.1 softmax回归

**softmax回归将神经网络输出转换成概率结果**

将原本的sigmod 变成softmax

<img src="https://cdn.jsdelivr.net/gh/paipai2001/img@main/blogs/snipaste20230809_200144.jpg" alt="snipaste20230809_200144" style="zoom:50%;" />

- 如何理解

假设输出结果为:2.3,4.1,5.6
softmax的计算输出结果为:
y1_p = e^2.3/(e^2.3+e^4.1+e^5.6)
y1_p = e^4.1/(e^2.3+e^4.1+e^5.6)
y1_p = e^5.6/( e^2.3+e^4.1+e^5.6)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
![snipaste20230809_200432](https://cdn.jsdelivr.net/gh/paipai2001/img@main/blogs/snipaste20230809_200432.jpg)

### 3.6.2交叉熵损失

> **交叉熵损失函数**: 衡量神经网络预测的概率和真实结果的概率之间的距离
>
> 交叉熵适合用于一个样本对应一个目标值

### 1 公式

![snipaste20230809_200523](https://cdn.jsdelivr.net/gh/paipai2001/img@main/blogs/snipaste20230809_200523.jpg)

为了能够衡量距离,目标值需要进行one-hot编码,能与概率值――对应,如下图

![snipaste20230809_200535](https://cdn.jsdelivr.net/gh/paipai2001/img@main/blogs/snipaste20230809_200535.jpg)

计算

-[0log(0.10)+0log(0.05)+0log(0.15)+0log(0.10)+0log(0.05)+0log(0.20)+1log(0.10)…]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98

上述的结果为-1log(0.10),那么为了减少这一个样本的损失。神经网络应该怎么做?

答:提高对应目标值为1的位置输出概率大小。

### 2 损失大小

神经网络最后的损失为平均每个样本的损失大小

- 对所有样本的损失求和取平均值

**即对所有样本的损失求和取其平均值**

### 3.6.4 **softmax、交叉熵损失API**

- tf.nn.softmax_cross_entropy_with_logits(labels=None, logits=None, name=None)
- abels:标签值(真实值)
- logits:样本加权之后的值
- return:返回损失值列表
- 损失大小求平均

- tf.reduce_mean(input_tensor)
- 计算张量的尺寸的元素平均值
- 交叉熵的优化:梯度下降

## 3.7 案例:Mnist手写数字识别

### 3.7.1 数据集介绍

![snipaste20230809_201054](https://cdn.jsdelivr.net/gh/paipai2001/img@main/blogs/snipaste20230809_201054.jpg)

文件说明:

- train-images-idx3-ubyte.gz: training set images(9912422 bytes)

- train-labels-idx1-ubyte.gz: training set labels (28881 bytes)

- t10k-images-idx3-ubyte.gz: test set images (1648877 bytes)

- t10k-labels-idx1-ubyte.gz: test set labels (4542 bytes)
> 网址: http://yann.lecun.com/exdb/mnist

### 1 特征值

Mnist数据集可以从官网下载,网址: http://yann.lecun.com/exdb/mnist/下载下来的数据集被分成两部分:55000行的训练数据集(mnist.train)和10000行的测试数据集 (mnist.test)。每一个MNIST数据单元有两部分组成:一张包含手写数字的图片和一个对应的标签。我们把这些图片设为"xs",把这些标签设为"ys”。训练数据集和测试数据集都包含xs和ys,比如训练数据集的图片是mnist.train.images,训练数据集的标签是mnist.train.labels。
![snipaste20230809_201248](https://cdn.jsdelivr.net/gh/paipai2001/img@main/blogs/snipaste20230809_201248.jpg)

我们可以知道图片是黑白图片,每一张图片包含28像素X28像素。我们把这个数组展开成一个向量,长度是28x28 = 784。因此,在MNIST训练数据集中,mnist.trajn.images是一个形状为[60000,784]的张量。

![snipaste20230809_201312](https://cdn.jsdelivr.net/gh/paipai2001/img@main/blogs/snipaste20230809_201312.jpg)

### 2 目标值

MNIST中的每个图像都具有相应的标签,**目标值:0到9之间的数字表示图像中绘制的数字**。用的是one-hot编码
nn[0,0,0,1,0,0,0,0,0,0] mnist.train.labels [55000,10]

![snipaste20230809_201431](https://cdn.jsdelivr.net/gh/paipai2001/img@main/blogs/snipaste20230809_201431.jpg)

### 3.7.2 minst数据获取API

TensorFlow框架自带了获取这个数据集的接口,所以不需要自行读取。

- from tensorflow.examples.tutorials.mnist import input_data
- mnist = input_data.read_data_sets(path, one_hot=True)
- mnist.train.next_batch(100)(提供批量获取功能)
- mnist.train.images、labels
- mnist.test.images、labels

### 3.7.3 实战:Mnist手写数字识别

### 1 网络设计

我们采用只有一层,即最后一个输出层的神经网络,也称之为全连接(full connected) 层神经网络

<img src="https://cdn.jsdelivr.net/gh/paipai2001/img@main/blogs/snipaste20230809_201819.jpg" alt="snipaste20230809_201819" style="zoom:33%;" />

### 2 全连接层计算:即构造权重和偏置

- tf.matmul(a, b, name=None)+bias

- return:全连接结果,供交叉损失运算
- tf.train.GradientDescentOptimizer(learning_rate) :梯度下降(优化交叉熵)
- learning_rate:学习率
- method: minimize(loss):最小优化损失

``` 1 特征值
全连接
[None, 784] * W[784, 10] + Bias = [None, 10]
构建全连接层:
y_predict = tf.matmul(x, W) + Bias
构造损失:
loss = tf.nn.softmax_cross_entropy_with_logits(labels=y_true, logits=y_predict,name=None)
如何计算准确率?
np.argmax(y_predict, axis=1)
tf.argmax(y_true, axis=1)
y_predict [None, 10]
y_true [None, 10]
tf.equal()

3 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
def full_connection():
# 用全连接对手写数字进行识别
# 1、准备数据
mnist=input_data.read_data_sets("./data/mnist_data",one_hot = True)
# 用占位符定义真实数据
X=tf.placeholder(dtype = tf.float32,shape = [None,784]) # 特征值
y_true=tf.placeholder(dtype = tf.float32,shape = [None,10]) # 真实值

# 2、构建模型 - 全连接
# y_predict[None,10]=X[None,784]*weights[784,10]+bias[10]
weights=tf.Variable(initial_value = tf.random_normal(shape = [784,10],stddev = 0.01))
bias=tf.Variable(initial_value = tf.random_normal(shape = [10],stddev = 0.1))
y_predict=tf.matmul(X,weights)+bias

# 3、构造损失函数
loss_list=tf.nn.softmax_cross_entropy_with_logits(logits = y_predict,labels = y_true)
loss=tf.reduce_mean(loss_list)

# 4、优化损失(梯度下降)
optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.01).minimize(loss)

# 5、计算准确率 tf.argmax()计算最大值所在列数
bool_list=tf.equal(tf.argmax(y_predict,axis = 1),tf.argmax(y_true,axis = 1))
accuracy=tf.reduce_mean(tf.cast(bool_list,tf.float32))
# 初始化变量
init = tf.global_variables_initializer()

# 开启会话
with tf.Session() as sess:
# 初始化变量
sess.run(init)

# 开始训练
for i in range(100):
# 获取真实值
image, label = mnist.train.next_batch(100)
# 因为optimizer返回的是None 所以用_,来接
_, loss_value, accuracy_value = sess.run([optimizer, loss, accuracy], feed_dict = {X: image, y_true: label})

print("第%d次的损失为%f,准确率为%f" % (i + 1, loss_value, accuracy_value))

return None

if __name__ == '__main__':
# 如果你报numpy.ufunc size changed错误,尝试更新一下Numpy的版本
# pip install numpy==1.16.0
full_connection()

'''
第1次的损失为2.292725,准确率为0.160000
第2次的损失为2.331739,准确率为0.020000
…………………………
第100次的损失为1.510217,准确率为0.730000
'''

4 准确率计算

如何提高准确率?
1)增加训练次数
2)调节学习率
3)调节权重系数的初始化值
4)改变优化器

4.1 卷积神经网络简介

4.1.1 卷积神经网络与传统多层神经网络对比

  • 传统意义上的多层神经网络只有输入层 隐藏层 输出层 其中隐藏层的层数根据需要而定,没有明确的理论推导来说明到底多少层合适

  • 卷积神经网络(Convolutional Neural Networks, CNN),在原来多层神经网络的基础上,加入了更加有效的特征学习部分,具体操作就是在原来的全连接层前面加入了卷积层、激活层和池化层。卷积神经网络出现,使得神经网络层数得以加深,“深度”学习由此而来。

通常所说的深度学习,一般指的是这些CNN等新的结构以及一些新的方法(比如新的激活函数Relu等),解决了传统多层神经网络的一些难以解决的问题

4.1.2 发展历史

snipaste20230809_203917

  1. 网络结构加深
  2. 增强卷积模块功能
  3. 从分类任务到检测任务
  4. 增加新的功能单元

4.1.3 卷积神经网络ImageNet比赛错误率

lmageNet可以说是计算机视觉研究人员进行大规模物体识别和检测时,最先想到的视觉大数据来源,最初由斯坦福大学李飞飞等人在CVPR 2009的一篇论文中推出,并坡用于替代 PASCAL数据集(后者在数据规模和多样性上都不如lmageNet)和LabelMe数据集(在标准化上不如 ImageNet) 。

lmageNet不但是计算机视觉发展的重要推动者,也是这一波深度学习热潮的关键驱动力之一。

截至2016年,ImageNet中含有超过1500万由人手工注释的图片网址,也就是带标签的图片,标签说明了图片中的内容,超过2.2万个类别。

4.2 卷积神经网络原理

4.2.1 卷积神经网络三个结构

神经网络(neural networks)的基本组成包括输入层、隐藏层、输出层。而卷积神经网络的特点在于隐藏层分为卷积层、激活层和池化层(pooling layer,又叫下采样层subsample)。

  • 卷积层: 通过在原始图像上平移来提取特征

  • 激活层: 增加非线性分割能力

  • 池化层(下采样层): 减少学习的参数,降低网络的复杂度(最大池化和平均池化)

  • **全连接层:**实现分类效果

  • **输出层:**进行损失计算并输出分类结果

snipaste20230809_204500

4.2.2 卷积层

卷积神经网络中每层卷积层由若干卷积单元(卷积核)组成,每个卷积单元的参数都是通过反向传播算法最佳化得到的。

卷积运算的目的是特征提取,第一层卷积层可能只能提取一些低级的特征如边缘、线条和角等层级,更多层的网络能从低级特征中迭代提取更复杂的特征。

卷积核又叫 filter、过滤器、模型参数、卷积单元

1 卷积核(Filter)的四大要素

  1. 卷积核个数
  2. 卷积核大小
  3. 卷积核步长
  4. 卷积核零填充大小

2 卷积核计算-大小

卷积核大小一般为 1 * 1 3 * 3 5 * 5

卷积核我们可以理解为一个观察的人,带着若干权重和一个偏置去观察,进行特征加权运算。

下图只有权重,少了偏置,一般情况下都要加上偏置!

snipaste20230809_204803

通常的卷积核大小 1,3,5,是经过研究人员实验证明比较好的效果。这个人观察之后会得到一个运算结果,
那么这个人想观察所有这张图的像素怎么办?那就需要平移(即步长):

snipaste20230809_204815

3 步长

需要去平移卷积核观察这张图片,需要的参数就是步长

假设移动的步长为一个像素,那么最终这个人观察的结果以下图为例:

5x5的图片,3x3的卷积大小去一个步长运算得到3x3的大小观察结果

snipaste20230809_205016

如果移动的步长为2那么结果是这样

5x5的图片,3x3的卷积大小,2个步长运算得到2x2的大小观察结果

snipaste20230809_205027

4 卷积核个数

不同的卷积核带的权重和偏置都不一样,即随机初始化的参数

那么如果在某一层结构当中,不止是一个人观察,多个人(卷积核)一起去观察。那就得到多张观察结果。

也就是说,一个卷积核得到一个观察结果,多个卷积核就是多个观察结果

5 零填充大小

Filter观察窗口的大小和移动步长有时会导致超过图片像素宽度!

解决办法:

  1. 不要超出图像宽度的观察结果
  2. 零填充

零填充就是在图片像素外围填充一圈值为0的像素。

snipaste20230809_205439

6 计算输出大小

如果已知输入图片形状,卷积核数量,卷积核大小,以及移动步长,那么输出图片形状如何确定?

H1:图像大小 就那三个参数 D1通道数

  • 输入体积大小 H1 W1 * D1*
  • 四个超参数︰
    • Filter数量K
    • Filter大小F
    • 步长S
    • 零填充大小P
  • 输出体积大小 H2 * W2 D2*
    • H2=(H1- F+2P)/S+1 ((高-过滤器大小+2零填充)/步长+1)
    • W2= (W1- F+2P)/S+1
    • D2=k

计算案例:

1、假设已知的条件:输入图像32 * 32 * 1 , 50个Filter,大小为5 * 5,移动步长为1,零填充大小为1。请求出输出大小?
H2= (H1 -F +2P)/S + 1 =(32 - 5+2 * 1)/1 + 1 = 30

w2=(W1-F+2P)/S + 1 =(32 -5+2 * 1)/1 + 1 = 30

D2= K= 50(D2等于过滤器的数量K)
所以输出大小为[30,30,50]

2、假设已知的条件∶输入图像32 * 32 * 1 ,50个Filter,大小为3*3,移动步长为1,未知零填充。输出大小32 * 32,求零填充大小?
H2= (H1 -F +2P)/S + 1 = (32 - 3+2 * P)/1 +1 = 32

w2=(W1 -F+2P)/S+ 1 =(32-3+2 * P)/1 +1 = 32

故P=1

所以零填充大小为:1*1

7 多通道图片如何观察

如果是一张彩色图片,那么就有三种表分别为R,G,B。原本每个人需要带一个3x3或者其他大小的卷积核,现在需要带3张3x3的权重和一个偏置,总共就27个权重。最终每个人还是得出一张结果:

snipaste20230809_205626

8 卷积网络API

  • tf.nn.conv2d(input, filter, strides=, padding=, name=None)

  • 计算给定4-D input和filter张量的2维卷积

  • input:给定的输入张量,具有[batch,heigth,width,channel],类型一定为 float32,64

  • filter:指定过滤器的权重数量,[filter_height, filter_width, in_channels,out_channels]

  • strides: strides = [1, stride,stride,1],步长。 例如,如果步长为1,则strides=[1,1,1,1]

  • padding: “SAME"“,“VALID”,具体解释见下面。

  • Tensorflow的零填充方式有两种方式,SAME和VALID

    • SAME︰越过边缘取样,取样的面积和输入图像的像素宽度一致。公式:ceil(H/S)

      • H为输入的图片的高或者宽,S为步长

      • 无论过滤器的大小是多少,零填充的数量由API自动计算。

    • VALID:不越过边缘取样,取样的面积小于输入人的图像的像素宽度。不填充。、

      • 一般填same , valid如果越过边缘的话,观察结果直接丢弃掉
        在Tensorflow当中,卷积API设置"SAME”之后,如果步长为1,输出高宽与输入大小一样(重要)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
卷积网络API
tf.nn.conv2d(input, filter, strides=, padding=)
input:输入图像
要求:形状[batch,heigth,width,channel]
类型为float32,64
filter:
weights
变量initial_value=random_normal(shape=[F, F, 3/1, K])
strides:
步长 1
[1, 1, 1, 1]
padding: “SAME”
“SAME”:越过边缘取样
“VALID”:不越过边缘取样
1)掌握filter要素的相关计算公式
2)filter大小
1x1,3x3,5x5
步长 1
3)每个过滤器会带有若干权重和1个偏置

4.2.3 激活函数

随着神经网络的发展,大家发现原有的sigmoid等激活函数并不能达到好的效果,所以才去新的激活函数。

  • Relu
  • Tanh
  • sigmoid

不同激活函数网站演示:http://playground.tensorflow.org

1 Relu

snipaste20230810_194226

效果:

snipaste20230810_194235

2 Relu激活函数API

  • tf.nn.relu(features, name=None)
    • features: 卷积后加上偏置的结果
    • return: 激活函数的计算结果

为什么采用新的激活函数

  • Relu优点
    • 有效解决梯度消失问题
    • 计算速度非常快,只 需要判断输入是否大于0。SGD(批梯度下降)的求解速度速度远快于sigmoid和tanh
  • sigmoid缺点
    • 采用sigmoid等函数,计算量相对大,而采用Relu 激活函数,整个过程的计算量节省很多。在深层网络中,sigmoid函数反向传播时,很容易就会出现梯度消失的情况

4.2.4 池化层(Polling)

Pooling层主要的作用是特征提取,通过去掉Feature Map中不重要的样本,进一步减少参数数量。即通过池化层尽可能保留图像的主要特征,不会影响图像模型的最终效果,减少模型的复杂度,避免过拟合现象。

Pooling的方法很多,通常采用最大池化

  • max_polling:取池化窗口的最大值
  • avg_polling:取池化窗口的平均值

snipaste20230810_195138

1 池化层计算

池化层计算: H2=(H1- F+2P)/S+1 ((高-过滤器大小+2零填充)/步长+1)

池化层也有窗口的大小以及移动步长,那么之后的输出大小怎么计算? 计算公式同卷积计算公式一样

计算:224x224x64,窗口为2,步长为2 输出结果?

H2 =(224 -2+ 2 * 0)/2 +1 = 112
w2 =(224- 2+ 2 * 0)/2 +1 =112

通常池化层采用2x2大小、步长为2窗口,零填充为默认P=0

输入图片大小为200×200,依次经过一层卷积(kernel size 5×5,padding 1,stride 2) ,
pooling (kernel size 3×3,padding 0,stride 1),又一层卷积(kernel size 3×3,padding 1,stride 1)之后,

输出特征图大小为:
A.95 B.96 C.97 D.98 E.99 F.100

答案:C

h2=99 w2=99 d2=5 h3=97 d3=3 h4=97 d4=3

注:卷积层向下取整,池化层向上取整

2 池化层API

  • tf.nn.max_pool(value,ksize=, strides=, padding=,name=None)
    • 输入上执行最大池数
    • value: 4-D Tensor形状[batch, height,width, channels]
    • channel:并不是原始图片的通道数,而是多少filter观察
    • ksize:池化窗口大小,[1,ksize, ksize,1]
    • strides:步长大小,[1,strides,strides,1]
    • padding:“SAME”",“VALID”,使用的填充算法的类型,默认使用 “SAME”

4.2.5 全连接层

全连接层计算:即构造权重和偏置

  • tf.matmul(a, b, name=None)+bias
    • return:全连接结果,供交叉损失运算
  • tf.train.GradientDescentOptimizer(learning_rate) :梯度下降(优化交叉熵)
    • learning_rate:学习率
    • method: minimize(loss):最小优化损失

y=w1x1+w2x2+…+b

y_predict[None,10]=X[None,784]*weights[784,10]+bias[10]

4.3 验证码识别