Как найти ближайшее слово к вектору с помощью BERT - PullRequest
3 голосов
/ 22 января 2020

Я пытаюсь получить текстовое представление (или ближайшее слово) встраивания данного слова с помощью BERT. В основном я пытаюсь получить функциональность, аналогичную gensim:

>>> your_word_vector = array([-0.00449447, -0.00310097, 0.02421786, ...], dtype=float32)
>>> model.most_similar(positive=[your_word_vector], topn=1))

До сих пор я мог генерировать контекстное встраивание слов, используя bert-as-service , но не могу понять как получить наиболее близкие слова к этому встраиванию. Я использовал предварительно обученную модель Берта (uncased_L-12_H-768_A-12) и не делал тонкой настройки.

Ответы [ 2 ]

4 голосов
/ 22 марта 2020

TL; DR

После ответа Джиндтриха Я реализую контекстно-зависимый искатель ближайших соседей. Полный код доступен в моем Github gist

Требуется BERT-подобная модель (я использую bert-embeddings ) и корпус предложений (я взял маленький от здесь ), обрабатывает каждое предложение и сохраняет контекстные вложения токенов в эффективно доступную для поиска структуру данных (я использую KDTree , но не стесняйтесь выбирать FAISS или HNSW или что-то еще).

Примеры

Модель построена следующим образом:

# preparing the model
storage = ContextNeighborStorage(sentences=all_sentences, model=bert)
storage.process_sentences()
storage.build_search_index()

Затем ее можно запросить для контекстуально наиболее похожих слов, например

# querying the model
distances, neighbors, contexts = storage.query(
    query_sent='It is a power bank.', query_word='bank', k=5)

В этом примере ближайшим соседом будет слово " bank " в предложении " Наконец, есть вторая версия Duo, которая имеет мощность 2000 мАч Банк , Мир Энергии Флип.".

Если, однако, мы ищем то же слово в другом контексте, например

distances, neighbors, contexts = storage.query(
    query_sent='It is an investment bank.', query_word='bank', k=5)

, то ближайший сосед будет в предложении " bank также был присвоен 5-звездочный рейтинг Superior Bauer за De c. 31, 2017, финансовые данные."

Если мы не хотим получить слово« банк »или его производное слово, мы можем отфильтровать их

distances, neighbors, contexts = storage.query(
     query_sent='It is an investment bank.', query_word='bank', k=5, filter_same_word=True)

, и тогда в предложении " Кахал является вице-председателем Deloitte UK, а ближайшим соседом будет слово" finance". Председатель Консультативного Корпоративного Финансового бизнеса с 2014 года (ранее возглавлял бизнес с 2005 года).".

Заявка в NER

Одним из замечательных применений этого подхода является интерпретируемое распознавание именованных сущностей . Мы можем заполнить поисковый индекс примерами, помеченными IOB, и затем использовать найденные примеры, чтобы вывести правильную метку для слова запроса.

Например, ближайший сосед " Безос объявил, что его два Служба доставки в сутки, Amazon Prime, превысила 100 миллионов подписчиков по всему миру."is" Расширенная сторонняя интеграция, включая Amazon Alexa, Google Assistant и IFTTT. ».

Но для " Атлантида c обладает достаточной энергией волн и приливов, чтобы вынести большую часть осадков Амазонки в море, таким образом, река не образует истинного delta"ближайший сосед" И, в этом году, наши истории - это работа путешествия из водопада Игуассу в Бразилии 1081 * на птицеферму в Атланте".

Таким образом, если бы эти соседи были помечены, мы могли бы заключить, что в первом контексте «Amazon» - это ORGanization, а во втором - это LOCation.

Код

Вот класс, который выполняет эту работу:

import numpy as np
from sklearn.neighbors import KDTree
from tqdm.auto import tqdm


class ContextNeighborStorage:
    def __init__(self, sentences, model):
        self.sentences = sentences
        self.model = model

    def process_sentences(self):
        result = self.model(self.sentences)

        self.sentence_ids = []
        self.token_ids = []
        self.all_tokens = []
        all_embeddings = []
        for i, (toks, embs) in enumerate(tqdm(result)):
            for j, (tok, emb) in enumerate(zip(toks, embs)):
                self.sentence_ids.append(i)
                self.token_ids.append(j)
                self.all_tokens.append(tok)
                all_embeddings.append(emb)
        all_embeddings = np.stack(all_embeddings)
        # we normalize embeddings, so that euclidian distance is equivalent to cosine distance
        self.normed_embeddings = (all_embeddings.T / (all_embeddings**2).sum(axis=1) ** 0.5).T

    def build_search_index(self):
        # this takes some time
        self.indexer = KDTree(self.normed_embeddings)

    def query(self, query_sent, query_word, k=10, filter_same_word=False):
        toks, embs = self.model([query_sent])[0]

        found = False
        for tok, emb in zip(toks, embs):
            if tok == query_word:
                found = True
                break
        if not found:
            raise ValueError('The query word {} is not a single token in sentence {}'.format(query_word, toks))
        emb = emb / sum(emb**2)**0.5

        if filter_same_word:
            initial_k = max(k, 100)
        else:
            initial_k = k
        di, idx = self.indexer.query(emb.reshape(1, -1), k=initial_k)
        distances = []
        neighbors = []
        contexts = []
        for i, index in enumerate(idx.ravel()):
            token = self.all_tokens[index]
            if filter_same_word and (query_word in token or token in query_word):
                continue
            distances.append(di.ravel()[i])
            neighbors.append(token)
            contexts.append(self.sentences[self.sentence_ids[index]])
            if len(distances) == k:
                break
        return distances, neighbors, contexts
3 голосов
/ 23 января 2020

BERT обеспечивает контекстное представление, т. Е. Совместное представление слова и контекста . В отличие от неконтекстовых вложений, не так ясно, что должно означать ближайшее слово.

Хорошее приближение близких слов, безусловно, является предсказанием того, что BERT делает как (маскированную) языковую модель. Это в основном говорит, что подобные слова могут быть в одном контексте. Однако этого нет в клиентском API службы bert-as-service . Вы можете либо самостоятельно реализовать слой предсказания (я думаю, что это просто умножение последнего слоя на матрицу встраивания + softmax, но, возможно, происходит дополнительная проекция), либо использовать другую реализацию, такую ​​как Transformers Hugginface .

Наиболее теоретически правильным (и дорогостоящим в вычислительном отношении) решением было бы запустить BERT для большого набора данных и сохранить пары слов и соответствующие контекстные представления, а затем использовать, например, faiss для получения ближайшего Соседи, которые также включают контекст, аналогично языковым моделям ближайших соседей .

...