# загружаем в окружение собранный ранее датасет
load("Brodsky.RData")
<- brodsky|>
poems select(-link)|>
filter(title != "Горбунов и горчаков" & title != "Исаак и Авраам")
# формируем список стоп-слов
library(stopwords)
<- c(
stopwords_ru stopwords("ru", source = "nltk"),
stopwords("ru", source = "snowball"),
stopwords("ru", source = "marimo"),
stopwords("ru", source = "stopwords-iso")
)# удаляем пересечения
<- unique(stopwords_ru)
stopwords_ru
# добавляем стоп-слова вручную
<- stopwords_ru|>
stopwords_ru_added append(c("б", "ибо", "м", "меж", "сиб", "увы", "год", "Б.",
"едва", "ль", "какой-то", "оттого", "рыба", "Рыба",
"черта", "глядеть", "тысяча", "брать", "ум", "угол", "хвост",
"похожий", "нежели", "вещь"))
#вручную удаляем слова "белый" и "жизнь"
<- stopwords_ru_added[-c(154, 383)] stopwords_ru_added
Использование латентно-семантического анализа на материале стихотворений И. А. Бродского
Сбор и подготовка данных
Материал: Использован датасет, собранный для предыдущего домашнего задания
Изменения в датасете: Из датасета убраны поэмы “Горбунов и Горчаков”, “Исаак и Авраам”, попавшие в первоначальный датасет по ошибке.
Ответы на комментарии к предыдущей работе:
В предыдущей работе с датасетом не было объяснено, почему количество текстов у меня и у В. Полухиной отличается. Причина кроется в изначальной ошибке: В. Полухина указывает, что проанализировала 1506 стихотворений И. Бродского, написанных на русском языке. В то же время представленные в прошлом мини-исследовании статистические данные отражали распределение цветовых обозначений в английских текстах. Таким образом, прямое сравнение данного датасета с материалом Полухиной невозможно из-за ошибки автора.
Отличие в объёме текстов между данным датасетом и исследованием В. Полухиной заключается в том, что текущий датасет был сформирован на основе веб-скраббинга с конкретного сайта, на котором представлены стихотворения из собрания сочинений И. Бродского. Однако это собрание не включает многие стихотворения, опубликованные в отдельных сборниках. Проблема также заключается в отсутствии по-настоящему полного собрания сочинений Бродского, что приводит к появлению неточностей в ряде исследований.
Более того, если для английских текстов существует хотя бы минимальная статистика распределения частот, то для русских стихотворений подобной информации не представлено вовсе. Первоначально это вызвало сомнение, но после дальнейшего анализа выяснилось, что действительно никакой статистики для русских стихов в книге нет. Таким образом, сравнивать результаты распределения цветов, полученные в ходе предыдущего исследования, с несуществующей статистикой у В. Полухиной нельзя.
Этапы работы
Стоп-слова
При первом подходе был использован следующий алгоритм (выделенный фрагмент текста отражает решения, которые не были включены в итоговый алгоритм подбора стоп-слов и выбора модели. Однако было принято решение расписать использованные подходы, чтобы обеспечить более полное понимание процесса формирования финального набора стоп-слов)
После уточнения выборки был сформирован список со стоп-словами. Выбрана именно “snowball”, так как: a) В “marimo” включены местоимения, некоторые глаголы, наречия, а также обозначения времен года и времени суток, что для поэзии может быть важно. b) “stopwords-iso” также оказывается мало подходящим для обработки поэтического текста, поскольку слова, которые в новостных текстах считаются малоинформативными и исключаются, в поэзии могут выполнять важные семантические или эстетические функции.
При использовании вышеописанного списка стоп-слов оказалось, что ни распределение по темам с помощью метода LSA, ни поиск ближайших соседей не предоставляют удовлетворительных результатов для анализа. Кроме того, работа с токенизированным корпусом сопровождалась значительным количеством “шума”. Это выявило необходимость предварительной лемматизации корпуса и расширения списка стоп-слов для улучшения качества обработки данных.
Таким образом, итоговый список стоп-слов был сформирован путем объединения всех уникальных слов, содержащихся в четырех списках из библиотеки “stopwords” (nltk, snowball, marimo, stopwords-iso). Это решение было принято несмотря на ранее упомянутую проблему, связанную с включением слов, которые в поэтическом тексте могут играть значимые семантические или эстетические роли.
Однако, одна из проблем, которая будет выявлена в дальнейшем – списки со стоп-словами в итоге содержали, например, отдельные цвета и еще много самостоятельных слов, которые в новотных сводках не были бы важны, однако у Бродского, например, слово “жизнь”. У Бродскго оно часто встречается рядом со словом “смерть”(“…рисуй о смерти, улица моя, // а ты, о птица, вскрикивай о жизни…”). Многие тексты поэта построены на приеме антитезы (жизнь/смерть,пожар/лед, белый/черный), поэтому не хотелось бы упускать некоторые слова. Так, из списка стоп-слов были вручную удалены слова “белый” и “жизнь”.
Лемматизация
Текст был аннотирован с использованием предустановленной модели syntagrus. Дополнительно была протестирована модель taiga, обученная на корпусе русской поэзии, однако её применение привело к значительному количеству некорректно определённых лемм.
Следущим шагом стала транформация в табличный формат, а также очищение корпуса от лишних наблюдений.
#загружаем модель и аннотируем текст
<- udpipe_load_model(file = "russian-syntagrus-ud-2.5-191206.udpipe")
syntagrus <- udpipe_annotate(syntagrus, brodsky$value, doc_id = brodsky$title)
brodsky_annotate
#превращаем в табличный вид и убираем лишнее
<- brodsky_annotate|>
brodsky_annotate_tbl as_tibble()|>
filter(upos != "PUNCT")|> #убираем наблюдения с пунктуацией
select(doc_id, lemma)|> #выбираем только название стиха и лемму
filter(!lemma %in% stopwords_ru_added)|> #удаляем стоп-слова
filter(!str_detect(lemma, "\\d"))|> #удаляем числа
filter(doc_id != "Горбунов и горчаков" & doc_id != "Исаак и Авраам") #удаляем наблюдения из двух поэм
<- brodsky_annotate_tbl|>
count_lemma count(lemma) #считаем количество употреблений каждой леммы
<- brodsky_annotate_tbl|>
brodsky_annotate_tbl_1 left_join(count_lemma)|> #присоединяем столбец с подсчетом к основной таблице
filter(n > 10) #удаляем совсем редкие леммы
TF-IDF, матрица термин-документ, получение LSA-модели
Так как в итоге мы получим матрицу типа термин-документ, вместо показателей абсолютной встречаемости применяем tf-idf.
#добавляем tf-idf
<- brodsky_annotate_tbl_1 |>
poems_tf_idf bind_tf_idf(lemma, doc_id, n) |>
arrange(tf_idf) |>
select(-n, -tf, -idf)
#создает матрицу термин-документ
<- poems_tf_idf |>
dtm cast_sparse(lemma, doc_id, tf_idf)
Применяем сингулярное разложение(SVD) к полученной матрице, используя пакет irlba и переходим к изучению тематики корпуса и поиску наиболее близких слов и документов.
library(irlba)
<- irlba(dtm, 50) lsa_space
Эмбеддинги слов
На данном этапе происходит преобразование матрицы терминов в удобный для анализа формат:
Сначала строкам и столбцам матрицы “U”, присваиваются имена: строки идентифицируются терминами из исходной dtm, а столбцы обозначаются как измерения (dim1, dim2 и т.д.).
Затем матрица преобразуется в таблицу, где каждому термину соответствуют его значения в различных документах.
Далее таблица переводится в длинный формат при помощи pivot_longer, где каждому термину соответствует список измерений и их значений.
Итоговая таблица подходит для дальнейшего анализа и визуализации.
# Этап №1
rownames(lsa_space$u) <- rownames(dtm)
colnames(lsa_space$u) <- paste0("dim", 1:50)
# Этап №2
<- lsa_space$u |>
word_emb as.data.frame() |>
rownames_to_column("word") |>
as_tibble()
# Этап №3
<- word_emb |>
word_emb_long pivot_longer(-word, names_to = "dimension", values_to = "value") |>
mutate(dimension = as.numeric(str_remove(dimension, "dim")))
Визуализация тем
Визуализируем также 9 основных тем и посмотрим, можно ли назвать их осмысленными.
|>
word_emb_long filter(dimension < 10)|>
group_by(dimension) |>
top_n(10, abs(value)) |>
ungroup() |>
mutate(word = reorder_within(word, value, dimension)) |>
ggplot(aes(word, value, fill = dimension)) +
geom_col(alpha = 0.8, show.legend = FALSE) +
facet_wrap(~dimension, scales = "free_y", ncol = 3) +
scale_x_reordered() +
coord_flip() +
labs(
x = NULL,
y = "Value",
title = "Первые 9 тематических областей у Бродского",
subtitle = "Топ-10 слов"
+
) scale_fill_viridis_c()
Судя по представленным графикам, данные, полученные методом LSA, выглядят частично осмысленными. В некоторых тематических группах видно, что слова имеют семантическую связь друг с другом. Это видно из их лексического или контекстуального сходства. Например:
- В первой группе доминируют слова, связанные с зимой (например, “зима” рядом со словами “снег”, “звезда”, “ветер”, “небо” и “сон”). Зима – частый фон в стихотворениях Бродского до 1972 г.. Некоторые стихотворения описывают этот период как время, когда все замедляется, замирает и засыпает (часто можно увидеть мотив нахождения дома)
Однако часть вышеперечисленных слов можно увидеть в большей части групп. Правда все вместе они встречаются только в группе №1.
- В третье группе наблюдается присутствие слов с философским и экзистенциальным подтекстом (“умирать”, “чувствовать”, “приближаться”). Эти слова оказались рядом, как мне кажется, из-за того что процесс “умирания” у Бродского часто соседствует с ощущением приближения чего-то нового. Мотив возрождения, нового начала. Это может быть связано с временами года(чаще в осенью) или со сменой дня и ночи
Остальные группы сложно интерпретировать как осмысленные, поскольку они включают комбинации уже упомянутых токенов. Слова, не затронутые в предыдущем анализе, но присутствующие на графике, сложно объединить и отнести к конкретным тематическим группам. Тем не менее, дальнейший анализ с использованием поиска ближайших слов и документов на основе эмбеддингов демонстрирует более содержательные результаты
Ближайшие слова
Используем эмбеддинги для поиска ближайших соседей. Начнем с поиска слов. Так как прошлое исслеодвание было построено вокруг цветов, похожие слова мы будем искать для пяти цветов. Для начала была использована библиотеку “widyr”. Написанная функция вычисляет ближайших соседей для заданной сущности (в нашем случае, слова) на основе косинусного сходства между векторами:
library(widyr)
<- function(df, feat, doc=F) {
nearest_neighbors <- function() {
inner_f widely(
~ {
<- .[rep(feat, nrow(.)), ]
y <- rowSums(. * y) /
res sqrt(rowSums(. ^ 2)) * sqrt(sum(.[feat, ] ^ 2)))
(
matrix(res, ncol = 1, dimnames = list(x = names(res)))
},sort = TRUE
)}if (doc) {
|> inner_f()(doc, dimension, value) }
df else {
|> inner_f()(word, dimension, value)
df |>
} select(-item2)
}
Далее, последовательно вычисляются 10 ближайших соседей для пяти слов, обозначающих цвета(“белый”, “черный”, “красный”, “синий”, “зеленый”), на основе их векторных представлений:
<- word_emb_long|>
white nearest_neighbors("белый")|>
head(10)|>
select(-value)|>
rename(белый = item1)
<- word_emb_long|>
black nearest_neighbors("черный")|>
head(10)|>
select(-value)|>
rename(черный = item1)
<- word_emb_long|>
red nearest_neighbors("красный")|>
head(10)|>
select(-value)|>
rename(красный = item1)
<- word_emb_long|>
blue nearest_neighbors("синий")|>
head(10)|>
select(-value)|>
rename(синий = item1)
<- word_emb_long|>
green nearest_neighbors("зеленый")|>
head(10)|>
select(-value)|>
rename(зеленый = item1)
<- mutate(white, black, red, blue, green)
colors
colors
Эти данные также можно считать осмысленными лишь частично. Некоторые ближайшие слова действительно связаны с цветами. Например, для слова “белый” присутствуют слова “памятник” и “всадник”, которые могут ассоциироваться с образом, содержащим белый цвет. Также слова “пожар” и “роза” для “красный” передают достаточно очевидные цветовые ассоциации. Однако в списке есть и менее явные связи, такие как “цифра” или “шуршать” для слова “зеленый”, что может указывать на контекстуальную связь.
Особого внимания заслуживает 10-е в списке ближайших соседей для “белый” — “темный”. На первый взгляд, это слово сложно отнести к категории схожих. Однако, если обратиться к ранее проведенному анализу цветовых обозначений (из прошлого домашнего задания), становится очевидным, что антонимичные пары, такие как “темный/светлый” и “черный/белый”, часто встречаются в одном и том же стихотворении в рамках антитезы. Поскольку модель ориентируется не на поиск синонимов в традиционном понимании, а на выявление слов, которые часто встречаются вместе в текстах, такой результат оказывается вполне закономерным.
Ближайшие документы
Теперь при помощи того же алгоритма определим ближайшие документы(стихотворения). Алгоритм здесь аналогичен тому, что был сделан для получения эмбеддингов ближаших слов. Отличие состоит в том, что информация о документах хранится в матрице правых сингулярных векторов, и нужно изменить “word” на “doc”.
rownames(lsa_space$v) <- colnames(dtm)
colnames(lsa_space$v) <- paste0("dim", 1:50)
<- lsa_space$v |>
doc_emb as.data.frame() |>
rownames_to_column("doc") |>
as_tibble()
<- doc_emb |>
doc_emb_long pivot_longer(-doc, names_to = "dimension", values_to = "value") |>
mutate(dimension = as.numeric(str_remove(dimension, "dim")))
Поскольку результаты анализа топиков явно выделили тему природы и зимы, представляется логичным провести поиск текстов, наиболее близких к стихотворению, посвященному этой тематике. В качестве проверки работы алгоритма было выбрано стихотворение «Откуда к нам пришла зима».
<- doc_emb_long|>
winter_poems nearest_neighbors("Откуда к нам пришла зима...", doc = TRUE)|>
head(10)|>
select(-item2)|>
rename(Стихотворение = item1)
winter_poems
Полученные результаты уже можно назвать по-настоящему осмысленными: первые два стихотворения с самыми высокими показателями действительно описывают зиму. Помимо этого, в стихотворениях встречаются и не такие очевидные пересечения:
- Образ губ:
- “Откуда к нам пришла зима”: “Умолкло все. Она сама // холодных губ не разжимает…”;
- “Заснешь с прикушенной губой” – здесь уже в названии видно пересечение
- В первых трех стихотоврения встречается упоминание снега.
Можно ли согласиться с тем, что эти тексты действительно близкие? Полагаясь на достаточно поверхностных анализ, можно сказать, что да.
Таким образом, алгоритм выдал вполне осмысленные данные, которые можно использовать в будущих исследованиях. Однако, для полноценной картины стоит попробовать обработать данные при помощи PMI и Word2Vec, но это останется на новогодние праздники.
Поздравляем! Вы открыли новое достижение! Вам подарок: