这是黑马程序员3天带你玩转Python深度学习TensorFlow框架学习笔记
视频链接:黑马程序员3天带你玩转Python深度学习TensorFlow框架
1、深度学习的介绍
1.1、深度学习与机器学习的区别
学习目标:知道深度学习与机器学习的区别 区别:深度学习没有特征提取
特征方面
- 机器学习的特征工程步骤是要靠手动完成的,而且需要大量领域专业知识
- 深度学习通常由多个层组成,它们通常将更简单的模型组合在一起,将数据从一层传递到另一层来构建更复杂的模型。通过训练大量数据自动得到模型,不需要人工特征提取环节
深度学习算法试图从数据中学习高级功能,这是深度学习的一个非常独特的部分。因此,减少了为每个问题开发新特征提取器的任务。适合用在难提取特征的图像、语音、自然语言处理领域
数据量和计算性能要求
机器学习需要的执行时间远少于深度学习,深度学习参数往往很庞大,需要通过大量数据的多次优化来训练参数
- 深度学习需要大量的训练数据集
- 训练深度神经网络需要大量的算力
- 可能要花费数天、甚至数周的时间,才能使用数百万张图像的数据集训练出一个深度网络。所以深度学习通常需要强大的GPU服务器来进行计算
算法代表
- 机器学习:朴素贝叶斯,决策树
- 深度学习:神经网络
1.2、深度学习的应用场景
- 图像识别:物体识别、场景识别、车型识别、人脸检测跟踪、人脸关键点定位、人脸身份认证
- 自然语言处理技术:机器翻译、文本识别、聊天对话
- 语音技术:语音识别
1.3、深度学习框架介绍
常见深度学习框架对比
- 最常用的框架当数TensorFlow和Pytorch,而 Caffe和Caffe2次之。
- PyTorch和 Torch更适用于学术研究(research) ; TensorFlow,Caffe,Caffe2更适用于工业界的生产环境部署(industrial production)
- Caffe适用于处理静态图像(static graph) ; Torch和PyTorch更适用于动态图像(dynamic graph) ; TensorFlow在两种情况下都很实用。
- Tensorflow和Caffe2可在移动端使用。
Tensorflow的安装
官网:https://www.tensorflow.org/
CPU版本 2 GPU版本:核芯数量多,更适合处理并行任务
pip install tensorflow==1.8 -i https://pypi.douban.com/simple
pip install tensorflow==1.15
pip install tensorflow-gpu==1.15
CPU:核芯的数量少; 每一个核芯的速度更快,性能更强;更适用于处理连续性(sequential)任务。
GPU:核芯的数量多; 但是每一个核芯的处理速度较慢;更适用于并行(parallel)任务。
Tensorflow特点
- 高度灵活(Deep Flexibility)
- 它不仅可以用来做神经网络算法研究,也可以用来做普通的机器学习算法,甚至是只要把计算表示成数据流图,都可以用TensorFlow。
- 语言多样(Language Options)
- TensorFlow使用C++实现的,然后用Python封装。谷歌号召社区通过SwIG开发更多的语言接口来支持TensorFlow。
- 设备支持
- TensorFlow可以运行在各种硬件上,同时根据计算的需要,合理将运算分配到相应的设备,比如卷积就分配到GPU上,也允许在CPU和GPU上的计算分布,甚至支持使用gRPC进行水平扩展。
- Tensorboard可视化
- TensorBoard是TensorFlow的一组Web应用,用来监控TensorFlow运行过程,或可视化Computation Graph。TensorBoard目前支持5种可视化:标量(scalars) 、图片(images) 、音频(audio) 、直方图(histograms)和计算图(Computation Graph)。TensorBoard的Events Dashboard可以用来持续地监控运行时的关键指标,比如loss、学习速率(learning rate)或是验证集上的准确率(accuracy)
2、TensorFlow
2.1、初始TensorFlow
官网:https://www.tensorflow.org/
TensorFlow程序通常被组织成一个构件图阶段和一个执行图阶段。
- 在构建图阶段,数据与操作的执行步骤被描述为一个图
- 在执行图阶段,使用会话(调用系统资源)执行构建好的图中的操作
- 图:这是TensorFlow将计算表示为指令之间的依赖关系的一种表示法,(数据+操作)
- 会话:TensorFlow跨一个或多个本地或远程设备运行数据流图的机制
- 张量(数据):TensorFlow中的基本数据对象
- 节点(操作):提供图当中执行的操作W
import tensorflow as tf
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
def tensorflow_demo1():
"""
tensoflow的基本结构
"""
a=2
b=3
c=a+b
print('python版本的加法操作:\n',c)
a_t=tf.constant(2)
b_t=tf.constant(3)
c_t=a_t+b_t
print('tensorflow版本的加法操作:\n',c_t)
with tf.Session() as sess:
c_t_value=sess.run(c_t)
print('开启会话的结果\n',c_t_value)
if __name__ == '__main__':
tensorflow_demo1()
'''
python版本的加法操作:
5
tensorflow版本的加法操作:
Tensor("add:0", shape=(), dtype=int32)
开启会话的结果
5
'''
2.2、图
图包含了一组tf.Operation代表的计算单元对象和tf.Tensor代表的计算单元之间流动的数据
一个图一个命名空间,互不干扰影响
- 默认图
- 自定义图
通常tensorflow会默认帮我们创建一张图
查看默认图的两种方法:
- 通过调用**tf.get_default_graph()**访问
- 通过graph属性访问
def graph_demo():
a_t = tf.constant(2)
b_t = tf.constant(3)
c_t = a_t + b_t
defalut_g=tf.get_default_graph()
print('默认图:',defalut_g)
print('a_t的图属性: ',a_t.graph)
print('c_t的图属性: ',c_t.graph)
with tf.Session() as sess:
c_t_value = sess.run(c_t)
print('sess的图属性:',sess.graph)
if __name__ == '__main__':
graph_demo()
'''
默认图: <tensorflow.python.framework.ops.Graph object at 0x0000026249C36E48>
a_t的图属性: <tensorflow.python.framework.ops.Graph object at 0x0000026249C36E48>
c_t的图属性: <tensorflow.python.framework.ops.Graph object at 0x0000026249C36E48>
sess的图属性: <tensorflow.python.framework.ops.Graph object at 0x0000026249C36E48>
'''
自定义图
- 通过tf.Graph() 自定义图
- 通过tf.Graph.as_default() 图上下文管理器 去定义张量和节点,图上下文管理器会自动将张量和节点绑定在图中
- 开启会话,需要传入自定义图
def create_graph():
new_graph=tf.Graph()
with new_graph.as_default():
a_new = tf.constant(20)
b_new = tf.constant(30)
c_new = a_new + b_new
print("c_new:", c_new)
print("a_new的图属性:", a_new.graph)
print("c_new的图属性:", c_new.graph)
with tf.Session(graph = new_graph) as sess:
c_new_value=sess.run(c_new)
print('自定义图的运算结果:',c_new_value)
print('sess的图属性:',sess.graph)
if __name__ == '__main__':
create_graph()
'''
c_new: Tensor("add:0", shape=(), dtype=int32)
a_new的图属性: <tensorflow.python.framework.ops.Graph object at 0x000001D3439BF278>
c_new的图属性: <tensorflow.python.framework.ops.Graph object at 0x000001D3439BF278>
自定义图的运算结果: 50
sess的图属性: <tensorflow.python.framework.ops.Graph object at 0x000001D3439BF278>
'''
TensorBoard可视化工具
tensorflow可用于训练大规模深度神经网络所需的计算,使用该工具设计的计算往往复杂而深奥。为了更方便tensorflow程序的理解、调试与优化,tensorflow提供了TensorBoard可视化工具
实现程序可视化过程:
- 将图进行数据序列化,为events文件
- 打开终端,启动tensorBoard
1、将图进行数据序列化,为events文件
TensorBoard通过读取TensorFlow的事件文件来运行,故需要将图序列化为Summary对象
tf.summary.FileWriter(path, graph=sess.graph)
2、打开终端,启动tensorBoard
tensorboard --logdir=path
def graph_demo():
a_t = tf.constant(2)
b_t = tf.constant(3)
c_t = a_t + b_t
with tf.Session() as sess:
c_t_value = sess.run(c_t)
print('sess的图属性:',sess.graph)
tf.summary.FileWriter('./temp/summary',graph = sess.graph)
if __name__ == '__main__':
graph_demo()
在终端输入,注意路径
tensorboard --logdir=‘./machine_learning/temp/summary’
点击http://MSI:6006 跳到tensorBoard页面
OP 操作对象 operation
类型 | 实例 |
---|
标量运算 | add,sub, mul, div,exp, log, greater, less,equal | 向量运算 | concat,slice,splot, constant,rank,shape, shuffle | 矩阵运算 | matmul, matrixinverse,matrixdateminant | 带状态的运算 | Variable, assgin, assginadd | 神经网络组件 | softmax,sigmoid,relu,convolution,max_pool | 存储,恢复 | Save,Restroe | 队列及同步运算 | Enqueue,Dequeue,MutexAcquire,MutexRelease | 控制流 | Merge,Switch, Enter,Leave,Nextlteration |
操作函数 | 操作对象 |
---|
tf.constant(Tensor对象) | 输入Tensor对象-Const输出 Tensor对象 | tf.add(Tensor对象1,Tensor对象2) | 输入(Tensor对象1,Tensor对象2) ,add对象,输出 Tensor对象3 |
一个b操作对象(Operation)是TensorFlow图中的一个节点,可以接收0个或者多个输入Tensor对象,并且可以输出0个或者多个Tensor,Operation对象是通过op构造函数(如tf.matmul())创建的。
例如: c = tf.matmul(a, b)创建了一个Operation对象,类型为 MatMul类型,它将张量a, b作为输入,c作为输出,,并且输出数据,打印的时候也是打印的数据。其中tf.matmul)是函数,在执行matmul函数的过程中会通过MatMul类创建—个与之对应的对象
注意,打印出来的是张量值,可以理解成OP当中包含了这个值。并且每一个OP指令都对应一个唯一的名称,如上面的Const:0,这个在TensorBoard上面也可以显示
请注意,tf.Tensor对象以输出该张量的tf.Operation明确命名。张量名称的形式为“<OP_NAME>:< i >",其中: . "<OP_NAME>"是生成该张量的指令的名称 . "< i >"是一个整数,它表示该张量在指令的输出中的索引
指令名称
一张图有对应自己的命名空间
tf.Graph对象为其包含的 tf.Operation对象定义的一个命名空间。TensorFlow 会自动为图中的每个指令选择一个唯一名称,用户也可以指定描述性名称,使程序阅读起来更轻松。我们可以以以下方式改写指令名称
- 每个创建新的tf.Operation或返回新的tf.Tensor的 API函数可以接受可选的name参数。
例如,tf.constant(42.0, name=“answer”)创建了一个名为“answer"的新tf.Operation并返回一个名为“answer:0”的tf.Tensor。如果默认图已包含名为"answer"的指令,则TensorFlow 会在名称上附加“1"、“2”等字符,以便让名称具有唯一性。
- 当修改好之后,我们在Tensorboard显示的名字也会被修改
2.3、会话
会话创建
- **tf.Session:**用于完整的程序当中
- tf.InteractiveSession:用于交互式上下文中的TensorFlow,例如shell
1 TensorFlow使用tf.Session类来表示客户端程序(通常为Python程序,但也提供了使用其他语言的类似接口)与C++运行时之间的连接
2 tf.Session对象使用分布式TensorFlow运行时提供对本地计算机中的设备和远程设备的访问权限。
快速查看张量的值,在会话中调用 对象.eval() 查看
会话上下文管理器
会话可能拥有的资源,如tf.Variable,tf.QueueBase和tf.ReaderBase。当这些资源不再需要时,释放这些资源非常重要。因此,需要调用tf.Session.close会话中的方法,或将会话用作上下文管理器。
tf.Session(target=‘’,graph=None,config=None)
- target:如果将此参数留空(默认设置),会话将仅使用本地计算机中的设备。可以指定grpc://网址,以便指定TensorFlow服务器的地址,这使得会话可以访问该服务器控制的计算机上的所有设备
- graph:默认情况下,新的tf.Session将绑定到当前的默认图
- config:此参数允许您指定一个tf.ConfigProto以便控制会话的行为。例如,ConfigProto协议用于打印设备使用信息
with tf.Session() as sess:
sess.run(sth)
sess=tf.Session()
sum_t=sess.run(c_t)
sess.close()
sess = tf.Session(config=tf.ConfigProto(allow_soft_placement=True, log_device_placement=True))
会话run()
run(fetches, feed_dict=None, options=None, run_metadata=None)
- 通过使用sess.run()来运行operation
- fetches:传入单一的operation,或者列表、元组(属于tensorflow的类型)
- feed_dict:参数运行调用者覆盖图中张量的值,运行时赋值,与**tf.placeholder()**占位符搭配使用,则会检查值的形式是否与占位符兼容
注: 使用tf.operation.eval()也可以运行operation,但需要在会话中运行
a = tf.constant(5.0)
b = tf.constant(6.0)
c = a + b
sess = tf.Session()
print(sess.run(c))
print(c.eval(session=sess))
feed_dict()操作
请注意运行时候报的错误error:
RuntimeError:如果这Session是无效状态(例如已关闭)。
TypeError:如果fetches或者feed_dict键的类型不合适。
valueError:如果fetches或feed_dict键无效或引用Tensor不存在的键。
- placeholder提供占位符,run时候通过feed_dict传参
def feed_dict_demo():
a=tf.placeholder(tf.float32)
b=tf.placeholder(tf.float32)
c=tf.add(a,b)
with tf.Session(config=tf.ConfigProto(allow_soft_placement=True, log_device_placement=True)) as sess:
c_value=sess.run([a,b,c],feed_dict={a:3.2,b:2.3})
print('c_value: ',c_value)
if __name__ == '__main__':
feed_dict_demo()
'''
Device mapping: no known devices.
Add: (Add): /job:localhost/replica:0/task:0/device:CPU:0
Placeholder_1: (Placeholder): /job:localhost/replica:0/task:0/device:CPU:0
Placeholder: (Placeholder): /job:localhost/replica:0/task:0/device:CPU:0
c_value: [array(3.2, dtype=float32), array(2.3, dtype=float32), 5.5]
'''
2.4、张量
2.4.1、定义
TensorFlow的张量就是一个N维数组,类型为tf.Tensor。
标量:一个数字----0阶张量
向量:一维数组-----1阶张量
矩阵:二维数组------2阶张量
3阶张量
- type:数据类型
- 未指定类型时,默认类型:
- 整型:tf.int32
- 浮点型:tf.float32
- shape:形状(阶)
张量的类型
数据类型 | 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位无符号整型 |
张量的阶
阶 | 数学实例 | Python | 例子 |
---|
0 | 表量 | (只有大小) | s = 483 | 1 | 向量 | (大小和方向) | v = [1.1,2.2,3.3] | 2 | 矩阵 | (数据表) | m = [[1,2,3],[4,5.6],[7,8,9]] | 3 | 3阶张量 | (数据立体) | t = [[[2],[4],[6]],[[8],[10],[12]],[[14],[16],[18]]] | n | n阶 | (自己想想看) | |
def tensor_demo():
"""
张量的演示
"""
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:", tensor1)
print("tensor2:", tensor2)
print("linear_square:", linear_squares)
return None
if __name__ == "__main__":
tensor_demo()
'''
tensor1: Tensor("Const:0", shape=(), dtype=float32)
tensor2: Tensor("Const_1:0", shape=(4,), dtype=int32)
linear_square: Tensor("Const_2:0", shape=(4, 1), dtype=int32)
'''
2.4.2、创建张量
创建固定值张量
tf.zeros(shape, dtype=tf.float32,name=None)
创建所有元素设置为零的张量。此操作返回一个dtype具有形状shape和所有元素设置为零的类型的张量。
tf.zeros_like(tensor,dtype=None, name=None)
给tensor定单张量(),此操作返回tensor与所有元素设置为零相同的类型和形状的张量。
tf.ones(shape, dtype=tf.float32, name=None)
创建一个所有元素设置为1的张量。此操作返回一个类型的张量,dtype形状shape和所有元素设置为1。
tf.ones_like(tensor, dtype=None, name=None)
给tensor定单张量(),此操作返回tensor与所有元素设置为1相同的类型和形状的张量。
tf.fill(dims, value, name=None)
创建一个填充了标量值的张量。此操作创建一个张量的形状dims并填充它value.
tf.constant(value, dtype=None,shape=None, name=‘Const’"
创建一个常数张量。
创建随机张量
一般我们经常使用的随机数函数Math.randoml()产生的是服从均匀分布的随机数,能够模拟等概率出现的情况,例如扔一个骰子,1到6点的概率应该相等,但现实生活中更多的随机现象是符合正态分布的,例如20岁成年人的体重分布等。
假如我们在制作一个游戏,要随机设定许许多多NPC的身高,如果还用Math.random),生成从140到220之间的数字,就会发现每个身高段的人数是一样多的,这是比较无趣的,这样的世界也与我们习惯不同,现实应该是特别高和特别矮的都很少,处于中间的人数最多,这就要求随机函数符合正态分布。
tf.truncated_normal(shape, mean=0.0, stddev=1.0, dtype=tf.float32,seed=None,name=None)
从截断的正态分布中输出随机值,和 tf.random_normal(0一样,但是所有数字都不超过两个标准差
tf.random_normal(shape, mean=0.0,stddev=1.0, dtype=tf.float32, seed=None, name=None)
从正态分布中输出随机值,由随机正态分布的数字组成的矩阵
其它特殊的创建张量的OP
-
tf.Variable -
tf.placeholder
2.4.3、张量的变换
1 类型改变
- tf.string_to_number(string_tensor, out_type=None, name=None)
- tf.to_double(x, name=‘ToDouble’)
- tf.to_float(x, name=‘ToFloat’)
- tf.to_bfloat16(x, name=“ToBFloat16”)
- tf.to_int32(x, name=‘Tolnt32’)
- tf.to_int64(x, name=‘Tolnt64’)
- tf.cast(x, dtype, name=None),通用类型转换
def cast_demo():
a=tf.constant(4.0)
b=tf.cast(a,dtype = tf.float64)
print(a)
print(b)
if __name__ == '__main__':
cast_demo()
'''
Tensor("Const:0", shape=(), dtype=float32)
Tensor("Cast:0", shape=(), dtype=float64)
'''
2 形状改变 tensorflow的张量具有两种形状变换,动态形状和静态形状
- tf.reshape(tensor,shape):动态创建新张量,当原形状固定的时候,动态改变张量的时候,张量的元素个数必须一致。如shape(2,3) 元素个数为6,则动态改变张量的时候,也要确保元素的个数为6
- tensor.set_shape(shape):改变静态形状
静态形状:初始创建张量时的形状
什么情况下可以改变静态形状:
只有在形状还没有完全固定下来的情况下,如shape(?,?);转换形状的时候,只能一维到一维,二维到二维,而不能跨维度改变形状,如 shape=[None, 10],前面无所谓变化,但后面一个必须要10
def shape_demo():
a = tf.constant(4.0)
b = tf.placeholder(dtype=tf.float32, shape=[None, 10])
print('a:',a)
print('b:',b)
b.set_shape((2,10))
print('修改b静态形状',b)
c_dynamic=tf.reshape(b,shape = (10,2))
print('修改b动态形状',c_dynamic)
if __name__ == '__main__':
shape_demo()
'''
a: Tensor("Const:0", shape=(), dtype=float32)
b: Tensor("Placeholder:0", shape=(?, 10), dtype=float32)
修改b静态形状 Tensor("Placeholder:0", shape=(2, 10), dtype=float32)
修改b动态形状 Tensor("Reshape:0", shape=(10, 2), dtype=float32)
'''
张量的数学运算,自己去查api
- 算术运算符(如tf.add())
- 基本数学函数
- 矩阵运算(如tf.matmul())
- reduce操作(如tf.reduce_sum()、tf.reduce_mean()
- 序列索引操作
2.5、变量OP
TensorFlow变量是表示程序处理的共享持久状态的最佳方法。变量通过tf.Variable类进行操作。
变量的特点:
- 存储持久化,如变量存储模型参数
- 可修改值
- 可指定被训练
2.5.1 创建变量
tf.Variable(initia_value=None, trainable=True, collections=None, name=None)
- initial_value:初始化的值
- trainable:是否被训练
- collections:新变量将添加到列出的图的集合中collections,默认为[GraphKeys.GLOBAL_VARIABLES],如果trainable是True变量也被添加到图形集合GraphKeys.TRAINABLE_VARIABLES
注意:创建完变量后,需要进行变量初始化,且运行该初始化后,才能使用变量
init = tf.global_variables_initializer()
sess.run(init)
def variable_demo():
a=tf.Variable(initial_value = 30)
b=tf.Variable(initial_value = 50)
c=tf.add(a,b)
print('a:',a)
print('b:',b)
print('c:',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,',a_value)
print('b_value,',b_value)
print('c_value,',c_value)
if __name__ == '__main__':
variable_demo()
'''
a: <tf.Variable 'Variable:0' shape=() dtype=int32_ref>
b: <tf.Variable 'Variable_1:0' shape=() dtype=int32_ref>
c: Tensor("Add:0", shape=(), dtype=int32)
a_value, 30
b_value, 50
c_value, 80
'''
2.5.2、修改变量的命名空间
tf.variable_scope()
- 会在OP变量的名字前面添加新的命名空间名
- 命名空间会使图的结构更加清晰
def scope_demo():
with tf.variable_scope('myscore'):
a = tf.Variable(initial_value = 30)
b = tf.Variable(initial_value = 50)
with tf.variable_scope('yourscore'):
c = tf.add(a, b)
print('a:', a)
print('b:', b)
print('c:', c)
if __name__ == '__main__':
scope_demo()
'''
a: <tf.Variable 'myscore/Variable:0' shape=() dtype=int32_ref>
b: <tf.Variable 'myscore/Variable_1:0' shape=() dtype=int32_ref>
c: Tensor("yourscore/Add:0", shape=(), dtype=int32)
'''
2.6、高级API
2.6.1、其他基础API
1、tf.app 这个模块相当于为TensorFlow进行的脚本提供一个main函数入口,可以定义脚本运行的flags
2、tf.image TensorFlow的图像处理操作。主要是一些颜色变换、变形和图像的编码和解码
3、tf.gfile 这个模块提供了一组文件操作函数
4、tf.summary 用来生成TensorBoard可用的统计日志,目前Summary主要提供了4种类型: audio、image、histogram、scalar
5、tf.python_io 用来读写TFRecords文件
6、tf.train 这个模块提供了一些训练器,与tf.nn结合起来,实现一些网络的优化计算
7、tf.nn 这个模块提供了一些构建神经网络的底层函数。TensorFlow构建网络的核心模块,其中包含了添加各种层的函数,比如添加卷积层、池化层等。
2.6.2、高级API
1、tf.keras Kears本来是一个独立的深度学习库,tensorflow将其学习过来,增加这部分模块在于快速构建模型
2、tf.layers 高级API,以便高级的概念层来定义一个模型。类似tf.kears
3、tf.contrib tf.contrib.layers提供够将计算图中的网络层、正则化、摘要操作,是构建计算图的高级操作,但是tf.contrib包含不稳定和实验代码,有可能以后API会改变
4、tf.estimator 一个estimator相当于model + training + evaluate 的合体。在模块中,已经实现了几种简单的分类器和回归其,包括:Baseline,learning 和 DNN。这里的DNN的网络,只是全连接网络,没有提供卷积之类的
关于tensorflow的API展示
2.7、案例:实现线性回归
回顾:根据数据建立回归模型,w1x1+w2x2+…+b = y,通过真实值与预测值之间建立误差,使用梯度下降优化得到损失最小对应的权重和偏置。最终确定模型的权重和偏置参数。最后可以用这些参数进行预测。
- 构建线性回归模型
- 构造损失函数(均方误差)
- 优化损失(梯度下降)
使用到的API
运算:
- 矩阵运算:tf.matmul(x,w)
- 平方:tf.square(error)
- 均方:tf.reduce_mean(error)
梯度下降优化:
tf.train.GradientDescentOptimizer(learning_rate)
分析
1)准备真实数据:
tf.random_normal()
x:特征值,形状:(100,1),随机指定100个点,只有一个特征值 y_true:目标值,形状:(100,1) y_true = 0.8x + 0.7 ,100个样本 即假设x和y之间的关系满足: y =kx + b, 最后求出k≈0.8,b=0.7 为正确的答案
即y_predict=x*weights+bias
2)构建模型
y_predict = tf.matmul(x, weights) + bias
3)构造损失函数(均方误差)
error = tf.reduce_mean(tf.square(y_predict - y_true))
4)优化损失:梯度下降优化器
optimizer = tf.train.GrandientDescentOptimizer(learning_rate=0.01).minimize(error)
def linear_regression():
X=tf.random_normal(shape=[100,1])
y_true=tf.matmul(X,[[0.8]])+0.7
weigths=tf.Variable(initial_value = tf.random_normal(shape = [1,1]))
bias=tf.Variable(initial_value = tf.random_normal(shape = [1,1]))
y_predict=tf.matmul(X,weigths)+bias
error=tf.reduce_mean(tf.square(y_predict-y_true))
optimizer=tf.train.GradientDescentOptimizer(learning_rate = 0.01).minimize(error)
init=tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init)
print('训练前模型参数为:权重%f, 偏置%f, 损失值%f '%(weigths.eval(),bias.eval(),error.eval()))
for i in range(100):
sess.run(optimizer)
print('第%d次训练前模型参数为:权重%f, 偏置%f, 损失值%f '%(i+1,weigths.eval(),bias.eval(),error.eval()))
if __name__ == '__main__':
linear_regression()
'''
训练前模型参数为:权重-2.152965, 偏置-1.636178, 损失值14.106889
第1次训练前模型参数为:权重-2.086562, 偏置-1.589710, 损失值15.449847
第2次训练前模型参数为:权重-2.022885, 偏置-1.536769, 损失值12.712150
第3次训练前模型参数为:权重-1.953819, 偏置-1.488234, 损失值11.221785
.....
第98次训练前模型参数为:权重0.379509, 偏置0.383643, 损失值0.302479
第99次训练前模型参数为:权重0.386806, 偏置0.389533, 损失值0.247333
第100次训练前模型参数为:权重0.396212, 偏置0.395469, 损失值0.296869
'''
学习率的设置、步数的设置与梯度爆炸
学习率越大,训练到较好结果的步数越小;学习率越小,训练到较好结果的步数越大。
但是学习过大会出现梯度爆炸现象。
- 在极情况下,权重的值变得非常大,以至于溢出,导致 NaN值
- 如当线性回归的学习率设置为100时候,迭代100次的时候就出现权重和偏置为NaN
如何解决梯度爆炸问题(深度神经网络当中更容易出现)
- 重新设计网络
- 调整学习率
- 使用梯度截断(在训练过程中检查和限制梯度的大小)
- 使用激活函数
2.8、其他功能
2.8.1、增加变量显示
目的:在TensorBoard当中观察模型的参数、损失值等变量值的变化
1、收集变量
- tf.summary.scalar(name=",tensor) 收集对于损失函数和准确率等单值变量,name为收集变量的名字,tensor为对象
- tf.summary.histogram(name=‘’,tensor) 收集高维度的变量参数
- tf.summary.image(name=",tensor) 收集输入的图片张量能显示图片
2、合并变量写入事件文件
-
创建事件文件 tf.summary.FileWriter('path, graph=sess.graph) -
merged = tf.summary.merge_all() 合并变量 -
运行合并: summary = sess.run(merged),每次迭代都需运行 -
添加:FileWriter.add_summary(summary,i),i表示第几次的值
- 创建事件文件
- 收集变量
- 合并变量
- 运行合并变量
- 将迭代后的变量写入到事件文件中
def linear_regression():
X=tf.random_normal(shape=[100,1])
y_true=tf.matmul(X,[[0.8]])+0.7
weigths=tf.Variable(initial_value = tf.random_normal(shape = [1,1]))
bias=tf.Variable(initial_value = tf.random_normal(shape = [1,1]))
y_predict=tf.matmul(X,weigths)+bias
error=tf.reduce_mean(tf.square(y_predict-y_true))
optimizer=tf.train.GradientDescentOptimizer(learning_rate = 0.01).minimize(error)
init=tf.global_variables_initializer()
tf.summary.scalar('error',error)
tf.summary.histogram('weights',weigths)
tf.summary.histogram('bias',bias)
merged=tf.summary.merge_all()
with tf.Session() as sess:
sess.run(init)
print('训练前模型参数为:权重%f, 偏置%f, 损失值%f '%(weigths.eval(),bias.eval(),error.eval()))
file_writer=tf.summary.FileWriter('./temp/linear/',graph = sess.graph)
summary=sess.run(merged)
for i in range(100):
sess.run(optimizer)
print('第%d次训练前模型参数为:权重%f, 偏置%f, 损失值%f '%(i+1,weigths.eval(),bias.eval(),error.eval()))
file_writer.add_summary(summary,i)
if __name__ == '__main__':
linear_regression()
在Terminal输入 tensorboard --logdir=‘./temp/linear’
2.8.2、增加命名空间
tf.variable_scope(name=‘’)
使得代码结构更加信息,TensorBoard图结构更加清楚
def linear_regression():
with tf.variable_scope('prepare_data'):
X=tf.random_normal(shape=[100,1],name='feature')
y_true=tf.matmul(X,[[0.8]])+0.7
with tf.variable_scope('create_model'):
weigths=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,weigths)+bias
with tf.variable_scope('loss_function'):
error=tf.reduce_mean(tf.square(y_predict-y_true))
with tf.variable_scope('optimizer'):
optimizer=tf.train.GradientDescentOptimizer(learning_rate = 0.01).minimize(error)
init=tf.global_variables_initializer()
tf.summary.scalar('error',error)
tf.summary.histogram('weights',weigths)
tf.summary.histogram('bias',bias)
merged=tf.summary.merge_all()
with tf.Session() as sess:
sess.run(init)
print('训练前模型参数为:权重%f, 偏置%f, 损失值%f '%(weigths.eval(),bias.eval(),error.eval()))
file_writer=tf.summary.FileWriter('./temp/linear/',graph = sess.graph)
summary=sess.run(merged)
for i in range(100):
sess.run(optimizer)
print('第%d次训练前模型参数为:权重%f, 偏置%f, 损失值%f '%(i+1,weigths.eval(),bias.eval(),error.eval()))
file_writer.add_summary(summary,i)
if __name__ == '__main__':
linear_regression()
2.8.3、模型的保存与加载
tf.train.Saver(var_list=None,max_to_keep=5)
- 保存和加载模型(保存文件格式: checkpoint文件)
- var_list:指定将要保存和还原的变量。它可以作为一个dict或一个列表传递
- max_to_keep:指示要保留的最近检查点文件的最大数量。创建新文件时,会删除较旧的文件。如果无或0,则保留所有检查点文件。默认为5(即保留最新的5个检查点文件。)
步骤:
注意:
1、path,目录一定要提前创建
2、保存的是会话(里面是具体模型的参数)
3、保存模型的格式为 ckpt
- 实例化Saver
- 保存:saver.save(sess, path)
- 加载:saver.restore(sess, path)
def linear_regression():
with tf.variable_scope('prepare_data'):
X=tf.random_normal(shape=[100,1],name='feature')
y_true=tf.matmul(X,[[0.8]])+0.7
with tf.variable_scope('create_model'):
weigths=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,weigths)+bias
with tf.variable_scope('loss_function'):
error=tf.reduce_mean(tf.square(y_predict-y_true))
with tf.variable_scope('optimizer'):
optimizer=tf.train.GradientDescentOptimizer(learning_rate = 0.01).minimize(error)
init=tf.global_variables_initializer()
tf.summary.scalar('error',error)
tf.summary.histogram('weights',weigths)
tf.summary.histogram('bias',bias)
merged=tf.summary.merge_all()
with tf.Session() as sess:
sess.run(init)
print('训练前模型参数为:权重%f, 偏置%f, 损失值%f '%(weigths.eval(),bias.eval(),error.eval()))
file_writer=tf.summary.FileWriter('./temp/linear/',graph = sess.graph)
summary=sess.run(merged)
saver=tf.train.Saver()
if os.path.exists('./temp/model/checkpoint'):
saver.restore(sess, './temp/model/my_linear.ckpt')
print('训练后模型参数为:权重%f, 偏置%f, 损失值%f ' % (weigths.eval(), bias.eval(), error.eval()))
if __name__ == '__main__':
linear_regression()
'''
训练前模型参数为:权重-1.358256, 偏置2.844705, 损失值6.984871
训练后模型参数为:权重0.486487, 偏置0.413228, 损失值0.159787
'''
2.8.4、命令行参数设置
tf.app.flags(DEFINE),它支持应用从命令行接受参数,可以用来指定集群配置等。在tf.app.flags()下面有各种定义参数的类型
- DEFINE_string(flag _name,default_value, docstring)
- DEFINE_integer(flag_name, default_value, docstring)
- DEFINE_boolean(flag_name, default_value, docstring)
- DEFINE_float(flag_name, default_value, docstring)
- tf.app.flags,在flags有一个FLAGS标志,它在程序中可以调用到我们前面具体定义的flag_name
- 通过tf.app.run() 会自动启动 main(argv) 函数
tf.app.flags.DEFINE_integer("max_step",0,"训练模型的步数")
tf.app.flags.DEFINE_string("model_dir", " ","模型保存的路径+模型名字")
FLAGS = tf.app.flags.FLAGS
tf.app.flags(DEFINE) 使用
import tensorflow as tf
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
tf.app.flags.DEFINE_integer("max_step", 100, "训练模型的步数")
tf.app.flags.DEFINE_string("model_dir", "Unknown", "模型保存的路径+模型的名字")
FLAGS = tf.app.flags.FLAGS
def command_demo():
"""
命令行参数演示
"""
print("max_step:", FLAGS.max_step)
print("model_dir:", FLAGS.model_dir)
if __name__ == '__main__':
command_demo()
'''
max_step: 100
model_dir: Unknown
'''
tf.app.run()使用
def main(argv):
"""
这里是总文件,各种模块调用写在这里
"""
print(argv)
if __name__ == '__main__':
tf.app.run()
'''
['E:/RuanJian_2/PyCharm/PyCode/heima/junior/machine_learning/tensorflow_demo1.py']
'''
3、数据读取
3.1、文件读取流程
3.1.1、文件读取流程
shuffle 洗牌
第一阶段:构造文件名队列
第二阶段:读取与解码
第三阶段:批处理 (需要手动开启线程)
注意:这些操作需要启动运行这些队列操作的线程,以便我们在进行文件读取的过程中能够顺利进行入队出队操作
1、构造文件名队列 将需要读取的文件的文件名放入到文件名队列
tf.train.string_input_producer(string_tensor, shuffle=True)
- string_tensor:包含文件名+路径的1阶张量, 一般传入列表
- num_epochs:过几遍数据,默认无限过数据
- return:文件队列
2、读取与解码 从队列当中读取文件内容,并进行解码操作。
2.1、读取文件内容
阅读器默认每次只读取一个样本
具体来说:
文本文件默认一次读取一行,图片文件默认一次读取一张图片,二进制文件一次读取指定字节数(最好是一个样本的字节数),TFRecords默认一次读取一个example
- tf.TextLineReader: 读取文本
- 阅读文本文件逗号分隔值(CSV)格式,默认按行读取
- return:读取器实例
- tf.WholeFileReader: 用于读取图片文件
- tf.FixedLengthRecordReader(record_bytes) : 读取二进制文件
- 要读取每个记录是固定数量字节的二进制文件
- orecord_bytes:整型,指定每次读取(一个样本)的字节数
- return:读取器实例
1、它们有共同的读取方法: read(file_queue),并且都会返回一个Tensors元组(key文件名字,value默认的内容(一个样本))
2、由于默认只会读取一个样本,所以如果想要进行批处理,需要使用tf.train.batch或tf.train.shuffle_batch进行批处理操作,便于之后指定每批次多个样本的训练。
即file_queue=tf.train.strng_input_produce(string_tensor, shuffle=True)
key,value=读取器.read(file_queue)
key: 文件名
value: 一个样本
2.2、内容解码
读取不同类型的文件,也应该对读取到的不同类型的内容进行相对应的解码操作,解码成统一的Tensor格式
- tf.decode_csv(): 解码文本文件内容·
- tf.image.decode_jpeg(contents即样本)
- 将JPEG编码的图像解码为uint8张量
- return :uint8张量,3-D形状[height, width, channels].
- tf.image.decode_png(contents即样本)
- 将PNG编码的图像解码为uint8张量
- return: 张量类型,3-D形状[height, width, channels]
- tf.decode_raw()︰解码二进制文件内容
- 与tf.FixedLengthRecordReader搭配使用,二进制读取为uint8类型
解码阶段,默认所有的内容都解码成tf.uint8类型,如果之后需要转换成指定类型则可使用 tf.cast() 进行相应转换。
3 批处理
解码之后,可以直接获取默认的一个样本内容了,但如果想要获取多个样本,需要加入到新的队列进行批处理。
-
tf.train.batch(tensors, batch_size, num_threads = 1, capacity = 32,name=None)
-
tf.train.shuffle_batch() : 随机批量操作
3.1.2、线程操作
以上用到的队列都是tf.train.QueueRunner对象。
每个QueueRunner都负责一个阶段,tf.train.start_queue_runners()函数会要求图中的每个QueueRunner启动它的运行队列操作的线程。(这些操作需要在会话中开启)
tf.train.start_queue_runners(sess=None, coord=None)
- 收集图中所有的队列线程,默认同时启动线程
- sess:所在的会话
- coord:线程协调器
- return:返回所有线程
tf.train.Coordinator()
- 线程协调器,对线程进行管理和协调
- request_stop():请求停止
- should_stop():询问是否结束
- join(threads=None, stop_grace_period_secs=120):回收线程
- return:线程协调器实例
3.2、图片读取
3.2.1、图像基本知识
组成图片最基本单位是 像素
1、图片三要素 组成一张图片特征值是所有的像素值,有三个维度:图片长度、图片宽度、图片通道数
图片的通道数是什么?
描述一个像素点,如果是灰度图,那么只需要一个数值来描述它,就是单通道
如果一个像素点,有RGB三种颜色来描述它,及时三通道
灰度图:单通道,[长,宽,1]
彩色图片:三通道,[长,宽,3] 像素点为 长 X 宽 X 3
2、张量形状
在TensorFlow中如何用张量表示一张图片呢?
一张图片可以被表示成一个3D张量,即其形状为[height,width, channel],height就表示高,width表示宽,channel表示通道数。我们会经常遇到3D和4D的表示
- 单个图片:[height, width, channel] 3D张量
- 多个图片:[batch,height,width, channel],batch表示一个批次的张量数量 4D张量
3.2.2、图片特征值处理
为什么要缩放图片到统一大小?
在进行图像识别的时候,每个图片样本的特征数量要保持相同。所以需要将所有图片张量大小统一转换
缩小放大图片
tf.image**.resize_images**(images, size)
-
images:4-D形状[batch, height, width, channels]或3-D形状的张量[height, width, channels]的图片数据 -
size:1-D int32张量:new_height,new_width,图像的新尺寸 -
返回4-D格式或者3-D格式图片 存储:unit8(节省空间) 矩阵计算:float32(提高精度)
3.2.3、案例:狗图片读取
流程: 1)构造文件名队列 2)读取与解码,使样本的形状和类型统一 3)批处理,一次性处理多张图片
def picture_read(file_list):
file_queue=tf.train.string_input_producer(file_list)
reader=tf.WholeFileReader()
key,value=reader.read(file_queue)
print("key:", key)
print("value:", value)
image=tf.image.decode_jpeg(value)
print('image:',image)
image_resize=tf.image.resize_images(image,[200,200])
print('image_resize:',image_resize)
image_resize.set_shape(shape = [200,200,3])
image_batch=tf.train.batch([image_resize],batch_size = 100,num_threads = 1,capacity = 100)
print("image_batch:", 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, image_new, image_resize_new, image_batch_new = sess.run([key, value, image, image_resize, image_batch])
print("key_new:", key_new)
print("value_new:", value_new)
print("image_new:", image_new)
print("image_resize_new:", image_resize_new)
print("image_batch_new:", image_batch_new)
coord.request_stop()
coord.join(threads)
pass
if __name__ == '__main__':
file_name=os.listdir('./data/image/dog')
file_list=[os.path.join('./data/image/dog',file) for file in file_name]
print(file_name)
print(file_list)
picture_read(file_list)
'''
['dog.1.jpg', 'dog.10.jpg', 'dog.100.jpg', 'dog.11.jpg', 'dog.12.jpg', 'dog.13.jpg', 'dog.14.jpg', 'dog.15.jpg', ....., 'dog.99.jpg']
['./data/image/dog\\dog.1.jpg', './data/image/dog\\dog.10.jpg', './data/image/dog\\dog.100.jpg', './data/image/dog\\dog.11.jpg', './data/image/dog\\dog.12.jpg', './data/image/dog\\dog.13.jpg', './data/image/dog\\dog.14.jpg', './data/image/dog\\dog.15.jpg',...., './data/image/dog\\dog.99.jpg']
key: Tensor("ReaderReadV2:0", shape=(), dtype=string)
value: Tensor("ReaderReadV2:1", shape=(), dtype=string)
image: Tensor("DecodeJpeg:0", shape=(?, ?, ?), dtype=uint8)
image_resize: Tensor("resize_images/Squeeze:0", shape=(200, 200, ?), dtype=float32)
image_batch: Tensor("batch:0", shape=(100, 200, 200, 3), dtype=float32)
key_new: b'./data/image/dog\\dog.50.jpg'
value_new: b'\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xff\xdb\x00C\x00\n\x07\x07\x08\x07\x06\n\x08\x08\x08\x0b\n\n\x0b\x0e\x18\x10\x0e\r\r\x0e\x1d\x15\x16\x11\x18#\x1f%$"\x1f"!&+7/&)4)!"0A149;>>>%.DIC<H7=>;\xff\xdb\x00C\x01\n\x0b\x0b\x0e\r\x0e\x1c\x10\x10\x1c;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\xff\xc0\x00\x11\x08\x00\xc7\x00\x9d\x03\x01"\x00\x02\x11\x01\x03\x11\x01\xff\xc4\x00\x1f\x00\x00\x01\x05\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\xff\xc4\x00\xb5\x10\x00\x02\x01\x03\x03\x02\x04\x03\x05\x05\x04\x04\x00\x00\x01}\x01\x02\x03\x00\x04\x11\x05\x12!1A\x06\x13Qa\x07"q\x142\x81\x91\xa1\x08#B\xb1\xc1\x15R\xd1\xf0$3br\x82\t\n\x16\x17\x18\x19\x1a%&\'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94\x95\x96\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xff\xc4\x00\x1f\x01\x00\x03\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\xff\xc4\x00\xb5\x11\x00\x02\x01\x02\x04\x04\x03\x04\x07\x05\x04\x04\x00\x01\x02w\x00\x01\x02\x03\x11\x04\x05!1\x06\x12AQ\x07aq\x13"2\x81\x08\x14B\x91\xa1\xb1\xc1\t#3R\xf0\x15br\xd1\n\x16$4\xe1%\xf1\x17\x18\x19\x1a&\'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94\x95\x96\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xff\xda\x00\x0c\x03\x01\x00\x02\x11\x03\x11\x00?'
image_new: [[[ 62 78 67]
[ 42 58 47]
[ 69 85 74]
...
[193 223 225]
[194 221 230]
[149 172 188]]
[[ 55 71 60]
[ 46 62 51]
[ 56 72 61]
...
[231 231 231]
[231 231 231]
[231 231 231]]]
image_resize_new: [[[ 62. 78. 67. ]
[ 46.3 62.3 51.3 ]
[ 57.39 73.39 62.39 ]
...
[231. 231. 231. ]
[231. 231. 231. ]
[231. 231. 231. ]]]
image_batch_new: [[[[ 0. 0. 0. ]
[ 0. 0. 0. ]
[ 0. 0. 0. ]
...
[[125.09195 128.09195 137.09195 ]
[171.11003 174.03503 183.26003 ]
[129.0711 131.0711 143.2211 ]
...
[ 15.224976 15.224976 15.224976 ]
[ 12.149994 12.149994 12.149994 ]
[ 11. 11. 11. ]]]
[ 77.06506 45.09503 13.125 ]
[ 74.14496 43.14496 15.1449585]
[ 78.745026 47.745026 18.745026 ]]]]
Process finished with exit code 0
'''
3.3、二进制数据
3.3.1、CIFAR10二进制数据集介绍
CIFAR10数据集官网: https://www.cs.toronto.edu/~kriz/cifar.html
数据集分为五个训练批次和一个测试批次,每个批次有 10000 张图像。测试批次恰好包含来自每个类别的 1000 个随机选择的图像。训练批次包含随机顺序的剩余图像,但一些训练批次可能包含来自一个类的图像多于另一个。在它们之间,训练批次恰好包含来自每个类别的 5000 张图像
二进制版本数据文件
二进制版本包含文件data_batch_1.bin,data_batch_2.bin,…,data_batch_5.bin以及test_batch.bin
cifar10 的特征值是 image(3072Bytes) 目标值是 label 0-9 (1Bytes)
这些文件中的每一个格式如下,数据中每个样本包含了特征值和目标值:
<1×标签><3072×像素> … <1×标签><3072×像素> 第一个字节是第一个图像的标签,它是一个0-9范围内的数字。接下来的3072个字节是图像像素的值。前1024个字节是红色通道值,下1024个绿色,最后1024个蓝色。值以行优先顺序存储,因此前32个字节是图像第一行的红色通道值。每个文件都包含10000个这样的3073字节的“行"图像,但没有任何分隔行的限制。因此每个文件应该完全是30730000字节长。 即一个样本的形式为: 1+1024r+1024g+1024b=3073个字节
3.3.2、CIFAR10二进制数据读取
流程分析
1、构建文件名队列
file_queue=tf.train.string_input_producer(file_list)
2、读取与解码
reader=tf.FixedLengthRecordReader(3073)
key,value=reader.read(file_queue)
decoded=tf.decoded_raw(value,tf.uint8)
对tensor对象进行切片 label
一个样本image(3072字节=1024r+1024g+1024b)
[[r[32,32]],[g[32,32]],[b[32,32]]] 32x32=1024
shape=(3,32,32) = (channels,height,width)
故需要转换为TensorFlow的图像表示(height,width,channels)
tf.transpose(image,[a,b,c])
如 image_new=tf.transpose(image,[1,2,0]) 参数二是将image的原来索引位置,换成现在对应的索引。
即image_new 0,1,2的位置的值,对应image 1,2,0位置的值
3、批处理
tf.train.batch(tensors, batch_size, num_threads = 1, capacity = 32,name=None)
NHWC与NCHW
在读取设置图片形状的时候有两种格式:
设置为“NHWC"时,排列顺序为 [batch, height, width,channels];
设置为“NCHW"时,排列顺序为[batch, channels, height, width]。
其中N表示这批图像有几张,H表示图像在竖直方向有多少像素,W表示水平方向像素数,C表示通道数。
Tensorflow默认的[height, width, channel]
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):
file_queue=tf.train.string_input_producer(file_list)
reader=tf.FixedLengthRecordReader(self.all_bytes)
key,value=reader.read(file_queue)
print('key: ',key)
print('value: ',value)
decode=tf.decode_raw(value,tf.uint8)
print('decode: ',decode)
label=tf.slice(decode,[0],[self.label_bytes])
image=tf.slice(decode,[self.label_bytes],[self.image_bytes])
print('label: ',label)
print('image: ',image)
image_reshaped=tf.reshape(image,shape = [self.channels,self.height,self.width])
print('image_reshaped: ',image_reshaped)
image_transposed=tf.transpose(image_reshaped,[1,2,0])
print('image_transposed: ',image_transposed)
image_cast=tf.cast(image_transposed,tf.float32)
label_batch,image_batch=tf.train.batch([label,image_cast],batch_size = 100,num_threads = 1,capacity = 100)
print('label_batch: ',label_batch)
print('image_batch: ',image_batch)
with tf.Session() as sess:
print('------------------开启会话------------------')
coord=tf.train.Coordinator()
threads=tf.train.start_queue_runners(sess=sess,coord = coord)
key_new,value_new,decode_new,label_new,image_new,image_reshaped_new,image_transposed_new,label_batch_new,image_batch_new=sess.run([key,value,decode,label,image,image_reshaped,image_transposed,label_batch,image_batch])
print('key_new: ',key_new)
print('value_new: ',value_new)
print('decode_new',decode_new)
print('label_new',label_new)
print('image_new',image_new)
print('image_reshaped_new\n',image_reshaped_new)
print('image_transposed_new\n',image_transposed_new)
print('label_batch_new\n',label_batch_new)
print('image_batch_new\n',image_batch_new)
coord.request_stop()
coord.join(threads)
return None
if __name__ == '__main__':
file_name=os.listdir('./data/cifar-10-batches-bin')
file_list=[os.path.join('./data/cifar-10-batches-bin',file) for file in file_name if file[-3:]=='bin']
print('file_llist: ',file_list)
cifar=Cifar()
cifar.read_and_decode(file_list)
'''
file_llist: ['./data/cifar-10-batches-bin\\data_batch_1.bin', './data/cifar-10-batches-bin\\data_batch_2.bin', './data/cifar-10-batches-bin\\data_batch_3.bin', './data/cifar-10-batches-bin\\data_batch_4.bin', './data/cifar-10-batches-bin\\data_batch_5.bin', './data/cifar-10-batches-bin\\test_batch.bin']
key: Tensor("ReaderReadV2:0", shape=(), dtype=string)
value: Tensor("ReaderReadV2:1", shape=(), dtype=string)
decode: Tensor("DecodeRaw:0", shape=(?,), dtype=uint8)
label: Tensor("Slice:0", shape=(1,), dtype=uint8)
image: Tensor("Slice_1:0", shape=(3072,), dtype=uint8)
image_reshaped: Tensor("Reshape:0", shape=(3, 32, 32), dtype=uint8)
image_transposed: Tensor("transpose:0", shape=(32, 32, 3), dtype=uint8)
label_batch: Tensor("batch:0", shape=(100, 1), dtype=uint8)
image_batch: Tensor("batch:1", shape=(100, 32, 32, 3), dtype=float32)
------------------开启会话------------------
key_new: b'./data/cifar-10-batches-bin\\data_batch_1.bin:0'
value_new: b'\x06;+2Dbw\x8b\x91\x95\x95\x83}\x8e\x90\x89\x81\x89\x86|\x8b\x8b\x85\x88\x8b\x98\xa3\xa8\x9f\x9e\x9e\x98\x94\x10\x00\x123Xx\x80\x7f~tjeiqmpwmi}\x7fz\x83|y\x83\x84\x85\x85{wz\x19\x101Sn\x81\x82yqppji\x80|\x82\x7fzsx\x82\x83\x8b\x7f~\x7f\x82\x8e\x82vxm!&Wjsuriky}mq\x92\x85\x7fvu\x7fz\x84\x89\x][SME7;:A;.9h\x8cTH'
decode_new [ 6 59 43 ... 140 84 72]
label_new [6]
image_new [ 59 43 50 ... 140 84 72]
image_reshaped_new
[[[ 59 43 50 ... 158 152 148]
[ 16 0 18 ... 123 119 122]
[ 25 16 49 ... 118 120 109]
...
[ 96 34 26 ... 70 7 20]
[ 96 42 30 ... 94 34 34]
[116 94 87 ... 140 84 72]]]
image_transposed_new
[[[ 59 62 63]
[ 43 46 45]
[ 50 48 43]
...
[216 184 140]
[151 118 84]
[123 92 72]]]
label_batch_new
[[6]
[0]
[2]
[7]
[2]
....
[2]
[8]]
image_batch_new
[[[[ 29. 43. 10.]
[ 22. 36. 4.]
[ 25. 37. 16.]
...
[197. 219. 137.]
[198. 219. 137.]
[199. 222. 140.]]
....]]]]
'''
3.4、tfrecords读取
3.4.1、什么是TFRecords文件
TFRecords其实是一种二进制文件,虽然它不如其他格式好理解,但是它能更好的利用内存,更方便复制和移动,并且不需要单独的标签文件。即tfrecords 特征值和目标值是绑定在一起的 使用步骤:
- 获取数据
- 将数据填入到Example协议内存块(protocol buffer)
- 将协议内存块序列化为字符串,并且通过tf.python_io.TFRecordwriter写入到TFRecords文件。
文件格式 * . tfrecords
3.4.2、Example结构解析
Example:
features {
feature{
key: "iamge"
value:{
bytes_list {
value: '\x06;+2Dbw\x8b\x91\x95\x95\x83}\x8e\x90\x89\x81\x89\x86|\x8b\x8b'
}
}
}
feature{
key: "label"
value:{
int64_list {
value: 9
}
}
}
}
- tf.train.Example协议内存块(protocol buffer)(协议内存块包含了字段Features)
- Features包含了一个Feature字段
- Feature中包含要写入的数据、并指明数据类型。
- 这是一个样本的结构,批数据需要循环存入这样的结构。 即一个example就是一个样本
example = tf.train.Example(features=tf.train.Features(feature={
“image” : tf.train.Feature(bytes_list=tf.train.BytesList(value=[Bytes])), “label” : tf.train.Feature(int64_list=tf.train.Int64List(value=[Value]))
}))
- tf.train.Example(features=None)
- 写入tfrecords文件
- features: tf.train.Features类型的特征实例
- return: example格式协议块
- tf.train.Features(feature=None)
- 构建每个样本的信息键值对
- feature:字典数据,key为要保存的名字
- value为tf.train.Feature实例
- return: Features类型
- tf.train.Feature(options)
- options:
- bytes_list=tf.train. BytesList(value=[Bytes])
- int64_list=tf.trai.Int64List(value=[Value])
- 支持存入的类型如下
- tf.train.Int64List(value=[Value]) 整型
- tf.train.BytesList(value=[Bytes]) 字节
- tf.train.FloatList(value=[value]) 浮点型
这种example结构很好地实现了数据和标签(训练的类别标签)或者其他属性数据存储在同一个文件中
example.SerializeToString() 将example 序列化到本地
3.4.1、tfrecords文件存储
- 构造存储实例,tf.python_io.TFRecordWriter(path)
- 写入tfrecords文件
- path: TFRecords文件的路径
- return:写文件
- method方法
- write(record):向文件中写入一个example
- close():关闭文件写入器
- 循环将数据填入到Example协议内存块(protocol buffer)
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):
file_queue=tf.train.string_input_producer(file_list)
reader=tf.FixedLengthRecordReader(self.all_bytes)
key,value=reader.read(file_queue)
decode=tf.decode_raw(value,tf.uint8)
label=tf.slice(decode,[0],[self.label_bytes])
image=tf.slice(decode,[self.label_bytes],[self.image_bytes])
image_reshaped=tf.reshape(image,shape = [self.channels,self.height,self.width])
image_transposed=tf.transpose(image_reshaped,[1,2,0])
image_cast=tf.cast(image_transposed,tf.float32)
label_batch,image_batch=tf.train.batch([label,image_cast],batch_size = 100,num_threads = 1,capacity = 100)
with tf.Session() as sess:
print('------------------开启会话------------------')
coord=tf.train.Coordinator()
threads=tf.train.start_queue_runners(sess=sess,coord = coord)
label_batch_new,image_batch_new=sess.run([label_batch,image_batch])
coord.request_stop()
coord.join(threads)
return label_batch_new,image_batch_new
def write_to_tfrecords(self,label_batch,image_batch):
with tf.python_io.TFRecordWriter('./temp/cifar10/cifar10.tfrecords') as tfWriter:
for i in range(label_batch.size):
image=image_batch[i].tostring()
label=label_batch[i][0]
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]))
}))
tfWriter.write(example.SerializeToString())
if __name__ == '__main__':
file_name=os.listdir('./data/cifar-10-batches-bin')
file_list=[os.path.join('./data/cifar-10-batches-bin',file) for file in file_name if file[-3:]=='bin']
print('file_llist: ',file_list)
cifar=Cifar()
label_batch,image_batch=cifar.read_and_decode(file_list)
cifar.write_to_tfrecords(label_batch,image_batch)
3.4.2、tfrecords文件读取
读取这种文件整个过程与其他文件一样,只不过需要有个解析Example的步骤。从TFRecords文件中读取数据,可以使用tf.TFRecordReader的 tf.parse_single_example解析器。这个操作可以将Example协议内存块(protocol buffer)解析为张量。
feature = tf.parse_single_example( values,features={
"image" : tf.FixedLenFeature([], tf.string),
"label" : tf.FixedLenFeature([], tf.int64)
})
- tf.parse_single_example(serialized, features=None, name=None)
- 解析一个单一的Example原型
- serialized:标量字符串Tensor,一个序列化的Example
- features: dict字典数据,键为读取的名字,值为FixedLenFeature
- return:一个键值对组成的字典,键为读取的名字
- tf.FixedLenFeature(shape, dtype)
- shape:输入数据的形状,一般不指定,为空列表
- dtype:输入数据类型,与存储进文件的类型要一致。
- 类型只能是float32, int64, string(对应bytes类型)
tfrecords文件读取步骤分析
- 构造文件名队列
- 读取与解码
- 读取
- 解析example
- 解码
- 批处理
- 开启会话
def write_to_tfrecords(self,label_batch,image_batch):
with tf.python_io.TFRecordWriter('./temp/cifar10/cifar10.tfrecords') as tfWriter:
for i in range(label_batch.size):
image=image_batch[i].tostring()
label=label_batch[i][0]
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]))
}))
tfWriter.write(example.SerializeToString())
def read_tfrecords(self,file_name):
file_queue=tf.train.string_input_producer([file_name])
tfReader=tf.TFRecordReader()
key,value=tfReader.read(file_queue)
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.channels])
print('image_reshaped\n',image_reshaped)
image_batch,label_batch=tf.train.batch([image_reshaped,label],batch_size = 100,num_threads = 1,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_new,label_new,image_decoded_new,image_reshaped_new,image_batch_new,label_batch_new=sess.run([image,label,image_decoded,image_reshaped,image_batch,label_batch])
print('image_new\n',image_new)
print('label_new\n',label_new)
print('image_decoded_new\n',image_decoded_new)
print('image_reshaped_new\n',image_reshaped_new)
print('image_batch_new\n',image_batch_new)
print('label_batch_new\n',label_batch_new)
coord.request_stop()
coord.join(threads)
return None
if __name__ == '__main__':
path='./temp/cifar10/cifar10.tfrecords'
cifar.read_tfrecords(path)
3.5、神经网络基础
3.5.1、定义
**人工神经网络(Artificial Neural Network,简写为ANN)也简称为神经网络(NN)。**是一种模仿生物神经网络(动物的中枢神经系统,特别是大脑)结构和功能的计算模型。经典的神经网络结构包含三个层次的神经网络。分别为输入层,输出层以及隐藏层。
其中每层的圆圈代表一个神经元,隐藏层和输出层的神经元有输入的数据计算后输出,输入层的神经元只是输入
神经网络的特点:
- 每个连接都有个权值
- 同一层神经元之间没有连接
- 最后的输出结构对应的层也称之为全连接层
那么为什么设计这样的结构呢?首先从一个最基础的结构说起,神经元。以前也称之为感知机。神经元就是要模拟人的神经元结构。
一个神经元通常具有多个树突,主要用来接受传入信息;而轴突只有一条,轴突尾端有许多轴突末梢可以给其他多个神经元传递信息。轴突末梢跟其他神经元的树突产生连接,从而传递信号。这个连接的位置在生物学上叫做**"突触”**。
感知机(PLA : Perceptron Learning Algorithm)
感知机就是模拟这样的大脑神经网络处理数据的过程。感知机模型如下图:
感知机是一种最基础的分类模型,类似于逻辑回归,不同的是,**感知机的激活函数用的是sign,而逻辑回归用的sigmoid。**感知机也具有连接的权重和偏置
3.5.2、神经网络原理
神经网络解决多分类问题最常用的方法是设置n个输出节点,其中n为类别的个数。
任意事件发生的概率都在0和1之间,且总有某一个事件发生(概率的和为1)。如果将分类问题中“一个样例属于某一个类别”看成一个概率事件,那么训练数据的正确答案就符合一个概率分布。如何将神经网络前向传播得到的结果也变成概率分布呢? Softmax回归就是一个非常常用的方法。
softmax回归
softmax回归将神经网络输出转换成概率结果
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)
这样子就把神经网络的输出也变成了一个概率输出
交叉熵损失函数: 衡量神经网络预测的概率和真实结果的概率之间的距离
交叉熵适合用于一个样本对应一个目标值
为了能够衡量距离,目标值需要进行one-hot编码,能与概率值――对应,如下图
计算
-[0log(0.10)+0log(0.05)+0log(0.15)+0log(0.10)+0log(0.05)+0log(0.20)+1log(0.10)…]
上述的结果为-1log(0.10),那么为了减少这一个样本的损失。神经网络应该怎么做?
答:提高对应目标值为1的位置输出概率大小。
损失大小
神经网络最后的损失为平均每个样本的损失大小。
即对所有样本的损失求和取其平均值
softmax、交叉熵损失API
tf.nn.softmax_cross_entropy_with_logits(labels=None, logits=None, name=None)
- abels:标签值(真实值)
- logits:样本加权之后的值
- return:返回损失值列表
损失大小求平均
tf.reduce_mean(input_tensor)
计算张量的尺寸的元素平均值
交叉熵的优化:梯度下降
3.5.3、案例:minst手写数字识别
数据集介绍
文件说明:
- 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
特征值
Mnist数据集可以从官网下载,网址: http://yann.lecun.com/exdb/mnist/下载下来的数据集被分成两部分:55000行的训练数据集(mnist.train)和10000行的测试数据集 (mnist.test)。每一个MNIST数据单元有两部分组成:一张包含手写数字的图片和一个对应的标签。我们把这些图片设为"xs",把这些标签设为"ys”。训练数据集和测试数据集都包含xs和ys,比如训练数据集的图片是mnist.train.images,训练数据集的标签是mnist.train.labels。
我们可以知道图片是黑白图片,每一张图片包含28像素X28像素。我们把这个数组展开成一个向量,长度是28x28 = 784。因此,在MNIST训练数据集中,mnist.trajn.images是一个形状为[60000,784]的张量。
目标值
MNIST中的每个图像都具有相应的标签,目标值:0到9之间的数字表示图像中绘制的数字。用的是one-hot编码 nn[0,0,0,1,0,0,0,0,0,0] mnist.train.labels [55000,10]
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
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets('mnist_data', one_hot=True)
print(mnist.train.next_batch(1))
print(mnist.train.images[0])
print(mnist.train.labels[0])
mnist手写体识别网络层设计
我们采用只有一层,即最后一个输出层的神经网络,也称之为全连接(full connected) 层神经网络
全连接层计算:即构造权重和偏置
- tf.matmul(a, b, name=None)+bias
- tf.train.GradientDescentOptimizer(learning_rate) :梯度下降(优化交叉熵)
- learning_rate:学习率
- method: minimize(loss):最小优化损失
计算准确率:对One-hot概率 求平均值
- equal_list = tf.equal(tf.argmax(y_predict,1), tf.argmax(y_label,1) # 1按列,比对预测值和真实值,如果相同置1
- accuracy = tf.reduce_mean(tf.cast(equal_list, tf.float32))
全连接分析:
y=w1x1+w2x2+…+b
y_predict[None,10]=X[None,784]*weights[784,10]+bias[10]
error=tf.nn.softmax_cross_entropy_with_logits(labels=y_true, logits=y_predict)
def full_connection():
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])
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
loss_list=tf.nn.softmax_cross_entropy_with_logits(logits = y_predict,labels = y_true)
loss=tf.reduce_mean(loss_list)
optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.01).minimize(loss)
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)
_, 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__':
full_connection()
'''
第1次的损失为2.292725,准确率为0.160000
第2次的损失为2.331739,准确率为0.020000
…………………………
第100次的损失为1.510217,准确率为0.730000
'''
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)。
- 卷积层: 通过在原始图像上平移来提取特征
- 激活层: 增加非线性分割能力
- 池化层(下采样层): 减少学习的参数,降低网络的复杂度(最大池化和平均池化)
- **全连接层:**实现分类效果
- **输出层:**进行损失计算并输出分类结果
卷积神经网络的结构
- CONV: 卷积层
- RELU: 激活层
- POOL: 池化层
- FC(Full Connection): 全连接层
4.2.2、卷积层(Convolutional Layer)
卷积核又叫 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* 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,输出高宽与输入大小一样(重要)
4.2.3、激活函数
随着神经网络的发展,大家发现原有的sigmoid等激活函数并不能达到好的效果,所以才去新的激活函数。
不同激活函数网站演示:http://playground.tensorflow.org
Relu函数
效果:
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:取池化窗口的平均值
池化层计算: 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
例子2:
输入图片大小为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
注:卷积层向下取整,池化层向上取整
池化层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
- 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]
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
4.2.6、卷积神经网络总结
4.3、案例:CNN-Mnist手写数字识别
4.3.1、网络层设计
自己设计一个简单的卷积神经网络做图像识别。由于神经网络的黑盒子特性,如果想自己设计复杂网络通常还是比较困难的,可以使用一些现有的网络结构如之前的GoogleNet、vGG等等。
简单的网络结构
具体参数
- 第一层
- 卷积:32个filter、大小5 * 5、strides=1、padding=“SAME”。
- padding=same 指输出图像和输入一样,故宽高都跟原来的值一样
- 激活:Relu
- 池化:大小2x2、strides2
- 第二层
- 卷积:64个filter、大小5 * 5、strides=1、padding=“SAME”
- 激活:Relu
- 池化:大小2x2、strides2
- 全连接层
经过每一层图片数据大小的变化需要确定,Mnist输入的每批次若干图片数据[None,784],如果要经过卷积计算,需要变成[None, 28, 28,1] , 28 x 28=784
卷积网络API: tf.nn.conv2d( input,filter, strides=,padding=)
-
第一层卷积大层
-
c -
32个filter 大小F:5 * 5 步长S:1 padding=“SAME” -
padding=same 指输出图像和输入一样,故宽高都跟原来的值一样 -
输入图片形状 [betch, heigth,width, channel] ,要求:类型为float32,64 -
input:输入图像[None,28,28,1] filter: 变量initial_value=random_normal(shape=[F,F,channels,K]) ? weights = tf.Variable(initial_value=tf.random_normal(shape=[5,5,1,32])) ? bias = tf.Variable(initial_value=tf.random_normal(shape=[32])) strides: 步长1 ? [1,1,1,1] padding: "SAME” SAME”︰越过边缘取样 “VALID”":不越过边缘取样 -
输出形状:因为是padding=same,故输出宽高跟输入一致
-
激活层: Relu tf.nn.relu(features, name=None) -
池化层: 大小2*2 步长2 ,默认padding=0 tf.nn.max_pool(value,ksize=, strides=, padding=,name=None)
-
输出形状:[None,14,14,32] -
第二层卷积大层
-
卷积层: tf.nn.conv2d( input,filter, strides=,padding=) -
64个filter 大小5 * 5 步长:1 padding=“SAME” -
输入现状: [None,14,14,32] -
filter: 变量initial_value=random_normal(shape=[F,F,channels,K]) ? weights = tf.Variable(initial_value=tf.random_normal(shape=[5,5,32,64])) ? bias = tf.Variable(initial_value=tf.random_normal(shape=[64])) strides: 步长1 ? [1,1,1,1] padding: "SAME” SAME”︰越过边缘取样 “VALID”":不越过边缘取样 -
输出形状:因为是padding=same,故输出宽高跟输入一致
-
激活层: Relu tf.nn.relu(features, name=None) -
池化层: 大小2*2 步长2 ,默认padding=0 tf.nn.max_pool(value,ksize=, strides=, padding=,name=None)
-
输出形状:[None,7,7,64] -
全连接层
- tf.reshape()
- [None,7,7,64]-----转换为二阶矩阵-> pool2 = [None,7 * 7 * 64]
- 最终的目标值形式[None,10]=[None,7 * 7 * 64] * [7 * 7 * 64 ,10](权重)
- y_predict = tf.matmul(pool2,weights) + bias
CNN-Minst卷积神经网络简单案例:
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
def create_weights(shape):
return tf.Variable(initial_value = tf.random_normal(shape = shape,stddev = 0.01))
def create_model(x):
with tf.variable_scope('conv1'):
input_x=tf.reshape(x,shape = [-1,28,28,1])
conv1_weights=create_weights(shape = [5,5,1,32])
conv1_bias=create_weights(shape = [32])
conv1_x=tf.nn.conv2d(input = input_x,filter = conv1_weights,strides = [1,1,1,1],padding="SAME")+conv1_bias
relu1_x=tf.nn.relu(conv1_x)
pool1_x=tf.nn.max_pool(value =relu1_x,ksize = [1,2,2,1],strides = [1,2,2,1],padding = "SAME")
with tf.variable_scope('conv2'):
conv2_weights = create_weights(shape = [5, 5, 32, 64])
conv2_bias = create_weights(shape = [64])
conv2_x = tf.nn.conv2d(input = pool1_x, filter = conv2_weights, strides = [1, 1, 1, 1],padding = "SAME") + conv2_bias
relu2_x = tf.nn.relu(conv2_x)
pool2_x = tf.nn.max_pool(value = relu2_x, ksize = [1, 2, 2, 1], strides = [1, 2, 2, 1], padding = "SAME")
with tf.variable_scope('full_connection'):
x_fc=tf.reshape(pool2_x,shape = [-1,7*7*64])
weights_fc=create_weights(shape = [7*7*64,10])
bias_fc=create_weights(shape = [10])
y_predict=tf.matmul(x_fc,weights_fc)+bias_fc
return y_predict
def full_connection():
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])
y_predict=create_model(X)
loss_list=tf.nn.softmax_cross_entropy_with_logits(logits = y_predict,labels = y_true)
loss=tf.reduce_mean(loss_list)
optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.01).minimize(loss)
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(300):
image, label = mnist.train.next_batch(100)
_, 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__':
full_connection()
'''
第66次的损失为2.066249,准确率为0.200000
第67次的损失为1.938220,准确率为0.310000
第68次的损失为1.739915,准确率为0.450000
第69次的损失为1.615815,准确率为0.470000
'''
网络的优化和改进
- 调整学习率、随机初始化的权重和偏置
- 调整优化器,优化损失函数
- 使用不同的激活函数
- 重新设计网络
- 使用梯度截断(在训练过程中检查和限制梯度的大小)
- 对于深度网络模型,添加batch normalization层或者droupout层
- batch normalization层 (批量标准化,让这层的网络输出的权重、偏置或者参数分布归一化,即分布在同样的规律内)
- droupout层 (使当前的神经元失效,减低模型的复杂度)
def weight__variables(shape ) :
w = tf.Variable(tf.random_normal(shape=shape,mean=0.0,stddev=0.1)
return w
卷积神经网络结构在imagenet比赛对比
卷积网络其它用途
- Yolo: GoogleNet+ bounding boxes
- SD: VGG + region proposalso
- Faster-RCNN: VGG, ResNet
4.4、案例:验证码图片识别
步骤分析
- 计算损失率
- 验证码图片(如NZPP)–》一个样本对应四个特征值。–》使用sigmoid交叉熵
- 手写数字图片识别((0~9之间的某一个数)–》一个样本对应一个特征值–》使用softmax交叉熵
- NZPP–》根据字母的顺序标记 [13,25,15,15] -->[4,26]
- –>[ [ 0, 0,0,0,0,o,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0 ], [ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],[0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0]]
- 数据集
特征值 目标值怎么用 - 如何分类?
- 如何比较输出结果和真实值的正确性?如何衡量损失?
手写数字识别案例 softmax+交叉嫡 [4,26] ->[4*26] sigmoid交叉嫡 准确率如何计算? 核心:对比真实值和预测值最大值所在位置手写 - 数字识别案例
y_predict [None,10] tf.argmax(y_predict,axis=1) y_predict [None,4,26] tf.argmax(y_predict, axis=2/-1) [True, True, True, False] -> tf.reduce_all() -> False - 流程分析
- 读取图片数据
- 解析csv文件,将标签值NZPP->[13,25,15,15]
- 将filename和标签值联系起来
- 构建卷积神经网络->y_predict
- 构造损失函数
- 优化损失
- 计算准确率
- 开启会话、开启线程
步骤分析
特征值目标值–模型
特征值:6000张图片-目标值―—对应
-
读取图片数据
- key, value = read(file_queue)key:文件名- labels.csv -目标值value:一个样本的内容
-
解析CSV文件,建立文件名和标签值对应表格 -
将一个样本的特征值和目标值一一对应
- 通过文件名查表(csv_data)
-
建立卷积神经网络模型== 》得出y_predict -
计算sigmoid交叉嫡损失 -
优化 -
计算准确率
import tensorflow as tf
import pandas as pd
import numpy as np
import glob
def read_pic():
file_names=glob.glob('./data/GenPics/*.jpg')
file_queue=tf.train.string_input_producer(file_names)
reader=tf.WholeFileReader()
filename,image=reader.read(file_queue)
decoded_image=tf.image.decode_jpeg(image)
decoded_image.set_shape([20,80,3])
cast_image=tf.cast(decoded_image,tf.float32)
filename_batch,image_batch=tf.train.batch([filename,cast_image],batch_size = 100,num_threads = 1,capacity = 200)
return filename_batch,image_batch
def parse_csv():
csv_data=pd.read_csv('./data/GenPics/labels.csv',names = ['file_num','chars'],index_col = 'file_num')
labels=[]
for label in csv_data['chars']:
letter=[]
for word in label:
letter.append(ord(word)-ord('A'))
labels.append(letter)
csv_data['labels']=labels
return csv_data
def filename2label(filenames,csv_data):
labels=[]
for file_name in filenames:
file_num="".join((list(filter(str.isdigit,str(file_name)))))
target_labels=csv_data.loc[int(file_num),'labels']
labels.append(target_labels)
return np.array(labels)
def create_weights(shape):
return tf.Variable(initial_value = tf.random_normal(shape = shape,stddev = 0.01))
def create_model(x):
with tf.variable_scope('conv1'):
conv1_weights=create_weights(shape = [5,5,3,32])
conv1_bias=create_weights(shape = [32])
conv1_x=tf.nn.conv2d(input = x,filter = conv1_weights,strides = [1,1,1,1],padding="SAME")+conv1_bias
relu1_x=tf.nn.relu(conv1_x)
pool1_x=tf.nn.max_pool(value =relu1_x,ksize = [1,2,2,1],strides = [1,2,2,1],padding = "SAME")
with tf.variable_scope('conv2'):
conv2_weights = create_weights(shape = [5, 5, 32, 64])
conv2_bias = create_weights(shape = [64])
conv2_x = tf.nn.conv2d(input = pool1_x, filter = conv2_weights, strides = [1, 1, 1, 1],padding = "SAME") + conv2_bias
relu2_x = tf.nn.relu(conv2_x)
pool2_x = tf.nn.max_pool(value = relu2_x, ksize = [1, 2, 2, 1], strides = [1, 2, 2, 1], padding = "SAME")
with tf.variable_scope('full_connection'):
x_fc=tf.reshape(pool2_x,shape = [-1,5*20*64])
weights_fc=create_weights(shape = [5*20*64,4*26])
bias_fc=create_weights(shape = [4*26])
y_predict=tf.matmul(x_fc,weights_fc)+bias_fc
return y_predict
if __name__ == '__main__':
filename,image=read_pic()
csv_data=parse_csv()
x=tf.placeholder(tf.float32,shape = [None,20,80,3])
y_true=tf.placeholder(tf.float32,shape = [None,4*26])
y_predict=create_model(x)
loss_list=tf.nn.sigmoid_cross_entropy_with_logits(labels = y_true,logits = y_predict)
loss=tf.reduce_mean(loss_list)
optimizer=tf.train.AdamOptimizer(learning_rate = 0.1).minimize(loss)
equal_list=tf.reduce_all(tf.equal(tf.argmax(tf.reshape(y_predict,shape = [-1,4,26]),axis = 2),
tf.argmax(tf.reshape(y_true, shape = [-1, 4,26]), axis = 2)),axis=1)
accuracy=tf.reduce_mean(tf.cast(equal_list,tf.float32))
init=tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init)
coor=tf.train.Coordinator()
threads=tf.train.start_queue_runners(sess = sess,coord = coor)
for i in range(150):
filename_value,image_value=sess.run([filename,image])
labels=filename2label(filename_value,csv_data)
labels_value=tf.reshape(tf.one_hot(labels,depth = 26),[-1,4*26]).eval()
_,error,accuracy_value=sess.run([optimizer,loss,accuracy],feed_dict = {x:image_value,y_true:labels_value})
print('第%d次训练损失为%f,准确率为%f'%(i+1,error,accuracy_value))
coor.request_stop()
coor.join(threads)
|