Osadzanie zdań BERT z transformatorów
Próbuję uzyskać wektory zdań ze stanów ukrytych w modelu BERT. Patrząc na instrukcję huggingface BertModel tutaj , które mówią:
from transformers import BertTokenizer, BertModel
tokenizer = BertTokenizer.from_pretrained('bert-base-multilingual-cased')
model = BertModel.from_pretrained("bert-base-multilingual-cased")
text = "Replace me by any text you'd like."
encoded_input = tokenizer(text, return_tensors='pt')
output = model(**encoded_input)
Więc pierwsza uwaga, jak to jest na stronie internetowej, to / nie działa. Dostajesz:
>>> Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'BertTokenizer' object is not callable
Ale wygląda na to, że niewielka zmiana rozwiązuje ten problem, ponieważ nie wywołujesz bezpośrednio tokenizera, ale poproś go o zakodowanie danych wejściowych:
encoded_input = tokenizer.encode(text, return_tensors="pt")
output = model(encoded_input)
OK, pomijając tensory, które dostałem, mają jednak inny kształt niż się spodziewałem:
>>> output[0].shape
torch.Size([1,11,768])
To dużo warstw. Której warstwy należy użyć do osadzania zdań? [0]? [-1]? Uśrednianie kilku? Naszym celem jest uzyskanie z nimi podobieństwa cosinusowego, więc potrzebuję odpowiedniego wektora 1xN zamiast tensora NxK.
Widzę, że popularny projekt bert-as-a-service wydaje się być używany[0]
Czy to jest poprawne? Czy istnieje dokumentacja dotycząca poszczególnych warstw?
Odpowiedzi
Nie sądzę, aby istniała jedna miarodajna dokumentacja mówiąca, czego używać i kiedy. Musisz eksperymentować i mierzyć, co jest najlepsze dla twojego zadania. Ostatnie obserwacje dotyczące BERT są ładnie podsumowane w tym artykule:https://arxiv.org/pdf/2002.12327.pdf.
Myślę, że zasada jest taka:
Użyj ostatniej warstwy, jeśli masz zamiar dostroić model do konkretnego zadania. I dopracuj, kiedy tylko możesz, wystarczy kilkaset, a nawet dziesiątki przykładów treningowych.
Użyj niektórych warstw środkowych (7 lub 8), jeśli nie możesz precyzyjnie wyregulować modelu. Intuicja jest taka, że warstwy najpierw opracowują coraz bardziej abstrakcyjną i ogólną reprezentację danych wejściowych. W pewnym momencie reprezentacja zaczyna być bardziej ukierunkowana na zadanie przedtreningowe.
Bert-as-services domyślnie używa ostatniej warstwy (ale można ją konfigurować). Tutaj byłoby [:, -1]. Jednak zawsze zwraca listę wektorów dla wszystkich tokenów wejściowych. Za [CLS]osadzenie zdania uważa się wektor odpowiadający pierwszemu specjalnemu (tak zwanemu ) tokenowi. To skąd [0]pochodzi snipper, do którego się odnosisz.
Chociaż obecna odpowiedź Jindricha jest ogólnie poprawna, nie rozwiązuje całkowicie pytania. OP zapytał, której warstwy powinien użyć do obliczenia cosinusowego podobieństwa między osadzeniami zdań, a krótka odpowiedź na to pytanie brzmi: żadna . Metryka taka jak podobieństwo cosinusowe wymaga, aby wymiary wektora miały równy i znaczący wkład, ale nie jest to przypadek BERT. Jacob Devlin (jeden z autorów artykułu BERT) napisał :
Nie jestem pewien, jakie to wektory, ponieważ BERT nie generuje znaczących wektorów zdań. Wygląda na to, że chodzi o średnie łączenie tokenów słów w celu uzyskania wektora zdań, ale nigdy nie sugerowaliśmy, że wygeneruje to znaczące reprezentacje zdań. I nawet jeśli są przyzwoitymi reprezentacjami, gdy są wprowadzane do DNN przeszkolonego do dalszego zadania, nie oznacza to, że będą miały znaczenie w kategoriach odległości cosinusowych. (Ponieważ odległość cosinusowa jest przestrzenią liniową, w której wszystkie wymiary mają jednakową wagę).
Nie oznacza to jednak, że nie możesz używać BERT do takiego zadania. Oznacza to po prostu, że nie możesz używać wstępnie wytrenowanych ciężarów po wyjęciu z pudełka. Możesz albo wyszkolić klasyfikator na BERT, który uczy się, które zdania są podobne (używając [CLS]tokena), albo możesz użyć transformatorów zdań, które mogą być używane w scenariuszach bez nadzoru, ponieważ zostały przeszkolone w tworzeniu znaczących reprezentacji zdań.