tensorflow常用方法

记载一些tensorflow常用方法,方便以后查找。

激活函数

激活函数不会更改输入数据的维度,也就是输入和输出的维度是相同的。TensorFlow 中有如下激活函数,它们定义在tensorflow-1.1.0/tensorflow/python/ops/nn.py 文件中。加粗的是常用的函数。

tf.nn.relu()

图片3

relu 在x<0 时硬饱和。由于x="">0 时导数为1,所以,relu 能够在x>0 时保持梯度不衰减,从而缓解梯度消失问题,还能够更很地收敛,并提供了神经网络的稀疏表达能力。但是,随着训练的进行,部分输入会落到硬饱和区,导致对应的权重无法更新,称为“神经元死亡”。softplus可以看作是ReLU 的平滑版本.

tf.nn.sigmoid()

图片1

sigmoid 函数的优点在于,它的输出映射在$(0,1)$内,单调连续,非常适合用作输出层,并且求导比较容易。但是,它也有缺点,因为软饱和性(软饱和是指激活函数$h(x)$在取值趋于无穷大时,它的一阶导数趋于0。硬饱和是指当$|x| > c$时,其中$c$ 为常数,$f ‘(x)=0$。relu 就是一类左侧硬饱和激活函数),一旦输入落入饱和区,$f ‘(x)$就会变得接近于0,很容易产生梯度消失。

tf.nn.tanh()

图片2

tanh 函数也具有软饱和性。因为它的输出以0 为中心,收敛速度比sigmoid 要很。但是仍
无法解决梯度消失的问题。

tf.nn.elu()

tf.nn.bias_add()

tf.nn.crelu()

tf.nn.relu6()

tf.nn.softplus()

tf.nn.softsign()

tf.nn.dropout()

防止过拟合,用来舍弃某些神经元

选择策略

当输入数据特征相差明显时,用tanh 的效果会很好,且在循环过程中会不断扩大特征效果并显示出来。当特征相差不明显时,sigmoid 效果比较好。同时,用sigmoid 和tanh 作为激活函数时,需要对输入进行规范化,否则激活后的值全部都进入平坦区,隐层的输出会全部趋同,丧失原有的特征表达。而relu 会好很多,有时可以不需要输入规范化来避免上述情况。
因此,现在大部分的卷积神经网络都采用relu 作为激活函数。我估计大概有85%~90%的神经网络会采用ReLU,10%~15%的神经网络会采用tanh,尤其用在自然语言处理上。

分类函数

sigmoid_cross_entropy_with_logits

softmax

log_softmax

softmax_cross_entropy_with_logits

sparse_softmax_cross_entropy_with_logits

优化方法

BGD、SGD、Momentum 和Nesterov Momentum 是手动指定学习率的,其余算法能够自动调节学习率。

梯度下降法(BGD 和SGD)

class tf.train.GradientDescentOptimizer
BGD 的全称是batch gradient descent,即批梯度下降。这种方法是利用现有参数对训练集中的每一个输入生成一个估计输出$y_i$,然后跟实际输出$y_i$ 比较,统计所有误差,求平均以后得到平均误差,以此作为更新参数的依据。它的迭代过程为:
(1)提取训练集中的所有内容${x_1, …, x_n}$,以及相关的输出$y_i$;
(2)计算梯度和误差并更新参数。
这种方法的优点是,使用所有训练数据计算,能够保证收敛,并且不需要逐渐减少学习率;缺点是,每一步都需要使用所有的训练数据,随着训练的进行,速度会越来越慢。那么,如果将训练数据拆分成一个个批次(batch),每次抽取一批数据来更新参数,是不是会加速训练呢?这就是最常用的SGD。
SGD 的全称是stochastic gradient descent,即随机梯度下降。因为这种方法的主要思想是将数据集拆分成一个个批次(batch),随机抽取一个批次来计算并更新参数,所以也称为MBGD(minibatch gradient descent)。SGD 在每一次迭代计算mini-batch 的梯度,然后对参数进行更新。与BGD 相比,SGD 在训练数据集很大时,仍能以较很的速度收敛。但是,它仍然会有下面两个缺点。
(1)由于抽取不可避免地梯度会有误差,需要手动调整学习率(learning rate),但是选择合适的学习率又比较困难。尤其在训练时,我们常常想对常出现的特征更新速度很一些,而对不常出现的特征更新速度慢一些,而SGD 在更新参数时对所有参数采用一样的学习率,因此无法满足要求。
(2)SGD 容易收敛到局部最优,并且在某些情况下可能被困在鞍点。为了解决学习率固定的问题,又引入了Momentum 法。

Momentum法(Momentum 和Nesterov Momentum)

class tf.train.MomentumOptimizer
Momentum 是模拟物理学中动量的概念,更新时在一定程度上保留之前的更新方向,利用当前的批次再微调本次的更新参数,因此引入了一个新的变量v(速度),作为前几次梯度的累加。因此,Momentum 能够更新学习率,在下降初期,前后梯度方向一致时,能够加速学习;在下降的中后期,在局部最小值的附近来回震荡时,能够抑制震荡,加很收敛。
Nesterov Momentum 法由Ilya Sutskever 在Nesterov 工作的启发下提出的,是对传统Momentum法的一项改进,其基本思路如图所示。
图片4
标准Momentum 法首先计算一个梯度(短的1 号线),然后在加速更新梯度的方向进行一个大的跳跃(长的1 号线);Nesterov 项首先在原来加速的梯度方向进行一个大的跳跃(2 号线),然后在该位置计算梯度值(3 号线),然后用这个梯度值修正最终的更新方向(4 号线)。

Adagrad法(Adagrad 和AdagradDAO)

class tf.train.AdagradOptimizer
class tf.train.AdagradDAOptimizer
Adagrad 法能够自适应地为各个参数分配不同的学习率,能够控制每个维度的梯度方向。这种方法的优点是能够实现学习率的自动更改:如果本次更新时梯度大,学习率就衰减得很一些;如果这次更新时梯度小,学习率衰减得就慢一些。

Adadelta法

class tf.train.AdadeltaOptimizer
Adagrad 法仍然存在一些问题:其学习率单调递减,在训练的后期学习率非常小,并且需要手动设置一个全局的初始学习率。Adadelta 法用一阶的方法,近似模拟二阶牛顿法,解决了这些问题。

RMSProp 法

class tf.train.RMSPropOptimizer
RMSProp 法与Momentum 法类似,通过引入一个衰减系数,使每一回合都衰减一定比例。在实践中,对循环神经网络(RNN)效果很好。

Adam

class tf.train.AdamOptimizer
Adam 的名称来源于自适应矩估计(adaptive moment estimation)。Adam 法根据损失函数针对每个参数的梯度的一阶矩估计和二阶矩估计动态调整每个参数的学习率。

Ftrl法

class tf.train.FtrlOptimizer

模型存储与加载

模型存储

模型存储主要是建立一个tf.train.Saver()来保存变量,并且指定保存的位置,一般模型的扩展名为.ckpt
使用样例

加载模型

如果有已经训练好的模型变量文件,可以用saver.restore 来进行模型加载:

1
2
3
4
5
6
7
with tf.Session() as sess:
tf.initialize_all_variables().run()
ckpt = tf.train.get_checkpoint_state(ckpt_dir)
if ckpt and ckpt.model_checkpoint_path:
print(ckpt.model_checkpoint_path)
saver.restore(sess, ckpt.model_checkpoint_path) # 加载所有的参数
# 从这里开始就可以直接使用模型进行预测,或者接着继续训练了

图的存储与加载

当仅保存图模型时,才将图写入二进制协议文件中,例如:

1
2
3
v = tf.Variable(0, name='my_variable')
sess = tf.Session()
tf.train.write_graph(sess.graph_def, '/tmp/tfmodel', 'train.pbtxt')

当读取时,又从协议文件中读取出来:

1
2
3
4
5
6
with tf.Session() as _sess:
with gfile.FastGFile("/tmp/tfmodel/train.pbtxt",'rb') as f:
graph_def = tf.GraphDef()
graph_def.ParseFromString(f.read())
_sess.graph.as_default()
tf.import_graph_def(graph_def, name='tfgraph')

加载数据

TensorFlow 作为符号编程框架,需要先构建数据流图,再读取数据,随后进行模型训练。TensorFlow 官方网站给出了以下读取数据3 种方法。

  • 预加载数据(preloaded data):在TensorFlow 图中定义常量或变量来保存所有数据。
  • 填充数据(feeding):Python 产生数据,再把数据填充后端。
  • 从文件读取数据(reading from file):从文件中直接读取,让队列管理器从文件中读取数据。

预加载数据

预加载数据的示例如下:

1
2
3
x1 = tf.constant([2, 3, 4])
x2 = tf.constant([4, 0, 1])
y = tf.add(x1, x2)

这种方式的缺点在于,将数据直接嵌在数据流图中,当训练数据较大时,很消耗内存。

填充数据

使用sess.run()中的feed_dict 参数,将Python 产生的数据填充给后端。

1
2
3
4
5
6
7
8
9
10
11
import tensorflow as tf
# 设计图
a1 = tf.placeholder(tf.int16)
a2 = tf.placeholder(tf.int16)
b = tf.add(x1, x2)
# 用Python 产生数据
li1 = [2, 3, 4]
li2 = [4, 0, 1]
# 打开一个会话,将数据填充给后端
with tf.Session() as sess:
print sess.run(b, feed_dict={a1: li1, a2: li2})

填充的方式也有数据量大、消耗内存等缺点,并且数据类型转换等中间环节增加了不小开销。这时最好用第三种方法,在图中定义好文件读取的方法,让TensorFlow 自己从文件中读取数据,并解码成可使用的样本集。

从文件读取数据