Как компьютер определяет, о чем говорится в тексте | Вводный курс ML

Как компьютер определяет, о чем говорится в тексте

Все курсы > Вводный курс > Занятие 19

Сегодня мы рассмотрим тему обработки естественного языка (Natural Language Processing, NLP), области на стыке математики, информатики и лингвистики, занимающейся пониманием (анализом) и созданием (синтезом) текстов с помощью компьютера.

В частности мы разберем тему (1) предварительной обработки языка (language pre-processing), (2) а также изучим два несложных способа анализа содержания текста (topic identification).

Постановка задачи

В школьных учебниках можно встретить такое задание: «Прочитайте текст. Определите его тему». У человека, как правило, это не вызывает никаких сложностей. Теперь представьте, что вы компьютер. Как понять, о чем говорится в тексте?

В качестве примера, возьмем следующий текст на английском языке:

When we were in Paris we visited a lot of museums. We first went to the Louvre, the largest art museum in the world. I have always been interested in art so I spent many hours there. The museum is enourmous, so a week there would not be enough.

Если вы попросите меня описать содержание тремя или четырьмя словами, я бы сказал: «музей» (museum), «Лувр» (Louvre), «Париж» (Paris), «искусство» (art). Посмотрим, что скажет компьютер.

По традиции вначале откроем ноутбук к этому занятию

В лингвистике совокупность рассматриваемых текстов принято называть корпусом (corpus, мн.ч. кóрпусы, corpora).

Импортируем пакет библиотек для обработки естественного языка (Natural Language Toolkit или NLTK) и другие уже известные нам библиотеки.

Предварительная обработка текста

Шаг 1. Разделение на предложения

Вначале текст логично разделить на предложения или токенизировать. Хотя задача кажется тривиальной (достаточно разделить текст по точкам, восклицательным и вопросительным знакам), есть несколько сложностей. Например, фраза «Мороженое стоит 100 руб. 20 коп.» может быть ошибочно разбита на два предложения. Помимо сокращений есть и другие сложности. Например, из-за опечатки предложение может заканчиваться пробелом, а не знаком препинания.

Для решения этой задачи можно использовать стандартный метод библиотеки NTLK sent_tokenize.

Метод использует уже обученную модель, в нашем случае, для английского языка: nltk_data/tokenizers/punkt/english.pickle. Также можно использовать модели для других языков или обучить собственную модель.

Шаг 2. Разделение на слова

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

Сделаем то же самое для всего текста.

Шаг 3. Перевод в нижний регистр, удаление стоп-слов и знаков пунктуации

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

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

Как вы вероятно заметили, мы дополнительно удалили пунктуацию с помощью метода isalpha().

Шаг 4. Лемматизация

Кроме того, с точки зрения анализа содержания слова «музеи» и «музей» означают одно и то же, но компьютер посчитает их разными словами. Значит, слова нужно привести к начальной (словарной) форме или лемме.

WordNet Lemmatizer библиотеки NLTK использует базу данных WordNet⧉ для поиска словарной формы слова.

Шаг 5. Стемминг

Стемминг (stemming), в отличие от лемматизации, ориентирован на поиск основы слова (stem). Попробуем применить и его.

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

Как вы видите, list comprehension позволяет в одну строчку создавать список. Подробнее этот прием мы рассмотрим на следующем курсе.

Теперь применим более агрессивный стеммер Ланкастера.

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

Мешок слов

Принцип метода мешка слов (Bag of Words, BoE) чрезвычайно прост. Мы считаем как часто встречается каждое слово в тексте.

Несмотря на простоту, при правильной предобработке текста (в первую очередь удалении стоп-слов, которые и будут наиболее частотными) этот метод показывает неплохие результаты. Применим его к нашему тексту с помощью класса Counter модуля Collections.

Этот же метод можно реализовать с помощью класса CountVectorizer библиотеки Scikit-learn.

Преобразуем матрицу csr в привычный формат массива Numpy.

Строки предсталяют собой предложения (документы), столбцы — слова (токены).

Есть два способа посмотреть на используемые токены. С помощью атрибута vocabulary_.

Здесь числа это не частотность слов, а просто их порядковый номер или индекс. Также токены можно вывести с помощью метода get_feature_names().

Результат для удобства также можно преобразовать в датафрейм.

Итог работы CountVectorizer может показаться менее наглядным, чем метод мешка слов (и кроме того у него нет встроенной возможности лемматизации и стемминга), однако этот класс позволяет выразить слова с помощью числовых векторов.

Это очень важный результат, о котором мы поговорим в конце занятия.

Метод TF-IDF

Чуть более сложный и продвинутый метод определения значимости слов в тексте называется TF-IDF (term frequency — inverse document frequency).

Основная идея

Если слово часто встречается во всех документах (это в первую очередь касается предлогов, союзов и других стоп-слов), то вряд ли эти слова имеют большое значение. И наоборот, если слово встречаться только в одном документе, вероятно оно в большей степени определяет его содержание.

Другими словами, определяется не только значимость слова в тексте, но и значимость слова с учётом всех текстов.

Простой пример и формула

Предположим, что у нас есть два текста и мы посчитали частотность слов в каждом из них (т.е. создали мешок слов).

Пример взят из Википедии
Расчет tf-idf для слова this

Поставим себе задачу рассчитать TF-IDF для слова this в каждом из документов. На первом этапе вычисляем частоту слова (term frequency или TF) относительно всех слов в документе.

$$ tf(this, d_1) = \frac{частотность \space слова}{всего \space слов} = \frac{1}{5} = 0.2 $$

$$ tf(this, d_2) = \frac{частотность \space слова}{всего \space слов} = \frac{1}{7} \approx 0.14 $$

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

На втором этапе рассчитаем IDF слова this. То есть мы делим общее количество документов в корпусе на количество документов, в которых встречается искомый токен, и берем логарифм частного.

$$ idf(this, D) = log\left(\frac{всего\spaceдокументов}{документов\spaceс\spaceтокеном}\right) = log\left( \frac{2}{2} \right) = 0 $$

Остаётся перемножить TF и IDF.

$$ tf-idf (this, d_1, D) = 0.2 \times 0 = 0 $$

$$ tf-idf (this, d_2, D) = 0.14 \times 0 = 0 $$

Этот показатель равен нулю, что отражает низкую значимость слова this для обоих документов. Теперь сделаем аналогичный расчет для слова example.

Расчет tf-idf для слова example

$$ tf(example, d_1) = \frac{0}{5} = 0 $$

$$ tf(example, d_2) = \frac{3}{7} \approx 0.429 $$

$$ idf(example, D) = log\left( \frac{2}{1} \right) \approx 0.301 $$

$$ tf-idf (example, d_1, D) = 0 \times 0.301 = 0 $$

$$ tf-idf (example, d_2, D) = 0.429 \times 0.301 \approx 0.129 $$

В данном случае слово example имеет заметно большее значение для второго документа. Для первого документа его значимость по-прежнему равна нулю (такого слова там нет).

Логика формулы следующая, чем выше частота слова в документе (tf) и чем реже оно встречается в целом в документах (ifd), тем выше общий показатель (tf-idf).

Также замечу, что на практике эти формулы часто модифицируют. Например, к знаменателю формулы расчета idf добавляют единицу, чтобы избежать деления на ноль, если документов с таким токеном не нашлось.

$$ idf = log\left(\frac{всего\spaceдокументов}{документов\spaceс\spaceтокеном + 1}\right) $$

TF-IDF с помощью библиотеки Scikit-learn

Применим этот метод к нашему исходному тексту про Париж и музеи. Вначале последовательно используем классы CountVectorizer и TfidfTransformer.

Способ 1. CountVectorizer + TfidfTransformer

TF или частоту слов мы можем взять из предыдущего раздела.

Теперь нужно рассчитать IDF.

Остается TF x IDF.

Теперь мы можем посмотреть на показатель TF-IDF для конкретного слова в конкретном документе.

Всего после обработки метод оставил 34 слова.

Такого же результата можно добиться применив метод Tfidfvectorizer.

Способ 2.  Tfidfvectorizer

Мы можем посмотреть какие слова остались после фильтрации.

В частности мы видим, что метод Tfidfvectorizer удалил наречие always, которое было сохранено при использовании CountVectorizer и TfidfTransformer.

Также можно посмотреть IDF слов.

Посмотрим на количество документов и количество токенов (слов).

Рассчитаем значение TF-IDF для каждого слова по каждому тексту.

В целом упражнение можно завершить. Однако напомню, что нашей целью было определить тему текста. Для этого мы также можем рассчитать среднее значение TF-IDF для каждого слова по всем текстам.

Для этого вычислим среднее арифметическое по строкам матрицы, приведенной выше (подробное объяснение кода вы найдете в ноутбуке).

И создадим датафрейм, отсортировав слова по убыванию средних весов.

Результаты

Как мы видим, ни один из алгоритмов не справился со своей задачей на отлично. Отчасти это связано с ограничениями самих методов, отчасти с тем, что мы взяли очень небольшой текст для анализа.

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

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

Кроме того, можно использовать классификаторы для анализа тональности текста (sentiment analysis).

Вопросы для закрепления

Перечислите способы предварительной обработки текста

Ответ: (1) разделение на предложения, (2) разделение на слова, (3) перевод в нижний регистр, удаление стоп-слов и знаков препинания, (4) лемматизация (приведение к начальной форме) и (5) стемминг (выявление основы)

В чем отличие мешка слов от метода TF-IDF?

Ответ: (1) мешок слов предполагает простой подсчет частоты конкретного слова в тексте, (2) метод TF-IDF учитывает как значимость слова в одном документе, так и частоту этого слова во всех документах корпуса (чем эта частота ниже, тем выше значимость слова)

Что позволяет нам выполнять математические операции над текстом?

Ответ: преобразование в векторы (векторизация)

Подведем итог

Сегодня мы впервые поработали с текстами. В частности, научились предварительно обрабатывать их и превращать в числа или векторизовывать. Для этого мы использовали метод мешка слов и метод TF-IDF.

На следующем занятии мы поговорим про анализ временных рядов.

guest
5 комментариев
Межтекстовые Отзывы
Посмотреть все комментарии
Андрей

Здравствуйте, Дмитрий! Можете привести какой-нибудь пример работы с векторами? Спасибо!

Андрей

Спасибо!

Андрей

Скажите, а имеет ли значение какое основание логарифма использовать в формуле TF-IDF?