多标签分类的 sigmoid 非线性阈值

茶碱

我正在尝试使用DenseNet 架构对来自https://www.kaggle.com/nih-chest-xrays/data 的x 射线图像进行分类. 该模型生成一个二元标签向量,其中每个标签表示 14 种可能病理的存在或不存在:肺不张、心脏肥大、实变、水肿、积液、肺气肿、纤维化、疝气、浸润、肿块、结节、胸膜增厚、肺炎和气胸。例如,健康患者的标签为 [0,0,0,0,0,0,0,0,0,0,0,0,0,0],而水肿和积液患者的标签为 [0,0,0,0,0,0,0,0,0,0,0,0,0,0] [0,0,0,1,1,0,0,0,0,0,0,0,0,0] 的标签。我用 tensorflow 构建了这个模型,因为这是一个多标签分类问题,所以我使用的成本函数是 tf.reduce_mean(tf.losses.sigmoid_cross_entropy(labels, logits)),它用 AdamOptimizer 最小化。但是,当我检查 sigmoid 输出时,这些值都低于 0.5,导致 tf.round(logits) 为每个预测生成零。不同输入的实际 logits 不同,并且在 10000 次迭代后为非零值,因此我认为梯度消失不是问题。我有两个问题:

  1. 这个问题可能是由模型的错误实现引起的吗?
  2. 如果我将 sigmoid 函数的阈值从 0.5 降低到 0.25 以提高模型精度,我会“作弊”吗?

谢谢。

这是模型的代码:

def DenseNet(features, labels, mode, params):

depth = params["depth"]
k = params["growth"]

if depth == 121:
    N = db_121
else:
    N = db_169

bottleneck_output = 4 * k

#before entering the first dense block, a conv operation with 16 output channels
#is performed on the input images

with tf.variable_scope('input_layer'):
    #l = tf.reshape(features, [-1, 224, 224, 1])
    feature_maps = 2 * k
    l = layers.conv(features, filter_size = 7, stride = 2, out_chn = feature_maps)
    l = tf.nn.max_pool(l,
                       padding='SAME',
                       ksize=[1,3,3,1],
                       strides=[1,2,2,1],
                       name='max_pool')

# each block is defined as a dense block + transition layer
with tf.variable_scope('block1'):
    for i in range(N[0]):
        with tf.variable_scope('bottleneck_layer.{}'.format(i+1)):
            bn_l = layers.batch_norm('BN', l)
            bn_l = tf.nn.relu(bn_l, name='relu')
            bn_l = layers.conv(bn_l, out_chn=bottleneck_output, filter_size=1)
        l = layers.add_layer('dense_layer.{}'.format(i+1), l, bn_l)
    l = layers.transition_layer('transition1', l)

with tf.variable_scope('block2'):
    for i in range(N[1]):
        with tf.variable_scope('bottleneck_layer.{}'.format(i+1)):
            bn_l = layers.batch_norm('BN', l)
            bn_l = tf.nn.relu(bn_l, name='relu')
            bn_l = layers.conv(bn_l, out_chn=bottleneck_output, filter_size=1)
        l = layers.add_layer('dense_layer.{}'.format(i+1), l, bn_l)
    l = layers.transition_layer('transition2', l)

with tf.variable_scope('block3'):
    for i in range(N[2]):
        with tf.variable_scope('bottleneck_layer.{}'.format(i+1)):
            bn_l = layers.batch_norm('BN', l)
            bn_l = tf.nn.relu(bn_l, name='relu')
            bn_l = layers.conv(bn_l, out_chn=bottleneck_output, filter_size=1)
        l = layers.add_layer('dense_layer.{}'.format(i+1), l, bn_l)
    l = layers.transition_layer('transition3', l)

# the last block does not have a transition layer
with tf.variable_scope('block4'):
    for i in range(N[3]):
        with tf.variable_scope('bottleneck_layer.{}'.format(i+1)):
            bn_l = layers.batch_norm('BN', l)
            bn_l = tf.nn.relu(bn_l, name='relu')
            bn_l = layers.conv(bn_l, out_chn=bottleneck_output, filter_size=1)
        l = layers.add_layer('dense_layer.{}'.format(i+1), l, bn_l)

# classification (global max pooling and softmax)
with tf.name_scope('classification'):
    l = layers.batch_norm('BN', l)
    l = tf.nn.relu(l, name='relu')
    l = layers.pooling(l, filter_size = 7)
    l_shape = l.get_shape().as_list()
    l = tf.reshape(l, [-1, l_shape[1] * l_shape[2] * l_shape[3]])
    l = tf.layers.dense(l, units = 1000, activation = tf.nn.relu, name='fc1', kernel_initializer=tf.contrib.layers.xavier_initializer())
    output = tf.layers.dense(l, units = 14, name='fc2', kernel_initializer=tf.contrib.layers.xavier_initializer()) # [batch_size, 14]

cross_entropy = tf.nn.sigmoid_cross_entropy_with_logits(labels=labels, logits=output) # cost function
cost = tf.reduce_mean(cross_entropy, name='cost_fn')
彼得·巴雷特·布莱恩

茶碱!首先,让我重复一下我留下的评论,以防这个答案最终对你(或许其他人)有用:

我认为您走在正确的道路上,但您可能以错误的方式思考问题。可能是正数 (1s) 比负数 (0s) 频率低得多。根据您的损失函数,考虑可能会驱动 softmax 层做什么(直观地说,成为一个猜测全 1 或全 0 的模型会更好吗?)。我认为你在正确的轨道上。考虑精确度、召回率以及您真正希望模型做什么。如果这不能引导您走向正确的方向,很高兴写出完整的答案

你的问题有点棘手,因为我不知道预测值之间关系的完整背景(预测类别是否独立,严重依赖等)此外,你将不得不调用精度值并回想一下(你认为假阳性更糟吗?假阴性?它们同样糟糕吗?)。我认为对于初始通过,可能值得尝试weighted_cross_entropy_with_logits您可以根据指导您的精确召回决策的启发式使模型偏向于做出正面和负面的判断(在医疗数据上,我认为假阴性是一件非常糟糕的事情

此答案基于对您的问题的 1000 英尺视图,因此如果它对您不起作用,很高兴修改我的答案!如果您正在寻找纯粹的准确性(以牺牲精度/召回平衡为代价),则可能值得尝试证明,在训练集中,您可以近似测试集中类别的频率(然后对各个预测加权比赛)。只要仔细实施,你的阈值想法就已经死了(不要在训练和测试之间共享频率信息等)

编辑:如果文档中不明显,本节将帮助指导您在适当的情况下构建自定义损失函数!

  qz * -log(sigmoid(x)) + (1 - z) * -log(1 - sigmoid(x))
= qz * -log(1 / (1 + exp(-x))) + (1 - z) * -log(exp(-x) / (1 + exp(-x)))
= qz * log(1 + exp(-x)) + (1 - z) * (-log(exp(-x)) + log(1 + exp(-x)))
= qz * log(1 + exp(-x)) + (1 - z) * (x + log(1 + exp(-x))
= (1 - z) * x + (qz +  1 - z) * log(1 + exp(-x))
= (1 - z) * x + (1 + (q - 1) * z) * log(1 + exp(-x))

(1 - z) * x + l * (log(1 + exp(-abs(x))) + max(-x, 0))

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章