Это пример поиска сходства с векторами признаков размерности 300 (1,2 КБ для 32-битных чисел с плавающей запятой). ).
Вы можете хранить векторы слов в геометрической структуре данных, sklearn.neighbors .BallTree, чтобы значительно ускорить поиск, избегая потерь высокой размерности, связанных с деревьями k-d (нет ускорения, когда размерность превышает ~100). Их можно легко мариновать и распаковывать и хранить в памяти, если вам нужно избежать загрузки spaCy. Подробности реализации смотрите ниже. Демо, источник.
Другие ответы с линейным поиском работают (и я просто хотел бы отметить, что будьте осторожны при использовании сходства косинусов, если какой-либо из ваших векторов равен нулю), но будут медленными для больших словарей. Библиотека en_core_web_lg
компании spaCy содержит около 680 тыс. слов с векторами слов. Поскольку каждое слово обычно занимает несколько байтов, это может привести к использованию памяти в несколько ГБ.
Мы можем сделать наш поиск нечувствительным к регистру и удалить нечастые слова, используя таблицу частотности слов (начиная с версии 3.0, spaCy имеет встроенную таблицу, но теперь вам нужно загружать их отдельно), чтобы сократить словарный запас до ~ 100 тыс. слов. Однако поиск по-прежнему линейный и может занять пару секунд, что может быть неприемлемо.
Существуют библиотеки для быстрого поиска сходства, однако они могут быть довольно громоздкими и сложными в установке, и предназначены для векторов функций порядка МБ или ГБ с ускорением графического процессора и остальными.
Мы также можем не захотеть всегда загружать весь словарь spaCy при каждом запуске приложения, поэтому мы собираем/распаковываем словарь по мере необходимости.
import spacy, numpy, pickle
import sklearn.neighbors as nbs
#load spaCy
nlp=spacy.load("en_core_web_lg")
#load lexeme probability table
lookups = load_lookups("en", ["lexeme_prob"])
nlp.vocab.lookups.add_table("lexeme_prob", lookups.get_table("lexeme_prob"))
#get lowercase words above frequency threshold with vectors, min_prob=-20
words = [word for word in nlp.vocab.strings if nlp.vocab.has_vector(word) and word.islower() and nlp.vocab[word].prob >= -18]
wordvecs = numpy.array([nlp.vocab.get_vector(word) for word in words]) #get wordvectors
tree = nbs.BallTree(wordvecs) #create the balltree
dict = dict(zip(words,wordvecs)) #create word:vector dict
После обрезки словаря мы можем выбрать слова, dict и balltree и загрузить их, когда они нам понадобятся, без повторной загрузки spaCy:
#pickle/unpickle the balltree if you don't want to load spaCy
with open('balltree.pkl', 'wb') as f:
pickle.dump(tree,f,protocol=pickle.HIGHEST_PROTOCOL)
#...
#load wordvector balltree from pickle file
with open('./balltree.pkl','rb') as f:
tree = pickle.load(f)
Получив слово, получите его вектор слова, найдите в дереве индекс ближайшего слова, затем найдите это слово в словаре:
#get wordvector and lookup nearest words
def nearest_words(word):
#get vectors for all words
try:
vec = to_vec[word]
#if word is not in vocab, set to zero vector
except KeyError:
vec = numpy.zeros(300)
#perform nearest neighbor search of wordvector vocabulary
dist, ind = tree.query([vec],10)
#lookup nearest words using indices from tree
near_words = [vocab[i] for i in ind[0]]
return near_words
person
Jackson Walters
schedule
07.02.2021