[ЗАСТАВКА] В этом уроке мы научимся строить тематические модели с помощью библиотеки BigARTM. Как вы знаете, тематические модели позволяют находить темы в коллекциях текстовых документов. Сегодня мы будем работать с набором конспектов по нескольким школьным предметам. Эти конспекты мы предварительно скачали с одного интернет-ресурса, помогающего школьникам готовиться к ЕГЭ. Мы постараемся найти в этих конспектах темы, связанные с математикой, физикой, химией и другими школьными предметами. Давайте начнем. Для начала импортируем все необходимые модули. Нам понадобится matplotlib, чтобы строить графики. И, разумеется, нам понадобится artm. Первое и самое важное, что нужно сделать, это импортировать данные. Как вы знаете, тематические модели работают с коллекцией текстовых документов, представленной в так называемом формате мешка слов. Таким образом, нам важно, сколько раз слово встретилось в документе и не важен порядок слов. Кроме того, в документ, помимо слов, могут входить элементы других классов или модальностей. Например, в документ могут входить хештеги или ссылки. Эти объекты можно по-разному учитывать в модели, поэтому желательно отдельно указать, к какому классу принадлежит тот или иной объект. Такие данные можно представлять в разных форматах, но один из наиболее удобных и понятных человеку — это vowpal_wabbit формат. BigARTM умеет с ним работать. Давайте посмотрим, что это такое. Коллекция в vowpal_wabbit- формате — это текстовый документ, в котором отдельная строчка соответствует отдельному документу. Вначале может идти название документа, но в нашей коллекции названий документов нет. Затем идет вертикальная черта, после которой мы пишем название класса или модальности. Для простоты в нашей модели будет только одна модальность — текстовая. Мы ее так и назовем — текст. Затем через пробел идут отдельные слова. После слова может стоять двоеточие и количество раз, сколько оно встретилось в данный момент. Когда BigARTM будет считывать данные, он посчитает суммарное количество раз, сколько слово встретилось в документе, и запишет в матрицу частоту слов. Матрицу частоту слов BigARTM представляет в своем внутреннем формате, называемом батчами. Батчи — это несколько отдельных файлов. Нам их нужно сохранить в отдельную папку. Поэтому давайте на компьютере создадим новую папку (пустую) и назовем ее school_batches. Теперь мы готовы к импортированию данных. Мы создаем объект класса artm.batchvectorizer. В параметре data_path мы указываем путь к нашему текстовому файлу, который мы только что смотрели, — school.txt. Дальше мы указываем, что наш формат — это vowpal_wabbit. И указываем путь к target_folder, то есть к папке, в которую нужно складывать батчи. Мы назвали ее school_batches. Кроме того, есть необязательный аргумент batch_size, то есть количество документов в одном батче. Давайте укажем его равным 100. Если вы его не указываете, то BigARTM по умолчанию будет складывать тысячу документов в один батч. Запустим ячейку и посмотрим, что в нашей папке появились файлы с батчами. BigARTM гораздо быстрее загружает данные уже из батчей, а не из текстового файла. Поэтому если вы захотите строить модель снова на этих же данных, то вам уже можно будет загрузить их прямо из батчей. Для этого мы создаем объект класса batch_vectorizer и в качестве data_path указываем уже путь к нашей папке с батчами, то есть school_batches. И говорим, что наш формат данных (data_format) — это batches. Давайте строить модель. Для этого мы создаем объект класса ARTM. Обязательный аргумент, который здесь нужно указать, это количество тем. Сейчас мы укажем количество тем равным 10, потому что мы знаем, что в нашей коллекции 10 школьных предметов. Обычно нужно попробовать разные величины и посмотреть, при каком количестве тем тема получается наиболее интерпретируемой. Кроме того, удобно указать названия для тем. Давайте укажем их как subject плюс индекс тем от 0 до 9. И кроме того, нужно указать классы. Классы указываются как словарь, в котором ключами являются названия модальностей, те же самые, что мы указывали в vowpal_wabbit-документе, и веса этих модальностей. Вес модальности — это то число, на которое мы будем умножать счетчики вхождений слова в документ. Например, если в нашей коллекции две модальности (тексты и хештеги), и мы знаем, что в одном документе встречается один или два хештега, то логично поставить вес при модальности хештега, равный 10. Когда мы будем домножать вот эти счетчики 1 или 2 на 10, и они будут как-то сильнее влиять на модель. Однако если в модели только одна модальность, это совершенно неважно, и главное, чтобы вес был не нулевым. Давайте поставим единичку. Когда мы обучим эту модель, мы получим две матрицы — фи и тета. Матрица фи — это матрица распределений слов в темах, а матрица тета — матрица распределения тем в документах. Первая позволяет понять, о чем наши темы, какие слова в нее входят, а вторая — о чем документы, какие темы входят в наши документы. Давайте создадим также некоторые метрики модели, по которой сможем понимать, насколько хорошо она обучилась. Для создания метрики мы пользуемся методом model_artm.scores.add и в качестве параметра указываем ту или иную метрику. Для начала давайте создадим score под названием перплексия. Перплексия позволяет понять, насколько хорошо наша модель описывает данные. Чем меньше перплексия, тем лучше. Мы создаем объект класса PerplexityScore и обязательно указываем ему название, чтобы потом мы могли обращаться к этой метрике. И также указываем некоторые дополнительные значения по умолчанию. Кроме того, давайте создадим метрики разреженности матрицы фи и тета. Здесь такая логика, что если одно слово входит в небольшое количество тем, например, в одну или две, то матрица фи будет разреженной, в ней будет много нулей. То же самое с матрицей тета: если документ относится только к одной или двум темам, то матрица тета будет разреженной. Поэтому считается, что чем больше разреженность, тем лучше. Также давайте создадим метрику под названием top_words. Это топы слов, которые входят в наши темы. По ним мы сможем понять, о чем наша тема, то есть интерпретировать ее. Здесь нам нужно указать, сколько слов мы хотим выводить в параметре num_tokens. Давайте укажем 15. И также по какому классу ее считать. Класс нужно указывать в любой метрике, которая меряет что-то по матрице фи, потому что в матрице фи у нас немного будут разные модальности. Итак, давайте создадим модель. Добавим метрики. Теперь нам нужно инициализировать модель. Для этого artm пройдет по всем батчам, в которых хранятся наши данные, и загрузит их в свой словарь. Мы указываем название этого словаря dictionary и также указываем путь к нашим данным, это наш batch_vectorizer. Когда мы собрали словарь, то есть прошлись по всем батчам и нашли все слова, которые входят в наши документы, мы можем инициализировать модель. В процессе инициализации создадутся две матрицы (фи и тета) и заполнятся случайными значениями. Мы указываем словарь, чтобы вспомнить те слова, которые мы нашли в результате использования метода gather_dictionary. Чтобы зафиксировать случайные приближения для матрицы фи и тета, мы можем указать параметр seed. Давайте укажем его seed = –1. Тогда если мы в следующий раз будем снова строить модель, то матрицы фи и тета инициализируются теми же самыми значениями, и мы увидим ту же самую модель, то есть мы как бы сможем сопоставлять темы между собой. Это удобно. Инициализируем модель. Теперь мы готовы обучать модель. Для обучения модели в BigARTM есть два метода. Это fit_offline и fit_online. На маленьких коллекциях (а у нас маленькая коллекция) удобно использовать offline-обучение. В результате offline-обучения BigARTM проходит по всей коллекции много раз, а именно столько раз, сколько мы укажем в параметре num_collection_passes. Давайте укажем 40. И вот в этом большом цикле он проходит столько раз по каждому документу, сколько мы укажем в параметре num_document_passes. Давайте укажем один проход. Также мы, разумеется, указываем в качестве параметра batch_vectorizer, то есть путь к нашим данным, по которым нужно производить обучение. Кроме того, модель можно обучать с помощью метода fit_online. Он проходит по коллекции гораздо меньшее количество раз, например, один или два. Но приходится настраивать некоторые дополнительные параметры сглаживания. Это удобно для больших коллекций, на которых сделать много проходов по коллекции просто долго. Итак, мы дожидаемся, пока наша модель обучится сделать все итерации. Обычно рекомендуется делать 30–40 проходов по коллекции, чтобы модель хорошо сошлась. Однако давайте посмотрим, в какой же момент она по-настоящему сошлась. Это удобно отслеживать по метрике перплексии. Построим ее график. От номера прохода по коллекции. Чтобы обратиться к результатам подсчета этой метрики, мы обращаемся к переменной model_artm.score_tracker и указываем имя нашей метрики, которое мы задавали при ее создании. Это PerplexityScore. В этом классе есть разные переменные. Например, это value. Переменная value хранит историю измерения этой метрики на каждом проходе по всей коллекции. Также Там, например, есть переменная last_value, то есть она показывает последнюю измеренную перплексию во всей коллекции. Давайте построим график. Мы видим, что уже где-то хотя бы к 20-й итерации наша модель полностью сошлась. То есть для нашей коллекции достаточно делать только 20 проходов по batch. Давайте выведем топы слов, то есть посмотрим, о чем наши темы. Для этого мы опять обращаемся к score_tracker, указываем имя нашей метрики, которая находит топы слов (top words), и спрашиваем token для каждой отдельной темы. Делаем это в цикле по всем темам. Здесь мы можем посмотреть на слова. Ну что мы видим? Что, например, первая тема — это что-то связанное с обществознанием, вторая — что-то про литературу (герой, человек, ребенок), третья (предложение, слово, речь) — это что-то про русский язык, и так далее. Можно посмотреть, что это за темы. Давайте также посмотрим на разреженность матриц. Эти величины мы потом будем сравнивать с разреженной моделью. Для этого мы обращаемся к score_tracker, спрашиваем значение метрики SparsityPhiScore и SparsityThetaScore и спрашиваем последнее измеренное значение. Мы видим, что матрица phi у нас достаточно разреженная — в ней 75 % нулей, а матрица theta неразреженная — в ней только 10 % нулей. Мы видели, что в наших темах много общеупотребительных слов. Например, это слова «быть», «который», «они», «что», «свой», «этот». Они очень мешают интерпретации тем, пониманию, о чем, собственно, получилась тема. Для того чтобы исключить их из нашей модели, особенно из топов слов, удобно пользоваться регуляризацией моделей. Для этого и придумана библиотека ARTM. Давайте рассмотрим пример регуляризатора, а именно мы воспользуемся разреживающим регуляризатором для матрица phi. Посмотрим, как его можно создавать. Мы пользуемся методом model_artm.regularizers.add и в качестве аргумента передаем объект класса регуляризатора. В нашем случае это SmoothSparseRegularizer. Мы обязательно создаем ему имя, чтобы потом к нему можно было обращаться, как и со всеми объектами в BigARTM, задаем коэффициент и можем задать название словаря. Если мы задаем название словаря, а это именно тот словарь, который мы получали при вызове функции get a dictionary, то при регуляризации вот этот вот коэффициент, который мы здесь указали, будет домножаться на частоту слова во всей коллекции. То есть суммарно значения будут получаться не очень большие. Если мы не указываем словарь, то регуляризация для каждого слова будет одинаковой, и значение коэффициента можно указывать поменьше, например −2,5 или −5, цифры такого порядка. Если мы используем словарь, то и коэффициент, который отрицательный, то есть у нас этот отрицательный коэффициент −100, например, то слово будет... чем более вероятно слово во всей коллекции, чем большую частоту оно имеет, тем менее вероятно оно будет входить в отдельные темы. Это именно тот эффект, которого мы добиваемся, поэтому мы создаем такой регуляризатор. Иногда может случиться так, что вы хотите применять регуляризатор только какой-то отдельной модальности, тогда еще можно указывать параметр class_ids и создавать список тех модальностей, к которым вы хотите его применять. После добавления регуляризатора мы снова обучаем модель, то есть вызываем метод fit_offline. Сейчас давайте сделаем 15 проходов по коллекции, чтобы модель точно успела сойтись. После этого мы снова посмотрим на топы слов. Что мы видим? Мы видим, что наш регуляризатор пока не принес почти никакого эффекта в нашу модель. Мы снова видим вот эти слова: «быть», «который», которые мешают интерпретации тем. Давайте попробуем изменять коэффициент регуляризации. Если мы зададим его очень большим, например −1e в шестой (минус миллион), все темы просто обнулятся, то есть модель перестанет быть совершенно интерпретируемой, поскольку у нас уже не будет тем. Если вы так опробуете некоторые величины, то сможете нащупать, найти оптимальное значение коэффициента. Я нашла его заранее. Он равен 5 * 1e в четвертой со знаком минус. Давайте снова дообучим нашу модель и посмотрим на топы слов. [БЕЗ_ЗВУКА] Наконец мы видим, что наши темы совершенно интерпретируемы. Первая тема — это тема про историю, вторая (ребенок, Лермонтов, поэма, сказка, отец, молодой) — тема по литературе, третья (слово, предложение, например, простой, речь) — это тема про русский язык. Следующая тема очень похожа на географию (Земля, энергия, вода, ядро, масса, количество, природный, океан). А может быть, география вместе с физикой. Дальше это тема про математику (x, a, число, b, функция). Дальше еще несколько тем тоже про обществознание и историю. Таким образом, с помощью регуляризации мы смогли получить более интерпретируемую тему. На что стоит обратить внимание? Что как и в любой модели с регуляризацией нужно аккуратно подбирать коэффициент. Например, если в линейной регрессии вы будете применять регуляризацию методом лассо, вам также придется подбирать коэффициент. Если вы зададите его маленьким, он не будет иметь никакого влияния на модель. Создадите большим — он обнулит веса при всех признаках. Вот этот вот оптимальный коэффициент приходится искать всегда, когда мы используем регуляризацию в той или иной модели. Еще один немаловажный момент состоит в том, что использовать регуляризацию в тематических моделях рекомендуется только после того, как модель хорошо сошлась. Особенно, если речь идет о разреживании. В нашем примере мы именно так и делали. Мы сначала обучили модель без регуляризации и потом добавили регуляризацию. В противном случае ваши темы могут намешаться. То есть, например, одна тема будет сразу и про физику, и про математику, и про историю, что в принципе неправильно. Давайте посмотрим на разреженность наших матриц и увидим, что разреженность матрицы phi стала значительно большей, чем была в предыдущий раз. Давайте вспомним, в прошлый раз у нас была разреженность 0,75, а сейчас 0,9, то есть стало гораздо больше нулей. И более того, больше нулей стало даже в матрице theta. Теперь там 33 %, хотя было 10. То есть мы видим, что регуляризатор действительно делает больше нулей в наших матрицах. Давайте напоследок посмотрим несколько полезных приемов, которые понадобятся вам при работе с библиотекой BigARTM. Во-первых, вы можете загружать и сохранять модели. Для этого можно пользоваться методом save и указывать файл, в который вы хотите сохранить модель. Файлу можно указывать совершенно любое расширение. BigARTM будет совершенно неважно. Я предпочитаю не указывать никакого расширения. Вы можете писать .dump, например, или .bin После этого вы сможете загрузить модель с помощью метода load. Хочу обратить внимание, что чтобы загрузить модель, нужно сначала создать объект класса ARTM, как мы делали это в начале урока, задать уже количество тем и только после этого загружать модель из бинарного файла. Еще два полезных приема — это доступ к самим матрицам. Давайте посмотрим, как это работает. Чтобы получить матрицу phi, можно воспользоваться методом get_phi. Метод get_phi возвращает нам pandas DataFrame, в котором по столбцам стоят отдельные темы. Это вот, собственно, имена тем, которые мы давали в самом начале: subject плюс index от 0 до 9. А по строкам идут наши слова, которые встречаются в нашей коллекции. Ну а значения — это их вероятности. Вот видим, что в матрице очень много нулей и изредка встречаются какие-то значения. То есть каждое слово принадлежит действительно буквально одной теме или двум. Это то, чего мы добивались. Таким же способом можно запросить матрицу theta и также получить pandas DataFrame, в котором по столбцам будут идти документы, а по строкам — темы, и также будут даваться значения. Вот мы видим, что, например, первый документ принадлежит в основном первой теме. Она имеет в нем большой вес. Кроме того, вы можете тематизировать новые документы, которые модель еще не видела. Для этого можно использовать метод transform. Здесь нужно указать новый batch_vectorizer (это batch_vectorizer уже по новым документам, которые модель еще не видела, то есть вам нужно самим его отдельно создать) и количество проходов по коллекции. После этого вы сможете получить для них матрицу theta и, например, категоризировать. Это удобно, если у вас есть много новых документов, которые идут сплошным потоком, вам нужно быстро определять по ним темы, находить категории, вы можете пользоваться методом transform. Итак, в этом уроке мы познакомились с форматом данных Vowpal Wabbit, научились импортировать его в BigARTM, научились создавать, обучать модели и также смотреть, какие у нас модели получились, и интерпретировать темы. Эти навыки понадобятся вам в следующем preview, посвященном построению тематических моделей в библиотеке BigARTM. Вы будете находить темы в коллекции текстов, записанных по научным видеолекциям.