Pull to refresh

Заметки об NLP (часть 7)

Reading time6 min
Views4.2K
(Первые части: 1 2 3 4 5 6). Как и обещал вчера, продолжаем обсуждать XDG и движемся к следующим темам. Возможно, мы двигаемся слишком быстро, и действительно имело бы смысл публиковать одну статью раз в два-три дня, чтобы оставалось время всё обсудить. Но, наверно, пока «бензин есть», я буду продолжать писать. А потом можно будет вернуться и обговорить ранее освещённые вопросы. Мне кажется, что в компьютерной лингвистике разные темы настолько тесно связаны друг с другом, что разговор об одной из них без связи с другими малопродуктивен. А мы ещё не обо всём беседовали, так что лучше охватить взглядом как можно больше аспектов компьютерного анализа текста, а потом уже рассуждать о конкретике в рамках общей картины происходящего.

Ещё об XDG

В принципе, самые главные свойства и возможности XDG мы уже рассмотрели. Остальное проходит по разряду «вкусностей». Например, можно указать набор атрибутов, и приписать их не конкретному слову, а «классу». Далее слово попросту объявляется экземпляром этого класса и автоматически получает перечисленные атрибуты.

Из важного: продуманы количественно-качественные вопросы присоединения зависимых слов (в примере это уже было, но хочу упомянуть явно). Так, для глагола можно указать, что у него только один субъект, только один объект и сколько угодно обстоятельств (где, когда, почему). Для каждого присоединяемого слова указывается свой набор согласуемых атрибутов. Скажем, тот же глагол согласуется по лицу и числу с субъектом. Да, ещё: одно и то же слово может быть описано несколько раз в разных контекстах. Например, «столовая» — это и прилагательное, и существительное.

Также достаточно гибок механизм вывода результатов. Деревья можно рисовать на экране, выводить в текстовый или xml-файл. Принципы (такие как valency principle, tree principle) можно разрабатывать самостоятельно, пополняя библиотеку проекта. Для этого, конечно, придётся освоить Mozart/Oz :)

Так что скажем спасибо автору этого проекта Ralph'у Debusmann'у и больше копать XDG вглубь не будем. Маленькая подсказка для желающих ознакомиться с XDG поближе: читать лучше всего не только мануал, но и диссертацию автора. Мануал-справочник не очень годится в качестве учебника, читать его тяжело. А диссертация — наборот, аккуратно знакомит читателя с идеями XDG на простых примерах.

Трибанки

Теперь ещё пару слов о таком важном явлении как трибанки (treebanks). К сожалению, в них я пока слабо разбираюсь — всё руки не доходят.

Мы тут уже рассуждали о том, откуда берутся правила грамматики. Понятно, что их можно либо сочинять вручную, либо «каким-то образом» извлекать из существующих текстов. Простые статистические подходы (наводящие на меня тоску) просто анализируют чистый текст в первозданном виде, опираясь на довольно условные «эвристические» критерии. Например: стоящие рядом слова с большей вероятностью зависят друг от друга, чем слова, стоящие поодаль. Мне кажется, далеко на таких принципах не уедешь; для себя я пользуюсь таким критерием: можно ли на идеях предлагаемого парсера естественного языка написать парсер несравнимо более простого Паскаля? Если нельзя, то, по-моему, тут и говорить не о чем.

Но статистический метод можно «натравить» и на специально подготовленный текст, а это уже совсем другое дело. Трибанк — это коллекция разобранных предложений (то есть графов разбора), подготовленных вручную. По-моему, именно такие банки и должны использоваться для автоматического извлечения правил генерации деревьев.

Логичным образом трибанки делятся на phrase-structure treebanks и dependency treebanks. Вероятно, самый известный пример банка первого рода — Penn treebank, синтаксис аннотаций которого является де-факто стандартом для «хомскианских» трибанков. Dependency treebanks — явление более свежее, их пока явно меньше. Наверно, чаще других упоминается The Prague Dependency Treebank чешского языка. Есть и другие, конечно (см. проекты со словом «dependency» в названии). Для русского, как обычно, какая-то работа «в процессе», но ничего конкретного сказать пока нельзя.

Я пока в трибанки глубоко не лезу, потому что там полно технических особенностей. Как выполняется аннотирование, каков синтаксис, что конкретно описывается, а что не описывается и так далее. В общем, есть такое ощущение, что можно закопаться с головой в тему и не выкопаться. Так что закапываясь, лучше бы точно знать, чего ищешь, а я пока ещё этого для себя не решил.

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

Пожалуй, для меня самым сложным вопросом в этой теме остаётся такой: допустим, правила из трибанка извлекли, и они даже позволяют получить хороший разбор текста. А что дальше? Поможет ли такой парсер в дальнейшем анализе текста? Мы это сейчас обсудим.

Прагматика

Честно говоря, тема подкралась неожиданно :) Я хотел сначала рассказать о своих идеях использования XDG, но лучше отложим их до следующего раза. А здесь остаётся как раз немного места для слегка отвлечённого философствования.

Мы уже говорили о синтаксисе предложения. Понимать синтаксис — значит уметь строить граф синтаксических зависимостей между словами. На следующем уровне понимания находится семантика — понимание смысла предложения. Под «пониманием» могут скрываться самые разные вещи, но давайте пока оставим этот термин как есть. Следующим уровнем является прагматика, отвечающая за использование языка.

Когда вы пишете компьютерную программу, почти вся обработка исходного текста компьютером так или иначе сводится к преобразования одного вида текста в другой вид текста, то есть, фактически, к машинному переводу. Программа на языке высокого уровня может быть превращена в C-код, далее C-код может быть автоматически соптимизирован и превращён в текст на ассемблере или сразу в машинные инструкции. На входе текст, на выходе текст — этим и занимаются все компиляторы, трансляторы, оптимизаторы и иже с ними. Но в самом конце этой цепочки возникает этап, ради которого всё и затевалось: выполнение программы процессором. То есть набор пассивных данных (текст) превращается в управляющую последовательность, запускающую некий активный процесс. В этом и состоит смысл языка программирования: управлять действием.

С естественным языком всё значительно сложнее. Если я пишу компилятор Паскаля, я твёрдо знаю конечную цель: это машинный переводчик, транслирующий программу на Паскале в программу в машинных кодах, исполняемую центральным процессором. Если я пишу парсер (то есть пока лишь синтаксический анализатор — относительно ранний и простой модуль компилятора!) естественного языка, надо чётко решить для себя, что будет дальше, на следующих этапах. Даже если вопрос с семантикой/смыслом текста будет успешно решён, всё равно не ясно, что будет происходить за семантикой.

На практике очевидны три применения обработки текста. Первое: машинный перевод. Здесь вопрос о прагматике даже не возникает — текст переведён на другой язык, и пусть читатель сам думает, что с ним дальше делать :) Второе: пополнение некоей «базы знаний» или «базы фактов» на основе сведений, содержащихся в тексте. Третье: речевой интерфейс. Здесь по сути естественный язык (вернее, его узкое подмножество) заменяет собой язык программирования. Открыть окно, нажать кнопку, запустить приложение.

Наверняка можно придумать и массу других применений — от игровых и учебных до аналитических. Можно играть в «съедобное — не съедобное»: человек вводит «я съел Х», а компьютер, в зависимости от Х, пишет, верит он вам или не верит. Можно использовать парсер как часть системы проверки правописания или распознавания речи: допустим, при распознавании мы не можем понять, сказал человек «мой конь и ржёт, и скачет» или «мой кони ржёт и скачет». Парсер сразу поймёт, что «мой кони ржёт» — конструкция некорректная, и поможет системе выбрать первый вариант.

Штука в том, что дальнейший сценарий использования (языка или парсера) оставляет отпечаток на самом парсере, как бы странно это ни звучало. Например, если мы играем в «съедобное — не съедобное», можно настроить грамматику (XDG, допустим) так, чтобы при попытке обработать конструкцию вида «я съел 'несъедобное'» происходила ошибка разбора.

Менее надуманный пример — система машинного перевода. Фразы «они разбили лагерь» и «они разбили машину» разбираются совершенно одинаково — субъект-глагол-объект. Однако дальше возникает другая проблема: что такое «разбили»? Первое «разбили» на английский следует переводить как «set up», второе как «crash». То есть синтаксически мы вроде бы разобрались, но в переводе нам это несильно помогло. При этом, заметим, синтаксический анализатор, не видящий разницы между «set up» и «crash» всё равно прекрасно сгодится для проверки правописания русского. Разбили и разбили — сад, палатку, машину, чашку, сердце. Какая разница, как это по-английски?

В этом я вижу некоторую проблему (хотя могу и заблуждаться) для автоматического извлечения правил из трибанка: вся эта работа сконцентрирована вокруг задачи получения правильного дерева разбора, но что с этим деревом делать дальше, не обсуждается. Можно возразить: задача парсера — парсить, а что там с семантикой, пусть следующие модули в цепочке разбираются. Однако если следующий модуль (скажем, семантический анализатор) будет столь же сложен, что и парсер, толку с такого парсера? Мне кажется, что на этапе синтаксического анализа хорошо бы выжать максимум информации из текста в рамках поставленной задачи.

Есть попытки «объективно» оценить качество того или иного парсера, но всё обычно сводится к тому, генерирует ли парсер правильные деревья разбора или нет.

Так, на сегодня ставим точку. Завтра (ну или когда будет настроение :) ) продолжим. А может, на следующей части и закончим :)
Tags:
Hubs:
Total votes 39: ↑29 and ↓10+19
Comments17

Articles