Что такое классы C для функции потерь NLLLoss в Pytorch? - PullRequest
4 голосов
/ 13 января 2020

Я спрашиваю о C классах для функции потери NLLLoss .

В документации говорится:

Потеря отрицательного логарифмического правдоподобия. Полезно обучить проблеме классификации с C классами.

В основном все после этого момента зависит от того, знаете ли вы, что такое класс C, и я подумал, что знаю, что такое C класс был, но документация не имеет особого смысла для меня. Особенно, когда он описывает ожидаемые входные данные (N, C) where C = number of classes. Вот где я запутался, потому что я думал, что класс C относится только к выводу . Насколько я понимаю, класс C был одним из самых горячих векторов классификаций. В учебниках я часто обнаруживал, что NLLLoss часто сочетался с LogSoftmax для решения задач классификации.

Я ожидал использовать NLLLoss в следующем примере:

# Some random training data
input = torch.randn(5, requires_grad=True)
print(input)  # tensor([-1.3533, -1.3074, -1.7906,  0.3113,  0.7982], requires_grad=True)
# Build my NN (here it's just a LogSoftmax)
m = nn.LogSoftmax(dim=0)
# Train my NN with the data
output = m(input)
print(output)  # tensor([-2.8079, -2.7619, -3.2451, -1.1432, -0.6564], grad_fn=<LogSoftmaxBackward>)
loss = nn.NLLLoss()
print(loss(output, torch.tensor([1, 0, 0])))

Приведенное выше вызывает следующую ошибку в последней строке:

ValueError: Ожидается 2 или более измерений (получено 1)

Мы можем игнорировать ошибку, потому что Я явно не понимаю, что я делаю. Здесь я объясню мои намерения из приведенного выше исходного кода.

input = torch.randn(5, requires_grad=True)

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

m = nn.LogSoftmax(dim=0)

Документация для LogSoftmax говорит, что вывод будет такой же формы, как ввод, но я только видел примеры LogSoftmax(dim=1) и поэтому я застрял, пытаясь сделать эту работу, потому что я не могу найти относительный пример.

print(loss(output, torch.tensor([1, 0, 0])))

Так что теперь у меня есть вывод NN, и я хочу узнать потери из моей классификации [1, 0, 0]. В данном примере на самом деле не имеет значения, что представляют собой какие-либо данные. Я просто хочу потерю одного горячего вектора, который представляет классификацию.

В этот момент я застреваю, пытаясь устранить ошибки из функции потерь, связанные с ожидаемыми выходными и входными структурами. Я попытался использовать view(...) для вывода и ввода, чтобы исправить форму, но это просто вызывает у меня другие ошибки.

Так что это возвращает к моему первоначальному вопросу, и я покажу пример из документации Чтобы объяснить мою путаницу:

m = nn.LogSoftmax(dim=1)
loss = nn.NLLLoss()
input = torch.randn(3, 5, requires_grad=True)
train = torch.tensor([1, 0, 4])
print('input', input)  # input tensor([[...],[...],[...]], requires_grad=True)
output = m(input)
print('train', output, train)  # tensor([[...],[...],[...]],grad_fn=<LogSoftmaxBackward>) tensor([1, 0, 4])
x = loss(output, train)

Опять же, у нас есть dim=1 на LogSoftmax, что меня сейчас смущает, потому что посмотрите на данные input. Это тензор 3x5, и я потерян.

Вот документация по первому входу для функции NLLLoss:

Ввод: (N, C) (N, C) где C = количество классов

Входные данные сгруппированы по количеству классов?

Итак, каждый строка входного тензора связана с каждым элементом обучающего тензора?

Если я изменю второе измерение входного тензора, то ничего не сломается и я не понимаю, что происходит.

input = torch.randn(3, 100, requires_grad=True)
# 3 x 100 still works?

Так что я не понимаю, что такое класс C, и я думал, что класс C был классификацией (например, метка) и имеет смысл только на выходах NN.

Я надеюсь, вы понимаете мою путаницу, потому что не должна форма входов для NN быть независимой от формы одного горячего вектора, используемого для классификации ?

Как примеры кода, так и документация говорят, что форма входов определяется количество классификаций, и я не совсем понимаю, почему.

Я пытался изучить документацию и учебные пособия, чтобы понять, что мне не хватает, но после нескольких дней не смог пройти этот пункт Я решил задать этот вопрос. Это было унизительно, потому что я думал, что это будет одна из самых простых вещей для изучения.

Ответы [ 2 ]

3 голосов
/ 13 января 2020

Я согласен с вами, что документация для nn.NLLLoss() далека от идеала, но я думаю, что мы можем прояснить вашу проблему здесь, во-первых, уточнив, что "класс" часто используется как синоним "категории" в машине Контекст обучения.

Поэтому, когда PyTorch говорит о C классах, он фактически ссылается на количество различных категорий , на которых вы пытаетесь обучить свою сеть. Так, в классическом примере категориальной нейронной сети, пытающейся провести классификацию между «кошками» и «собаками», C = 2, поскольку это либо кошка, либо собака.

Специально для этой проблемы классификации она также считает, что у нас есть одно единственное значение истинности в массиве наших категорий (изображение не может изображать и кошку, и собаку, но всегда только одну), поэтому мы можем удобно указать соответствующую категорию изображения по его индексу (скажем, что 0 будет означать кошку, а 1 собаку). Теперь мы можем просто сравнить выходные данные сети с нужной нам категорией.

НО, для того, чтобы это работало, нам также должно быть понятно, на что ссылаются эти значения потерь (в нашем сетевом выводе), поскольку наша сеть обычно делает прогнозы через softmax для разных выходных нейронов , что означает, что у нас обычно более одного значения. К счастью, PyTorch nn.NLLLoss делает это автоматически для вас.

В приведенном выше примере с LogSoftmax фактически выдается только одно выходное значение, что является критическим случаем для этого примера. Таким образом, вы в основном имеете только указание на то, существует или не существует что-то, но не имеет особого смысла использовать его в примере классификации, особенно в случае регрессии (но это потребует совершенно другого функция потерь для начала).

Наконец, но не в последнюю очередь, вы также должны учитывать тот факт, что у нас обычно есть 2D-тензоры в качестве входных данных, поскольку пакетирование (одновременное вычисление нескольких выборок) обычно считается необходимым шагом чтобы соответствовать производительности. Даже если вы выберете размер партии 1, это все равно требует, чтобы ваши входные данные имели размер (batch_size, input_dimensions), и, следовательно, ваши выходные тензоры формы (batch_size, number_of_categories).

Это объясняет, почему большинство примеров вы найдете в Интернете выполняем LogSoftmax() над dim=1, поскольку это «ось распределения», а не ось пакета (которая будет dim=0).

Если вы просто хотите решить свою проблему самый простой способ - расширить ваш случайный тензор на дополнительное измерение (torch.randn([1, 5], requires_grad=True)), а затем сравнить только на одно значение в выходном тензоре (print(loss(output, torch.tensor([1])))

2 голосов
/ 13 января 2020

По сути, вам не хватает понятия batch.

Короче говоря, каждый вход в потери (и тот, который передается через сеть) требует измерения batch (т.е. сколько использованных выборок) .

Разбивка, шаг за шагом:

Ваш пример против документации

Каждый шаг будет сравниваться с каждым шагом, чтобы сделать его более понятным (документация сверху, ваш пример ниже )

Входы

input = torch.randn(3, 5, requires_grad=True)
input = torch.randn(5, requires_grad=True)

В первом случае (документы) создается вход с 5 признаками и используются 3 выборки. В вашем случае есть только размер batch (5 образцов), у вас нет функций , которые необходимы . Если вы хотели иметь один образец с 5 функциями, которые вы должны сделать:

input = torch.randn(5, requires_grad=True)

LogSoftmax

LogSoftmax выполняется для измерения пространственных объектов, вы делаете это для всех партий.

m = nn.LogSoftmax (dim = 1) # применить к элементам m = nn.LogSoftmax (dim = 0) # применить к пакету

Обычно для этой операции нет смысла, поскольку выборки независимы друг друга.

Цели

Поскольку это мультиклассовая классификация, и каждый элемент в векторе представляет выборку, можно передать столько чисел, сколько нужно (при условии, что оно меньше, чем число объектов , в случае с примером документации это 5, следовательно, [0-4] в порядке).

train = torch.tensor([1, 0, 4])
train = torch.tensor([1, 0, 0])

Полагаю, вы также хотели передать один горячий вектор в качестве цели. PyTorch не работает таким образом, так как неэффективна память (зачем хранить все как закодированные в горячем виде, когда вы можете просто точно определить класс, в вашем случае это будет 0).

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

Окончательный

Вы не должны используйте torch.nn.LogSoftmax вообще для этой задачи. Просто используйте torch.nn.Linear как последний слой и используйте torch.nn.CrossEntropyLoss с вашими целями.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...