Градиент потери DistilBERT для измерения важности токена

Я пытаюсь получить доступ к градиенту потерь в DistilBERT по отношению к каждому весу внимания в первом слое. Я мог получить доступ к вычисленному значению градиента выходной матрицы весов с помощью следующего кода, когда requires_grad=True

loss.backward()
for name, param in model.named_parameters():
    if name == 'transformer.layer.0.attention.out_lin.weight':
       print(param.grad)  #shape is [768,768]

где model - загруженная модель дистилберта. У меня вопрос, как получить градиент относительно внимания [SEP] или [CLS] или других токенов? Он мне нужен, чтобы воспроизвести рисунок об «Градиентных оценках важности функций для внимания к [SEP]» по следующей ссылке: https://medium.com/analytics-vidhya/explainability-of-bert-through-Внимание-7dbbab8a7062

Аналогичный вопрос для той же цели был задан ниже, но это не моя проблема: Проблема с измерением важности токена BERT. Нет оценок




Ответы (1)


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

Минимальное решение от Форум PyTorch:

yGrad = torch.zeros(1,1)
def extract(xVar):
    global yGrad
    yGrad = xVar    

xx = Variable(torch.randn(1,1), requires_grad=True)
yy = 3*xx
zz = yy**2

yy.register_hook(extract)

#### Run the backprop:
print (yGrad) # Shows 0.
zz.backward()
print (yGrad) # Show the correct dzdy

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

person Jindřich    schedule 21.04.2020
comment
Большое спасибо за ответ. Теперь я вижу, как сохранить градиент промежуточных узлов. Другой вопрос: каждый слой в DistilBERT вызывает экземпляр класса MultiHeadSelfAttention для локального вычисления весов внимания. Таким образом, единственное место для вызова register_hook () находится внутри forward () класса, в котором вычисляются веса. Если предположить, что наша модель DistilBERT имеет 6 слоев, то register_hook () вызывается 6 раз. yGrad в этом случае будет списком для сохранения градиента весов всех слоев. Правильно ли многократно вызывать register_hook () внутри forward ()? Благодарность - person Mila; 22.04.2020
comment
Да, тогда просто yGrad список добавляется к списку в ловушке. - person Jindřich; 22.04.2020
comment
Спасибо, быстрый вопрос: если я использую список для сохранения градиентов каждого слоя, добавляется ли он в том же порядке, что и слои от 1 до 6, или из-за вычислений обратного градиента он сохраняет значения градиента в обратном порядке, т.е. уровень 6 как первый элемент в списке и уровень 1 как последний элемент? - person Mila; 24.04.2020
comment
Это будет в зарезервированном порядке. - person Jindřich; 24.04.2020