- PVSM.RU - https://www.pvsm.ru -

Синтаксический анализ текстов с помощью SyntaxNet

Для одной из задач мне понадобился синтаксический анализатор русскоязычных текстов. Что это такое. Например, у нас есть предложение «Мама мыла раму». Нам нужно получить связи слов в этом предложении в виде дерева:

image

Из этого дерева понятно, что связаны слова «мама» и «мыла», а также «мыла» и «раму», а слова «мама» и «раму» напрямую не связаны.

Статья будет полезна тем, кому понадобился синтаксический анализатор, но не понятно, с чего начать.

Я занимался этой темой несколько месяцев назад, и на тот момент нашел не много информации по поводу того, где бы взять готовый и желательно свободный анализатор.

На Хабре есть отличная статья [1] об опыте работы с MaltParser. Но с тех пор некоторые пакеты, используемые для сборки, переехали в другие репозитории и чтобы собрать проект с нужными версиями библиотек, придется хорошенько потрудиться.

Есть и другие варианты, среди которых SyntaxNet. На Хабре я не нашел ничего про SyntaxNet, поэтому восполняю пробел.

Что такое SyntaxNet

По сути SyntaxNet [2] — это основанная на TensorFlow библиотека определения синтаксических связей, использует нейронную сеть. В настоящий момент поддерживается 40 языков [3], в том числе и Русский.

Установка SyntaxNet

Весь процесс установки описан [4] в официальной документации. Дублировать инструкцию здесь смысла не вижу, отмечу лишь один момент. Для сборки используется Bazel. Я попробовал собрать проект с его помощью у себя на виртуалке с Ubuntu 16.04 x64 Server с выделенными 4-мя процессорами и 8 ГБ оперативной памяти и это не увенчалось успехом — вся память съедается и задействуется своп. После нескольких часов я прервал процесс установки, и повторил всё, выделив уже 12 ГБ оперативной памяти. В этом случае все прошло гладко, в пике задействовался своп только на 20 МБ.

Возможно есть какие-то настройки, с помощью которых можно поставить систему в окружении с меньшим объемом оперативной памяти. Возможно сборка выполняется в нескольких параллельных процессах и стоило выделять виртуальной машине только 1 процессор. Если вы знаете, напишите в комментариях.

После завершения установки я оставил для этой виртуальной машины 1ГБ памяти и этого хватает с запасом для того, чтобы успешно парсить тексты.

В качестве размеченного корпуса я выбрал Russian-SynTagRus, он более объемный по сравнению с Russian и с ним точность должна получаться выше.

Использование SyntaxNet

Чтобы распарсить предложение, переходим в каталог tensorflow/models/syntaxnet и запускаем (путь к модели — абсолютный):

echo "Мама мыла раму" | syntaxnet/models/parsey_universal/parse.sh /home/tensor/tensorflow/Russian-SynTagRus > result.txt

В файле result.txt получаем примерно такой вывод, я заменил данные в 6-й колонке на "_", чтобы тут не переносились строки, иначе неудобно читать:

cat result.txt
1	Мама	_	NOUN	_	_	2	nsubj	__
2	мыла	_	VERB	_	_	0	ROOT	_	_
3	раму	_	NOUN	_	_	2	dobj	__

Данные представлены в формате CoNLL-U [5]. Тут наибольший интерес представляют следующие колонки:

1. порядковый номер слова в предложении,
2. слово (или символ пунктуации),
4. часть речи, тут [6] можно посмотреть описание частей речи,
7. номер родительского слова (или 0 для корня).

То есть мы имеем дерево, в которм слово «мыла» — корень, потому что оно находится в той строке, где колонка номер 7 содержит «0». У слова «мыла» порядковый номер 2. Ищем все строки, в которых колонка номер 7 содержит «2», это дочерние элементы к слову «мыла». Итого, получаем:

image

Кстати, если разобраться подробнее, то дерево не всегда удачно представляется все зависимости. В ABBYY Compreno [7], например, в дерево добавляют дополнительные связи [8], которые указывают на связь элементов, находящихся в разных ветках дерева. В нашем случае таких связей мы не получим.

Интерфейс

Если вам критична скорость парсинга текстов, то можно попробовать разобраться с TensorFlow Serving [9], с помощью него можно загружать модель в память один раз и далее получать ответы существенно быстрее. К сожалению, наладить работу через TensorFlow Serving оказалось не так просто, как казалось изначально. Но в целом это возможно. Вот пример [10], как это удалось сделать для корейского языка. Если у вас есть пример как это сделать для русского языка, напишите в комментариях.

В моем случае скорость парсинга не была сильно критичной, поэтому я не стал добивать тему с TensorFlow Serving и написал простое API для работы с SyntaxNet [11], чтобы можно было держать SyntaxNet на отдельном сервере и обращаться к нему по HTTP.

В этом репозитории есть и веб интерфейс, который удобно использовать для отладки, чтобы посмотреть как именно распарсилось предложение.

image
image
Чтобы получить результат в JSON, делаем такой запрос:

curl -d text="Мама мыла раму." -d format="JSON" http://<host where syntax-tree installed>

Получаем такой ответ:

[{
  number: "2",
  text: "мыла",
  pos: "VERB",
  children: [
    {
      number: "1",
      text: "мама",
      pos: "NOUN",
      children: [ ]
    },
    {
      number: "3",
      text: "раму",
      pos: "NOUN",
      children: [ ]
    }
  ]
}]

Уточню один момент. Bazel интересным образом устанавливает пакеты, так, что часть бинарников хранится в ~/.cache/bazel. Чтобы получить доступ к их исполнению из PHP, я на локальной машине добавил права на этот каталог для пользователя веб сервера. Наверное той же цели можно добиться более культурным путем, но для экспериментов этого достаточно.

Что еще

Еще есть MaltParser, о котором я упомянул вначале. Позже я обнаружил, что тут можно скачать размеченный корпус SynTagRus [12] и даже успешно обучил на нем свежую версию MaltParser, но не нашел пока времени довести дело до конца и собрать MaltParser целиком, чтобы получать результат парсинга. Эта система немного иначе строит дерево и мне для своей задачи интересно сравнить результаты, получаемые с помощью SyntaxNet и MaltParser. Возможно в следующий раз удастся написать об этом.

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

Автор: mnv

Источник [13]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/python/220820

Ссылки в тексте:

[1] отличная статья: https://habrahabr.ru/post/148124/

[2] SyntaxNet: https://github.com/tensorflow/models/tree/master/syntaxnet

[3] 40 языков: https://github.com/tensorflow/models/blob/master/syntaxnet/universal.md

[4] описан: https://github.com/tensorflow/models/tree/master/syntaxnet#installation

[5] CoNLL-U: http://universaldependencies.org/format.html

[6] тут: http://universaldependencies.org/ru/pos/index.html

[7] ABBYY Compreno: https://habrahabr.ru/company/abbyy/blog/269191/

[8] дополнительные связи: https://hsto.org/files/8a3/8db/cb2/8a38dbcb2b354e4e91f9ef9b5907bcb8.png

[9] TensorFlow Serving: https://github.com/tensorflow/serving

[10] Вот пример: https://github.com/dsindex/syntaxnet

[11] API для работы с SyntaxNet: https://github.com/mnvx/syntax-tree

[12] размеченный корпус SynTagRus: http://universaldependencies.org/

[13] Источник: https://habrahabr.ru/post/317564/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best