Gensim — библиотека обработки естественного языка предназначения для «Тематического моделирования».
С его помощью можно обрабатывать тексты, работать с векторными моделями слов (такими как Word2Vec, FastText и т. д.) и создавать тематические модели текстов.
Что такое Gensim?
Тематическое моделирование — это метод извлечения основных тем которым посвящен обрабатываемый текст. В пакете Gensim реализованы основные алгоритмы тематического моделирования LDA и LSI (о которых мы расскажем позже в этом посте).
Вы можете заметить, что эти алгоритмы доступны и в других пакетах, таких как scikit, R и т. д. Но скорость обработки и качество результата и оценки тематических моделей не имеют аналогов в gensim, плюс так в пакете много удобных средств для обработки текста.
Еще одно существенное преимущество gensim: он позволяет обрабатывать большие текстовые файлы, не загружая весь файл в память.
В этой статье я расскажу:
И многое другое..
Чтобы работать с текстовыми документами, Gensim требует, чтобы слова (или как еще их называют в данном случае токены) были преобразованы в уникальные идентификаторы. Для этого в Gensim необходимо создать объект Dictionary, который сопоставит каждое слово с уникальным идентификатором.
Итак, как создать Словарь
?
Преобразуем текст или предложения в [список слов] и передадим этот список объекту corpora.Dictionary().
Я покажу как это сделать, в следующем разделе. А сейчас рассмотрим зачем нужен объект словаря и как его использовать?
Объект словаря обычно используется для создания так называемого мешка слов «bag of words«. Именно этот словарь и «bag of words» (Corpus) используются в качестве входных данных для тематического моделирования и других моделей, на которых специализируется Gensim.
Какие типы текстов может обрабатывать Gensim? Входной текст может быть в одной из 3 разных форм:
Если вам необходимо обработать большой по объему тестовый файл, то Gensim может загрузить текст и обновить словарь, по одной строчке за раз, без загрузки всего текстового файла в системную память. Мы покажем, как это сделать в следующих двух разделах.
Но, прежде определенимся с терминалогией обработки естественного языка.
«Токен» обычно означает «слово». «Документ» обычно может относиться как к «предложению» так и к «абзацу», а «корпус» обычно представляет собой «собрание документов» в виде пакета слов или как его еще называют мешка слов. То есть для каждого документа корпус содержит идентификатор каждого слова и его частоту в этом документе. В результате информация о порядке слов теряется.
Далее рассмотрим, как создать словарь из списка предложений.
Вы можете создать словарь из списка предложений, из текстового файла, который содержит несколько строк текста, и из нескольких таких текстовых файлов, содержащихся в каталоге.
Начнем со «Список предложений».
Если у вас есть несколько предложений, вам нужно преобразовать каждое предложение в список слов.
import gensim from gensim import corpora from pprint import pprint # How to create a dictionary from a list of sentences? documents = ["The Saudis are preparing a report that will acknowledge that", "Saudi journalist Jamal Khashoggi's death was the result of an", "interrogation that went wrong, one that was intended to lead", "to his abduction from Turkey, according to two sources."] documents_2 = ["One source says the report will likely conclude that", "the operation was carried out without clearance and", "transparency and that those involved will be held", "responsible. One of the sources acknowledged that the", "report is still being prepared and cautioned that", "things could change."] # Tokenize(split) the sentences into words texts = [[text for text in doc.split()] for doc in documents] # Create dictionary dictionary = corpora.Dictionary(texts) # Get information about the dictionary print(dictionary) #> Dictionary(33 unique tokens: ['Saudis', 'The', 'a', 'acknowledge', 'are']...)
В итоге мы получим словарь из 34 уникальных токена (или слова). Давайте посмотрим уникальные идентификаторы для каждого из этих токенов.
# Show the word to id map print(dictionary.token2id) #> {'Saudis': 0, 'The': 1, 'a': 2, 'acknowledge': 3, 'are': 4, #> 'preparing': 5, 'report': 6, 'that': 7, 'will': 8, 'Jamal': 9, #> "Khashoggi's": 10, 'Saudi': 11, 'an': 12, 'death': 13, #> 'journalist': 14, 'of': 15, 'result': 16, 'the': 17, 'was': 18, #> 'intended': 19, 'interrogation': 20, 'lead': 21, 'one': 22, #> 'to': 23, 'went': 24, 'wrong,': 25, 'Turkey,': 26, 'abduction': 27, #> 'according': 28, 'from': 29, 'his': 30, 'sources.': 31, 'two': 32}
Мы успешно создали объект Dictionary. Gensim будет использовать этот словарь для создания корпуса, в котором слова в документах заменяются соответствующим идентификатором, предоставленным этим словарем.
Если вы загрузите новые документы в будущем, также нужно будет обновить существующий словарь, чтобы включить новые слова.
documents_2 = ["The intersection graph of paths in trees", "Graph minors IV Widths of trees and well quasi ordering", "Graph minors A survey"] texts_2 = [[text for text in doc.split()] for doc in documents_2] dictionary.add_documents(texts_2) # If you check now, the dictionary should have been updated with the new words (tokens). print(dictionary) #> Dictionary(45 unique tokens: ['Human', 'abc', 'applications', 'computer', 'for']...) print(dictionary.token2id) #> {'Human': 0, 'abc': 1, 'applications': 2, 'computer': 3, 'for': 4, 'interface': 5, #> 'lab': 6, 'machine': 7, 'A': 8, 'of': 9, 'opinion': 10, 'response': 11, 'survey': 12, #> 'system': 13, 'time': 14, 'user': 15, 'EPS': 16, 'The': 17, 'management': 18, #> 'System': 19, 'and': 20, 'engineering': 21, 'human': 22, 'testing': 23, 'Relation': 24, #> 'error': 25, 'measurement': 26, 'perceived': 27, 'to': 28, 'binary': 29, 'generation': 30, #> 'random': 31, 'trees': 32, 'unordered': 33, 'graph': 34, 'in': 35, 'intersection': 36, #> 'paths': 37, 'Graph': 38, 'IV': 39, 'Widths': 40, 'minors': 41, 'ordering': 42, #> 'quasi': 43, 'well': 44}
Вы также можете создать словарь из текстового файла или из каталога содержащего несколько текстовых файлов.
Приведенный ниже пример считывает файл построчно и использует функци gensim simple_preprocess для обработки одной строки файла за раз.
Преимущество в том, что вы можете прочитать весь текстовый файл, не загружая файл в память сразу.
Допустим что файл будет называться файл sample.txt, чтобы продемонстрировать это.
from gensim.utils import simple_preprocess from smart_open import smart_open import os # Create gensim dictionary form a single tet file dictionary = corpora.Dictionary(simple_preprocess(line, deacc=True) for line in open('sample.txt', encoding='utf-8')) # Token to Id map dictionary.token2id #> {'according': 35, #> 'and': 22, #> 'appointment': 23, #> 'army': 0, #> 'as': 43, #> 'at': 24, #> ... #> }
Мы создали словарь из одного текстового файла.
Теперь, как читать по одной строке из нескольких файлов?
Предположим, что у вас есть несколько текстовых файлов в одном каталоге, вам будет необходимо определить новый класс и добавить метод __iter__. Метод __iter__() должен перебирать все файлы в данном каталоге и возвращать обработанный список токенов слов.
Давайте определим класс с именем ReadTxtFiles, который принимает путь к каталогу, содержащему текстовые файлы.
class ReadTxtFiles(object): def __init__(self, dirname): self.dirname = dirname def __iter__(self): for fname in os.listdir(self.dirname): for line in open(os.path.join(self.dirname, fname), encoding='latin'): yield simple_preprocess(line) path_to_text_directory = "lsa_sports_food_docs" dictionary = corpora.Dictionary(ReadTxtFiles(path_to_text_directory)) # Token to Id map dictionary.token2id # {'across': 0, # 'activity': 1, # 'although': 2, # 'and': 3, # 'are': 4, # ... # }
Сейчас вы знаете, как создать словарь из списка и из текстового файла.
Следующим важным объектом, с которым вам нужно ознакомиться, чтобы работать в Gensim, — это корпус слов (или как его еще называют корпус типа «Мешок Слов«). Все объекты корпуса содержит id слова и его частоту в каждом документе.
После создания словаря, все, что вам нужно сделать, это передать токенизированный список слов в Dictionary.doc2bow()
Давайте создадим корпус для простого списка (my_docs), содержащего 2 предложения.
# List with 2 sentences my_docs = ["Who let the dogs out?", "Who? Who? Who? Who?"] # Tokenize the docs tokenized_list = [simple_preprocess(doc) for doc in my_docs] # Create the Corpus mydict = corpora.Dictionary() mycorpus = [mydict.doc2bow(doc, allow_update=True) for doc in tokenized_list] pprint(mycorpus) #> [[(0, 1), (1, 1), (2, 1), (3, 1), (4, 1)], [(4, 4)]]
Как это интерпретируется?
В строке 1 (0, 1) означает, что слово с id = 0 появляется один раз в 1-м документе. Аналогично (4, 4) во втором элементе списка означает, что слово с идентификатором 4 встречается 4 раза во втором документе. И так далее.
Если нужно будет преобразовать этот массив обратно в текст.
word_counts = [[(mydict[id], count) for id, count in line] for line in mycorpus] pprint(word_counts) #> [[('dogs', 1), ('let', 1), ('out', 1), ('the', 1), ('who', 1)], [('who', 4)]]
Обратите внимание, что при таком преобразование теряется порядок слов. Сохраняется только слово и информация о его частоте.
Использовать слова из списков в Python довольно просто, потому что весь текст уже в памяти. Однако у вас может быть большой файл, который вы возможно не захотите загружать целиком в память.
Вы можете импортировать такие файлы по одной строке за раз, определив класс и функцию __iter__, которая итеративно считывает файл по одной строке за раз и выдает объект корпуса. Но как создать объект корпуса?
__iter__ () из BoWCorpus читает строку из файла, обрабатывает ее с помощью simple_preprocess() и передает его в dictionary.doc2bow(). Чем это отличается от класса ReadTxtFiles, который мы создали ранее? В новом классе я использую функцию smart_open() из пакета smart_open, так как оно позволяет построчно открывать и читать большие файлы из различных источников, таких как S3, HDFS, WebHDFS, HTTP или локальных и сжатых файлов.
from gensim.utils import simple_preprocess from smart_open import smart_open import nltk nltk.download('stopwords') # run once from nltk.corpus import stopwords stop_words = stopwords.words('english') class BoWCorpus(object): def __init__(self, path, dictionary): self.filepath = path self.dictionary = dictionary def __iter__(self): global mydict # OPTIONAL, only if updating the source dictionary. for line in smart_open(self.filepath, encoding='latin'): # tokenize tokenized_list = simple_preprocess(line, deacc=True) # create bag of words bow = self.dictionary.doc2bow(tokenized_list, allow_update=True) # update the source dictionary (OPTIONAL) mydict.merge_with(self.dictionary) # lazy return the BoW yield bow # Create the Dictionary mydict = corpora.Dictionary() # Create the Corpus bow_corpus = BoWCorpus('sample.txt', dictionary=mydict) # memory friendly # Print the token_id and count for each line. for line in bow_corpus: print(line) #> [(0, 1), (1, 1), (2, 1), (3, 1), (4, 1), (5, 1), (6, 1), (7, 1), (8, 1), (9, 1), (10, 1), (11, 1)] #> [(12, 1), (13, 1), (14, 1), (15, 1), (16, 1), (17, 1)] #> ... truncated ...
Это довольно просто. Смотрите примеры ниже.
# Save the Dict and Corpus mydict.save('mydict.dict') # save dict to disk corpora.MmCorpus.serialize('bow_corpus.mm', bow_corpus) # save corpus to disk
Мы сохранили словарь и объект корпуса. Давайте загрузим их обратно.
# Load them back loaded_dict = corpora.Dictionary.load('mydict.dict') corpus = corpora.MmCorpus('bow_corpus.mm') for line in corpus: print(line)
Термин «частота термина — обратная частота документа» (TF-IDF) также является моделью «мешка слов», но в отличие от обычного корпуса, TFIDF содержит веса токенов (слов), которые часто встречаются в документах.
Другими словами, когда мы анализируем текстовые данные, мы часто сталкиваемся со словами, которые встречаются в двух и более документах. Такие часто встречающиеся слова, как правило, не содержат полезной или отличительной информации, поэтому фактор их наличия нужно как то уменьшить или полностью удалить из рассмотрения.
Как рассчитывается TFIDF?
Tf-Idf вычисляется путем умножения локального компонента, такого как термин частота (TF), на глобальный компонент, то есть обратную частоту документа (IDF), и провести (необязательно) нормализация по длине блока.
В результате слова, которые наиболее часто встречаются в документе, будут понижены в весе.
Существуют различные варианты формул для TF и IDF. Gensim использует информационно-поисковую систему SMART. Вы можете вручную указать, какую формулу использовать, указав параметр smartirs в TfidfModel. См. help(models.TfidfModel) для более подробной информации.
Итак, как получить веса в TFIDF?
from gensim import models import numpy as np documents = ["This is the first line", "This is the second sentence", "This third document"] # Create the Dictionary and Corpus mydict = corpora.Dictionary([simple_preprocess(line) for line in documents]) corpus = [mydict.doc2bow(simple_preprocess(line)) for line in documents] # Show the Word Weights in Corpus for doc in corpus: print([[mydict[id], freq] for id, freq in doc]) # [['first', 1], ['is', 1], ['line', 1], ['the', 1], ['this', 1]] # [['is', 1], ['the', 1], ['this', 1], ['second', 1], ['sentence', 1]] # [['this', 1], ['document', 1], ['third', 1]] # Create the TF-IDF model tfidf = models.TfidfModel(corpus, smartirs='ntc') # Show the TF-IDF weights for doc in tfidf[corpus]: print([[mydict[id], np.around(freq, decimals=2)] for id, freq in doc]) # [['first', 0.66], ['is', 0.24], ['line', 0.66], ['the', 0.24]] # [['is', 0.24], ['the', 0.24], ['second', 0.66], ['sentence', 0.66]] # [['document', 0.71], ['third', 0.71]]
Обратите внимание на разницу в весах слов между исходным корпусом и корпусом с весами по tfidf.
Слова «is» и «the» встречаются в двух документах и у них понижены веса. Слово «this», встречающееся во всех трех документах, полностью исключено. Проще говоря, слова, встречающиеся в документах чаще, приобретают меньший вес.
Gensim предоставляет встроенное API для загрузки популярных наборов текстовых данных.
Полный список доступных наборов данных и моделей поддерживается здесь .
Использовать API для загрузки набора данных так же просто, как вызвать метод api.load() с правильными данными или названием модели.
В приведенном ниже примере показано, как загрузить модель glove-wiki-gigaword-50.
import gensim.downloader as api # Get information about the model or dataset api.info('glove-wiki-gigaword-50') # {'base_dataset': 'Wikipedia 2014 + Gigaword 5 (6B tokens, uncased)', # 'checksum': 'c289bc5d7f2f02c6dc9f2f9b67641813', # 'description': 'Pre-trained vectors based on Wikipedia 2014 + Gigaword, 5.6B tokens, 400K vocab, uncased (https://nlp.stanford.edu/projects/glove/).', # 'file_name': 'glove-wiki-gigaword-50.gz', # 'file_size': 69182535, # 'license': 'http://opendatacommons.org/licenses/pddl/', # (... truncated...) # Download w2v_model = api.load("glove-wiki-gigaword-50") w2v_model.most_similar('blue') # [('red', 0.8901656866073608), # ('black', 0.8648407459259033), # ('pink', 0.8452916741371155), # ('green', 0.8346816301345825), # ... ]
Теперь вы знаете, как загружать наборы данных и предварительно обученные модели с помощью gensim.
Давайте загрузим набор данных text8, который представляет собой не что иное, как «Первые 100 000 000 байтов простого текста из Википедии». Затем из этого мы c генерируем биграммы и триграммы.
Но что такое биграммы и триграммы? и зачем они нужны?
Очень часто некоторые слова встречаются парами (биграмма) или группами по три (триграмма). Потому что два слова, объединенные вместе, образуют определенную сущность. Например: слово «французский» относится как к языку так и к региону, а слово «революция» может относиться к планетарной революции. Но их сочетание, «французская революция», означает нечто совершенно иное.
Очень важно формировать биграммы и триграммы из предложений, особенно при работе с моделями мешков слова.
Так как же создавать биграммы?
С моделью Gensim Phrases это довольно просто и эффективно. Созданная модель фраз позволяет индексировать, поэтому просто передайте исходный текст (список) в построенную модель фраз, чтобы сформировать биграммы. Пример показан ниже:
dataset = api.load("text8") dataset = [wd for wd in dataset] dct = corpora.Dictionary(dataset) corpus = [dct.doc2bow(line) for line in dataset] # Build the bigram models bigram = gensim.models.phrases.Phrases(dataset, min_count=3, threshold=10) # Construct bigram print(bigram[dataset[0]]) # ['anarchism', 'originated', 'as', 'a', 'term', 'of', 'abuse', 'first', 'used', # 'against', 'early', 'working_class', 'radicals', 'including', 'the', 'diggers', # 'of', 'the', 'english', 'revolution', 'and', 'the', 'sans_culottes', 'of', 'the', # 'french_revolution', 'whilst',...]
Биграммы готовы. Можете ли вы угадать, как создать триграмму?
Просто повторите процедуру для создание моделей биграмм. Смотрите пример ниже.
# Build the trigram models trigram = gensim.models.phrases.Phrases(bigram[dataset], threshold=10) # Construct trigram print(trigram[bigram[dataset[0]]])
Целью тематических моделей является извлечение основных тем из заданного набора текстовых документов. Каждый документ в тексте рассматривается как комбинация тем, а каждая тема — как комбинация связанных слов.
Тематическое моделирование может выполняться с помощью таких алгоритмов, как скрытое распределение Дирихле (LDA) и скрытое семантическое индексирование (LSI).
В обоих случаях вам нужно указать количество тем в качестве входных данных. Модель темы, в свою очередь, предоставит ключевые слова темы для каждой темы и процентное содержание тем в каждом документе.
Качество тем сильно зависит от качества обработки текста и количества тем, которые вы предоставляете алгоритму.
Шаг 0: Загрузите необходимые пакеты и импортируйте стоп-слова.
# Step 0: Import packages and stopwords from gensim.models import LdaModel, LdaMulticore import gensim.downloader as api from gensim.utils import simple_preprocess, lemmatize from nltk.corpus import stopwords import re import logging logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s') logging.root.setLevel(level=logging.INFO) stop_words = stopwords.words('english') stop_words = stop_words + ['com', 'edu', 'subject', 'lines', 'organization', 'would', 'article', 'could']
Шаг 1: Импортируйте набор данных. Я собираюсь использовать набор данных text8, который можно загрузить с помощью API-загрузчика gensim.
# Step 1: Import the dataset and get the text and real topic of each news article dataset = api.load("text8") data = [d for d in dataset]
Шаг 2: Подготовьте загруженные данные, удалив стоп-слова и лемматизируя их. Для лемматизации, gensim использует пакет pattern. Поэтому обязательно запустите pip install pattern в своем терминале. Я настроил лемматизацию таким образом, что сохраняются только существительные (NN), прилагательные (JJ) и местоимения (RB). Только лишь потому что я предпочитаю, чтобы в качестве ключевых слов темы использовались только такие слова. Это исключительно мой личный выбор.
# Step 2: Prepare Data (Remove stopwords and lemmatize) data_processed = [] for i, doc in enumerate(data[:100]): doc_out = [] for wd in doc: if wd not in stop_words: # remove stopwords lemmatized_word = lemmatize(wd, allowed_tags=re.compile('(NN|JJ|RB)')) # lemmatize if lemmatized_word: doc_out = doc_out + [lemmatized_word[0].split(b'/')[0].decode('utf-8')] else: continue data_processed.append(doc_out) # Print a small sample print(data_processed[0][:5]) #> ['anarchism', 'originated', 'term', 'abuse', 'first']
data_processed теперь обрабатывается как список слов. Теперь вы можете использовать его для создания словаря и корпуса, которые затем будут использоваться в качестве входных данных для модели LDA.
# Step 3: Create the Inputs of LDA model: Dictionary and Corpus dct = corpora.Dictionary(data_processed) corpus = [dct.doc2bow(line) for line in data_processed]
Теперь у нас есть словарь и корпус. Давайте создадим модель темы LDA с 7 темами, используя LdaMulticore(). 7 тем — произвольный выбор на данный момент.
# Step 4: Train the LDA model lda_model = LdaMulticore(corpus=corpus, id2word=dct, random_state=100, num_topics=7, passes=10, chunksize=1000, batch=False, alpha='asymmetric', decay=0.5, offset=64, eta=None, eval_every=0, iterations=100, gamma_threshold=0.001, per_word_topics=True) # save the model lda_model.save('lda_model.model') # See the topics lda_model.print_topics(-1) # [(0, '0.001*"also" + 0.000*"first" + 0.000*"state" + 0.000*"american" + 0.000*"time" + 0.000*"book" + 0.000*"year" + 0.000*"many" + 0.000*"person" + 0.000*"new"'), # (1, '0.001*"also" + 0.001*"state" + 0.001*"ammonia" + 0.000*"first" + 0.000*"many" + 0.000*"american" + 0.000*"war" + 0.000*"time" + 0.000*"year" + 0.000*"name"'), # (2, '0.005*"also" + 0.004*"american" + 0.004*"state" + 0.004*"first" + 0.003*"year" + 0.003*"many" + 0.003*"time" + 0.003*"new" + 0.003*"war" + 0.003*"person"'), # (3, '0.001*"atheism" + 0.001*"also" + 0.001*"first" + 0.001*"atheist" + 0.001*"american" + 0.000*"god" + 0.000*"state" + 0.000*"many" + 0.000*"new" + 0.000*"year"'), # (4, '0.001*"state" + 0.001*"also" + 0.001*"many" + 0.000*"world" + 0.000*"agave" + 0.000*"time" + 0.000*"new" + 0.000*"war" + 0.000*"god" + 0.000*"person"'), # (5, '0.001*"also" + 0.001*"abortion" + 0.001*"first" + 0.001*"american" + 0.000*"state" + 0.000*"many" + 0.000*"year" + 0.000*"time" + 0.000*"war" + 0.000*"person"'), # (6, '0.005*"also" + 0.004*"first" + 0.003*"time" + 0.003*"many" + 0.003*"state" + 0.003*"world" + 0.003*"american" + 0.003*"person" + 0.003*"apollo" + 0.003*"language"')]
Lda_model.print_topics показывает, какие слова внесли вклад в какую из 7 тем, а также вес вклада слова в эту тему.
Вы можете увидеть такие слова, как «also», «many», встречающиеся в разных темах. Поэтому я бы добавил такие слова в список stop_words, чтобы удалить их.
Объект lda_model поддерживает индексацию. То есть, если вы передаете документ (список слов) в lda_model, он предоставляет 3 типа тем:
Что такое phi?
phi — это вероятность того, что слово относится к этой конкретной теме. А сумма значений phi для данного слова складывается с количеством раз, когда это слово встречалось в документе.
Например, в приведенном ниже выводе для 0-го документа слово с id = 0 относится к теме №6, а значение phi равно 3.999. Это означает, что слово с id = 0 появилось 4 раза в 0-м документе.
# Reference: https://github.com/RaRe-Technologies/gensim/blob/develop/docs/notebooks/topic_methods.ipynb for c in lda_model[corpus[5:8]]: print("Document Topics : ", c[0]) # [(Topics, Perc Contrib)] print("Word id, Topics : ", c[1][:3]) # [(Word id, [Topics])] print("Phi Values (word id) : ", c[2][:2]) # [(Word id, [(Topic, Phi Value)])] print("Word, Topics : ", [(dct[wd], topic) for wd, topic in c[1][:2]]) # [(Word, [Topics])] print("Phi Values (word) : ", [(dct[wd], topic) for wd, topic in c[2][:2]]) # [(Word, [(Topic, Phi Value)])] print("------------------------------------------------------\n") #> Document Topics : [(2, 0.96124125), (6, 0.038569752)] #> Word id, Topics : [(0, [2, 6]), (7, [2, 6]), (10, [2, 6])] #> Phi Values (word id) : [(0, [(2, 2.887749), (6, 0.112249866)]), (7, [(2, 0.90105206), (6, 0.09893738)])] #> Word, Topics : [('ability', [2, 6]), ('absurdity', [2, 6])] #> Phi Values (word) : [('ability', [(2, 2.887749), (6, 0.112249866)]), ('absurdity', [(2, 0.90105206), (6, 0.09893738)])] #> ------------------------------------------------------ #> Document Topics : [(6, 0.9997751)] #> Word id, Topics : [(0, [6]), (10, [6]), (16, [6])] #> Phi Values (word id) : [(0, [(6, 5.9999967)]), (10, [(6, 2.9999983)])] #> Word, Topics : [('ability', [6]), ('academic', [6])] #> Phi Values (word) : [('ability', [(6, 5.9999967)]), ('academic', [(6, 2.9999983)])] #> ------------------------------------------------------ #> Document Topics : [(6, 0.9998023)] #> Word id, Topics : [(1, [6]), (10, [6]), (15, [6])] #> Phi Values (word id) : [(1, [(6, 0.99999917)]), (10, [(6, 5.999997)])] #> Word, Topics : [('able', [6]), ('academic', [6])] #> Phi Values (word) : [('able', [(6, 0.99999917)]), ('academic', [(6, 5.999997)])] #> ------------------------------------------------------
Синтаксис использования модели LSI аналогичен тому, как мы строили модель LDA, за исключением того, что мы будем использовать LsiModel().
from gensim.models import LsiModel # Build the LSI Model lsi_model = LsiModel(corpus=corpus, id2word=dct, num_topics=7, decay=0.5) # View Topics pprint(lsi_model.print_topics(-1)) #> [(0, '0.262*"also" + 0.197*"state" + 0.197*"american" + 0.178*"first" + ' #> '0.151*"many" + 0.149*"time" + 0.147*"year" + 0.130*"person" + 0.130*"world" ' #> '+ 0.124*"war"'), #> (1, '0.937*"agave" + 0.164*"asia" + 0.100*"aruba" + 0.063*"plant" + 0.053*"var" ' #> '+ 0.052*"state" + 0.045*"east" + 0.044*"congress" + -0.042*"first" + ' #> '0.041*"maguey"'), #> (2, '0.507*"american" + 0.180*"football" + 0.179*"player" + 0.168*"war" + ' #> '0.150*"british" + -0.140*"also" + 0.114*"ball" + 0.110*"day" + ' #> '-0.107*"atheism" + -0.106*"god"'), #> (3, '-0.362*"apollo" + 0.248*"lincoln" + 0.211*"state" + -0.172*"player" + ' #> '-0.151*"football" + 0.127*"union" + -0.125*"ball" + 0.124*"government" + ' #> '-0.116*"moon" + 0.116*"jews"'), #> (4, '-0.363*"atheism" + -0.334*"god" + -0.329*"lincoln" + -0.230*"apollo" + ' #> '-0.215*"atheist" + -0.143*"abraham" + 0.136*"island" + -0.132*"aristotle" + ' #> '0.124*"aluminium" + -0.119*"belief"'), #> (5, '-0.360*"apollo" + 0.344*"atheism" + -0.326*"lincoln" + 0.226*"god" + ' #> '0.205*"atheist" + 0.139*"american" + -0.130*"lunar" + 0.128*"football" + ' #> '-0.125*"moon" + 0.114*"belief"'), #> (6, '-0.313*"lincoln" + 0.226*"apollo" + -0.166*"football" + -0.163*"war" + ' #> '0.162*"god" + 0.153*"australia" + -0.148*"play" + -0.146*"ball" + ' #> '0.122*"atheism" + -0.122*"line"')]
Модель вхождения слов — это модель, которая может предоставить числовые векторы для данного слова. Используя API Gensim, вы можете загрузить предварительно созданные модели вхождения слов, такие как word2vec, fasttext, GloVe и ConceptNet. Они построены на больших корпусах часто встречающихся текстовых данных, таких как википедия, новости Google и т. д.
Однако, если вы работаете в специализированной нише, такой как техническая документация, возможно у вас не получится получить вложение слов для всех слов. Поэтому в таких случаях желательно тренировать собственную модель.
Gensim Word2Vec так же позволяет вам обучить вашу собственную модель встраивания слов для вашего корпуса.
from gensim.models.word2vec import Word2Vec from multiprocessing import cpu_count import gensim.downloader as api # Download dataset dataset = api.load("text8") data = [d for d in dataset] # Split the data into 2 parts. Part 2 will be used later to update the model data_part1 = data[:1000] data_part2 = data[1000:] # Train Word2Vec model. Defaults result vector size = 100 model = Word2Vec(data_part1, min_count = 0, workers=cpu_count()) # Get the word vector for given word model['topic'] #> array([ 0.0512, 0.2555, 0.9393, ... ,-0.5669, 0.6737], dtype=float32) model.most_similar('topic') #> [('discussion', 0.7590423822402954), #> ('consensus', 0.7253159284591675), #> ('discussions', 0.7252693176269531), #> ('interpretation', 0.7196053266525269), #> ('viewpoint', 0.7053568959236145), #> ('speculation', 0.7021505832672119), #> ('discourse', 0.7001898884773254), #> ('opinions', 0.6993060111999512), #> ('focus', 0.6959210634231567), #> ('scholarly', 0.6884037256240845)] # Save and Load Model model.save('newmodel') model = Word2Vec.load('newmodel')
Мы обучили и сохранили модель Word2Vec для нашего документа. Однако, когда придет новый набор данных, нам нужно будет обновить модель, чтобы учесть новые слова.
В существующей модели Word2Vec вызовите build_vocab() для нового набора данных, а затем вызовите метод train().
# Update the model with new data. model.build_vocab(data_part2, update=True) model.train(data_part2, total_examples=model.corpus_count, epochs=model.iter) model['topic'] # array([-0.6482, -0.5468, 1.0688, 0.82 , ... , -0.8411, 0.3974], dtype=float32)
Мы только что увидели, как получить векторы слов для модели Word2Vec, которую мы только что обучили. Тем не менее, gensim позволяет загружать самые предварительно обученные модели через API загрузчика. Давайте посмотрим, как извлечь векторы слов из пары таких моделей.
import gensim.downloader as api # Download the models fasttext_model300 = api.load('fasttext-wiki-news-subwords-300') word2vec_model300 = api.load('word2vec-google-news-300') glove_model300 = api.load('glove-wiki-gigaword-300') # Get word embeddings word2vec_model300.most_similar('support') # [('supporting', 0.6251285076141357), # ... # ('backing', 0.6007589101791382), # ('supports', 0.5269277691841125), # ('assistance', 0.520713746547699), # ('supportive', 0.5110025405883789)]
У нас есть 3 разных модели. Вы можете оценить, какая из них работает лучше, используя соответствующую функцию evaluate_word_analogies()
# Word2ec_accuracy word2vec_model300.evaluate_word_analogies(analogies="questions-words.txt")[0] #> 0.7401448525607863 # fasttext_accuracy fasttext_model300.evaluate_word_analogies(analogies="questions-words.txt")[0] #> 0.8827876424099353 # GloVe accuracy glove_model300.evaluate_word_analogies(analogies="questions-words.txt")[0] #> 0.7195422354510931
В отличие от Word2Vec, модель Doc2Vec обеспечивает векторизованное представление группы слов, взятых вместе как единое целое. Это не просто среднее из векторов слов в предложении.
Давайте использовать набор данных text8 для обучения Doc2Vec.
import gensim import gensim.downloader as api # Download dataset dataset = api.load("text8") data = [d for d in dataset]
Обучающие данные для Doc2Vec должны быть списком TaggedDocuments. Чтобы создать его, мы передаем список слов и уникальное целое число в качестве входных данных для models.doc2vec.TaggedDocument().
# Create the tagged document needed for Doc2Vec def create_tagged_document(list_of_list_of_words): for i, list_of_words in enumerate(list_of_list_of_words): yield gensim.models.doc2vec.TaggedDocument(list_of_words, [i]) train_data = list(create_tagged_document(data)) print(train_data[:1]) #> [TaggedDocument(words=['anarchism', 'originated', ... 'social', 'or', 'emotional'], tags=[0])]
Входные данные подготовлены. Чтобы обучить модель, нам нужно инициализировать модель Doc2Vec, построить словарный запас и, наконец, обучить модель.
# Init the Doc2Vec model model = gensim.models.doc2vec.Doc2Vec(vector_size=50, min_count=2, epochs=40) # Build the Volabulary model.build_vocab(train_data) # Train the Doc2Vec model model.train(train_data, total_examples=model.corpus_count, epochs=model.epochs)
Чтобы получить вектор предложения, передайте его в виде списка слов в метод infer_vector().
print(model.infer_vector(['australian', 'captain', 'elected', 'to', 'bowl'])) #> array([-0.11043505, 0.21719663, -0.21167697, -0.10790558, 0.5607173 , #> ... #> 0.16428669, -0.31307793, -0.28575218, -0.0113026 , 0.08981086], #> dtype=float32)
Мягкое косинусное сходство похоже на косинусное сходство, но, кроме того оно так же рассматривает семантические отношения между словами через их векторное представление.
Для вычисления мягкое косинусное сходство нам понадобится модель встраивания слов, такая как Word2Vec или FastText.
Сначало, вычислим similarity_matrix. Затем преобразуем входные предложения в совокупность слов и передадим их в softcossim() вместе с матрицей подобия.
from gensim.matutils import softcossim from gensim import corpora sent_1 = 'Sachin is a cricket player and a opening batsman'.split() sent_2 = 'Dhoni is a cricket player too He is a batsman and keeper'.split() sent_3 = 'Anand is a chess player'.split() # Prepare the similarity matrix similarity_matrix = fasttext_model300.similarity_matrix(dictionary, tfidf=None, threshold=0.0, exponent=2.0, nonzero_limit=100) # Prepare a dictionary and a corpus. documents = [sent_1, sent_2, sent_3] dictionary = corpora.Dictionary(documents) # Convert the sentences into bag-of-words vectors. sent_1 = dictionary.doc2bow(sent_1) sent_2 = dictionary.doc2bow(sent_2) sent_3 = dictionary.doc2bow(sent_3) # Compute soft cosine similarity print(softcossim(sent_1, sent_2, similarity_matrix)) #> 0.7868705819999783 print(softcossim(sent_1, sent_3, similarity_matrix)) #> 0.6036445529268666 print(softcossim(sent_2, sent_3, similarity_matrix)) #> 0.60965453519611
Ниже приведены некоторые полезные метрики сходства и расстояния, основанные на моделях встраивания слов, таких как fasttext и GloVe.
# Which word from the given list doesn't go with the others? print(fasttext_model300.doesnt_match(['india', 'australia', 'pakistan', 'china', 'beetroot'])) #> beetroot # Compute cosine distance between two words. print(fasttext_model300.distance('king', 'queen')) #> 0.22957539558410645 # Compute cosine distances from given word or vector to all words in `other_words`. print(fasttext_model300.distances('king', ['queen', 'man', 'woman'])) #> [0.22957546 0.465837 0.547001 ] # Compute cosine similarities print(fasttext_model300.cosine_similarities(fasttext_model300['king'], vectors_all=(fasttext_model300['queen'], fasttext_model300['man'], fasttext_model300['woman'], fasttext_model300['queen'] + fasttext_model300['man']))) #> array([0.77042454, 0.534163 , 0.45299897, 0.76572555], dtype=float32) # Note: Queen + Man is very similar to King. # Get the words closer to w1 than w2 print(glove_model300.words_closer_than(w1='king', w2='kingdom')) #> ['prince', 'queen', 'monarch'] # Find the top-N most similar words. print(fasttext_model300.most_similar(positive='king', negative=None, topn=5, restrict_> [('queen', 0.63), ('prince', 0.62), ('monarch', 0.59), ('kingdom', 0.58), ('throne', 0.56)] # Find the top-N most similar words, using the multiplicative combination objective, print(glove_model300.most_similar_cosmul(positive='king', negative=None, topn=5)) #> [('queen', 0.82), ('prince', 0.81), ('monarch', 0.79), ('kingdom', 0.79), ('throne', 0.78)]
Gensim реализует суммирование textrank с помощью функции sumumize() в модуле суммирования. Все, что вам нужно сделать, это передать текстовую строку вместе с коэффициентом суммирования вывода или максимальным количеством слов в итоговом выводе.
Нет необходимости разбивать предложение на токенизированный список, потому что gensim сам выполняет разбиение, используя встроенный метод split_sentences() в модуле gensim.summarization.texcleaner.
Давайте подведем итог вырезке из новой статьи в sample.txt.
from gensim.summarization import summarize, keywords from pprint import pprint text = " ".join((line for line in smart_open('sample.txt', encoding='utf-8'))) # Summarize the paragraph pprint(summarize(text, word_count=20)) #> ('the PLA Rocket Force national defense science and technology experts panel, ' #> 'according to a report published by the') # Important keywords from the paragraph print(keywords(text)) #> force zhang technology experts pla rocket
В этой статье я постарался рассказать о различных особенностях и возможностях gensim. Надеюсь вы получили представление о том, как работать с текстами и манипулировать ими. Приведенные выше примеры могут служить хорошими шаблонами, с которых вы можете начать изучать gensim.
Оригинал статьи Gensim Tutorial – A Complete Beginners Guide
Краткий перевод: https://vuejs.org/guide/components/v-model.html Основное использование v-model используется для реализации двусторонней привязки в компоненте. Начиная с Vue…
Сегодня мы рады объявить о выпуске Vue 3.4 «🏀 Slam Dunk»! Этот выпуск включает в…
Vue.js — это универсальный и адаптируемый фреймворк. Благодаря своей отличительной архитектуре и системе реактивности Vue…
Недавно, у меня истек сертификат и пришлось заказывать новый и затем устанавливать на хостинг с…
Каким бы ни было ваше мнение о JavaScript, но всем известно, что работа с датами…
Все, кто следит за последними событиями в мире адаптивного дизайна, согласятся, что введение контейнерных запросов…