Loss
1. L2损失
tf.nn.l2_loss(t, name=None)
output = sum(t ** 2) / 2
2. 交叉熵损失
C
=
−
1
n
∑
x
[
y
ln
a
+
(
1
−
y
)
ln
(
1
−
a
)
]
C=-\frac{1}{n}\sum_{x}[y\ln a +(1-y)\ln(1-a)]
C=−n1∑x[ylna+(1−y)ln(1−a)]
a是经过sigmoid激活的
a
=
σ
(
∑
j
w
j
x
j
+
b
)
a=\sigma (\sum_{j}w_jx_j + b)
a=σ(∑jwjxj+b),在0到1之间
交叉熵为什么能作为代价函数?
首先,它是非负的,加号里的每一项都是负的,整个式子前面是负的,负负得正
其次,实际输出值很接近期望输出值时,交叉熵会非常接近0
它有均方代价函数不具备的特征,能够避免学习速率降低的问题
TensorFlow四个交叉熵函数
tf.nn.sigmoid_cross_entrogy_with_logits
tf.nn.softmax_cross_entrogy_with_logits
tf.nn.sparse_softmax_cross_entrogy_with_logits
tf.nn.weighted_cross_entrogy_with_logits
(1)
tf.nn.sigmoid_cross_entrogy_with_logits(logits, targets, name=None)
# logits: 网络模型中的W*X矩阵,不需要经过sigmoid
# targets: 标签值,可包含多个1或0,和logits的shape相同
不能用于多分类问题,例如一个实际取值范围为0-4,目标标签y也是0-4,实际取值经过sigmoid后取值范围变为0-1,ln(.)是负的,公式中(1-y)会出现负数,而且-yln(a)有可能会非常大
(2)
对于单目标多分类问题,分类之间是独立且互斥的,这时候就要用 softmax的交叉熵函数
- 先看softmax激活:
tf.nn.softmax(logits, dim=-1, name=None)
# logits:Tensor
# dim: 默认为-1,最后一个维度
softmax = exp(logits) / reduce_sum(exp(logits), dim)
softmax输出0-1之间的概率分布
- logsoftmax激活:
tf.nn.log_softmax(logits, dim=-1, name=None)
logsoftmax = logits - log(reduce_sum(exp(logits), dim))
- softmax交叉熵损失:
tf.nn.softmax_cross_entrogy_with_logits(logits, labels, dim=-1, name=None)
注意:只适合单目标二分类或者多分类问题
对于多分类问题,我们需要对输入进行编码,例如我们的年龄分为5类,人工编码为0,1,2,3,4,因为输出值是5维特征,因此我们需要人工做onehot encoding,分别编码为00001,00010,00100,01000,10000,作为函数的输入
(3)
tf.nn.sparse_softmax_cross_entrogy_with_logits(logits, labels, name=None)
# logits: shape=[batch_size, num_classes]
# labels: shape=[batch_size, num_classes] num_classes从0开始编码
是softmax_cross_entrogy_with_logits
的易用版本,对于CIFAR-10、ImageNet都是多类单标签问题,label都是0,1,2,3,…每次转成onehot encoding比较麻烦,使用sparse_softmax_cross_entrogy_with_logits
,TensorFlow内部进行编码
(4)
tf.nn.weights_cross_entrogy_with_logits(targets, logits, pos_weight, name=None)
let x = logits, z = targets q = pos_weights
是softmax_cross_entrogy_with_logits
的拓展版,pos_weight
的目的是增加或者减少正样本在算Cross Entropy的损失,原理:在正样本算出的值前面乘以一个系数
loss = qz * -log(sigmoid(x)) + (1 - z) * -log(1 - sigmoid(x))
=(1 - z) * x + (1 + (q - 1) * z) * log(1 + exp( - x))
Optimizer
两个作用:计算损失的梯度,将梯度应用到变量的更新中去
Optimizer是一个基类:class tf.train.Optimizer
使用的时候,需要实例化它的一个子类,如下面的例子:
# 传入学习率参数创建一个优化器类的对象实例
opt = GradientDescentOptimizer(learning_rate=0.1)
# 添加一系列操作节点到计算图最小化cost来更新变量
opt_op = opt.minimize(cost, var_list=<list of variables>) # 传入损失函数和变量列表,var_list不传的话更新的是所有的变量
用处理的梯度更新模型参数
调用minimize()
有两个连续步骤:计算梯度,应用到变量上,如下:
- 使用
comepute_gradients()
计算梯度 - 使用
apply_gradients()
把处理过的梯度用来更新varibles
如果想在应用在变量之前对梯度做一些特别处理时,需要在这两步之间再加上一些处理:
- 使用
comepute_gradients()
计算梯度 - 根据需求对计算出的梯度做一些处理
- 使用
apply_gradients()
把处理过的梯度用来更新varibles
例子:
# 传入学习率参数创建一个优化器类的对象实例
opt = GradientDescentOptimizer(learning_rate=0.1)
# 计算梯度
# grads_and_vars是一个元组列表(变量所对应的梯度, 变量)
grads_and_vars = opt.compute_gradients(loss, <list of variables>)
# 对梯度元组中的每一个值进行操作
capped_grads_and_vars = [(MyCapper(gc[0]), gv[1]) for gv in grads_and_vars]
# 用新的梯度更新参数
opt.apply_gradients(capped_grads_and_vars)
优化器子类:
10个
tf.train.GradientDescentOptimizer()
tf.train.ProximalGradientDescentOptimizer()
tf.train.AdagradOptimizer()
tf.train.ProximalAdagradOptimizer()
tf.train.AdagradDAOptimizer()
tf.train.AdadeltaOptimizer()
tf.train.AdamOptimizer()
tf.train.RMSPropOptimizer()
tf.train.FtrlOptimizer()
tf.train.MomentumOptimizer()
这篇也有讲:https://blog.youkuaiyun.com/fzp95/article/details/83018744
比较
conv+relu+maxpool+linear_fc
tf.train.GradientDescentOptimizer()
tf.train.ProximalGradientDescentOptimizer()
tf.train.AdagradOptimizer()
tf.train.ProximalAdagradOptimizer()
tf.train.AdagradDAOptimizer()
tf.train.AdadeltaOptimizer() 在小网络上表现很差
tf.train.AdamOptimizer() 学习率选取很重要,不能太小,也不能太大,合适的学习率优化效果好。
tf.train.FtrlOptimizer() 最优学习率为0.1 不能太小
tf.train.MomentumOptimizer() 对于小学习率能够快速下降
tf.train.RMSPropOptimizer() 对学习率变化不太敏感 而且优化效果好