Тематическое моделирование с LDA
Выявляйте скрытые темы в корпусах документов с помощью пакета topicmodels
«Тематическое моделирование с LDA» — бесплатный урок R Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения R Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс R Academy содержит 4 уроков всего.
Что такое тематическое моделирование
Тематическое моделирование — это метод обучения без учителя, который выявляет скрытую тематическую структуру корпуса. Латентное размещение Дирихле (LDA) предполагает, что каждый документ представляет собой смесь K тем, а каждая тема — распределение вероятностей по словам.
# LDA key assumptions:
# 1. Each document is a mixture of K topics
# 2. Each topic is a distribution over all vocabulary words
# 3. Words are generated by first picking a topic, then a word from that topic
# Example: K=2 topics in a corpus about tech and cooking
# Topic 1 (Tech): neural(0.15) python(0.12) algorithm(0.10) ...
# Topic 2 (Cooking): recipe(0.14) flour(0.11) bake(0.10) ...
# A document about 'AI-generated recipes' might be:
# 70% Topic 1 (Tech) + 30% Topic 2 (Cooking)
cat('LDA parameters:\n')
cat('K = number of topics (you choose)\n')
cat('alpha = document-topic sparsity prior\n')
cat('beta = topic-word sparsity prior\n')DocumentTermMatrix из tm
Для LDA требуется матрица «документ–термин» (DTM): строки соответствуют документам, столбцы — словам, а ячейки содержат количество слов. Пакет tm с помощью DocumentTermMatrix() и пакет tidytext с помощью cast_dtm() создают данные в этом формате.
library(tm)
# Build a corpus from raw text
docs <- c(
'machine learning neural networks training algorithms',
'deep learning gradient backpropagation optimizer',
'python scikit numpy pandas machine learning',
'database sql tables indexes queries joins',
'relational schema normalization foreign primary',
'nosql mongodb document store redis queries'
)
corpus <- Corpus(VectorSource(docs))
corpus <- tm_map(corpus, removePunctuation)
corpus <- tm_map(corpus, removeWords, stopwords('english'))
corpus <- tm_map(corpus, stripWhitespace)
dtm <- DocumentTermMatrix(corpus)
cat('DTM shape:', nrow(dtm), 'docs x', ncol(dtm), 'terms\n')
cat('Sparsity: ', round(tm::sparsity(dtm) * 100, 1), '%\n')cast_dtm из tidytext
cast_dtm(document, term, value) напрямую преобразует тиббл с подсчётами слов в DocumentTermMatrix, совместимую с пакетом topicmodels. Это позволяет сохранить конвейер в стиле tidyverse.
library(tidytext)
library(dplyr)
# Tidy corpus
corpus <- tibble::tibble(
doc_id = c(rep(1, 3), rep(2, 3), rep(3, 3), rep(4, 3)),
text = c(
'neural networks deep learning training',
'gradient descent backpropagation optimizer',
'machine learning algorithms classification',
'database sql tables queries indexes',
'relational schema normalization joins',
'foreign primary key constraints transactions',
'recipe bake flour butter oven',
'cooking temperature simmer saute ingredients',
'kitchen knife herbs spices garnish'
)
)
dtm <- corpus |>
unnest_tokens(word, text) |>
anti_join(tidytext::stop_words, by = 'word') |>
count(doc_id, word) |>
cast_dtm(doc_id, word, n)
cat('DTM:', nrow(dtm), 'docs x', ncol(dtm), 'terms\n')LDA(dtm, k = 5): построение модели
LDA(dtm, k = K, control = list(seed = 42)) строит модель LDA с K темами, используя выборку Гиббса или VEM. Всегда задавайте seed для воспроизводимости — LDA является стохастическим методом, поэтому без фиксированного seed результаты разных запусков различаются.
library(tidytext)
library(dplyr)
library(topicmodels)
# Build DTM
corpus <- tibble::tibble(
doc_id = c(rep(1,3), rep(2,3), rep(3,3), rep(4,3), rep(5,3), rep(6,3)),
text = c(
'neural networks deep gradient','backpropagation training optimizer','learning model layers',
'database sql queries indexes','relational tables joins foreign','schema normalization constraints',
'recipe flour bake butter','cooking temperature oven simmer','kitchen knife herbs spices'
)[rep(c(1,2,3,4,5,6), each=1)]
)
dtm <- corpus |>
unnest_tokens(word, text) |>
count(doc_id, word) |>
cast_dtm(doc_id, word, n)
# Fit LDA with k=3 topics
lda_model <- LDA(dtm, k = 3, control = list(seed = 42))
cat('LDA model fitted: k=3 topics\n')
cat('Class:', class(lda_model), '\n')tidy(lda, matrix = 'beta'): вероятности тем для отдельных слов
tidy(lda_model, matrix = 'beta') извлекает вероятности тем для отдельных слов (матрицу beta). Каждая строка содержит вероятность того, что определённое слово было порождено определённой темой. Слова с высокой вероятностью показывают, о чём каждая тема.
library(topicmodels)
library(tidytext)
library(dplyr)
# Using the built-in AssociatedPress dataset
data('AssociatedPress', package = 'topicmodels')
lda <- LDA(AssociatedPress[1:50, ], k = 4, control = list(seed = 1234))
# Per-word topic probabilities (beta)
beta_tbl <- tidy(lda, matrix = 'beta')
cat('Beta matrix rows:', nrow(beta_tbl), '\n')
cat('Columns:', names(beta_tbl), '\n')
# Top words per topic
top_terms <- beta_tbl |>
group_by(topic) |>
slice_max(beta, n = 5) |>
ungroup()
cat('\nTop 5 words per topic:\n')
print(top_terms)Визуализация слов тем
Изобразите N наиболее характерных слов каждой темы на фасетной столбчатой диаграмме. Отсортируйте слова внутри каждой темы по значению beta с помощью reorder_within() и scale_x_reordered() из tidytext, чтобы в каждой фаске использовался правильный независимый порядок.
library(topicmodels)
library(tidytext)
library(dplyr)
library(ggplot2)
data('AssociatedPress', package = 'topicmodels')
lda <- LDA(AssociatedPress[1:100, ], k = 4, control = list(seed = 42))
tidy(lda, matrix = 'beta') |>
group_by(topic) |>
slice_max(beta, n = 8) |>
ungroup() |>
mutate(term = reorder_within(term, beta, topic)) |>
ggplot(aes(term, beta, fill = factor(topic))) +
geom_col(show.legend = FALSE) +
facet_wrap(~topic, scales = 'free') +
scale_x_reordered() +
coord_flip() +
labs(x = NULL, y = 'Beta', title = 'Top Words per LDA Topic') +
theme_minimal(base_size = 10)tidy(lda, matrix = 'gamma'): вероятности тем для документов
tidy(lda_model, matrix = 'gamma') извлекает вероятности тем для отдельных документов (матрицу gamma). Каждая строка содержит долю документа, которая, согласно оценке, относится к определённой теме. Документы с высоким значением gamma для темы 2 посвящены теме 2.
library(topicmodels)
library(tidytext)
library(dplyr)
data('AssociatedPress', package = 'topicmodels')
lda <- LDA(AssociatedPress[1:30, ], k = 3, control = list(seed = 99))
# Per-document topic proportions (gamma)
gamma_tbl <- tidy(lda, matrix = 'gamma')
cat('Gamma matrix rows:', nrow(gamma_tbl), '\n')
# Dominant topic per document
dominant_topic <- gamma_tbl |>
group_by(document) |>
slice_max(gamma, n = 1) |>
ungroup()
cat('\nDominant topic per document (first 8):\n')
print(head(dominant_topic, 8))Выбор K: количество тем
LDA требует заранее задать K. Распространённые подходы: (1) перплексия — меньшее значение лучше, но при увеличении K она монотонно уменьшается; (2) оценка связности (пакет ldatuning); (3) знания предметной области; (4) попробуйте K = 5, 10, 20 и вручную изучите списки слов.
library(topicmodels)
library(tidytext)
data('AssociatedPress', package = 'topicmodels')
# Evaluate perplexity for K = 2, 3, 4, 5
k_values <- 2:5
perplexities <- vapply(k_values, function(k) {
model <- LDA(AssociatedPress[1:50, ], k = k,
control = list(seed = 42))
perplexity(model)
}, numeric(1))
results <- data.frame(k = k_values, perplexity = round(perplexities, 1))
cat('Perplexity by K:\n')
print(results)
cat('\nNote: lower perplexity = better fit to training data\n')Назначение названий тем
LDA создаёт безымянные темы (1, 2, 3…). Аналитик должен присвоить каждой теме название, изучив её наиболее характерные слова. Создайте таблицу соответствий, связывающую номера тем с описательными названиями, и присоедините её к результатам.
library(topicmodels)
library(tidytext)
library(dplyr)
data('AssociatedPress', package = 'topicmodels')
lda <- LDA(AssociatedPress[1:100, ], k = 4, control = list(seed = 7))
# Top 5 words per topic for manual labelling
tidy(lda, matrix = 'beta') |>
group_by(topic) |>
slice_max(beta, n = 5) |>
summarise(top_words = paste(term, collapse = ', ')) |>
print()
# After inspecting top words, assign human-readable labels
topic_labels <- tibble::tibble(
topic = 1:4,
label = c('Politics', 'Economy', 'Sports', 'Science') # your interpretation
)
cat('\nTopic labels assigned (example):\n')
print(topic_labels)LDA для пользовательского корпуса
Ниже приведён полный конвейер LDA для пользовательского корпуса: токенизация, построение DTM, построение LDA, извлечение матрицы beta и отображение наиболее характерных слов каждой темы.
library(tidytext)
library(dplyr)
library(topicmodels)
# 9 documents across 3 latent topics
docs <- tibble::tibble(
id = 1:9,
text = c(
'machine learning neural deep training',
'algorithm gradient backpropagation network',
'python tensorflow keras model layers',
'database sql relational tables queries',
'joins indexes schema foreign primary',
'transactions normalization constraints',
'recipe ingredients bake flour butter',
'cooking temperature simmer oven saute',
'kitchen knife herbs spices garnish'
)
)
dtm <- docs |>
unnest_tokens(word, text) |>
count(id, word) |>
cast_dtm(id, word, n)
lda <- LDA(dtm, k = 3, control = list(seed = 123))
tidy(lda, 'beta') |>
group_by(topic) |>
slice_max(beta, n = 4) |>
summarise(words = paste(term, collapse = ', ')) |>
print()Ограничения LDA
LDA предполагает модель «мешка слов» (порядок слов не имеет значения), считает темы неизменными во всём корпусе и требует заранее выбрать K. Для динамических или иерархических тем рассмотрите STM (Structural Topic Model) или CTM (Correlated Topic Model), доступные в пакете stm.
# LDA assumptions and limitations
limitations <- data.frame(
Assumption = c(
'Bag of words', 'Fixed K', 'Topic independence',
'Static topics', 'No metadata'
),
Alternative = c(
'word2vec / BERT',
'ldatuning for K selection',
'CTM (Correlated Topic Model)',
'DTM (Dynamic Topic Model)',
'STM (Structural Topic Model)'
)
)
print(limitations, row.names = FALSE)Быстрая проверка
Что представляет матрица beta в LDA?
Итоги: тематическое моделирование с помощью LDA
Основные выводы:
- LDA выявляет K скрытых тем как распределения слов из матрицы «документ–термин»
- Создавайте DTM с помощью
cast_dtm(document, word, n)(tidytext) илиDocumentTermMatrix()(tm) LDA(dtm, k = K, control = list(seed = 42))строит модельtidy(lda, 'beta')= вероятности тем для отдельных слов (то, что определяет каждую тему)tidy(lda, 'gamma')= доли тем для отдельных документов (то, чему посвящён каждый документ)- Используйте оценки перплексии или связности, чтобы выбрать K
- Всегда вручную изучайте наиболее характерные слова, чтобы дать темам названия
library(topicmodels)
library(tidytext)
library(dplyr)
data('AssociatedPress', package = 'topicmodels')
lda <- LDA(AssociatedPress[1:20, ], k = 2, control = list(seed = 1))
tidy(lda, 'beta') |>
group_by(topic) |>
slice_max(beta, n = 3) |>
summarise(top_words = paste(term, collapse = ', ')) |>
print()Часто задаваемые вопросы
Урок «Тематическое моделирование с LDA» бесплатный?
Да — полный текст урока «Тематическое моделирование с LDA» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс R Academy, подпишись на CoddyKit PRO. Курс R Academy содержит 4 уроков всего.
Чему я научусь в уроке «Тематическое моделирование с LDA»?
Выявляйте скрытые темы в корпусах документов с помощью пакета topicmodels Ты практикуешь R Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать R Academy?
Предыдущий опыт не требуется. R Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.
Сколько времени занимает урок «Тематическое моделирование с LDA»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке R Academy?
Да. Каждый урок R Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Токенизация и удаление стоп-слов
- TF-IDF и анализ частотности терминов
- Анализ тональности в R
- Тематическое моделирование с LDA