Doc2Vec: Сходство между закодированными документами и невидимыми документами
У меня есть образец ~60000 документов. 700 из них мы закодировали вручную как имеющие определенный тип контента. Теперь мы бы хотели найти "наиболее похожие" документы на 700, которые мы уже написали вручную. Мы используем gensim doc2vec, и я не могу найти лучший способ сделать это.
Вот как выглядит мой код:
cores = multiprocessing.cpu_count()
model = Doc2Vec(dm=0, vector_size=100, negative=5, hs=0, min_count=2, sample=0,
epochs=10, workers=cores, dbow_words=1, train_lbls=False)
all_docs = load_all_files() # this function returns a named tuple
random.shuffle(all_docs)
print("Docs loaded!")
model.build_vocab(all_docs)
model.train(all_docs, total_examples=model.corpus_count, epochs=5)
Я не могу понять, как правильно идти вперед. Это то, что может сделать doc2vec? В конце я хотел бы получить ранжированный список из 60 000 документов, где первый является "наиболее похожим" документом.
Спасибо за любую помощь! Я потратил много времени, читая справочные документы Gensim и различные учебные пособия, и не смог понять это.
РЕДАКТИРОВАТЬ: я могу использовать этот код, чтобы получить документы, наиболее похожие на короткое предложение:
token = "words associated with my research questions".split()
new_vector = model.infer_vector(token)
sims = model.docvecs.most_similar([new_vector])
for x in sims:
print(' '.join(all_docs[x[0]][0]))
Если есть способ изменить это, чтобы вместо этого получить документы, наиболее похожие на 700 закодированных документов, я хотел бы узнать, как это сделать!
3 ответа
Ваш общий подход разумен. Несколько замечаний о вашей настройке:
- вам нужно будет указать
epochs=10
в вашемtrain()
призыв, чтобы действительно получить 10 учебных пропусков - и 10 или более чаще всего встречается в опубликованных работах sample
управляемая понижающая дискретизация помогает ускорить обучение и часто улучшает качество вектора, а значение может стать более агрессивным (меньшим) с большими наборами данныхtrain_lbls
не является параметромDoc2Vec
в любой недавнийgensim
версия
Существует несколько возможных способов интерпретации и достижения вашей цели "найти" наиболее похожие "документы из 700, которые мы уже написали вручную". Например, для документа-кандидата, как определить его сходство с набором 700 - как сходство с одним итоговым вектором "центроида" для полного набора? Или как его сходство с любым из документов?
Есть несколько способов получить один сводный вектор для набора:
в среднем их 700 векторов вместе
объединить все их слова в один синтетический составной документ, и
infer_vector()
на этом документе. (Но обратите внимание: тексты переданыgensim
Оптимизированные подпрограммы word2vec/doc2vec сталкиваются с внутренним пределом реализации в 10000 токенов - лишние слова молча игнорируются.)
На самом деле, most_similar()
Метод может принять список из нескольких векторов в качестве "положительной" цели и автоматически усреднит их вместе перед возвратом результатов. Так что, если, скажем, 700 идентификаторов документов (теги, используемые во время обучения) находятся в списке ref_docs
можно попробовать...
sims = model.docvecs.most_similar(positive=ref_docs, topn=0)
... и получить ранжированный список всех других документов в модели, по их сходству со средним числом всех positive
Примеры.
Однако альтернативная интерпретация того, что сходство документа с набором ссылок является его наибольшим сходством с каким-либо одним документом внутри набора, может быть лучше для вашей цели. Это может особенно иметь место, если сам набор ссылок варьируется по многим темам и, следовательно, не суммируется одним средним вектором.
Вы должны были бы вычислить эти сходства с вашими собственными циклами. Например, примерно:
sim_to_ref_set = {}
for doc_id in all_doc_ids:
sim_to_ref_set[doc_id] = max([model.docvecs.similarity(doc_id, ref_id) for ref_id in ref_docs])
sims_ranked = sorted(sim_to_ref_set.items(), key=lambda it:it[1], reverse=True)
Лучшие предметы в sims_ranked
будет наиболее похожим на любой элемент в наборе ссылок. (Предполагая, что идентификаторы набора ссылок также находятся в all_doc_ids
, первые 700 результатов снова будут выбранными документами, все с самоподобием 1.0
.)
n_similarity
выглядит как функция, которую вы хотите, но, похоже, она работает только с образцами в обучающем наборе.
Поскольку у вас есть только 700 документов для перекрестной проверки, использование sklearn не должно вызывать проблем с производительностью. Просто получите векторы из ваших 700 документов и используйте sklearn.metrics.pairwise.cosine_sdentifity, а затем найдите наиболее близкое соответствие. Затем вы можете найти те, которые имеют наибольшее сходство (например, используя `np.argmax). Некоторый не проверенный код, чтобы проиллюстрировать это:
from sklearn.metrics.pairwise import cosine_similarity
reference_vectors = ... # your vectors to the 700 documents
new_vector = ... # inferred as per your last example
similarity_matrix = cosine_similarity([new_vector], reference_vectors)
most_similar_indices = np.argmax(similarity_matrix, axis=-1)
Это также может быть изменено для реализации такого метода, как n_similarity
для ряда невидимых документов.
Я думаю, что вы можете делать то, что вы хотите с TaggedDocument. Основной вариант использования - просто добавить уникальный тег (идентификатор документа) для каждого документа, но здесь вы захотите добавить специальный тег ко всем 700 выбранным вами документам. Называй как хочешь, в этом случае я называю это TARGET
, Добавьте этот тег только к своим 700 документам, помеченным вручную, пропустите его для остальных 59 300.
TaggedDocument(words=gensim.utils.simple_preprocess(document),tags=['TARGET',document_id])
Теперь тренируйте свой Doc2Vec.
Далее вы можете использовать model.docvecs.similarity
чтобы оценить сходство между вашими немаркированными документами и пользовательским тегом.
model.docvecs.similarity(document_id,'TARGET')
А потом просто сортируй это. n_similarity
а также most_similar
Я не думаю, что будет подходящим для того, что вы хотите сделать.
60000 документов - это не очень много для Doc2Vec, но, возможно, вам повезет.