[ЗАСТАВКА] В этом видео мы посмотрим пример использования библиотеки gensim для тематического моделирования. В gensim реализована модель lda, обучение которой основано на эволюционном байесовском выводе. lda, действительно, очень популярны, и часто их используют просто как «чёрный ящик», подают матрице частоты слов и получают на выходе построенные матрицы. Причина состоит в том, что хорошо понять, как обучается lda, можно только, прослушав курс байесовских методов машинного обучения. С другой стороны, если вам не нужно вносить какие-то усложнения в эту модель, то почему бы не воспользоваться ей как «чёрным ящиком». Сегодня мы с вами будем строить модель for fun. Мы возьмём dataset, состоящий из текстовых описаний к комиксам xkcd. Для примера я покажу вам вот такой комикс, в котором рассказывается, как полезна может быть теорема Байеса в реальной жизни. Ну а теорема Байеса, как несложно догадаться, это основа байесовских методов машинного обучения. Что ж, давайте приступим к построению модели. Для начала нам, разумеется, нужно импортировать модули. Мы импортируем компоненты corpora и models из модуля gensim. Первый поможет нам импортировать данные, а второй — построить непосредственно модель. Данные у нас представлены в формате UCI Bag of Words. Это популярный формат, который позволяет максимально сжато представить разреженную матрицу частот слов. Давайте посмотрим пример. Это исходные данные. Здесь указаны ссылки на комиксы и текстовые описания того, что на них изображено. Из этих текстов я удалила всю пунктуацию и выполнила лимитизацию, то есть приведение каждого слова к начальной форме. В итоге я получила матрицу частот слов, которая представлена вот так. В этом файле в первой строке записано количество документов, во второй — количество слов, в третьей — общее количество слов во всех документах коллекции. Дальше в каждой строке идёт три числа — это номер документа, номер слова и количество, сколько раз это слово встретилось в документе. Обращу внимание, что нумерация и документов, и слов здесь ведётся с единицы, а не с нуля, как в некоторых языках программирования, например, в Python. Спрашивается, где же здесь тексты? А тексты здесь во втором файле, который обязательно должен идти рядом с первым. Это файл-словарь. Здесь в каждой строке с номером I записано слово, которое отвечает этому индексу. То есть, например, под индексом 1 у нас идёт слово boy. Итак, давайте импортируем эти файлы в нашу модель. Для этого мы создадим объект класса UciCorpus и укажем путь к двум нашим файлам. Также gensim требует, чтобы мы в отдельную переменную обязательно сохранили словарь. Это можно сделать с помощью метода create_dictionary у объекта data, который мы получили в первой строке. Теперь мы готовы к обучению модели. Это можно сделать, вызвав функцию ldamodel из компоненты models. Давайте посмотрим на её параметры. Первый параметр — это corpus, у нас это, он хранится в переменной data, мы её и указываем. Второй параметр, который, конечно, необязателен, но крайне желательно его указать, это id2word. Это, собственно, отображение индексов слов непосредственно в слова. У нас это как раз тот dictionary, который мы создали во второй строке предыдущей ячейки. Далее идут некоторые параметры, которые влияют на обучение модели. Если у вас коллекция небольшая, как в нашем случае, то можно сделать много проходов по коллекции, и тогда модель получится наиболее адекватной. Тогда нужно указать параметр passes большим, например 20. Если же у вас коллекция действительно большая, например, как «википедия», то делать по ней двадцать проходов будет очень долго. Тогда можно указывать один или два прохода, но нужно обязательно подобрать аккуратно другие параметры, влияющие на сходимость алгоритмов. Особенно это параметры decay и offset, но об этом мы не будем подробно говорить в нашей демонстрации. Кроме того, полезно указывать параметр distributed, например, в true, тогда вы сможете обучать модель сразу в нескольких параллельных процессах. Ещё два важных параметра модели — это alpha и eta. Они создают априорные параметры распределения Дирихле, точнее, параметры априорного распределения Дирихле для моделей. Об этом распределении Дирихле вам рассказали на лекции. Распределение Дирихле — это распределение над векторами, и параметр для него — это тоже вектор. Если в этом векторе параметра все числа одинаковые, то это симметричные распределения Дирихле, если разные — то асимметричные. Gensim поддерживает разные варианты. Вы можете подавать одно число, тогда оно будет... gensim повторит его несколько раз, оставит вектор, и это будет симметричный prayer. Вы можете подавать вектор любой, вы можете подавать строку — symmetric или asymmetric, тогда gensim как-то сам установит эти параметры, исходя из своих каких-то природных соображений, но не факт, что эти значения будут оптимальными. Также можно указывать auto, тогда gensim подберёт оптимальное значение асимметричного природного распределения, но, правда, при этом модель будет обучаться несколько дольше. На практике достаточно попробовать просто числа с каким-то шагом в интервале от 0 до 2, скажем, и выбрать те, при которых модель получается наиболее интерпретируемой. Я это сделала до демонстрации и подобрала alpha и eta, равными 1.25. Так мы и укажем. Модели lda требуется несколько минут для обучения. Чтобы не ждать, мы загрузим готовую модель, которую я сохранила. Для сохранения и загрузки моделей в gensim есть соответствующие функции. Первая — это ldamodel.save, здесь мы должны указать имя файла, куда сохранить модель. Я это сделала заранее. И также это метод LdaModel.load. Здесь мы должны указать файл, в который предварительно сохранена модель. Давайте загрузим готовую модель. Отдельно стоит обратить внимание на то, что, даже если вы уже зафиксировали все параметры, построенная модель будет очень зависеть от начального приближения, потому что изначально все параметры в модели инициализируются какими-то случайными числами. Поэтому каждый раз, если вам позволяет размер коллекции и ваше время, стоит построить модель несколько раз и выбрать ту, в которой темы лучше всего. Понять, какие темы хорошие или плохие можно, посмотрев на «топы» слов. Их можно вывести, вызвав функцию print_topics, указав, сколько тем вы хотите посмотреть и сколько слов вывести в каждой теме. Лексика в комиксах xkcd достаточно специфичная, и особенно русскоговорящему человеку не всегда понятная. Поэтому давайте скопируем наши темы в яндекс-переводчик и посмотрим переводы этих слов. Мы видим, что в первых двух темах много имён, и, честно сказать, не очень понятно, о чём эти темы. Третья тема, она посвящена комиксам в таком стиле: там любят рисовать график, и этот график как-то связан со временем, и идёт какой-то сопровождающий текст. Потом говорят слова: «рисунок», «метка», «линия», «год», «время». Третья тема — это тема для комиксов из серии, когда на нём есть мужчина, женщина, то есть два человека, и они что-то обсуждают. А четвёртая тема весьма специфична. Она рассказывает о каких-то... о выживании на острове, я бы сказала. Это слова: «ждать», «остров», «карта», «обнаружена», и какие-то сопутствующие слова. Кроме того, давайте посчитаем величину перплексии на нашей модели. Перплексия — это очень распространённая мера качества тематических моделей, и особенное значение она имеет в контексте байесовских моделей. В первую очередь, потому что она описывает, насколько хорошо ваше распределение, описывает ваши исходные данные. И интерпретировать его можно так: чем меньше значение перплексии, тем лучше. С другой стороны, у перплексии нет максимального значения. То есть непонятно, если нам вручили число, непонятно, что оно означает. И ещё очень важно, что величина перплексии зависит, во-первых, от данных, а во-вторых, от количества тем. Поэтому, во-первых, нельзя сравнивать перплексию модели, построенных на разных данных, а во-вторых, нельзя по перплексии выбирать количество тем. Это очень логично. Чем больше у нас тем, тем лучше наша матрица аппроксимирует исходную тему, то есть описывает её лучше, тем, соответственно, меньше перплексия. Иногда перплексию измеряют на тестовом dataset, то есть отдельно отложенных документах или частях документов, но это важно для научных статей, а для практики не очень важно, поэтому мы можем измерять перплексию прямо по обучающей выборке. Давайте это и сделаем. Сейчас мы убедимся в том, что перплексия, как само число, мало о чём говорит, то есть её можно использовать только для сравнения моделей, но нельзя понять, хорошая модель или плохая, только для одной модели. Ну собственно, так и получается. Давайте также обсудим ещё две важные функции, которые предоставляет gensim. Во-первых, это функция update, которая позволяет дообучить модель на новых данных. То есть, если, например, вы бы считали ещё один другой corpus, например, из новых комиксов, в переменную data2 и указали бы её как первый аргумент функции update и также указали бы, сколько сделать проходов по коллекции, чтобы дообучить модель, то gensim обновит все параметры. Ещё одна важная функция — это get_document_topics. Он позволяет для каждого документа найти распределение над множеством тем. Давайте получим распределение для другого документа и увидим, что в нём наибольшую роль играет третья тема. Напомним, что это тема про мужчин и женщин. И также мы увидим, что все числа в правом столбце суммируются к единице, что неудивительно, потому что в столбце матрицы тета — это дискретные вероятностные распределения. Что ж, в этом уроке мы познакомились с библиотекой gensim, с тем, как туда можно импортировать данные и как можно строить модель lda. Напоследок я покажу вам ещё пару комиксов xkcd, главные герои которых не знали, что существуют тематические модели, потому что, на самом деле, они могли бы, в каком-то смысле, решить их проблемы.