Читать нас в Telegram
Иллюстрация: Женя Родикова

Язык без слов: как токенизировать (разобрать) китайский язык?

Токенизация иероглифической письменности всегда представляла определённую трудность для сферы 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 и некоторые другие продвинутые функции, с которыми можно подробно ознакомиться здесь.

Источники

  1. Документация пакета jieba [Электронный ресурс] // Pypi.org. 20 января 2020 г. URL: https://pypi.org/project/jieba/ (дата обращения 28.04.2024).
  2. Документация пакета jieba [Электронный ресурс] // GitHub. 20 января 2020 г. URL: https://github.com/fxsjy/jieba(дата обращения 28.04.2024).