Слой InfogainLoss

Я хочу использовать в своей модели слой потерь типа InfogainLoss. Но у меня возникают трудности с его правильным определением.

  1. Есть ли какой-нибудь учебник / пример по использованию слоя INFOGAIN_LOSS?

  2. Должны ли входные данные для этого уровня, вероятности классов, быть выходными данными SOFTMAX слоя, или этого достаточно, чтобы ввести «верх» полностью связанного слоя?

INFOGAIN_LOSS требует трех входных данных: вероятностей классов, меток и матрицы H. Матрица H может быть предоставлена ​​либо как параметры слоя infogain_loss_param { source: "fiename" }.
Предположим, у меня есть скрипт python, который вычисляет H как numpy.array формы (L,L) с dtype='f4' (где L - количество меток в моей модели).

  1. Как я могу преобразовать свой numpy.array в binproto файл, который может быть предоставлен как infogain_loss_param { source } модели?

  2. Предположим, я хочу, чтобы H был предоставлен в качестве третьего входа (внизу) для слоя потерь (а не в качестве параметра модели). Как я могу это сделать?
    Могу ли я определить новый уровень данных, «верхний» которого будет H? Если да, то не будут ли данные этого уровня увеличиваться на каждой итерации обучения, как приращение данных обучения? Как я могу определить несколько несвязанных уровней входных «данных» и как caffe знает, что читать из слоя «данные» обучения / тестирования пакет за пакетом, в то время как из H слоя «данных» он знает, что читать только один раз за все обучение процесс?


person Shai    schedule 24.12.2014    source источник


Ответы (3)


1. Есть ли какой-нибудь учебник / пример по использованию слоя InfogainLoss?:
Хороший пример можно найти здесь: использование InfogainLoss для устранения дисбаланса классов.


2. Должны ли входные данные для этого уровня, вероятности классов, быть выходными данными слоя Softmax?
Исторически ответ был ДА в соответствии с Ответ Яира. Старая реализация "InfogainLoss" должна была быть выходом слоя "Softmax" или любого другого уровня, который гарантирует, что входные значения находятся в диапазоне [0..1].

OP заметил, что использование "InfogainLoss" поверх слоя "Softmax" может привести к числовой нестабильности. Его запрос на вытягивание, объединяющий эти два слоя в один (очень похоже на слой "SoftmaxWithLoss") , был принят и включен в официальные репозитории Caffe 14.04.2017. Математика этого комбинированного слоя приведена здесь.

«Внешний вид» обновленного слоя в точности такой же, как и у старого, за исключением того, что больше не нужно явно передавать ввод через слой "Softmax".


3. Как преобразовать numpy.array в файл binproto:

В питоне

H = np.eye( L, dtype = 'f4' ) 
import caffe
blob = caffe.io.array_to_blobproto( H.reshape( (1,1,L,L) ) )
with open( 'infogainH.binaryproto', 'wb' ) as f :
    f.write( blob.SerializeToString() )

Теперь вы можете добавить к прототипу модели слой INFOGAIN_LOSS с H в качестве параметра:

layer {
  bottom: "topOfPrevLayer"
  bottom: "label"
  top: "infoGainLoss"
  name: "infoGainLoss"
  type: "InfogainLoss"
  infogain_loss_param {
    source: "infogainH.binaryproto"
  }
}

4. Как загрузить H как часть слоя DATA

Цитата из сообщения Эвана Шелхамера:

В настоящее время невозможно заставить уровни данных загружать ввод с разной скоростью. При каждом прямом проходе все уровни данных будут продвигаться. Однако постоянный ввод H может быть выполнен путем создания входного файла lmdb / leveldb / hdf5, который представляет собой только H, поскольку уровень данных будет зацикливаться и продолжать загружать тот же H. Это, очевидно, тратит впустую дисковый ввод-вывод.

person Shai    schedule 25.12.2014
comment
Это полностью решает части 3 и 4 и является идеальным решением для тех, кто хочет повторно взвесить (или сбалансировать) потери по классам в Caffe, используя слой InfoGain и определение prototxt для H. - person AruniRC; 01.05.2018

Слой подводит итоги

-log(p_i)

и поэтому p_i должны быть в (0, 1], чтобы иметь смысл в качестве функции потерь (в противном случае более высокие оценки достоверности приведут к более высоким потерям). См. кривую ниже для значений log (p).

введите описание изображения здесь

Я не думаю, что они должны суммировать до 1, но, пропустив их через слой Softmax, достигаются оба свойства.

person Yair    schedule 04.05.2015
comment
это не совсем эта сумма, есть еще веса - person Shai; 04.05.2015
comment
Да, веса есть, но они не являются фактором, ограничивающим значение p_i. log (p) ‹0 для 0‹ x ‹1, поэтому -log (p) положительно, и потери увеличиваются по мере того, как p становится меньше (что вам и нужно). если p ›1 log (p)› 0 и поэтому -log (p) будет увеличиваться по мере того, как p становится меньше. Это противоположно тому, что должна делать функция потерь. Вот почему p должно быть в (0, 1]. Log (0) = -infinity, поэтому следует позаботиться о том, чтобы p = 0 не было, но это уже реализовано в самом слое. - person Yair; 05.05.2015
comment
Итак, если я правильно понимаю, ваш ответ на вопрос номер 2 таков: вход в слой info_gain должен быть вершиной слоя softmax. - person Shai; 05.05.2015
comment
да. Это было бы хорошим способом убедиться, что значения находятся в правильном диапазоне. - person Yair; 18.05.2015

Поскольку мне пришлось перерыть множество веб-сайтов, чтобы разгадать весь код, я подумал, что поделюсь своей реализацией:

Слой Python для вычисления H-матрицы с весами для каждого класса:

import numpy as np
import caffe


class ComputeH(caffe.Layer):
    def __init__(self, p_object, *args, **kwargs):
        super(ComputeH, self).__init__(p_object, *args, **kwargs)
        self.n_classes = -1

    def setup(self, bottom, top):
        if len(bottom) != 1:
            raise Exception("Need (only) one input to compute H matrix.")

        params = eval(self.param_str)
        if 'n_classes' in params:
            self.n_classes = int(params['n_classes'])
        else:
            raise Exception('The number of classes (n_classes) must be specified.')

    def reshape(self, bottom, top):
        top[0].reshape(1, 1, self.n_classes, self.n_classes)

    def forward(self, bottom, top):
        classes, cls_num = np.unique(bottom[0].data, return_counts=True)

        if np.size(classes) != self.n_classes or self.n_classes == -1:
            raise Exception("Invalid number of classes")

        cls_num = cls_num.astype(float)

        cls_num = cls_num.max() / cls_num
        weights = cls_num / np.sum(cls_num)

        top[0].data[...] = np.diag(weights)

    def backward(self, top, propagate_down, bottom):
        pass

и соответствующая часть из train_val.prototxt:

layer {
    name: "computeH"
    bottom: "label"
    top: "H"
    type: "Python"
    python_param {
        module: "digits_python_layers"
        layer: "ComputeH"
        param_str: '{"n_classes": 7}'
    }
    exclude { stage: "deploy" }
}
layer {
  name: "loss"
  type: "InfogainLoss"
  bottom: "score"
  bottom: "label"
  bottom: "H"
  top: "loss"
  infogain_loss_param {
    axis: 1  # compute loss and probability along axis
  }
  loss_param {
      normalization: 0
  }
  exclude {
    stage: "deploy"
  }
}
person Torsten Straßer    schedule 01.08.2018
comment
У меня есть 2 класса, и размер моей партии для TRAIN / TEST равен 16, и что, если при выборке все мои метки будут одинаковыми в этой партии (то есть из класса большинства). Это заставляет np.unique(bottom[0].data, return_counts=True) return (array([1.], dtype=float32), array([16])), что означает, что моему num_classes назначается 1 вместо 2. - person Tanmay Agrawal; 14.02.2020
comment
Если вы заранее знаете количество классов, вы можете указать его как константу. - person Torsten Straßer; 18.02.2020