Функция потерь Pytorch для создания похожих вложений

#pytorch #tensor #embedding #bert-language-model #loss

#pytorch #тензор #встраивание #bert-language-model #потеря

Вопрос:

Я работаю над моделью встраивания, где есть модель BERT, которая принимает текстовые вводы и выводит многомерный вектор. Цель модели — найти похожие вложения (высокое косинусное сходство) для похожих текстов и разные вложения (низкое косинусное сходство) для непохожих текстов.

При обучении в режиме мини-пакета модель BERT выдает N*D размерный результат, где N — размер пакета и D — выходной размер модели BERT.

Кроме того, у меня есть целевая матрица размерности N*N , которая содержит 1 в позиции [i, j] th, если sentence[i] и sentence[j] схожи по смыслу, а -1 если нет.

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

То, что я сделал, это просто умножил тензор на его транспонирование, а затем взял поэлементную сигмоиду.

 scores = torch.matmul(document_embedding, torch.transpose(document_embedding, 0, 1))
scores = torch.sigmoid(scores)

loss = self.bceloss(scores, targets)
 

Но, похоже, это не работает.

Есть ли какой-либо другой способ сделать это?

PS То, что я хочу сделать, похоже на метод, описанный в этой статье.

Ответ №1:

Для вычисления косинусного сходства между двумя векторами, которые вы бы использовали nn.CosineSimilarity . Однако я не думаю, что это позволяет получить парное сходство из набора n векторов. К счастью, вы можете реализовать ее самостоятельно с помощью некоторых тензорных манипуляций.

Давайте вызовем x ваш document_embedding формы (n, d) , где d — размер вложения. Мы возьмем n=3 и d=5 . So x состоит из [x1, x2, x3].T .

 >>> x = torch.rand(n, d)
tensor([[0.8620, 0.9322, 0.4220, 0.0280, 0.3789],
        [0.2747, 0.4047, 0.6418, 0.7147, 0.3409],
        [0.6573, 0.3432, 0.5663, 0.2512, 0.0582]])
 

Косинусное сходство является нормализованным скалярным произведением. x@x.T Умножение матрицы даст вам попарное скалярное произведение: которое содержит: ||x1||² , <x1/x2> , <x1/x3> , <x2/x1> , ||x2||² , и т.д…

 >>> sim = x@x.T
tensor([[1.9343, 1.0340, 1.1545],
        [1.0340, 1.2782, 0.8822],
        [1.1545, 0.8822, 0.9370]])
 

Для нормализации возьмите вектор всех норм: ||x1|| , ||x2|| , и ||x3|| :

 >>> norm = x.norm(dim=1)
tensor([1.3908, 1.1306, 0.9680])
 

Постройте матрицу, содержащую коэффициенты нормализации: ||x1||² , ||x1||.||x2|| , ||x1||.||x3|| , ||x2||.||x1|| , ||x2||² , и т.д…

 >>> factor = norm*norm.unsqueeze(1)
tensor([[1.9343, 1.5724, 1.3462],
        [1.5724, 1.2782, 1.0944],
        [1.3462, 1.0944, 0.9370]])
 

Затем нормализуйте:

 >>> sim /= factor
tensor([[1.0000, 0.6576, 0.8576],
        [0.6576, 1.0000, 0.8062],
        [0.8576, 0.8062, 1.0000]])
 

Альтернативно, более быстрый способ, позволяющий избежать необходимости создания матрицы норм, заключается в нормализации перед умножением:

 >>> x /= x.norm(dim=1, keepdim=True)
>>> sim = x@x.T
tensor([[1.0000, 0.6576, 0.8576],
        [0.6576, 1.0000, 0.8062],
        [0.8576, 0.8062, 1.0000]])
 

Для функции потерь я бы nn.CrossEntropyLoss сразу применил между предсказанной матрицей подобия и целевой матрицей вместо применения сигмоида BCE. Примечание: nn.CrossEntropyLoss включает nn.LogSoftmax .