Pytorch softmax вдоль разных масок без петли - PullRequest
0 голосов
/ 21 января 2019

Скажем, у меня есть вектор a с индексным вектором b такой же длины.Индексы находятся в диапазоне 0 ~ N-1, что соответствует N группам.Как я могу сделать softmax для каждой группы без цикла for?

Я делаю здесь какую-то операцию внимания.Числа для каждой группы не совпадают, поэтому я не могу изменить a в матрицу и использовать dim в стандартном Softmax() API.

Пример игрушки:

a = torch.rand(10)
a: tensor([0.3376, 0.0557, 0.3016, 0.5550, 0.5814, 0.1306, 0.2697, 0.9989, 0.4917,
        0.6306])
b = torch.randint(0,3,(1,10), dtype=torch.int64)
b: tensor([[1, 2, 0, 2, 2, 0, 1, 1, 1, 1]])

Я хочу сделать softmax, как

for index in range(3):
    softmax(a[b == index])

, но без цикла for, чтобы сэкономить время.

1 Ответ

0 голосов
/ 21 января 2019

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

Как правило, формула для softmaxдостаточно хорошо объяснено в документации PyTorch , где мы можем видеть, что это экспонента текущего значения, деленная на сумму всех классов.Причина для этого основана на теории вероятностей и, вероятно, немного за пределами моей зоны комфорта, но, по сути, она помогает вам поддерживать довольно простую производную обратного распространения при использовании ее в сочетании с популярной стратегией потерь, называемой "Cross Entropy Loss"(CE) (см. Соответствующую функцию в PyTorch здесь .

Кроме того, в описании для CE вы также можете видеть, что он автоматически объединяет две функции , а именно:(численно стабильная) версия функции softmax, а также потеря отрицательного логарифмического правдоподобия (NLLL).

Теперь вернемся к исходному вопросу и, надеюсь, разрешим вашу проблему:Ради вопроса - и как вы его задали - кажется, вы играете с популярным набором цифр MNIST с ручным переключением цифр, в котором мы хотим предсказать некоторые значения для вашего текущего входного изображения.

Ятакже предполагая, что ваш вывод a в какой-то момент будет выводом слоя из нейронной сети.Неважно, будет ли это ограничено определенным диапазоном или нет (например, путем применения некоторой формы функции активации), так как softmax будет в основном нормализацией.В частности, это даст нам, как обсуждалось ранее, некоторую форму распределения по всем прогнозируемым значениям и суммирует до 1 по всем классам.Чтобы сделать это, мы можем просто применить что-то вроде

soft_a = softmax(a, dim=0) # otherwise throws error if we don't specify axis
print(torch.sum(soft_a)) # should return "Tensor(1.)"

Теперь, если мы предположим, что вы хотите выполнить «классический» пример MNIST, вы можете затем использовать функцию argmax(), чтобы предсказать, какое значение вашегосистема считает правильный ответ и рассчитывает ошибку на основе этого, например, с помощью функции nn.NLLLoss().

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

Кроме того, имейте в виду, что вы хотите рассчитать попарные потери, то есть что-то для каждого индекса вашей продукции.Функция, которая приходит на ум для этой конкретной цели, будет nn.BCELoss(), которая вычисляет бинаризованную (поэлементную) версию Cross-Entropy.Для этого вы можете просто «вставить» свой исходный тензор предсказания a, а также свой основной тензор истинности b.Минимальный пример для этого будет выглядеть следующим образом:

bce = torch.nn.BCELoss(reduction="none") + to keep losses for each element separate
loss = bce(a,b) # returns tensor with respective pairwise loss

Если вы заинтересованы в одной потере, вы, очевидно, можете использовать BCELoss с другим аргументом для reduction, как описано в документации.Дайте мне знать, если я смогу уточнить некоторые части ответа для вас.

РЕДАКТИРОВАТЬ: Что-то еще, чтобы иметь в виду здесь: BCELoss() требует от вас вводить значения, которые потенциально могут быть близки к значению, которое выхочу предсказать.Это особенно проблема, если вы сначала вводите значения в функцию активации (например, sigmoid или tanh), которая затем может никогда не достичь значения, которое вы хотите предсказать, поскольку они ограничены интервалом!

...