在文章[TensorFlow] argmax, softmax_cross_entropy_with_logits, sparse_softmax_cross_entropy_with_logits函数详解中,提到了交叉熵损失函数的计算方式以及tensorflow中的输入和输出等。本篇文章会更细地讲一下tensorflow中交叉熵损失函数的应用,以及在优化过程中可能用到加权交叉熵损失函数的使用方式。
当存在多个类别时,通常使用交叉熵损失函数来衡量模型的效果,也是对模型调参的重要依据,交叉熵损失函数的公式为:
L = 1 N ∑ i L i = − 1 N ∑ i ∑ C = 1 M y i c l o g P i c \begin{aligned} L = \frac{1}{N}\sum_{i}L_i = -\frac{1}{N} \sum_{i} \sum_{C=1}^My_{ic}logP_{ic} \end{aligned} L=N1i∑Li=−N1i∑C=1∑MyiclogPic
代码:
import tensorflow as tf
import numpy as np
sess=tf.Session()
#logits代表wx+b的输出,注意:logits没有进行softmax
logits = np.array([[1, 2, 3],
[4, 5, 6],
[7, 10, 3],
[8, 2, 0],
[9, 6, 3]], dtype=np.float32)
#labels是[2,2,1,0,1]的ont-hot编码形式
labels = np.array([[0, 0, 1],
[0, 0, 1],
[0, 1, 0],
[1, 0, 0],
[0, 1, 0]], dtype=np.float32)
softmax_out=tf.nn.softmax(logits)
print("softmax_out is::")
print(sess.run(softmax_out))
print("labels * tf.log(softmax_out) is::")
print(sess.run(labels * tf.log(softmax_out))) # label和softmax_out取log后相乘
print("cross_entropy1 is::")
cross_entropy1 = -tf.reduce_sum(labels * tf.log(softmax_out), axis=1) # 每个样本的不同label,求损失和
print(sess.run(cross_entropy1))
结果:
softmax_out is::
[[9.0030573e-02 2.4472848e-01 6.6524094e-01]
[9.0030573e-02 2.4472848e-01 6.6524094e-01]
[4.7384717e-02 9.5174748e-01 8.6788135e-04]
[9.9719369e-01 2.4717962e-03 3.3452120e-04]
[9.5033026e-01 4.7314156e-02 2.3556333e-03]]
labels * tf.log(softmax_out) is::
[[-0.0000000e+00 -0.0000000e+00 -4.0760601e-01]
[-0.0000000e+00 -0.0000000e+00 -4.0760601e-01]
[-0.0000000e+00 -4.9455535e-02 -0.0000000e+00]
[-2.8102510e-03 -0.0000000e+00 -0.0000000e+00]
[-0.0000000e+00 -3.0509458e+00 -0.0000000e+00]]
cross_entropy1 is::
[4.0760601e-01 4.0760601e-01 4.9455535e-02 2.8102510e-03 3.0509458e+00]
1.两个函数的输出结果相同,区别在于输入的labels不同。
2.对于sparse_softmax_cross_entropy_with_logits, labels的size是[batch_size],每个label的取值范围是[0, num_classes-1],即每个样本的label就是0、1、2.
3.对于softmax_cross_entropy_with_logits, labels的size是[batch_size, num_classes],即sparse_softmax_cross_entropy_with_logits中labels的one-hot值。
代码:
import tensorflow as tf
import numpy as np
sess = tf.Session()
# logits代表wx+b的输出,注意:logits没有进行softmax
logits = np.array([[1, 2, 3],
[4, 5, 6],
[7, 10, 3],
[8, 2, 0],
[9, 6, 3]], dtype=np.float32)
print("cross_entropy2 is::")
cross_entropy2 = tf.nn.softmax_cross_entropy_with_logits(logits=logits, labels=labels)
print(sess.run(cross_entropy2))
# labels是[2,2,1,0,1]的ont-hot编码形式
labels = np.array([[0, 0, 1],
[0, 0, 1],
[0, 1, 0],
[1, 0, 0],
[0, 1, 0]], dtype=np.float32)
print("cross_entropy3 is::")
classes = tf.argmax(labels, axis=1) # array([2,2,1,0,1])
cross_entropy3 = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=logits, labels=classes)
print(sess.run(cross_entropy3))
结果:
cross_entropy2 is::
[4.0760595e-01 4.0760595e-01 4.9455538e-02 2.8102214e-03 3.0509458e+00]
cross_entropy3 is::
[4.0760595e-01 4.0760595e-01 4.9455538e-02 2.8102214e-03 3.0509458e+00]
1.主要用于进行不同样本的loss计算,但可通过权重来控制loss损失值
2.默认weights=1,等价于tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits)
3.weights为标量w时,等价于w*tf.reduce_mean(tf.nn.softmax_corss…)
4.weights为向量时,算出的每个loss需要乘以对应样本权重,再求均值
5.tf.losses.sparse_softmax_cross_entropy 同理等等价于tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits),只不过输入labels是非one-hot编码格式
代码:
import tensorflow as tf
import numpy as np
sess = tf.Session()
#logits代表wx+b的输出,注意:logits没有进行softmax
logits = np.array([[1, 2, 3],
[4, 5, 6],
[7, 10, 3],
[8, 2, 0],
[9, 6, 3]], dtype=np.float32)
#labels是[2,2,1,0,1]的ont-hot编码形式
labels = np.array([[0, 0, 1],
[0, 0, 1],
[0, 1, 0],
[1, 0, 0],
[0, 1, 0]], dtype=np.float32)
cross1 = tf.nn.softmax_cross_entropy_with_logits(labels=labels,logits=logits)
cross2 = tf.losses.softmax_cross_entropy(onehot_labels=labels, logits=logits)
cross3 = tf.losses.softmax_cross_entropy(onehot_labels=labels, logits=logits, weights=0.3)
print("cross1 is::")
print (sess.run(cross1))
print("cross2 is::")
print (sess.run(cross2))
print("tf.reduce_mean(cross1) is::")
print (sess.run(tf.reduce_mean(cross1)))
print("cross3 is::")
print (sess.run(cross3))
print("0.3*tf.reduce_mean(cross1) is::")
print (sess.run(0.3*tf.reduce_mean(cross1)))
结果:
cross1 is::
[4.0760595e-01 4.0760595e-01 4.9455538e-02 2.8102214e-03 3.0509458e+00]
cross2 is::
0.7836847
tf.reduce_mean(cross1) is::
0.7836847
tf.losses.softmax_cross_entropy可以对损失进行加权,当每个样本的权重不同时,可以按照样本的权重加权。
import tensorflow as tf
import numpy as np
sess = tf.Session()
#logits代表wx+b的输出,注意:logits没有进行softmax
logits = np.array([[1, 2, 3],
[4, 5, 6],
[7, 10, 3],
[8, 2, 0],
[9, 6, 3]], dtype=np.float32)
#labels是[2,2,1,0,1]的ont-hot编码形式
labels = np.array([[0, 0, 1],
[0, 0, 1],
[0, 1, 0],
[1, 0, 0],
[0, 1, 0]], dtype=np.float32)
cross2 = tf.losses.softmax_cross_entropy(onehot_labels=labels, logits=logits)
cross3 = tf.losses.softmax_cross_entropy(onehot_labels=labels, logits=logits, weights=0.3) # 每个样本都权重0.3
print("cross2 is::")
print (sess.run(cross2))
print("cross3 is::")
print (sess.run(cross3))
print("0.3*tf.reduce_mean(cross1) is::")
print (sess.run(0.3*tf.reduce_mean(cross1)))
cross4 = tf.losses.softmax_cross_entropy(onehot_labels=labels, logits=logits,weights=[1, 2, 3, 4, 5]) # 用于控制每个样本的权重
print("cross4 is::", cross4)
print (sess.run(cross4))
print("sum", 4.0760595e-01 * 1 + 4.0760595e-01 * 2 + 4.9455538e-02 * 3 + 2.8102214e-03 * 4 + 3.0509458e+00 * 5)
print("average", (4.0760595e-01 * 1 + 4.0760595e-01 * 2 + 4.9455538e-02 * 3 + 2.8102214e-03 * 4 + 3.0509458e+00 * 5)/5.)
结果:
cross2 is::
0.7836847
cross3 is::
0.23510543
0.3*tf.reduce_mean(cross1) is::
0.23510541
cross4 is::
3.3274307
sum 16.6371543496
average 3.3274308699199997
实际可能会遇到这样的情况,对于每个样本是没有权重的,但是针对不同的类别,有相应的权重,比如,类别3更不希望错误,所以类别3的损失权重可能会设置的比其他大。
对于我们需要重视的类别,可以给其较高的权重,(权重越高,损失越大,模型越会学好这个类别),如:
某个类别较少,则可以给出较高的权重,使其训练的更好
某个类别不允许错误,则需要尽量训练好这个数据,可以将其权重调高
#logits代表wx+b的输出,注意:logits没有进行softmax
logits = np.array([[1, 2, 3],
[4, 5, 6],
[7, 10, 3],
[8, 2, 0],
[9, 6, 3]], dtype=np.float32)
#labels是[2,2,1,0,1]的ont-hot编码形式
labels = np.array([[0, 0, 1],
[0, 0, 1],
[0, 1, 0],
[1, 0, 0],
[0, 1, 0]], dtype=np.float32)
class_weights = tf.constant([[1.0, 1.5, 4.0]]) # 3个类别的权重,用于控制每个类别的权重
weights = tf.reduce_sum(class_weights * labels, axis=1) # 向量化的权重
print("weights is::", sess.run(weights))
cross1 = tf.nn.softmax_cross_entropy_with_logits(labels=labels,logits=logits)
print("cross1 is::")
print(sess.run(cross1))
weighted_losses = cross1 * weights
print("weighted_losses is::")
print(sess.run(weighted_losses))
print("tf.reduce_mean(weighted_losses) is::")
print(sess.run(tf.reduce_mean(weighted_losses)))
softmax_out=tf.nn.softmax(logits)
print("softmax_out is::")
print(sess.run(softmax_out))
print("labels * tf.log(softmax_out) is::")
print(sess.run(labels * tf.log(softmax_out))) # label和softmax_out取log后相乘
sum = 4.0760601e-01 * 4 + 4.0760601e-01 * 4 + 4.9455535e-02 * 1.5 + 2.8102510e-03 * 1 + 3.0509458e+00 * 1.5 # 通过普通的计算方法看下是否与结果一致
print("sum/5 is::", sum/5)
结果:
weights is:: [4. 4. 1.5 1. 1.5]
cross1 is::
[4.0760595e-01 4.0760595e-01 4.9455538e-02 2.8102214e-03 3.0509458e+00]
weighted_losses is::
[1.6304238e+00 1.6304238e+00 7.4183308e-02 2.8102214e-03 4.5764189e+00]
tf.reduce_mean(weighted_losses) is::
1.582852
softmax_out is::
[[9.0030573e-02 2.4472848e-01 6.6524094e-01]
[9.0030573e-02 2.4472848e-01 6.6524094e-01]
[4.7384717e-02 9.5174748e-01 8.6788135e-04]
[9.9719369e-01 2.4717962e-03 3.3452120e-04]
[9.5033026e-01 4.7314156e-02 2.3556333e-03]]
labels * tf.log(softmax_out) is::
[[-0.0000000e+00 -0.0000000e+00 -4.0760601e-01]
[-0.0000000e+00 -0.0000000e+00 -4.0760601e-01]
[-0.0000000e+00 -4.9455535e-02 -0.0000000e+00]
[-2.8102510e-03 -0.0000000e+00 -0.0000000e+00]
[-0.0000000e+00 -3.0509458e+00 -0.0000000e+00]]
sum/5 is:: 1.5828520667
上面是针对不同的类别,权重不同,但是若不是针对固定的类别有权重,而是比如:将类别1预测为类别2,这种错误代价较高,反而将类别1预测为类别3代价较低,这种方式怎么来进行损失加权呢?如预测值和真实值预测权重矩阵为:
真实类别1 | 真实类别2 | 真实类别3 | |
---|---|---|---|
预测类别1 | 1 | 4 | 2 |
预测类别2 | 5 | 1 | 1 |
预测类别3 | 4 | 3 | 1 |
其中 w 01 = 4 w_{01}=4 w01=4表示预测类别为1实际类别为2的权重, w 02 = 2 w_{02}=2 w02=2表示预测类别为1实际类别为3的权重。
代码:
#logits代表wx+b的输出,注意:logits没有进行softmax
logits = np.array([[1, 2, 3],
[4, 5, 6],
[7, 10, 3],
[2, 8, 0],
[9, 6, 3]], dtype=np.float32)
#labels是[2,2,1,0,1]的ont-hot编码形式
labels = np.array([[0, 1, 0],
[0, 0, 1],
[0, 1, 0],
[1, 0, 0],
[0, 1, 0]], dtype=np.float32)
transfer_weights = tf.constant([[1.0, 4.0, 2.0],
[5.0, 1.0, 1.0],
[4.0, 3.0, 1.0]])
weighted_logits = tf.matmul(logits, transfer_weights)
print("weighted_logits is::")
print(sess.run(weighted_logits)) # 得到预测值为logits情况下,实际label=1、label=2、label=3下的加权logits
softmax_out=tf.nn.softmax(weighted_logits)
print("softmax_out is::")
print(sess.run(softmax_out))
print("labels * tf.log(softmax_out) is::")
print(sess.run(labels * tf.log(softmax_out))) # label和softmax_out取log后相乘
cross1 = tf.nn.softmax_cross_entropy_with_logits(labels=labels,logits=weighted_logits)
cross2 = tf.losses.softmax_cross_entropy(onehot_labels=labels, logits=weighted_logits)
print ("cross1 is::", sess.run(cross1))
print ("cross2 is::", sess.run(cross2))
sum = 8.000336 + 34. + 22. + 0. + 0.6931472 # 常规算法,与参数结果比较是否一致
print("average is::", sum/5.)
结果:
weighted_logits is::
[[23. 15. 7.]
[53. 39. 19.]
[69. 47. 27.]
[42. 16. 12.]
[51. 51. 27.]]
softmax_out is::
[[9.99664545e-01 3.35350080e-04 1.12497425e-07]
[9.99999166e-01 8.31528041e-07 1.71390690e-15]
[1.00000000e+00 2.78946810e-10 5.74952202e-19]
[1.00000000e+00 5.10908893e-12 9.35762291e-14]
[5.00000000e-01 5.00000000e-01 1.88756719e-11]]
labels * tf.log(softmax_out) is::
[[ -0. -8.000336 -0. ]
[ -0. -0. -34. ]
[ 0. -22. -0. ]
[ 0. -0. -0. ]
[ -0. -0.6931472 -0. ]]
cross1 is:: [ 8.000336 34. 22. 0. 0.6931472]
cross2 is:: 12.938696
average is:: 12.93869664
参考文章:
Tensorflow交叉熵的代码实现