深度学习
三天入门深度学习
1 |
|
1.1 深度学习与机器学习的区别
1.1.1 特征提取方面
- 机器学习的特征工程步骤是需要靠手动完成的 需要大量领域专业知识
- 深度学习通常由多个层组成 通常将更简单的模型组合在一起 将数据从一层传递到另一层来构建更复杂的模型 通过训练大量的数据自动得出的模型 不需要人工特征提取环节
深度学习算法试图从数据中学习高级功能,这是深度学习的一个非常独特的部分 因此 减少了每个问题开发新特征提取器的任务。适合用在难提取特征的图像、语音、自然语言处理领域。
1.1.2 数据量和计算性能要求
机器学习需要的执行时间远少于深度学习 深度学习参数往往很庞大 需要通过大量数据的多次优化来训练参数。
第一,深度学习需要大量的训练数据集
第二,训练深度神经网络需要大量的算力
可能需要花费数天,数周 才能训练出一个深度网络
- 需要强大的GPU
- 全面管理的分布式训练与预测服务——比如谷歌tensorflow云机器学习平台
1.1.3 算法代表
- 机器学习
- 朴素贝叶斯 决策树
- 深度学习
- 神经网络
1.2 深度学习的应用场景
-
图像识别
- 物体识别
- 场景识别
- 车型识别
- 人脸检测跟踪
- 人脸关键点定位
- 人脸身份认证
-
自然语言处理技术
- 机器翻译
- 文本识别
- 聊天对话
-
语音技术
- 语音识别
1.3 深度学习框架介绍
1.3.1 常见深度学习框架对比
1.3.2 TensorFlow 的特点
官网:https://www.tensorflow.org/
- 高度灵活
- 语言多样
- 设备支持
- Tensorboard可视化
1.3.3 Tensorflow的安装
1 CPU版本
1 |
|
2 GPU版本
- 很奇怪能用 但是后面的课程再看看
2.2 TF数据流图
2.2.1 案例:Tensorflow实现一个加法运算
1 代码
1 |
|
结果:
1 |
|
1 |
|
2 Tensorflow 结构分析
一个构建图的阶段 一个执行图的阶段
在构建阶段 数据与操作的执行步骤被描述成一个图
执行阶段 使用会话执行构建好图中的操作
- 图和会话:
- 图:这是Tensorflow将计算表示为指令之间的依赖关系的一种表示法 定义数据(张量)和操作
- 会话: Tensorflow 跨一个或多个本地或远程设备运行数据流图的机制
- 张量:Tensorflow中的基本数据对象
- 节点(op):提供图当中执行的操作
2.1.2 数据流图介绍
2.2.1 什么是图结构
图包含了一组tf.Operation代表的计算单元对象和tf.Tensor代表的计算单元之间流动的数据。
1 |
|
2.2.2 图相关操作
1 默认图
通常Tensorflow会默认创建一张图
1 |
|
查看默认图的两种方法:
- 通过调用tf.get_default_graph()访问 要将操作添加到默认图形中 直接创建OP即可
- op/sess都有graph属性 默认都在一张图中
1 |
|
2 创建图
1 |
|
- 通过tf.Graph()
- 如果要在这张图中创建OP tf.Graph.as_default()
1 |
|
Tensorflow可以看到写的程序可视化效果 就是Tensorboard
2.2.3 TensorBoard:可视化学习
Tensorflow可用于训练大规模深度神经网络所需的计算 使用往往涉及的计算复杂而深奥 为了更方便Tensorflow程序的理解 调试和优化 提供了可视化工具
1 数据序列化-events文件
Tensorboard 通过读取Tensorflow的事件文件来运行 需要将数据生成一个序列化的Summary protobuf对象
1 |
|
这将在指定目录中生成一个event文件 名称格式如下:
1 |
|
2 启动TensorBoard
1 |
|
2.2.4 OP
1 常见OP
1 |
|
1 |
|
注意,打印出来的是张量值 理解成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 |
|
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 |
|
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 |
|
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 |
|
张量 在计算机当中如何存储?
标量 一个数字 0阶张量
向量 一维数组 [2, 3, 4] 1阶张量
矩阵 二维数组 [[2, 3, 4], 2阶张量
[2, 3, 4]]
……
张量 n维数组 n阶张量
1 |
|
tensor1 = tf.constant(4.0)
tensor2 = tf.constant([1,2,3,4])
linear_squares = tf.constant([[4],[9],[16],[25]],dtype = tf.int32)
1 |
|
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 |
|
ndarray属性的修改
类型的修改
1)ndarray.astype(type)
tf.cast(tensor, dtype)
不会改变原始的tensor
返回新的改变类型后的tensor
2)ndarray.tostring()
形状的修改
1)ndarray.reshape(shape)
-1 自动计算形状
2)ndarray.resize(shape)
1 |
|
静态形状 - 初始创建张量时的形状
1)如何改变静态形状
什么情况下才可以改变/更新静态形状?
只有在形状没有完全固定下来的情况下
tensor.set_shape(shape)
2)如何改变动态形状
tf.reshape(tensor, shape)
不会改变原始的tensor
返回新的改变形状后的tensor
动态创建新张量时,张量的元素个数必须匹配
1 |
|
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 |
|
TensorFlow - 变量
存储模型参数
存储持久化
可修改值
指定被训练
1 |
|
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 |
|
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 |
|
1)构建模型
y = w1x1 + w2x2 + …… + wnxn + b
2)构造损失函数
均方误差
3)优化损失
梯度下降
1 |
|
准备真实数据
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 |
|
如何解决梯度爆炸
重新设计网络
调整学习率
使用梯度截断(在训练过程中检查和限制梯度大小)
使用激活函数
1 |
|
1)创建事件文件
2)收集变量
3)合并变量
4)每次迭代运行一次合并变量
5)每次迭代将summary对象写入事件文件
1 |
|
saver = tf.train.Saver(var_list=None,max_to_keep=5)
1)实例化Saver
2)保存
saver.save(sess, path)
3)加载
saver.restore(sess, path)
1 |
|
指定目录+模型名字
saver.save(sess,‘./tmp/ckpt/test/myregression.ckpt’)
saver.restore(sess,‘./tmp/ckpt/test/myregression.ckpt’)
1 |
|
checkpoint = tf.train.latest_checkpoint(“./tmp/model/”)
saver.restore(sess.checkpoint)
1 |
|
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 |
|
3.1 文件读取流程
1 |
|
1 |
|
3.1.1 文件读取流程
- 第一阶段: 构造文件名队列
- 第二阶段:读取与解码
- 第三阶段:批处理
注:这些操作需要启动运行这些队列操作的线程 以便我们在进行文件读取的过程中能够顺利进行入队出队操作
1 |
|
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 |
|
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 代码
-
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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.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 |
|
计算
-[0log(0.10)+0log(0.05)+0log(0.15)+0log(0.10)+0log(0.05)+0log(0.20)+1log(0.10)…]
1 |
|
3 代码
1 |
|
4 准确率计算
如何提高准确率?
1)增加训练次数
2)调节学习率
3)调节权重系数的初始化值
4)改变优化器
4.1 卷积神经网络简介
4.1.1 卷积神经网络与传统多层神经网络对比
-
传统意义上的多层神经网络只有输入层 隐藏层 输出层 其中隐藏层的层数根据需要而定,没有明确的理论推导来说明到底多少层合适
-
卷积神经网络(Convolutional Neural Networks, CNN),在原来多层神经网络的基础上,加入了更加有效的特征学习部分,具体操作就是在原来的全连接层前面加入了卷积层、激活层和池化层。卷积神经网络出现,使得神经网络层数得以加深,“深度”学习由此而来。
通常所说的深度学习,一般指的是这些CNN等新的结构以及一些新的方法(比如新的激活函数Relu等),解决了传统多层神经网络的一些难以解决的问题
4.1.2 发展历史
- 网络结构加深
- 增强卷积模块功能
- 从分类任务到检测任务
- 增加新的功能单元
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)。
-
卷积层: 通过在原始图像上平移来提取特征
-
激活层: 增加非线性分割能力
-
池化层(下采样层): 减少学习的参数,降低网络的复杂度(最大池化和平均池化)
-
**全连接层:**实现分类效果
-
**输出层:**进行损失计算并输出分类结果
4.2.2 卷积层
卷积神经网络中每层卷积层由若干卷积单元(卷积核)组成,每个卷积单元的参数都是通过反向传播算法最佳化得到的。
卷积运算的目的是特征提取,第一层卷积层可能只能提取一些低级的特征如边缘、线条和角等层级,更多层的网络能从低级特征中迭代提取更复杂的特征。
卷积核又叫 filter、过滤器、模型参数、卷积单元
1 卷积核(Filter)的四大要素
- 卷积核个数
- 卷积核大小
- 卷积核步长
- 卷积核零填充大小
2 卷积核计算-大小
卷积核大小一般为 1 * 1 3 * 3 5 * 5
卷积核我们可以理解为一个观察的人,带着若干权重和一个偏置去观察,进行特征加权运算。
下图只有权重,少了偏置,一般情况下都要加上偏置!
通常的卷积核大小 1,3,5,是经过研究人员实验证明比较好的效果。这个人观察之后会得到一个运算结果,
那么这个人想观察所有这张图的像素怎么办?那就需要平移(即步长):
3 步长
需要去平移卷积核观察这张图片,需要的参数就是步长。
假设移动的步长为一个像素,那么最终这个人观察的结果以下图为例:
5x5的图片,3x3的卷积大小去一个步长运算得到3x3的大小观察结果
如果移动的步长为2那么结果是这样
5x5的图片,3x3的卷积大小,2个步长运算得到2x2的大小观察结果
4 卷积核个数
不同的卷积核带的权重和偏置都不一样,即随机初始化的参数
那么如果在某一层结构当中,不止是一个人观察,多个人(卷积核)一起去观察。那就得到多张观察结果。
也就是说,一个卷积核得到一个观察结果,多个卷积核就是多个观察结果
5 零填充大小
Filter观察窗口的大小和移动步长有时会导致超过图片像素宽度!
解决办法:
- 不要超出图像宽度的观察结果
- 零填充
零填充就是在图片像素外围填充一圈值为0的像素。
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个权重。最终每个人还是得出一张结果:
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,输出高宽与输入大小一样(重要)
- 一般填same , valid如果越过边缘的话,观察结果直接丢弃掉
-
1 |
|
4.2.3 激活函数
随着神经网络的发展,大家发现原有的sigmoid等激活函数并不能达到好的效果,所以才去新的激活函数。
- Relu
- Tanh
- sigmoid
不同激活函数网站演示:http://playground.tensorflow.org
1 Relu
效果:
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:取池化窗口的平均值
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]