#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
.