Язык без слов: как токенизировать (разобрать) китайский язык?
Токенизация иероглифической письменности всегда представляла определённую трудность для сферы NLP. Самый наивный подход к токенизации в виде разделения текста по пробелам, очевидно, плохо подходит для такого материала, так как китайские тексты записываются без них. Этот факт связан с другой проблемой — выделение понятия слова (а соответственно, и разграничение синтаксиса и морфологии) для китайского проблематично ввиду большого числа компаундов и связанных морфем.
Диапазон CJK стандарта Юникод – 97 680 значений
Ещё одна проблема — очень большое количество иероглифов, что естественно для логографических письменностей, где каждый символ (логограф) связан с морфемой или словом, а не с фонемой. Этот факт нужно учитывать и при разработке нейросетей. Так, у токенизаторов современных больших языковых моделей размер словаря редко превышает 50 000 (у GPT-2 он равнялся 50 257), в то время как диапазон CJK (Chinese, Japanese, Korean) стандарта Юникод, включающий китайские иероглифы, содержит целых 97 680 значений. Впрочем, далеко не все из них используются в современном китайском, что упрощает нам задачу.
Одно из возможных решений — словарь для токенизации
Сейчас в NLP применяются большое количество алгоритмов для токенизации китайских текстов. Например, существуют подходы, где иероглифы сначала с помощью Юникода декомпозируются до отдельных графем в их составе, и они уже хранятся в словаре. Несмотря на то, что подобные решения неплохо подходят для нейросетевых моделей, токены в них вряд ли можно считать осмысленными языковыми единицами. Классические для корпусной лингвистики исследования n-грамм и коллокаций провести на таком материале не получится.
Одно из возможных решений — создание словаря, где можно задать, какие «слова» мы бы хотели получать от токенизатора на выходе. Так алгоритм сегментации сводится к поиску наиболее вероятных вариантов деления одной большой строки на множество из заранее заданных подстрок — слов в таком словаре.
Иногда это может оказаться нетривиальной задачей. В качестве классического примера неоднозначного синтаксического членения иногда приводят фразу 我喜欢新西兰花。. В зависимости от сегментации она может иметь несколько значений:
我 喜欢 新西兰 花。Я люблю новозеландские цветы.
我 喜欢 新 西兰花。Я люблю свежую брокколи.
Далее мы увидим, как с подобными трудностями справляются алгоритмы токенизации.
Как работает Jieba? Установка, обзор функций и создание словаря
Jieba — одна из самых популярных на сегодняшний день Python-библиотек для токенизации китайских текстов. Например, именно её использовали разработчики Яндекса, когда внедряли функцию перевода видео с китайского языка в браузере.
В основе принципа работы библиотеки также лежит готовый словарь. С помощью префиксного поиска алгоритм находит возможные слова из словаря в подаваемом на вход тексте. Затем строится взвешенный ациклический граф (веса определяются с помощью частот токенов, указанных в словаре), который содержит все возможные комбинации слов в строке. Из всех вариантов сегментации выбирается наиболее вероятный. Для разбиения строки, включающей слова не из словаря, используется алгоритм Витерби, основанный на скрытой марковской модели.
Установка библиотеки
Библиотеку можно установить с помощью менеджера пакетов pip, скачать из каталога PyPi или же напрямую клонировать репозиторий проекта в рабочую директорию.
pip install jieba
Основная функция cut
Основная функция для сегментации — cut. С помощью её параметра cut_all можно возвращать в виде генератора либо только наиболее вероятную сегментацию, либо все возможные варианты, а с помощью параметра HMM указать, нужно ли использовать при сегментации скрытую марковскую модель (СММ) или же полагаться только на значения частот из словаря:
import jieba
seg_list = jieba.cut('我喜欢新西兰花', cut_all = True)
print("Все варианты: " + "/ ".join(seg_list))
seg_list = jieba.cut('我喜欢新西兰花', cut_all = False)
print("Самый вероятный: " + "/ ".join(seg_list))
seg_list = jieba.cut("全方貧工之聯合。", HMM=False)
print("Без СММ: " + "/ ".join(seg_list))
seg_list = jieba.cut('全方貧工之聯合。', HMM=True)
print("С использованием СММ: " + "/ ".join(seg_list))
Вывод:
Все варианты: 我/ 喜欢/ 新西兰/ 西兰/ 西兰花/ 兰花
Самый вероятный: 我/ 喜欢/ 新西兰/ 花
Без СММ: 全/ 方/ 貧/ 工/ 之/ 聯/ 合/ 。
С использованием СММ: 全方/ 貧工/ 之/ 聯合/ 。
Для примера с брокколи в первом случае модель выдала все подстроки, которые могли бы быть отдельными сегментами: 新西兰 («Новая Зеландия»), 西兰 («княжество Силенд»), 西兰花 («брокколи»), 兰花 («орхидея»). Но самым вероятным будет вариант с цветами, так как частоты Новой Зеландии и брокколи в словаре библиотеки равны 647 и 12 соответственно.
Отдельно реализована функция cut_for_search, которая возвращает короткие слова, оптимальные для поиска в поисковых системах.
Скрытая марковская модель
Скрытая марковская модель может быть полезна для анализа любых нестандартных фраз и текстов. Например, для «Пролетарии всех стран, соединяйтесь!» без СММ модель выделила каждый иероглиф в качестве отдельного токена. Это объясняется тем, что в словаре есть все эти иероглифы по отдельности, а вот их сочетаний нет. Зато с её применением получились следующие осмысленные сегменты: 全方 (буквально «все стороны»), 貧工 (буквально «бедный, нищий + работа»), 之 — атрибутивная частица, 聯合 («союз»).
Создание своего словаря
В библиотеке по умолчанию есть готовый словарь с указанием частоты и частеречного тега для каждого слова в нём, но можно загрузить свой или использовать другие готовые словари из репозитория библиотеки:
jieba.set_dictionary(‘/data/…’)
Для создания своего словаря нужно следовать формату файла dict.txt из репозитория, где на каждой новой строке записываются максимум три значения через пробел: слово, его частота и частеречный тег (последние два пункта можно опустить или указать только что-то одно):
创新办 3 i
云计算 5
凱特琳 nz
台中
Также предусмотрены функции для добавления или удаления слов из словаря:
jieba.add_word(word, freq=None, tag=None)
jieba.delete_word(word)
Отдельно реализована функция, подбирающая для слова такую частоту, чтобы оно могло сегментироваться:
seg_list = jieba.cut('番茄炒蛋')
print("/ ".join(seg_list))
jieba.suggest_freq('番茄炒蛋', True)
seg_list = jieba.cut('番茄炒蛋')
print("/ ".join(seg_list))
Вывод:
番茄/ 炒蛋
番茄炒蛋
Также есть токенизация с разметкой частей речи (используется тегсет ICTCLAS):
import jieba.posseg as pseg
words = pseg.cut("我很喜欢冰激凌。")
for w in words:
print(w.word, w.flag)
Вывод:
我 r
很 d
喜欢 v
冰激凌 n
。 x
Где r — местоимение, d — наречие, v — глагол, n — существительное, x — не-слово.
Другие функции библиотеки Jieba
В библиотеке дополнительно реализован функционал для параллелизации вычислений, извлечения ключевых слов из текстов, поддержка библиотеки для информационного поиска Whoosh и некоторые другие продвинутые функции, с которыми можно подробно ознакомиться здесь.
Источники
- Документация пакета jieba [Электронный ресурс] // Pypi.org. 20 января 2020 г. URL: https://pypi.org/project/jieba/ (дата обращения 28.04.2024).
- Документация пакета jieba [Электронный ресурс] // GitHub. 20 января 2020 г. URL: https://github.com/fxsjy/jieba(дата обращения 28.04.2024).