真实愚蠢,这个地方之前以为自己明白了,其实只是知道tensorflow中怎么用,但是原理不是很清楚,自己太浮躁了。
交叉熵概念: 交叉熵是一个信息论中的概念,它原来是用来估算平均编码长度的。给定两个概率分布p和q,通过q来表示p的多分类交叉熵为:
H ( p , q ) = − ∑ x p ( x ) log q ( x ) ( 1 ) H(p,q) = -\sum_xp(x)\log q(x) (1) H(p,q)=−x∑p(x)logq(x)(1)
二分类交叉熵公式
H ( x ) = − p ( x ) log ( p ( x ) ) − ( 1 − p ( x ) ) log ( 1 − p ( x ) ) ( 2 ) H(x)=-p(x)\log (p(x))-(1-p(x))\log (1-p(x)) (2) H(x)=−p(x)log(p(x))−(1−p(x))log(1−p(x))(2)
二分类交叉函数不只是用来对两个类别进行分类的,比如说有两个类别猫和狗,判断一个图片是猫还是狗,可能是既有狗也有猫,图片里既有猫也有狗,预测概率为[0.7, 0.7],真实标签为[1, 1],那么 h = 2 ∗ ( − log 0.7 − log 0.3 ) h=2*(-\log0.7-\log0.3) h=2∗(−log0.7−log0.3),也就是说给每个类别分一个判别器,负责判断每个位置是不是本类别。
如果二分类的时候,列别互斥的时候要用公式(1),如果多分类不互斥要用(2)
注意,交叉熵刻画的是两个概率分布之间的距离,或可以说它刻画的是通过概率分布 q q q来表达概率分布 p p p的困难程度,p代表正确答案,q代表的是预测值,交叉熵越小,两个概率的分布约接近。
s o f t f m a x ( y ) i = e y i ∑ j = 1 n e y i softfmax(y)_i=\frac{e^{y_i}}{\sum_{j=1}^ne^{y_i}} softfmax(y)i=∑j=1neyieyi
softmax概念
s o f t f m a x ( y ) i = e y i ∑ j = 1 n e y i softfmax(y)_i=\frac{e^{y_i}}{\sum_{j=1}^ne^{y_i}} softfmax(y)i=∑j=1neyieyi
举个例子,假设有一个3分类问题,某个样例的正确答案是(1,0,0),这个模型经过softmax回归之后的预测答案是(0.5,0.4,0.1),那么预测和正确答案之间的交叉熵为:
sigmoid函数
s i g m o i d ( y ) = 1 / ( 1 + e − y ) sigmoid(y)=1/(1+e^{-y}) sigmoid(y)=1/(1+e−y)
正常的神经网络输出结果后都要进行上述两个函数处理,那么什么时候用哪个呢?
softmax的计算互斥,所以用于二分类 ,因为二分类的每类就是互斥的,同一张图片不可能同时属于多个类别。
sigmoid 的计算独立,所以用于多标签分类,因为多标签分类的每个标签都是独立的 ,同一张图片可以有多个类别
如果分类的结果相互排斥,也就是公式(1)对应的形式,那么要用softmax进行多分类,如果是二分类如公式(2)则要用sigmoid处理。
下面所讲tensorflow中三种交叉熵损失函数。
softmax_cross_entropy_with_logits包含两个作用:1、计算softmax回归,2、求cross_entropy。
记 s i = s o f t m a x ( y ) i , p i 是 真 实 值 s_i=softmax(y)_i,p_i是真实值 si=softmax(y)i,pi是真实值
c r o s s e n t r o p y = − ∑ x s ( x ) log q ( x ) cross_{entropy}=-\sum_xs(x)\log q(x) crossentropy=−x∑s(x)logq(x)
交叉熵损失函数是用来计算分类问题的损失值的,常用的有以下三种
tf.nn.sigmoid_cross_entropy_with_logits(_sentinel=None,labels=None, logits=None, name=None)
argument:
_sentinel:本质上是不用的参数,不用填
logits:一个数据类型(type)是float32或float64;
shape:[batch_size,num_classes],单样本是[num_classes]
labels:和logits具有相同的type(float)和shape的张量(tensor),
name:操作的名字,可填可不填
output:
loss,shape:[batch_size,num_classes]
Note:
y = l a b e l s y = labels y=labels
p i j = s i g m o i d ( l o g i t s i j ) = 1 1 + e − l o g i t s i j p_{ij} = sigmoid(logits{ij})=\frac{1}{1+e^{-logits_{ij}}} pij=sigmoid(logitsij)=1+e−logitsij1
l o s s i j = − [ y i j ∗ ln p i j + ( 1 − y i j ln ( 1 − p i j ) ] loss_ij = -[y_{ij}*\ln p_{ij} + (1-y_{ij}\ln (1-p_{ij})] lossij=−[yij∗lnpij+(1−yijln(1−pij)]
它对于输入的logits先通过sigmoid函数计算,再计算它们的交叉熵,但是它对交叉熵的计算方式进行了优化,使得结果不至于溢出它适用于每个类别相互独立但互不排斥的情况:例如一幅图可以同时包含一条狗和一只大象output不是一个数,而是一个batch中每个样本的loss,所以一般配合tf.reduce_mea(loss)使用
x = logits, z = labels
z * -log(sigmoid(x)) + (1 - z) * -log(1 - sigmoid(x))
= z * -log(1 / (1 + exp(-x))) + (1 - z) * -log(exp(-x) / (1 + exp(-x)))
= z * log(1 + exp(-x)) + (1 - z) * (-log(exp(-x)) + log(1 + exp(-x)))
= z * log(1 + exp(-x)) + (1 - z) * (x + log(1 + exp(-x))
= (1 - z) * x + log(1 + exp(-x))
= x - x * z + log(1 + exp(-x))
对于x<0,为了避免exp(-x)溢出,我们用下面的等式代替上面的
x - x * z + log(1 + exp(-x))
= log(exp(x)) - x * z + log(1 + exp(-x))
= - x * z + log(1 + exp(x))
为了确保稳定性,并且避免溢出,用下面的等式:
max(x, 0) - x * z + log(1 + exp(-abs(x)))
tensorflow实现:
importtensorflowas tf
import numpy asnp
def sigmoid(x):
return 1.0/(1+np.exp(-x))
# 5个样本三分类问题,且一个样本可以同时拥有多类
y = np.array([[1,0,0],[0,1,0],[0,0,1],[1,1,0],[0,1,0]]
logits = np.array([[12,3,2],[3,10,1],[1,2,5],[4,6.5,1.2],[3,6,1]])
y_pred = sigmoid(logits)
E1 = -y*np.log(y_pred)-(1-y)*np.log(1-y_pred)
print(E1) # 按计算公式计算的结果
sess =tf.Session()
y = np.array(y).astype(np.float64) # labels是float64的数据类型
E2 = sess.run(tf.nn.sigmoid_cross_entropy_with_logits(labels=y,logits=logits))
print(E2)
logits:一个数据类型(type)是float32或float64;
shape:[batch_size,num_classes]
labels:和logits具有相同type和shape的张量(tensor),,是一个有效的概率,sum(labels)=1, one_hot=True(向量中只有一个值为1.0,其他值为0.0)
name:操作的名字,可填可不填
output:
loss,shape:[batch_size]
Note:
它对于输入的logits先通过softmax函数计算,再计算它们的交叉熵,但是它对交叉熵的计算方式进行了优化,使得结果不至于溢出它适用于每个类别相互独立且排斥的情况,一幅图只能属于一类,而不能同时包含一条狗和一只大象output不是一个数,而是一个batch中每个样本的loss,所以一般配合tf.reduce_mean(loss)使用
y = l a b e l s y = labels y=labels
p i = s o f t m a x ( l o g i t s i ) = [ e l o g i t s i j ∑ j = 0 n u m c l a s s e s − 1 e l o g i t s i j ] p_i=softmax(logits_i)=[\frac{e^{logits_{ij}}}{\sum_{j=0}^{numclasses-1}e^{logits_{ij}}}] pi=softmax(logitsi)=[∑j=0numclasses−1elogitsijelogitsij]
l o s s i = − ∑ j = 0 n u m c l a s s e s − 1 y i j ∗ ln p i j loss_i =-\sum_{j=0}^{numclasses-1}y_{ij}*\ln p_{ij} lossi=−∑j=0numclasses−1yij∗lnpij
import tensorflow as tf
import numpy as np
def softmax(x):
sum_raw = np.sum(np.exp(x),axis=-1)
x1 = np.ones(np.shape(x))
for i inrange(np.shape(x)[0]):
x1[i] = np.exp(x[i])/sum_raw[i]
return x1
y = np.array([[1,0,0],[0,1,0],[0,0,1],[1,0,0],[0,1,0]])# 每一行只有一个1
logits =np.array([[12,3,2],[3,10,1],[1,2,5],[4,6.5,1.2],[3,6,1]])
y_pred =softmax(logits)
E1 = -np.sum(y*np.log(y_pred),-1)
print(E1)
sess = tf.Session()
y = np.array(y).astype(np.float64)
E2 = sess.run(tf.nn.softmax_cross_entropy_with_logits(labels=y,logits=logits))
print(E2)
'''E1 output
[1.68795487e-04 1.03475622e-03 6.58839038e-02 2.58349207e+00
5.49852354e-02]
E2 output
[1.68795487e-04 1.03475622e-03 6.58839038e-02 2.58349207e+00
5.49852354e-02]
argument:
_sentinel:本质上是不用的参数,不用填
logits:一个数据类型(type)是float32或float64;
shape:[batch_size,num_classes]
labels: shape为[batch_size],labels[i]是{0,1,2,……,num_classes-1}的一个索引, type为int32或int64
name:操作的名字,可填可不填
output:
loss,shape:[batch_size]
Note:
它对于输入的logits先通过softmax函数计算,再计算它们的交叉熵,但是它对交叉熵的计算方式进行了优化,使得结果不至于溢出它适用于每个类别相互独立且排斥的情况,一幅图只能属于一类,而不能同时包含一条狗和一只大象
output不是一个数,而是一个batch中每个样本的loss,所以一般配合tf.reduce_mean(loss)使用
计算公式:
和tf.nn.softmax_cross_entropy_with_logits()一样,只是要将labels转换成tf.nn.softmax_cross_entropy_with_logits()中labels的形式