激活函数与对应的损失函数选择(binary与multi-class如何选择损失函数)

之前一段时间,对激活函数和损失函数一直是懵懂的状态,只知道最后一层常用的激活函数是sigmoid或者softmax,而损失函数一般用的是cross-entropy或者dice loss(我看的都是分割方向的)。但是有几个点一直弄不清为什么:
1、为什么有时候二值分割问题还进行了one-hot编码?
2、sigmoid和softmax是可以任意切换的吗?

下面是我理解的东西,但不一定正确,仅供参考。

首先要知道,一般说到cross-entropy,都指的是categorical cross-entropy。
binary cross-entropy是需要特意说明为binary的。

那么,categorical cross-entropy的定义是什么呢?
就是对于输出的score,先进行softmax变换,再计算cross-entropy。
重点就在这儿,是进行了softmax变换!原因后面会说到。

binary cross-entropy的定义则是,对于对于输出的scroe,先进行sigmoid变换,再计算binary cross entropy。
看,这儿又是sigmoid!

原因就在于,cross entropy本身的定义就是在概率分布上,而概率分布则要求两个或两个以上的值加起来等于1。如果去看softmax函数,会发现,这个函数就有这个作用:使每一个样本的输出结果在所有的类上加起来等于1。而sigmoid函数,去看它的形状就可以发现,它只是单纯的将输出结果压缩到了0-1之间。

那么现在,我们想计算binary的cross-entropy怎么办呢?
有两种解决方案

第一,就是利用现有的专门针对binary的cross-entropy公式
在这里插入图片描述
可以发现,其实这儿的1-p(yi)就是做了与softmax类似的事,!!将输出的结果相加可以为1。
所以说,已经可以发现,它们之间那紧密的联系了。

第二,就是将binary转化为categorical的问题。
也就是直接利用cross-entropy的公式
在这里插入图片描述
可是上面也提到过,用这个公式的前提就是输出结果在所有的类上加起来为1。
那么,我们使用softmax激活函数就行了,但是别忘记,对于categorical cross entropy的问题,默认的label都是需要one-hot编码的。

2019/7/10更新:更正上面的话:label都是需要one-hot编码,其实严谨的说法是,对于cross-entropy公式而言,其要求是label进行了one-hot编码,但由于我们一般都是使用框架的api计算损失,因此不同的api会对label有着不同的要求,对于tensorflow而言,关于cross-entropy常用的就有下面三种

第一、二种的label都需要自己进行one-hot编码处理,第三种不需要
1: tf.nn.softmax_cross_entropy_with_logits(其实这种已经depressed,被第二种代替)
2:tf.nn.softmax_cross_entropy_with_logits_v2(这种相对于第一种而言,反向传播时也会传递label,这在非监督式学习中很有用)
3:tf.nn.sparse_softmax_cross_entropy_with_logits(可以用tf.argmax()转换one-hot为普通格式)

注意,上一句说的使用softmax函数,前提也是把数据先转化成了对应的格式,如下:

conv6 = Conv2D(2, (1, 1), activation='relu',padding='same')(conv5)
conv6 = core.Reshape((2,patch_height*patch_width))(conv6)
conv6 = core.Permute((2,1))(conv6)
# conv6最后的shape:(batch_size,height*width,2)
conv7 = core.Activation('softmax')(conv6)
model = Model(inputs=inputs, outputs=conv7)
# sgd = SGD(lr=0.01, decay=1e-6, momentum=0.3, nesterov=False)
model.compile(optimizer='sgd', loss='categorical_crossentropy',metrics=['accuracy'])

举一个小例子
2019-10-8 更新,下面的例子有点问题,重新举例
典型的categorical问题:
红灯:(1,0,0) 绿灯(0,1,0) 黄灯(0,0,1)
输出的结果(注:已经经过激活函数)preds=(0.25,0.6,0.15),所以预测结果为绿灯。
那么最后的cross-entropy loss=-1*[ln(0.25)*0 + ln(0.6)*1 + ln(0.15)*0]
若是二值问题,例如,男女预测,男为1,女为0,输出结果为0.6,
我们可以对其进行变化:
男(1,0) 女(0,1),输出(0.6, 0.4)

针对分割问题,现在有训练的一对数据 (images, labels)
其中,进入网络的labels格式为(batch_size,width,height,depth,1)
最后的channel等于1,但并不代表是二分类问题,可以是多分类。
若最后使用的loss api是tf.nn.sparse_softmax_cross_entropy_with_logits的话,那么,网络的输出格式应该是preds:(batch_size,width,height,depth,class_number),并且不能经过softmax变换,因为api的要求是with logits,
loss=tf.nn.sparse_softmax_cross_entropy_with_logits(preds,squeeze(labels))
从上边preds的格式也能看出,无论api是否为sparse,对于preds而言都需要为one-hot格式

stackoverflow上的一段代码能帮助更好的理解:
https://stackoverflow.com/questions/58159154/how-to-calculate-categorical-cross-entropy-by-hand

scce = tf.keras.losses.SparseCategoricalCrossentropy()
cce = tf.keras.losses.CategoricalCrossentropy()

labels_scce = K.variable([[0, 1, 2]]) 
labels_cce  = K.variable([[1,    0,  0], [0,    1,  0], [0,   0,   1]])
preds       = K.variable([[.90,.05,.05], [.50,.89,.60], [.05,.01,.94]])

loss_cce  = cce(labels_cce,   preds, from_logits=False)
loss_scce = scce(labels_scce, preds, from_logits=False)
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    sess.run([loss_cce, loss_scce])

print(K.get_value(loss_cce))
print(K.get_value(loss_scce))
# [0.10536055  0.8046684  0.0618754]
# [0.10536055  0.8046684  0.0618754]
"""
As to how to do it 'by hand', we can refer to the Numpy backend:
"""
np_labels = K.get_value(labels_cce)
np_preds  = K.get_value(preds)

losses = []
for label, pred in zip(np_labels, np_preds):
    pred /= pred.sum(axis=-1, keepdims=True)
    losses.append(np.sum(label * -np.log(pred), axis=-1, keepdims=False))
print(losses)
# [0.10536055  0.8046684  0.0618754]

最后提一下dice loss,这个只需要最后出来的是概率值即可,所以说一般选择为sigmoid激活函数。

conv10 = Conv2D(1, (1, 1), activation='sigmoid')(conv9)

model = Model(inputs=[inputs], outputs=[conv10])

model.compile(optimizer=Adam(lr=1e-5), loss=dice_coef_loss, metrics=[dice_coef])


def dice_coef(y_true, y_pred):
    smooth = 1.
    y_true_f = K.flatten(y_true)
    y_pred_f = K.flatten(y_pred)
    intersection = K.sum(y_true_f * y_pred_f)
    return (2. * intersection + smooth) / (K.sum(y_true_f) + K.sum(y_pred_f) + smooth)


def dice_coef_loss(y_true, y_pred):
    return -dice_coef(y_true, y_pred)

给上给我启发很大的网站,不知道不翻墙是否可以进:
https://towardsdatascience.com/understanding-binary-cross-entropy-log-loss-a-visual-explanation-a3ac6025181a

https://gombru.github.io/2018/05/23/cross_entropy_loss/

https://jamesmccaffrey.wordpress.com/2017/05/09/cross-entropy-for-binary-classification/

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值