Пороговое значение для сигмовидной нелинейности для мультиметочной классификации - PullRequest
0 голосов
/ 03 июня 2018

Я пытаюсь использовать архитектуру DenseNet для классификации рентгеновских изображений по https://www.kaggle.com/nih-chest-xrays/data. Модель создает вектор двоичных меток, где каждая метка указывает на наличие или отсутствие 14 возможныхпатологии: ателектаз, кардиомегалия, консолидация, отек, выпот, эмфизема, фиброз, грыжа, инфильтрация, масса, узелки, утолщение плевры, пневмония и пневмоторакс.Например, здоровый пациент будет иметь метку [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].Я построил эту модель с помощью тензорного потока, и, поскольку это проблема классификации по нескольким меткам, я использовал функцию стоимости tf.reduce_mean (tf.losses.sigmoid_cross_entropy (tags, logits)), которая минимизируется с помощью AdamOptimizer.Однако, когда я проверял выходной сигнал сигмоида, все значения были ниже 0,5, в результате чего tf.round (logits) выдает нули для каждого прогноза.Фактические логиты различны для разных входных данных и являются ненулевыми значениями после 10000 итераций, поэтому я не думаю, что исчезающие градиенты являются проблемой.У меня есть два вопроса:

  1. Может ли эта проблема быть вызвана неправильной реализацией модели?
  2. Буду ли я "обманывать", если я уменьшу пороговое значение для сигмоидальной функции до0,25 от 0,5 для повышения точности модели?

Спасибо.

Вот код для модели:

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')

1 Ответ

0 голосов
/ 05 июня 2018

Теофиллин!Во-первых, позвольте мне повторить комментарий, который я оставил на тот случай, если этот ответ сработает для вас в долгосрочной перспективе (и, возможно, для других людей):

Я думаю, что вы на правильном пути, но вы можетедумать о проблеме неправильно.Может быть так, что положительные (1 с) встречаются реже, чем отрицательные (0).Основываясь на своей функции потерь, подумайте о том, что может побудить сделать слой софтмакс (лучше ли было бы интуитивно полагать, что модель будет угадывать все 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))
...