Привет всем и заранее извиняюсь за, вероятно, довольно простой вопрос - у меня есть теория о том, что вызывает проблему, но было бы здорово подтвердить это людьми, которые знают об этом больше, чем я.
Я пытался реализовать этот фрагмент кода Python в Google Colab. Фрагмент предназначен для определения сходства предложений. Код работает нормально, но я обнаружил, что вложения и расстояния меняются каждый раз, когда я его запускаю, что не идеально для моего предполагаемого варианта использования.
import torch
from scipy.spatial.distance import cosine
from transformers import AutoModel, AutoTokenizer
# Import our models. The package will take care of downloading the models automatically
tokenizer = AutoTokenizer.from_pretrained("qiyuw/pcl-bert-base-uncased")
model = AutoModel.from_pretrained("qiyuw/pcl-bert-base-uncased")
# Tokenize input texts
texts = [
"There's a kid on a skateboard.",
"A kid is skateboarding.",
"A kid is inside the house."
]
inputs = tokenizer(texts, padding=True, truncation=True, return_tensors = "pt")
# Get the embeddings
with torch.no_grad():
embeddings = model(**inputs, output_hidden_states=True, return_dict=True).pooler_output
# Calculate cosine similarities
# Cosine similarities are in [-1, 1]. Higher means more similar
cosine_sim_0_1 = 1 - cosine(embeddings[0], embeddings[1])
cosine_sim_0_2 = 1 - cosine(embeddings[0], embeddings[2])
print("Cosine similarity between \"%s\" and \"%s\" is: %.3f" % (texts[0], texts[1], cosine_sim_0_1))
print("Cosine similarity between \"%s\" and \"%s\" is: %.3f" % (texts[0], texts[2], cosine_sim_0_2))
Я думаю, что проблема должна быть специфичной для модели, поскольку я получаю предупреждение о вновь инициализированных весах пулера, а Pooler_output в конечном итоге — это то, что код читает, чтобы сообщить о сходстве:
Some weights of RobertaModel were not initialized from the model checkpoint at qiyuw/pcl-roberta-large and are newly initialized: ['roberta.pooler.dense.bias', 'roberta.pooler.dense.weight']
You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.
Переключение на альтернативную модель, которая не выдает этого предупреждения (например, приговор-трансформеры/all-mpnet-base-v2), делает выходные данные воспроизводимыми, поэтому я думаю, что это из-за приведенного выше предупреждения об инициализации весов. Итак, вот мои вопросы:
заранее спасибо
Сомневаюсь, что это особенность конкретной модели. Известно, что предварительно обученные модели внедрения создают несколько разные внедрения для одного и того же ввода (в вашем случае — текстов).
Об этом ведется несколько открытых разговоров, однако я экспериментировал с этим как на пустой, так и на предварительно обученной модели RoBerta и обнаружил, что установка начальных значений модели для задач внедрения контролирует такое поведение. Однако настройки начального числа для параметров модели (например, смещения) может быть недостаточно, действительно, некоторые модели внедрения могут выполнять выборку из распределения токенов в конце процесса вывода.
Предупреждающее сообщение:
Some weights of RobertaModel were not initialized from the model checkpoint at qiyuw/pcl-roberta-large and are newly initialized: ['roberta.pooler.dense.bias', 'roberta.pooler.dense.weight']
You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.
подразумевает, что веса предварительно обученной модели не были загружены и использованы при инициализации. В зависимости от процесса инициализации этой конкретной модели Берта, весьма вероятно, что вы создаете новые внедрения при инициализации.
Кроме того, не инициализируйте модель каждый раз, когда вы запускаете скрипт. Я предлагаю вам использовать Jupyter Notebook для создания блока ячеек, инициализировать модель один раз и наблюдать, получаете ли вы одинаковые внедрения для одного и того же входного значения, прежде чем переходить к чему-то большему. разрабатывать, например, пытаться контролировать семена, независимо от того, какие слои их используют.
В качестве примера рассмотрим следующий код для слоя внедрения из документации pytorch:
from torch import nn
embedding = nn.Embedding(10, 3)
input = torch.LongTensor([[1, 2, 4, 5], [4, 3, 2, 9]])
embedding(input)
Если приведенный выше код будет запущен (независимо от того, инициализировали ли вы уже embedding
), вы получите новые вложения, сгенерированные из нормального распределения N(0,1). Если вы не выполните повторную инициализацию embeddings
, вы увидите, что сгенерированное значение input
не изменилось, сколько бы раз вы его ни запускали.
Действительно, модель Берта, которую вы используете выше, использует аналогичное (немного более сложное) использование слоя внедрения.
Вы правы, веса слоев модели для bert.pooler.dense.bias
и bert.pooler.dense.weight
инициализируются случайным образом. Вы можете инициализировать эти слои всегда одинаково для воспроизводимого вывода, но я сомневаюсь, что код вывода, который вы скопировали оттуда readme , верен. Как вы уже упомянули, слои пула не инициализируются, и их класс модели также следит за тем, чтобы пул_слой не добавлялся:
...
self.bert = BertModel(config, add_pooling_layer=False)
...
Оценочный сценарий репозитория следует вызвать в соответствии с файлом readme с помощью следующей команды:
python evaluation.py --model_name_or_path qiyuw/pcl-bert-base-uncased --mode test --pooler cls_before_pooler
Если вы посмотрите на это, ваш код вывода для qiyuw/pcl-bert-base-uncased должен быть следующим:
import torch
from scipy.spatial.distance import cosine
from transformers import AutoModel, AutoTokenizer
# Import our models. The package will take care of downloading the models automatically
tokenizer = AutoTokenizer.from_pretrained("qiyuw/pcl-bert-base-uncased")
model = AutoModel.from_pretrained("qiyuw/pcl-bert-base-uncased")
# Tokenize input texts
texts = [
"There's a kid on a skateboard.",
"A kid is skateboarding.",
"A kid is inside the house."
]
inputs = tokenizer(texts, padding=True, truncation=True, return_tensors = "pt")
# Get the embeddings
with torch.inference_mode():
embeddings = model(**inputs)
embeddings = embeddings.last_hidden_state[:, 0]
# Calculate cosine similarities
# Cosine similarities are in [-1, 1]. Higher means more similar
cosine_sim_0_1 = 1 - cosine(embeddings[0], embeddings[1])
cosine_sim_0_2 = 1 - cosine(embeddings[0], embeddings[2])
print("Cosine similarity between \"%s\" and \"%s\" is: %.3f" % (texts[0], texts[1], cosine_sim_0_1))
print("Cosine similarity between \"%s\" and \"%s\" is: %.3f" % (texts[0], texts[2], cosine_sim_0_2))
Выход:
Cosine similarity between "There's a kid on a skateboard." and "A kid is skateboarding." is: 0.941
Cosine similarity between "There's a kid on a skateboard." and "A kid is inside the house." is: 0.779
Могу ли я сделать выходные данные воспроизводимыми, инициализируя/засевая модель по-другому?
Да, ты можешь. Используйте torch.maunal_seed:
import torch
from transformers import AutoModel, AutoTokenizer
model_random = AutoModel.from_pretrained("qiyuw/pcl-bert-base-uncased")
torch.manual_seed(42)
model_repoducible1 = AutoModel.from_pretrained("qiyuw/pcl-bert-base-uncased")
torch.manual_seed(42)
model_repoducible2 = AutoModel.from_pretrained("qiyuw/pcl-bert-base-uncased")
print(torch.allclose(model_random.pooler.dense.weight, model_repoducible1.pooler.dense.weight))
print(torch.allclose(model_random.pooler.dense.weight, model_repoducible2.pooler.dense.weight))
print(torch.allclose(model_repoducible1.pooler.dense.weight, model_repoducible2.pooler.dense.weight))
Выход:
False
False
True