Время прочтения: 4 мин.

На какие вопросы поможет ответить тематическое моделирование?

Быстро понять, какие темы поднимаются в тексте.

• Выделить наиболее обсуждаемые топики в комментариях.

• Лучше узнать свою аудиторию для таргетирования.

• Находить сообщества, которые интересуются нужными темами.

В моей задаче необходимо было разобрать темы обсуждений в сообществах социальной сети Вконтакте. При решении такой объемной задачи очень важно правильно разбить ее на несколько маленьких пунктов. Для себя я выделила такие: поиск сообществ Вконтакте, написание парсеров для сбора комментариев, предобработка извлеченных данных, моделирование, интерпретация результатов, построение тематических профилей.

Первая проблема, с которой я столкнулась при использовании VK API для сбора комментариев, состояла в том, что метод wall.getComments, который позволяет нам извлечь комментарии к записям со стены сообщества, может вернуть только 100 записей. Используя этот код, вы можете собрать их все.

def vk(method, **params):
    url  = 'https://api.vk.com/method/%s' % method
    data = {
               'wall.getComments'        : {'owner_id': owner_id,
                                    'post_id': post_id,
                                     'count' : 100},
    }.get(method,{})
    
    data.update(params)
    data.update({'access_token':access_token,"v": V,})
        resp  = requests.post(url,data=data)
    answer = resp.json()
       if 'error' in answer:
        print('error:',answer )
        return []
    return answer ['response']
if __name__ == "__main__":    
    all_post=pd.DataFrame() 
    for i in range(n):
        offset=i*100
        post=pd.DataFrame(vk('wall.get', owner_id= owner_id,
                        count = 100,offset=offset))
        all_post=pd.concat([all_post,post],ignore_index=True)
        time.sleep(2)

Второй этап, представляющий из себя предобработку данных, заключается в извлечении из полученных словарей необходимых признаков комментария и самого текста. Для этого пишем функции на основе регулярных выражений.

Например, таким образом мы извлекаем текст комментария.

import re
finded=[]
finded=[]
def text_comment(data):
    try:
        finded=re.search("'text': .*'",str(data)).group()[8:]
    except:
        finded=0
    return finded
all_comments['text_comment']=all_comments['items'].apply(text_comment)

Затем, для обработки моделью, тексты стоит очистить от пунктуации, пробелов и цифр, пишем еще одну функцию.

def drop_punctuation(text):   #пробелы
    text = re.sub('\s+', ' ', text)
    #все кроме слов
    reg = re.compile('[^a-zA-Zа-яА-Я ]')
    text=reg.sub('', text)
    return text
data['text'] = (data['Заголовок'].astype(str)+' '+data['Комментарий'].astype(str)).apply(lambda x:drop_punctuation(str(x)))

Чтобы установить структурированные семантические отношения между словами, необходимо все слова комментариев преобразовать в их базовую форму. Я воспользовалась морфологическим анализатором для русскоязычных текстов pymystem3.

from pymystem3 import Mystem 
m = Mystem() 
# применяем лемматизацию 
data['text_bow'] = data_['text'].apply(lambda x: m.lemmatize(x)) 

Далее необходимо избавиться от шумовых слов, удалить общие стоп-слова мы можем используя список слов, собранных на GitHub. Но что же делать с зависимыми или контекстными шумами? Для этого, основываясь на своем мнении я составила еще один список слов, который позже присоединила к существующему.

file_sw = open('stopwords_ru.txt', encoding='utf-8')
stop_words = [line.strip() for line in file_sw]
stop_words.extend([‘Здесь будут ваши слова’])
def doc_to_words_ru(doc, stop_words):
    stop_words = set(stop_words)
    words = [w for w in gensim.utils.simple_preprocess(str(doc), deacc=True, max_len=100) if w not in stop_words]
    return words
data['text_clean'] = [doc_to_words_ru(t, stop_words) for t in data['text_bow']]

В тексте могут встречаться специфические сочетания для сообщества или темы комментария пользователей, для тематического моделирования очень важно не упустить подобные тонкости, поэтому сгенерируем биграммы. После того как у нас есть популярные сочетания составляем конечный словарь терминов, отсекая при этом слишком редкие и частые.

from gensim import corpora, models 
combination_of_words = models.Phrases(data.text_clean, min_count=3, threshold=5) 
combination_of_words_mod = models.phrases.Phraser(combination_of_words) 
def make_combination_of_words(texts): 
    return [combination_of_words_mod[doc] for doc in texts] 
list_of_texts = make_combination_of_words(data.text_clean) 
dictionary_texts = corpora.Dictionary(list_of_texts) 
dictionary_texts.filter_extremes(no_below=3, no_above=0.8) 
word_corpus = [dictionary_texts.doc2bow(text) for text in list_of_texts]

Когда предобработка данных закончена, нам предстоит заняться моделированием тематических профилей сообщества и визуализация результатов.

lda_model = models.ldamodel.LdaModel(corpus= word_corpus, id2word= dictionary_texts, num_topics=6, passes=5 )

 Параметр num_topics необходимо установить перебором, на основе значения когерентности модели используя обратный метод локтя.

Визуализируем результат:

import pyLDAvis.gensim
pyLDAvis.gensim.prepare(lda_model, word_corpus, dictionary_texts)

У нас получилась интерактивная картинка, которая отображает многомерные топики в двумерном пространстве. С помощью подобной визуализации мы можем просмотреть каждый топик, а так же увидеть вклад каждого слова. Для получения тематик комментариев, на основе полученных характеризующих наборов слов, необходимо самостоятельно проинтерпретировать результаты и заименовать их, удобным для нас образом так, чтобы они ответили на поставленные в начале исследования вопросы.