Несогласованное векторное представление с использованием преобразователей BertModel и BertTokenizer

У меня есть BertTokenizer (tokenizer) и BertModel (model) из библиотеки transformers. Я предварительно обучил модель с нуля с помощью нескольких статей в Википедии, просто чтобы проверить, как она работает.

После предварительного обучения модели я хочу извлечь векторное представление слоя для данного предложения. Для этого я вычисляю среднее из 11 скрытых (размером 768) векторов. Я делаю это следующим образом (line - это одиночный String):

padded_sequence = tokenizer(line, padding=True)
        
indexed_tokens = padded_sequence['input_ids']
attention_mask = padded_sequence["attention_mask"]

tokens_tensor = torch.tensor([indexed_tokens])
attention_mask_tensor = torch.tensor([attention_mask])

outputs = model(tokens_tensor, attention_mask_tensor)
hidden_states = outputs[0]

line_vectorized = hidden_states[0].data.numpy().mean(axis=0)

Все идет нормально. Я могу сделать это для каждого предложения индивидуально. Но теперь я хочу делать это партиями, т.е. У меня есть несколько предложений, и вместо того, чтобы повторять каждое предложение, я отправляю соответствующие тензорные представления, чтобы получить все векторы сразу. Я делаю это следующим образом (lines это list of Strings):

padded_sequences = self.tokenizer_PYTORCH(lines, padding=True)
        
indexed_tokens_list = padded_sequences['input_ids']
attention_mask_list = padded_sequences["attention_mask"]
        
tokens_tensors_list = [torch.tensor([indexed_tokens]) for indexed_tokens in indexed_tokens_list]
attention_mask_tensors_list = [torch.tensor([attention_mask ]) for attention_mask in attention_mask_list ]
        
tokens_tensors = torch.cat((tokens_tensors_list), 0)
attention_mask_tensors = torch.cat((attention_mask_tensors_list ), 0)

outputs = model(tokens_tensors, attention_mask_tensors)
hidden_states = outputs[0]

lines_vectorized = [hidden_states[i].data.numpy().mean(axis=0) for i in range(0, len(hidden_states))]

Проблема заключается в следующем: Мне нужно использовать отступы, чтобы можно было соответствующим образом объединить тензоры токенов. Это означает, что индексированные токены и маски внимания могут быть больше, чем в предыдущем случае, когда предложения оценивались индивидуально. Но когда я использую заполнение, я получаю разные результаты для предложений, которые были заполнены.

ПРИМЕР: у меня есть два предложения (на французском, но это не имеет значения):

sentence_A = appareil digestif - статья из Википедии, свободная энциклопедия

sentence_B = sauter a la navigation sauter a la recherche cet article est une ebauche careant la biologie

Когда я оцениваю два предложения по отдельности, я получаю:

sentence_A:

indexed_tokens =  [10002, 3101, 4910, 557, 73, 3215, 9630, 2343, 4200, 8363, 10000]
attention_mask =  [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
line_vectorized =  [-0.9304411   0.53798294 -1.6231083 ...]

sentence_B:

indexed_tokens =  [10002, 2217, 6496, 1387, 9876, 2217, 6496, 1387, 4441, 405, 73, 6451, 3, 2190, 5402, 1387, 2971, 10000]
attention_mask =  [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
line_vectorized =  [-0.8077076   0.56028104 -1.5135447  ...]

Но когда я оцениваю два предложения в пакете, я получаю:

sentence_A:

indexed_tokens =  [10002, 3101, 4910, 557, 73, 3215, 9630, 2343, 4200, 8363, 10000, 10004, 10004, 10004, 10004, 10004, 10004, 10004]
attention_mask =  [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0]
line_vectorized =  [-1.0473819   0.6090186  -1.727466  ...]

sentence_B:

indexed_tokens =  [10002, 2217, 6496, 1387, 9876, 2217, 6496, 1387, 4441, 405, 73, 6451, 3, 2190, 5402, 1387, 2971, 10000]
attention_mask =  [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
line_vectorized =  [-0.8077076   0.56028104 -1.5135447  ...]

То есть поскольку sentence_B больше, чем sentence_A, sentence_A был дополнен, а маска внимания также была дополнена нулями. Индексированные токены теперь содержат дополнительные токены (10004, что я предполагаю empty). Векторное представление sentence_B НЕ изменилось. Но векторное представление sentence_A ИЗМЕНИЛОСЬ.

Я хотел бы знать, работает ли это так, как задумано или нет (я полагаю, что нет). Думаю, я что-то делаю не так, но не могу понять, что именно.

Любые идеи?


person oooliverrr    schedule 23.09.2020    source источник


Ответы (1)


Когда вы делаете это в одном предложении в пакете, максимальная длина предложения - это максимальное количество токенов, однако, когда вы делаете это в пакете, максимальная длина предложений остается неизменной во всем пакете, что по умолчанию составляет максимальное количество жетоны в самом длинном предложении. Максимальные значения 1 в этом случае указывают, что это не <PAD> токен, а 0 указывает <PAD> токен. Лучший способ контролировать это - определить максимальную длину последовательности и усечь предложения, длина которых превышает максимальную длину последовательности.

Это можно сделать с помощью альтернативного метода для токенизации текста партиями (одно предложение может рассматриваться как размер партии 1):

tokenizer = BertTokenizer.from_pretrained("<your bert model>", do_lower_case=True)
encoding = tokenizer.batch_encode_plus(lines, return_tensors='pt',padding=True, truncation=True, max_length=50, add_special_tokens = True) ## Change the max_length to the required max length
indexed_tokens = encoding['input_ids']
attention_mask = encoding['attention_mask']
person Ashwin Geet D'Sa    schedule 23.09.2020
comment
Конечно, но это не помогает. Проблема в том, что я получаю разные векторы всякий раз, когда данный indexed_token дополняется, и я добавляю соответствующие нули к вниманию_mask. Независимо от того, как был получен этот жетон. - person oooliverrr; 23.09.2020
comment
Я обновил ответ, дайте мне знать, если он полезен. - person Ashwin Geet D'Sa; 23.09.2020