层次
网络层+设备层----->数据结构层----->图计算层----->API层----->应用层
设计理念
- 将图的定义和图的运行分开
- TensorFlow中涉及的运算都要放在图中,而图的运行只发生在会话中。
编程模型
计算过程:输入----->影藏层----->在输出前使用ReLu激活函数----->输出层/Logit----->Softmax计算出结果中各个类别的概率分布----->使用交叉熵来度量两个概率分布之间的相似性----->计算梯度----->SGD训练
边
TF的边有两种连接关系:数据依赖,数据控制。实线边表示数据依赖,代表数据,即张量。虚线边,控制依赖,这类边上没有数据流过。虚边常用代码如下:tf.Grapg.control_dependengcies(control_inputs)
节点
节点又叫算子,代表一个操作。一般用来表示施加的数学运算,也可以表示数据输入的起点以及输出的终点,或者是读取/写入持久变量的终点。类别:数学的运算操作,数据的运算操作,矩阵的运算操作,有状态的操作,神经网络构建操作,检查点操作,队列和同步操作,控制张量流动的操作。
图
把操作任务描述成有向无环图。
会话
启动图的第一步就是创建一个Session对象,会话提供在图中执行一些操作的一些方法。一般模式是建立会话同时生成一张空图,在会话中添加节点和边形成一张图,然后执行。
设备
一块可以用来运算并且拥有自己的地址空间的硬件,如CPU和GPU。TF可以明确指定操作在那个设备上执行。
变量
变量是一种特殊的数据,他在图中有固定的位置,不像普通张量那样可以流动。
内核
操作是对抽象操作的一个统称,而内核则是能够运行在特定设备上的一种对操作的实现,因此,同一个操作可能对应多个内核。
常用API
图
操作 | 描述 |
---|---|
tf.Graph.init() | 创建一个空图 |
tf.Graph.as_default() | 将图设为默认图 |
tf.Graph.device(device_name_or_function) | 运行图所使用的设备 |
tf.Graph.name_scope(name) | 为节点创建层次化的名称 |
操作
操作 | 描述 |
---|---|
tf.Operation.name | 操作的名称 |
tf.Operation.type | 操作的类型 |
tf.Operation.inputs | 操作的输入 |
tf.Operation.outpots | 操作的输出 |
tf.Operation.control_inputs | 操作的依赖 |
tf.Operation.run(feed_dict=None,session=None) | 在会话中运行该操作 |
tf.Operation.get_attr | 获取操作的属性值 |
Tensor类
操作 | 描述 |
---|---|
tf.Tensor.dtype | 张量的数据类型 |
tf.Tensor.name | 张量的名称 |
tf.Tensor.value_index | 张量在操作输出中的索引 |
tf.Tensor.graph | 张量所在的图 |
tf.Tensor.op | 产生该张量的操作 |
tf.Tensor.consumers() | 返回使用该张量的操作列表 |
tf.Tensor.eval(feed_dict=None,session=None) | 在会话中求张量的值,需要使用sess.as_default或者eval(session=sess) |
tf.Tensor.get_shape() | 返回用于表示张量的形状的类TensorFlow |
tf.Tensor.set_shape(shape) | 更新张量的形状 |
tf.Tensor.device | 更新计算张量的设备 |
可视化
API | 描述 |
---|---|
tf.summary.FileWrite.init(logdir,graph=None,max_queue=10,flush_secs=120,graph_def=None) | 创建FileWritter和事件文件,会在logdir中创建一个新的事件文件 |
tf.summary.FileWrite.add_summary(summary,global_step=None) | 将摘要添加到事件文件 |
tf.summary.FileWrite.add_event(event) | 向事件文件中添加一个事件 |
tf.summary.FileWrite.add_graph(graph,global_step=None,graph_def=None) | 向事件文件中添加一个图 |
tf.summary.FileWrite.get_logdir() | 获取事件文件的路径 |
tf.summary.FileWrite.flush() | 将所有事件写入磁盘 |
tf.summary.FileWrite.close() | 将事件写入磁盘,并关闭文件操作符 |
tf.summary.scalar(name,tensor,collections=None) | 输出包含单个标量值的摘要 |
tf.summary.histogram(name,values,collections=none) | 输出包含直方图的摘要 |
tf.summary.audio(name,tensor,sample_rate,max_outputs=3,collections=None) | 输出包含音频的摘要 |
tf.summary.images(name,tensor,max_uotputs=3,collections=None) | 输出包含图片的摘要 |
tf.summary.merge(inputs,collections=None,name=None) | 合并摘要,包含所有输入摘要的值 |
批标准化
批标准化(BN)是为了克服神经网络层数加深导致难以训练的问题。
统计机器学习有一个ICS理论,这是一个经典假设:源域和目标域的数据分布是一致的。即训练数据和测试数据是满足相同分布的。
Covariate Shift是指训练集的样本数据和目标样本集分布不一致时,训练得到的模型无法更好的泛华。是分布不一致下的一个分支问题。解决办法一般是将训练样本和目标样本的比例对训练样本做一个矫正。通过引入批标准化来规范化某些层次或者所有层的输入,从而固定每层输入信号的均值和方差。
方法:一般用在非线性映射之前,对x=Wu+b做规范化,使结果的均值为0,方差为1。
**优点:**加大探索的步长,加快收敛的速度。更容易跳出局部最小值。破坏原来的数据分布,一定程度上缓和过拟合。
激活函数
激活函数运行时激活神经网络中的某一部分神经元,将激活信息向后传入下一层的神经网络。神经网络之所以能够解决非线性问题,本质上就是因为激活函数加入了非线性的因素,弥补了线性模型的表达能力。**激活函数不会改变输入数据的维度。**常用的激活函数有:sigmoid,tanh,relu,softplus。
- sigmoid函数,使用方法
import tensorflow as tf
a = tf.constant([[1.0,2.0],[1.0,2.0],[1.0,2.0]])
sess=tf.Session()
print sess.run(tf.sigmoid(a))
输出:
[[0.7310586 0.880797 ]
[0.7310586 0.880797 ]
[0.7310586 0.880797 ]]
sigmoid函数的优点在于输出映射在(0,1)之间,单调连续,非常适合做输出层,求导也比较容易。缺点是软饱和性。
- tanh函数
tanh也具有软饱和性,因为他的输出是以0为中心,收敛速度比sigmod要快,但是仍然无法解决梯度消失的问题。 - relu函数,使用如下:
a = tf.constant([[1.0,2.0],[1.0,2.0],[1.0,2.0]])
with tf.Session() as sess:
b=tf.nn.relu(a)
print sess.run(b)
输出:
[[1. 2.]
[1. 2.]
[1. 2.]]
- dropout函数。一个神经元将以概率keep_prob决定是否被抑制,如果被抑制,该神经元的输出就为0。如果不被抑制,输出值将被放大到原来的1/keep_prob倍。
a = tf.constant([[1.0,2.0,3.0,4.0]])
with tf.Session() as sess:
b=tf.nn.dropout(a,0.5,noise_shape=[1,4])
print sess.run(b)
b=tf.nn.dropout(a,0.5,noise_shape=[1,1])
print sess.run(b)
输出:
[[0. 4. 0. 0.]]
[[2. 4. 6. 8.]]
激活函数的选择:输入数据特征相差明显时,用tanh,不明显是用sigmoid。使用上面两个的时候需要对特征进行规范化,否则激活后的值全部进入平坦区。relu不需要输入规范化来避免上述情况。
卷积函数
卷积函数是构建神经网络的重要支架,是一批图像上扫描的二位过滤器。
- tf.nn.convolution,计算N维卷积的和。
- tf.nn.conv2d,对一个四维输入数据input和四维的卷积核filter进行操作,然后对输入数据进行一个二维的卷积操作,最后得到卷积之后的结果。
- tf.nn.depthwise_conv2d(),输入张量的数据唯独是[batch,in_height,iin_width,in_channels],卷积核的维度是[filter_height,filter_width,in_channels,channel_multipiler]。
- tf.nn.separable_conv2d()
- tf.nn.atrous_conv2d()
- tf.nn.conv2d_transpose()
- tf.nn.conv1d()
- tf.nn.conv3d()
池化函数
池化函数一般位于卷积函数的下一层。利用一个矩阵窗口在张量上进行扫描,将每个矩阵窗口中的值通过取最大值或平均值来减少元素个数。
- tf.nn.avg_pool()
- tf.nn.max_pool()
- tf.nn.max_pool_with_argmax()
- tf.nn.max_pool3d()
- tf.nn.avg_pool3d()
- tf.nn.fractional_avg_pool()
- tf.nn.fractional_max_pool()
优化方法
加上训练优化一般都是基于梯度下降的。这里TensorFlow提供了许多的优化器。介绍下面8个。
- tf.train.GradientDescentOptimizer()
- tf.train.AdadeltaOptimizer()
- tf.train.AdagradOptimizer()
- tf.train.AdagradDAOptimizer()
- tf.train.MomentumOptimizer()
- tf.train.AdamOptimizer()
- tf.train.FtrlOptimizer()
- tf.train.RMSPropOptimizer()
1、BDG,批梯度下降法。
2、SD个,随机梯度下降法。
3、Momentum,模拟物理学动量的概念,更新时在一定程度上保留之前的更新方向。
4、Nesterov Momentum,对上面方法的一项改进。
5、Adagrad,自适应的为各个参数分配不同的学习率,能够控制每个维度的梯度方向。
6、Adadelta,用一阶的方法近似模拟牛顿二阶牛顿法。
7、RMSprop,和Momentum类似,对RNN效果很好。
8、Adam。
模型的加载与存储
如何将训练好的神经模型存储起来。
1、生成检查点文件,一般扩展名为.ckpt。通过在tf.train.Saver对象上调用Saver.save()完成。
2、生成图协议文件,.pb,tf.train.write_graph()来保存,tf.import_graph_def()加载。
队列和线程
NULL
加载数据
- 预加载数据
- 填充数据
- 从文件中读取数据
实现一个自定义操作。
NULL
参考书籍:《TensorFlow技术解析与实践》,作者:李嘉璇