- PVSM.RU - https://www.pvsm.ru -
Мы поговорим об использовании модных «Word embedding» не совсем по назначению — а именно для исправления опечаток (строго говоря, и ошибок тоже, но мы предполагаем, что люди грамотные и опечатываются). На хабре была довольно близкая статья [1], но здесь будет немного о другом.

Визуализация Word2Vec модели, полученная студентом. Обучалась на «Властелине колец». Явно что-то на черном наречии.
Дано: Словарь (множество слов).
Необходимо: Для входного слова с опечаткой (которого может и не быть в словаре) найти ближайшие слова из словаря на основе заранее заданной метрики. Эти найденные слова и являются вариантами исправления входного слова.
Для нашей задачи нам понадобится Word Embedding, или векторное представление слов (по поводу перевода термина до сих пор есть сомнения у русскоязычного сообщества), и снова на хабре есть замечательная статья [2], которая хорошо рассказывает об этом. Чтобы не повторяться, но и не гонять читателя по ссылкам — краткий экскурс в тему.
Word embedding — это векторное представление слов, то есть одному слову сопоставляется вектор фиксированной размерности, например, для условного слова «дом» — [0.1, 0.3, ..., 0.7]. Важное замечание: обычно размерность вектора значительно меньше размерности словаря, иначе это вырождается в one-hot encoding [3].
Количество элементов вектора зависит от многих условий: выбранного метода, требований задачи, погоды в Красноярске и многого другого. В текущих SotA реализациях это 100-500 значений (чаще 300).
Векторы получаются в процессе обучения, для этого алгоритму скармливается какой-то текст или тексты (от художественных произведений до дампа википедии), и для каждого слова итеративно высчитывается вектор.
Есть различные методы получения этих векторов, самые популярные сейчас это Word2Vec [4], GloVe [5], WordRank [6] и FastText [7].
Если коротко, то в основе лежит идея: слова, контексты которых похожи — скорее всего имеют схожие значения. Отсюда следует каким образом исправляются опечатки в первом приведенном примере [1]. То есть у нас есть два предложения: «найти туры с преключениями» и «найти туры с приключениями», контексты похожи, следовательно, слова «преключениями» и «приключениями» по смыслу похожи (это грубое приближение, но смысл такой).
У такого подхода исправления опечаток, кроме очевидных достоинств, есть один важный недостаток — все варианты ошибок, которые мы можем исправить, должны быть в тексте, на котором мы обучаемся. То есть мы не можем получить вектор для слова, которое раньше не встречали.
Все (известные автору) современные методы получения векторов слов, кроме FastText, оперируют словами как неделимыми сущностями (слово заменятся целочисленным индексом, и потом идет работа с индексом). В FastText (если еще точнее, то в его дополнении [8]) добавлено интересное предложение: а давайте считать эти вектора не для слов целиком, а для n-грамм из символов. Например, слово «стол» (с добавленными символами начала и конца слова, вроде "<стол>") преобразуется в следующий список: 3-граммы: <ст, сто, тол, ол>; 4-граммы: <сто, стол, тол>. Авторы предлагают использовать n-граммы от 3 до 6 включительно, не будем с ними спорить. Тогда результирующий вектор слова равен сумме векторов составляющих его n-грамм:
где
— множество всех n-грамм слова,
— вектор соответствующей n-граммы,
— вектор слова.
Какие важные изменения сулит нам этот подход?
Во-первых, авторы ввели это, чтобы лучше работало на языках с богатой морфологией (каким является русский). И действительно, морфологические изменения теперь меньше влияют на расстояние между словами, вот вам табличка для разных языков из той же статьи [8] в качестве пруфов:
| Язык | Датасет | Sg | CBOW | sisg- | sisg |
|---|---|---|---|---|---|
| Ar | WS353 | 51 | 52 | 54 | 55 |
| De | Gur350 | 61 | 62 | 64 | 70 |
| De | Gur65 | 78 | 78 | 81 | 81 |
| De | ZG222 | 35 | 38 | 41 | 44 |
| En | RW | 43 | 43 | 46 | 47 |
| En | WS353 | 72 | 73 | 71 | 71 |
| Es | WS353 | 57 | 58 | 58 | 59 |
| Fr | RG65 | 70 | 69 | 75 | 75 |
| Ro | WS353 | 48 | 52 | 51 | 54 |
| Ru | HJ | 59 | 60 | 60 | 66 |
Корреляция между экспертной оценкой и оценкой метода. SG и CBOW — соответственно skip-gram и continious bag of words варианты Word2Vec, sisg- — когда неизвестные слова заменяем нулевым вектором, и sisg — когда неизвестные слова заменяем на сумму их n-грамм.
Во-вторых, это немного шаг назад, в Word2Vec мы отходили от буквенного представления слова, пытаясь сблизить слова вроде «царь» и «король», «мама» и «сын», сейчас же мы возвращаемся к «буквенной близости», что для семантической задачи может и не очень хорошо [9], но для нашего варианта (напомню — исправление опечаток) это то что надо.
На этом теоретическая часть закончена, перейдем к практике.
Введем некоторые предусловия:
Тестовый код достаточно простой (спасибо, gensim [10]), весь скрипт есть здесь [11], непосредственно обучение модели в одну строчку:
model = gensim.models.FastText(sentences, size=300, window=3, min_count=2, sg=1, iter=35)
Небольшие пояснения:
sentences — список списков, каждый элемент — предложение, каждый элемент предложения — слово;
size — размер выходных векторов;
window — размер окна, слова в пределах окна мы считаем контекстом для слова в центре;
min_count — учитывать только слова, которые встречаются хотя бы 2 раза;
sg — использовать skip-gram вариант, а не CBOW;
iter — количество итераций.
Кроме того, есть два параметра, которые оставлены по умолчанию, их значение обсуждали выше, но вы можете с ними поиграться: min_n и max_n — нижний и верхний пороги, какие n-граммы брать (по умолчанию от 3 до 6 символов)
В качестве метрики возьмем ставшую уже классической в этой задачи меру сходства между векторами Cosine similarity [12], которая принимает значения от 0 до 1, где 0 — векторы абсолютно разные, 1 — векторы одинаковые:
где и
— компоненты соответствующих векторов.
Итак, мы разобрались с тем что у нас есть, и чего мы хотим, на всякий случай еще раз:
Теперь посмотрим, что у нас получится, для тестов возьмем следующие пары — слово с опечаткой и в скобках задуманное слово:
челавек (человек), стулент (студент), студечнеский (студенческий), чиловенчость (человечность), учавствовать (участвовать), тактка (тактика), вообщем (вообще), симпотичный (симпатичный), зделать (сделать), сматреть (смотреть), алгаритм (алгоритм), ложить (положить).
Сводная таблица с результатами:
| Слово с опечаткой | Задуманное слово | № в списке ближайших | Значение метрики |
|---|---|---|---|
| челавек | человек | 1 | 0.88227 |
| стулент | студент | 10 | 0.67878 |
| студечнеский | студенческий | 1 | 0.85078 |
| чиловенчость | человечность | 1 | 0.75012 |
| учавствовать | участвовать | 6 | 0.87767 |
| тактка | тактика | 2 | 0.73543 |
| вообщем | вообще | (1) | 0.96243 |
| симпотичный | симпатичный | (1) | 0.92399 |
| зделать | сделать | 2 | 0.91553 |
| сматреть | смотреть | 1 | 0.80055 |
| алгаритм | алгоритм | (1) | 0.86162 |
| ложить | положить | 10 | 0.81719 |
Замечания:
Использование векторного представления несомненно может помочь в задаче исправления опечаток/ошибок, но в одиночку его использовать достаточно опасно, так как иногда (хоть и редко) возникают сильные ошибки.
По сути это еще одна метрика сравнения двух строк на схожесть, но уже уровнем выше, чем, например, то же расстояние Дамерау — Левенштейна [13]. Использование FastText как дополнение к другим методам вполне может повысить качество исправления опечаток.
Автор: Алексеев Алексей
Источник [14]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/python/280880
Ссылки в тексте:
[1] статья: https://habr.com/post/249215/
[2] статья: https://habr.com/company/ods/blog/329410/
[3] one-hot encoding: https://machinelearningmastery.com/why-one-hot-encode-data-in-machine-learning/
[4] Word2Vec: https://papers.nips.cc/paper/5021-distributed-representations-of-words-and-phrases-and-their-compositionality.pdf
[5] GloVe: https://nlp.stanford.edu/projects/glove/
[6] WordRank: https://arxiv.org/abs/1506.02761
[7] FastText: https://fasttext.cc/
[8] дополнении: https://arxiv.org/abs/1607.04606
[9] не очень хорошо: https://rare-technologies.com/wordrank-embedding-crowned-is-most-similar-to-king-not-word2vecs-canute/
[10] gensim: https://radimrehurek.com/gensim/
[11] здесь: https://gist.github.com/Kwentar/a957d29f7370f896b691c82ff9ebe7d2
[12] Cosine similarity: https://en.wikipedia.org/wiki/Cosine_similarity
[13] расстояние Дамерау — Левенштейна: https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D1%81%D1%81%D1%82%D0%BE%D1%8F%D0%BD%D0%B8%D0%B5_%D0%94%D0%B0%D0%BC%D0%B5%D1%80%D0%B0%D1%83_%E2%80%94_%D0%9B%D0%B5%D0%B2%D0%B5%D0%BD%D1%88%D1%82%D0%B5%D0%B9%D0%BD%D0%B0
[14] Источник: https://habr.com/post/358664/?utm_source=habrahabr&utm_medium=rss&utm_campaign=358664
Нажмите здесь для печати.