• Выделение именованных сущностей


    NER / Блог компании ABBYY / Хабр

    Первую часть статьи об основах NLP можно прочитать здесь. А сегодня мы поговорим об одной из самых популярных задач NLP – извлечении именованных сущностей (Named-entity recognition, NER) – и разберем подробно архитектуры решений этой задачи.



    Задача NER – выделить спаны сущностей в тексте (спан – непрерывный фрагмент текста). Допустим, есть новостной текст, и мы хотим выделить в нем сущности (некоторый заранее зафиксированный набор — например, персоны, локации, организации, даты и так далее). Задача NER – понять, что участок текста “1 января 1997 года” является датой, “Кофи Аннан” – персоной, а “ООН” – организацией.


    Что такое именованные сущности? В первой, классической постановке, которая была сформулирована на конференции MUC-6 в 1995 году, это персоны, локации и организации. С тех пор появилось несколько доступных корпусов, в каждом из которых свой набор именованных сущностей. Обычно к персонам, локациям и организациям добавляются новые типы сущностей. Самые распространенные из них — числовые (даты, денежные суммы), а также сущности Misc (от miscellaneous — прочие именованные сущности; пример — iPhone 6 ).

    Зачем нужно решать задачу NER


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

    Один из сценариев, когда решение задачи в классической постановке все-таки может понадобиться, — структуризация неструктурированных данных. Пусть у вас есть какой-то текст (или набор текстов), и данные из него нужно ввести в базу данных (таблицу). Классические именованные сущности могут соответствовать строкам такой таблицы или же служить содержанием каких-то ячеек. Соответственно, чтобы правильно заполнять таблицу, нужно перед этим выделить в тексте те данные, которые вы будете в нее вносить (обычно после этого есть еще один этап — идентификация сущностей в тексте, когда мы понимаем, что спаны “ООН” и “Организация Объединенных Наций” относятся к одной и той же организации; однако, задача идентификации или entity linking — это уже другая задача, и о ней мы подробно рассказывать в этом посте не будем).

    Однако, есть несколько причин, почему NER является одной из самых популярных задач NLP.

    Во-первых, извлечение именованных сущностей — это шаг в сторону “понимания” текста. Это может как иметь самостоятельную ценность, так и помочь лучше решать другие задачи NLP.

    Так, если мы знаем, где в тексте выделены сущности, то мы можем найти важные для какой-то задачи фрагменты текста. Например, можем выделить только те абзацы, где встречаются сущности какого-то определенного типа, а потом работать только с ними.

    Допустим, вам приходит письмо, и хорошо бы сделать сниппет только той части, где есть что-то полезное, а не просто “Здравствуйте, Иван Петрович”. Если уметь выделять именованные сущности, сниппет можно сделать умным, показав ту часть письма, где есть интересующие нас сущности (а не просто показать первое предложение письма, как это часто делается). Или же можно просто подсветить в тексте нужные части письма (или, непосредственно, важные для нас сущности) для удобства работы аналитиков.

    Кроме того, сущности – это жесткие и надежные коллокации, их выделение может быть важно для многих задач. Допустим, у вас есть название именованной сущности и, какой бы она ни была, скорее всего, она непрерывна, и все действия с ней нужно совершать как с единым блоком. Например, переводить название сущности в название сущности. Вы хотите перевести «Магазин “Пятерочка”» на французский язык единым куском, а не разбить на несколько не связанных друг с другом фрагментов. Умение определять коллокации полезно и для многих других задач — например, для синтаксического парсинга.

    Без решения задачи NER тяжело представить себе решение многих задач NLP, допустим, разрешение местоименной анафоры или построение вопросно-ответных систем. Местоименная анафора позволяет нам понять, к какому элементу текста относится местоимение. Например, пусть мы хотим проанализировать текст “Прискакал Чарминг на белом коне. Принцесса выбежала ему навстречу и поцеловала его”. Если мы выделили на слове “Чарминг” сущность Персона, то машина сможет намного легче понять, что принцесса, скорее всего, поцеловала не коня, а принца Чарминга.

    Теперь приведем пример, как выделение именованных сущностей может помочь при построении вопросно-ответных систем. Если задать в вашем любимом поисковике вопрос «Кто играл роль Дарта Вейдера в фильме “Империя наносит ответный удар”», то с большой вероятностью вы получите верный ответ. Это делается как раз с помощью выделения именованных сущностей: выделяем сущности (фильм, роль и т. п.), понимаем, что нас спрашивают, и дальше ищем ответ в базе данных.

    Наверное, самое важное соображение, благодаря которому задача NER так популярна: постановка задачи очень гибкая. Другими словами, никто не заставляет нас выделять именно локации, персоны и организации. Мы можем выделять любые нужные нам непрерывные фрагменты текста, которые чем-то отличаются от остального текста. В результате можно подобрать свой набор сущностей для конкретной практической задачи, приходящей от заказчика, разметить корпус текстов этим набором и обучить модель. Такой сценарий встречается повсеместно, и это делает NER одной из самых часто решаемых задач NLP в индустрии.

    Приведу пару примеров таких юзкейсов от конкретных заказчиков, в решении которых мне довелось принять участие.

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

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

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

    Если NER – это так полезно, то почему не используется повсеместно?


    Почему задача NER не везде решена и коммерческие заказчики до сих пор готовы платить за ее решение не самые маленькие деньги? Казалось бы, все просто: понять, какой кусок текста выделить, и выделить его.

    Но в жизни все не так легко, возникают разные сложности.

    Классической сложностью, которая мешает нам жить при решении самых разных задач NLP, являются разного рода неоднозначности в языке. Например, многозначные слова и омонимы (см. примеры в части 1). Есть и отдельный вид омонимии, имеющий непосредственное отношение к задаче NER — одним и тем же словом могут называться совершенно разные сущности. Например, пусть у нас есть слово “Вашингтон”. Что это? Персона, город, штат, название магазина, имя собаки, объекта, что-то еще? Чтобы выделить этот участок текста, как конкретную сущность, надо учитывать очень многое – локальный контекст (то, о чем был предшествующий текст), глобальный контекст (знания о мире). Человек все это учитывает, но научить машину делать это непросто.

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

    Пусть, например, мы хотим выделить названия магазинов.

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

    Другой пример — «Вас приветствует “Волхонка Престиж” — ваш любимый магазин брендов по доступным ценам». Наверное, слово “магазин” не надо включать в аннотацию — это явно не часть названия, а просто его описание. Кроме того, если включить в название это слово, нужно также включать и слова “- ваш любимый”, а этого, пожалуй, совсем не хочется делать.

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

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

    Ну а чем сложнее инструкция, там более квалифицированные разметчики вам требуются. Одно дело, когда разметчику нужно определить, является ли письмо текстом заказа или нет (хотя и здесь есть свои тонкости и пограничные случаи), а другое дело, когда разметчику нужно вчитываться в 50-страничную инструкцию, найти конкретные сущности, понять, что включать в аннотацию, а что нет.

    Квалифицированные разметчики — это дорого, и работают они, обычно, не очень оперативно. Деньги вы потратите точно, но совсем не факт, что получится идеальная разметка, ведь если инструкция сложная, даже квалифицированный человек может ошибиться и что-то неправильно понять. Для борьбы с этим используют многократную разметку одного текста разными людьми, что еще увеличивает цену разметки и время, за которое она готовится. Избежать этого процесса или даже серьезно сократить его не выйдет: чтобы обучаться, нужно иметь качественную обучающую выборку разумных размеров.

    Это и есть две основных причины, почему NER еще не завоевал мир и почему яблони до сих пор не растут на Марсе.

    Как понять, качественно ли решена задача NER


    Расскажу немного про метрики, которыми люди пользуются для оценки качества своего решения задачи NER, и про стандартные корпуса.

    Основная метрика для нашей задачи – это строгая f-мера. Объясним, что это такое.

    Пусть у нас есть тестовая разметка (результат работы нашей системы) и эталон (правильная разметка тех же текстов). Тогда мы можем посчитать две метрики – точность и полноту. Точность – доля true positive сущностей (т. е. сущностей, выделенных нами в тексте, которые также присутствуют в эталоне), относительно всех сущностей, выделенных нашей системой. А полнота – доля true positive сущностей относительно всех сущностей, присутствующих в эталоне. Пример очень точного, но неполного классификатора – это классификатор, который выделяет в тексте один правильный объект и больше ничего. Пример очень полного, но вообще неточного классификатора – это классификатор, который выделяет сущность на любом отрезке текста (таким образом, помимо всех эталонных сущностей, наш классификатор выделяет огромное количество мусора).

    F-мера же – это среднее гармоническое точности и полноты, стандартная метрика.

    Как мы рассказали в предыдущем разделе, создавать разметку — дорогое удовольствие. Поэтому доступных корпусов с разметкой не очень много.

    Для английского языка есть некоторое разнообразие — есть популярные конференции, на которых люди соревнуются в решении задачи NER (а для проведения соревнований создается разметка). Примеры таких конференций, на которых были созданы свои корпуса с именованными сущностями — MUC, TAC, CoNLL. Все эти корпуса состоят практически исключительно из новостных текстов.

    Основной корпус, на котором оценивается качество решения задачи NER — это корпус CoNLL 2003 (вот ссылка на сам корпус, вот статья о нем). Там примерно 300 тысяч токенов и до 10 тысяч сущностей. Сейчас SOTA-системы (state of the art — т. е. наилучшие на данный момент результаты) показывают на этом корпусе f-меру порядка 0,93.

    Для русского языка все намного хуже. Есть один общедоступный корпус (FactRuEval 2016, вот статья о нем, вот статья на Хабре), и он очень маленький – там всего 50 тысяч токенов. При этом корпус довольно специфичный. В частности, в корпусе выделяется достаточно спорная сущность LocOrg (локация в организационном контексте), которая путается как с организациями, так и с локациями, в результате чего качество выделения последних ниже, чем могло бы быть.

    Как решать задачу NER


    Сведение задачи NER к задаче классификации


    Несмотря на то что сущности часто бывают многословными, обычно задача NER сводится к задаче классификации на уровне токенов, т. е. каждый токен относится к одному из нескольких возможных классов. Есть несколько стандартных способов сделать это, но самый общий из них называется BIOES-схемой. Схема заключается в том, чтобы к метке сущности (например, PER для персон или ORG для организаций) добавить некоторый префикс, который обозначает позицию токена в спане сущности. Более подробно:

    B – от слова beginning – первый токен в спане сущности, который состоит из больше чем 1 слова.
    I – от словам inside – это то, что находится в середине.
    E – от слова ending, это последний токен сущности, которая состоит больше чем из 1 элемента.
    S – single. Мы добавляем этот префикс, если сущность состоит из одного слова.

    Таким образом, к каждому типу сущности добавляем один из 4 возможных префиксов. Если токен не относится ни к какой сущности, он помечается специальной меткой, обычно имеющей обозначение OUT или O.

    Приведем пример. Пусть у нас есть текст “Карл Фридрих Иероним фон Мюнхгаузен родился в Боденвердере”. Здесь есть одна многословная сущность — персона “Карл Фридрих Иероним фон Мюнгхаузен” и одна однословная — локация “Боденвердере”.

    Таким образом, BIOES — это способ отобразить проекции спанов или аннотаций на уровень токенов.

    Понятно, что по такой разметке мы однозначно можем установить границы всех аннотаций сущностей. Действительно, про каждый токен мы знаем, верно ли, что сущность начинается с этого токена или заканчивается на нем, а значит, закончить ли аннотацию сущности на данном токене, или расширять ее на следующие токены.

    Подавляющее большинство исследователей использует этот способ (или его вариации с меньшим количеством меток — BIOE или BIO), но у него есть несколько существенных недостатков. Главный из них заключается в том, что схема не позволяет работать с вложенными или пересекающимися сущностями. Например, сущность “МГУ имени М.В. Ломоносова” — это одна организация. Но Ломоносов сам по себе – это персона, и это тоже было бы неплохо задать в разметке. С помощью описанного выше способа разметки мы никогда не сможем передать оба эти факта одновременно (потому что у одного токена можем сделать только одну пометку). Соответственно, токен “Ломоносова” может быть либо частью аннотации организации, либо частью аннотации персоны, но никогда не тем и другим одновременно.

    Другой пример вложенных сущностей: “Кафедра математической логики и теории алгоритмов механико-математического факультета МГУ”. Здесь в идеале хотелось бы выделять 3 вложенных организации, но приведенный выше способ разметки позволяет выделить либо 3 непересекающиеся сущности, либо одну сущность, имеющую аннотацией весь приведенный фрагмент.

    Кроме стандартного способа свести задачу к классификации на уровне токенов, есть и стандартный формат данных, в котором удобно хранить разметку для задачи NER (а также для многих других задач NLP). Этот формат называется CoNLL-U.

    Основная идея формата такая: храним данные в виде таблицы, где одна строка соответствует одному токену, а колонки — конкретному типу признаков токена (в т. ч. признаком является и само слово — словоформа). В узком смысле формат CoNLL-U задает, какие именно типы признаков (т. е. колонки) включаются в таблицу — всего 10 типов признаков на каждый токен. Но исследователи обычно рассматривают формат шире и включают те типы признаков, которые нужны для конкретной задачи и метода ее решения.

    Приведем ниже пример данных в CoNLL-U-подобном формате, где рассмотрены 6 типов признаков: номер текущего предложения в тексте, словоформа (т. е. само слово), лемма (начальная форма слова), POS-таг (часть речи), морфологические характеристики слова и, наконец, метка сущности, выделяемой на данном токене.

    А как решали задачу NER раньше?


    Строго говоря, задачу можно решать и без машинного обучения — с помощью rule-based систем (в самом простом варианте — с помощью регулярных выражений). Это кажется устаревшим и неэффективным, однако нужно понимать, если у вас ограничена и четко очерчена предметная область и если сущность, сама по себе, не обладает большой вариативностью, то задача NER решается с помощью rule-based методов достаточно качественно и быстро.

    Например, если вам нужно выделить емейлы или числовые сущности (даты, денежные суммы или номера телефонов), регулярные выражения могут привести вас к успеху быстрее, чем попытка решить задачу с помощью машинного обучения.

    Впрочем, как только в дело вступают языковые неоднозначности разного рода (о части из них мы писали выше), такие простые способы перестают хорошо работать. Поэтому применять их имеет смысл только для ограниченных доменов и на простых и четко отделимых от остального текста сущностях.

    Несмотря на все вышесказанное, на академических корпусах до конца 2000-х годов SOTA показывали системы на основе классических методов машинного обучения. Давайте кратко разберем, как они работали.

    Признаки


    До появления эмбеддингов, главным признаком токена обычно являлась словоформа — т. е. индекс слова в словаре. Таким образом, каждому токену ставится в соответствие булев вектор большой размерности (размерности словаря), где на месте индекса слова в словаре стоит 1, а на остальных местах стоят 0.

    Кроме словоформы, в качестве признаков токена часто использовались части речи (POS-таги), морфологические признаки (для языков без богатой морфологии — например, английского, морфологические признаки практически не дают эффекта), префиксы (т. е. несколько первых символов слова), суффиксы (аналогично, несколько последних символов токена), наличие спецсимволов в токене и внешний вид токена.

    В классических постановках, очень важным признаком токена является тип его капитализации, например:

    • “первая буква большая, остальные маленькие”,
    • “все буквы маленькие”,
    • “все буквы большие”,
    • или вообще “нестандартная капитализация” ( наблюдаемая, в частности, для токена “iPhone”).

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

    Кроме всего этого, активно использовались газетиры – словари сущностей. Мы знаем, что Петя, Елена, Акакий – это имена, Иванов, Руставели, фон Гете – фамилии, а Мытищи, Барселона, Сан Пауло – города. Важно отметить, что словари сущностей сами по себе не решают задачу (“Москва” может быть частью названия организации, а “Елена” — частью локации), но могут улучшить ее решение. Впрочем, конечно, несмотря на неоднозначность, принадлежность токена словарю сущностей определенного типа — это очень хороший и значимый признак (настолько значимый, что обычно результаты решения задачи NER делятся на 2 категории — с использованием газетиров и без них).

    Если вам интересно, как люди решали задачу NER, когда деревья были большие, советую посмотреть статью Nadeau and Sekine (2007), A survey of Named Entity Recognition and Classification. Методы, которые там описаны, конечно, устаревшие (даже если вы не можете использовать нейросети из-за ограничений производительности, вы, наверное, будете пользоваться не HMM, как написано в статье, а, допустим, градиентным бустингом), но посмотреть на описание признаков может иметь смысл.

    К интересным признакам можно отнести шаблоны капитализации (summarized pattern в статье выше). Они до сих пор могут помочь при решении некоторых задач NLP. Так, в 2018 году была успешная попытка применить шаблоны капитализации (word shape) к нейросетевым способам решения задачи.

    Как решить задачу NER с помощью нейросетей?


    NLP almost from scratch


    Первая успешная попытка решить задачу NER с помощью нейросетей была совершена в 2011 году.

    В момент выхода этой статьи она показала SOTA-результат на корпусе CoNLL 2003. Но нужно понимать, что превосходство модели по сравнению с системами на основе классических алгоритмов машинного обучения было достаточно незначительным. В последующие несколько лет методы на основе классического ML показывали результаты, сравнимые с нейросетевыми методами.

    Кроме описания первой удачной попытки решить задачу NER с помощью нейростетей, в статье подробно описаны многие моменты, которые в большинстве работ на тему NLP оставляют за скобками. Поэтому, несмотря на то что архитектура нейросети, описанная в статье, устаревшая, со статьей имеет смысл ознакомиться. Это поможет разобраться в базовых подходах к нейросетям, используемых при решении задачи NER (и шире, многих других задач NLP).

    Расскажем подробнее об архитектуре нейросети, описанной в статье.

    Авторы вводят две разновидности архитектуры, соответствующие двум различным способам учесть контекст токена:

    • либо использовать «окно» заданной ширины (window based approach),
    • либо считать контекстом все предложение (sentence based approach).

    В обоих вариантах используемые признаки – это эмбеддинги словоформ, а также некоторые ручные признаки – капитализация, части речи и т.д. Расскажем подробнее о том, как они вычисляются.

    Мы получили на вход список слов нашего предложения: например, “The cat sat on the mat”.

    Пусть всего имеется K различных признаков для одного токена (например, такими признаками могут выступать словоформа, часть речи, капитализация, является ли наш токен первым или последним в предложении и т. п.). Все эти признаки мы можем считать категориальными (например, словоформе соответствует булев вектор длины размерности словаря, где 1 стоит только на координате соответствующей индексу слова в словаре). Пусть — булев вектор, соответствующий значению i-го признака j-го токена в предложении.

    Важно отметить, что в sentence based approach кроме категориальных признаков, определяемых по словам, используется признак — сдвиг относительно токена, метку которого мы пытаемся определить. Значение этого признака для токена номер i будет i-core, где core — номер токена, метку которого мы пытаемся определить в данный момент (этот признак тоже считается категориальным, и вектора для него вычисляются точно так же, как и для остальных).

    Следующий этап нахождения признаков токена — умножение каждого на матрицу , которая называется Lookup Table (таким образом булевы вектора “превращаются” в непрерывные). Напомним, что каждый из — булев вектор, в котором на одном месте стоит 1, а на остальных местах – 0. Таким образом при умножении на , происходит выбор одной из строк в нашей матрице. Эта строка и является эмбеддингом соответствующего признака токена. Матрицы (где i может принимать значения от 1 до K) – это параметры нашей сети, которые мы обучаем вместе с остальными слоями нейросети.

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

    Таким образом, для каждого токена получен непрерывный вектор признаков, являющийся конкатенацией результатов перемножения всевозможных на .

    Теперь разберемся с тем, как эти признаки используются в sentence based approach (window based идейно проще). Важно, что мы будем запускать нашу архитектуру по отдельности для каждого токена (т. е. для предложения “The cat sat on the mat” мы запустим нашу сеть 6 раз). Признаки в каждом запуске собираются одинаковые, за исключением признака, отвечающего за позицию токена, метку которого мы пытаемся определить — токена core.

    Берем получившиеся непрерывные вектора каждого токена и пропускаем их через одномерную свертку с фильтрами не очень большой размерности: 3-5. Размерность фильтра соответствует размеру контекста, который сеть одновременно учитывает, а количество каналов соответствует размерности исходных непрерывных векторов (сумме размерностей эмбеддингов всех признаков). После применения свертки получаем матрицу размерности m на f, где m — количество способов, которыми фильтр можно приложить к нашим данным (т. е. длина предложения минус длина фильтра плюс один), а f — количество используемых фильтров.

    Как и почти всегда при работе со свертками, после свертки мы используем пулинг — в данном случае max pooling (т. е. для каждого фильтра берем максимум его значения на всем предложении), после чего получаем вектор размерности f. Таким образом, вся информация, содержащаяся в предложении, которая может нам понадобиться при определении метки токена core, сжимается в один вектор (max pooling был выбран потому, что нам важна не информация в среднем по предложению, а значения признаков на его самых важных участках). Такой “сплюснутый контекст” позволяет нам собирать признаки нашего токена по всему предложению и использовать эту информацию, чтобы определить, какую метку должен получить токен core.

    Дальше пропускаем вектор через многослойный персептрон с какими-то функциями активации (в статье — HardTanh), а в качестве последнего слоя используем полносвязный с softmax размерности d, где d — количество возможных меток токена.

    Таким образом сверточный слой позволяет нам собрать информацию, содержащуюся в окне размерности фильтра, пулинг — выделить самую характерную информацию в предложении (сжав ее в один вектор), а слой с softmax — позволяет определить, какую же метку имеет токен номер core.

    CharCNN-BLSTM-CRF


    Поговорим теперь о архитектуре CharCNN-BLSTM-CRF, то есть о том, что было SOTA в период 2016-2018 (в 2018 появились архитектуры на основе эмбеддингов на языковых моделях, после которых мир NLP уже никогда не будет прежним; но эта сага не об этом). В применении к задаче NER архитектура впервые была описана в статьях Lample et al (2016) и Ma & Hovy (2016).

    Первые слои сети такие же, как в пайплайне NLP, описанном в предыдущей части нашего поста.

    Сначала вычисляется контекстно-независимый признак каждого токена в предложении. Признаки обычно собираются из трех источников. Первый – словоформенный эмбеддинг токена, второй – символьные признаки, третий — дополнительные признаки: информация про капитализацию, часть речи и т. п. Конкатенация всех этих признаков и составляет контекстно-независимый признак токена.

    Про словоформенные эмбеддинги мы подробно говорили в предыдущей части. Дополнительные признаки мы перечислили, но мы не говорили, как именно они встраиваются в нейросеть. Ответ простой — для каждой категории дополнительных признаков мы с нуля учим эмбеддинг не очень большого размера. Это в точности Lookup-таблицы из предыдущего параграфа, и учим их мы точно так же, как описано там.

    Теперь расскажем, как устроены символьные признаки.

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

    Перейдем теперь к описанию архитектуры CharCNN (а также, связанной с ней архитектуры CharRNN). Нам дан токен, который состоит из каких-то символов. На каждый символ мы будем выдавать вектор какой-то не очень большой размерности (например, 20) — символьный эмбеддинг. Символьные эмбеддинги можно предобучать, однако чаще всего они учатся с нуля — символов даже в не очень большом корпусе много, и символьные эмбеддинги должны адекватно обучиться.

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

    Есть 2 стандартных способа.

    Чуть более популярный из них – использовать одномерные свертки (поэтому эта часть архитектуры называется CharCNN). Делаем это мы точно так же, как мы это делали со словами в sentence based approach в предыдущей архитектуре.

    Итак, пропускаем эмбеддинги всех символов через свертку с фильтрами не очень больших размерностей (например, 3), получаем вектора размерности количества фильтров. Над этими векторами производим max pooling, получаем 1 вектор размерности количества фильтров. Он содержит в себе информацию о символах слова и их взаимодействии и будет являться вектором символьных признаков токена.

    Второй способ превратить символьные эмбеддинги в один вектор – подавать их в двустороннюю рекуррентную нейросеть (BLSTM или BiGRU; что это такое, мы описывали в первой части нашего поста). Обычно символьным признаком токена является просто конкатенация последних состояний прямого и обратного RNN.

    Итак, пусть нам дан контекстно-независимый вектор признаков токена. По нему мы хотим получить контекстно-зависимый признак.

    Это делается с помощью BLSTM или BiGRU. В i-й момент времени слой выдает вектор, являющийся конкатенацией соответствующих выходов прямого и обратного RNN. Этот вектор содержит в себе информацию как о предыдущих токенах в предложении (она есть в прямом RNN), так и о следующих (она есть в обратном RNN). Поэтому этот вектор является контекстно-зависимым признаком токена.

    Такая архитектура может использоваться в самых разных задачах NLP, поэтому ее считают важной частью пайплайна NLP.

    Вернемся, однако, к задаче NER. Получив контекстно-зависимые признаки всех токенов, мы хотим по каждому токену получить правильную метку для него. Это можно сделать разными способами.

    Более простой и очевидный способ – использовать в качестве последнего слоя полносвязный с softmax размерности d, где d — количество возможных меток токена. Таким образом мы получим вероятности токена иметь каждую из возможных меток (и можем выбрать самую вероятную из них).

    Этот способ работает, однако обладает существенным недостатком — метка токена вычисляется независимо от меток других токенов. Сами соседние токены мы учитываем за счет BiRNN, но метка токена зависит не только от соседних токенов, но и от их меток. Например, вне зависимости от токенов метка I-PER встречается только после B-PER или I-PER.

    Стандартный способ учесть взаимодействие между типами меток — использовать CRF (conditional random fields). Мы не будем подробно описывать, что это такое (вот здесь дано хорошее описание), но упомянем, что CRF оптимизирует всю цепочку меток целиком, а не каждый элемент в этой цепочке.

    Итак, мы описали архитектуру CharCNN-BLSTM-CRF, которая являлась SOTA в задаче NER до появления эмбеддингов на языковых моделях в 2018 году.

    В заключение поговорим немного о значимости каждого элемента архитектуры. Для английского языка CharCNN дает прирост f-меры приблизительно на 1%, CRF — на 1-1.5%, а дополнительные признаки токена к улучшению качества не приводят (если не использовать более сложные техники типа multi-task learning, как в статье Wu et al (2018)). BiRNN — основа архитектуры, которая, однако, может быть заменена трансформером.



    Надеемся, что нам удалось дать читателям некоторое представление о задаче NER. Хотя это задача важная, она достаточно простая, что и позволило нам описать ее решение в рамках одного поста.

    Иван Смуров,
    руководитель NLP Advanced Research Group

    Экспресс-NER. Или что делать если на нейронку нет времени?

    Время прочтения: 8 мин.

    Задача распознавания именованных сущностей (Named-entity recognition или NER) – одна из часто встречаемых при обработке текста методами NLP – выделить из текста фрагменты, которые соответствуют этим самым именованным сущностям. В формальном виде понятие именованной сущности было сформулировано на конференции MUC-6 в 1995 году. Тогда под ними подразумевались только персоны, локации и организации. На сегодняшний день этот перечень пополнился денежными суммами в комплекте с наименованием валюты, датами, марками различной продукции.

    Пример решения задачи NER представлен на иллюстрации ниже:

    Практическая польза решения задачи NER кажется очевидной. Выделение именованных сущностей – один из главных шагов на пути к пониманию текста. Анализируя текст договора с помощью этого метода, можно выявить материально заинтересованные стороны. Обрабатывая записи логов платёжной системы, можно понять кто, в каком количестве и валюте вносил денежные средства.

    Как и у любой задачи NLP, у NER есть свои подводные камни. Самый известный из них наглядно иллюстрируется анекдотом:

    «-Как найти площадь Ленина?

    -Надо длину Ленина умножить на ширину Ленина».

    Здесь слово «Ленин» представлено в составе двух сущностей: локации и персоны. Задача любой модели, решающей проблему NER, не только выделить саму сущность, но и правильно определить её тип.

    Сегодня самым качественным инструментом для решения задачи NER является LSTM – сети с долгой краткосрочной памятью. Это модификация рекуррентных нейронных сетей (RNN), особенность которых в способности обучаться долговременным зависимостям. Эта особенность позволяет им анализировать текст, опираясь не только на конкретное слово, которое сеть «видит» в данный момент, но и на взаимосвязь этого слова с «увиденными» ранее в рамках того же текста. Таким образом, модель учится понимать контекст. Это обеспечивает высокую точность работы модели.

    Но что делать, если решение задачи NER необходимо в сжатые сроки, и на написание и обучение нейронной сети просто нет времени?

    DISCLAIMER: Все ФИО, названия организаций и адреса почты сгенерированы искусственно. Любые совпадения с реальными лицами, организациями и существующими адресами электронной почты случайны!

    Пусть у нас есть файл, в котором собраны почтовые адреса.  В нашем случае их будет 223. Часть из них принадлежат физическим лицам, часть – организациям.

    USERNAME POST IS_HUMAN
    Сагадиев Мстислав Яковович  [email protected] 1
    Яблонова Ксения Федотовна  [email protected] 1
    Радыгина Владислава Яновна  [email protected] 1
    Ярцин Никон Денисович  [email protected] 1
    Жцикка-Консалтинг [email protected] 0
    Шошси-Стартап [email protected] 0
    ООО Кадазмё [email protected] 0
    ЗАО Сила Вернадского [email protected] 0
    IVAN IVANOVICH IVANOV [email protected] 1

    Наша задача будет заключаться в том, чтобы с помощью NER распознать тип сущности: «персона» или «организация» и маркировать те адреса, которые принадлежат физическим лицам, единицей. Для проверки качества работы алгоритмов сделаем разметку ответов, в качестве меры точности будем использовать f1-меру.

    Особое внимание стоит обратить на последние две записи в приведенном фрагменте файла. В «ЗАО Сила Вернадского» слово «Вернадский» соответствует сущности типа «организация», хотя может быть распознано и как «персона» — алгоритм должен уловить это отличие. В «IVAN IVANOVICH IVANOV» очевидно представлена сущность типа «персона», но она написана транслитом. Это должно усложнить работу алгоритму.

    Для решения задачи применим четыре инструмента, доступных в языке Python.

    1. nltk — https://www.nltk.org/
    2. natasha — https://natasha.readthedocs.io/ru/latest/
    3. stanza — https://stanfordnlp.github.io/stanza/
    4. pullenti — https://pullenti.ru/Default.aspx

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

    Для начала, импортируем все необходимые и вспомогательные модули, а также зададим глобальные переменные «morph» для nltk и natasha и глобальную переменную «stanza_nlp» для stanza:

    import nltk import pymorphy2 import pandas as pd import numpy as np import re from tqdm import tqdm import stanza from natasha import NamesExtractor from pullenti_wrapper.processor import Processor, PERSON from sklearn.metrics import f1_score stanza.download('ru') nltk.download('punkt') stanza_nlp = stanza.Pipeline(lang='ru', processors='tokenize,ner') morph = pymorphy2.MorphAnalyzer()

    Будем работать с таблицей почтовых адресов как с pandas DataFrame. Для каждого из наших алгоритмов определим отдельную функцию, которая будет получать на вход текст из столбца USERNAME. Если выделение сущности типа «персона» прошло успешно, функция вернет строку с этой сущностью. Если выделить сущность типа «персона» не удалось, функция вернёт 0.

    Функция для nltk:

    def name_recognize_nltk(text): prob_thresh = 0.4 text = str(text) result = '' global morph for word in nltk.word_tokenize(text): if re.search("максим", word.lower()): result += 'Максим ( максим)score:1.0 \\ ' else: for p in morph.parse(word): if ('Name' in p.tag and p.score >= prob_thresh) or ('Surn' in p.tag and p.score >= prob_thresh): result += '{:<12}({:>12})score:{:0.3}'.format(word, p.normal_form, p.score) result += ' \ ' if result != '': return result else: return 0

    Функция для natasha:

    def name_recognize_natasha(text): global morph extractor = NamesExtractor(morph) matches = extractor(text) if matches != None: result = '' for index, match in enumerate(matches): if match != None: result += str(match.fact) if result !='': return result else: return 0

    Функция для stanza:

    def name_recognize_stanza(text): result = '' global stanza_nlp doc = stanza_nlp(text) for sent in doc.sentences: for ent in sent.ents: if ent.type == 'PER': result += f'entity: {ent.text}\ttype: {ent.type}' + " " if result != '': return result else: return 0

    Функция для pullenti:

    def name_recognize_pullenti(text): result = '' processor = Processor([PERSON]) ner_result = processor(text) if ner_result.matches != []: for match in ner_result.matches: result += str(match)+ ' ' if result != '': return result else: return 0

    Обратите внимание на функцию для nltk – случай с именем «Максим» разобран отдельно. В nltk задача NER сводится к задаче классификации. Если score, который выдаст модель, будет ниже значения prob_thresh=0.4, сущность будет считаться нераспознанной. Для имени «Максим» эта значение score равно 0.2, поэтому это имя в качестве сущности не будет распознано. Слишком низкое значение prob_thresh скажется на качестве модели: повысится количество ложноположительных срабатываний. Поэтому, чтобы не занижать prob_thresh, но распознать Максима, этот случай был разобран и дописан вручную. Список имён, для которых требуется дополнительная обработка, можно пополнить еще.

    Запустим все наши алгоритмы выделения сущностей и оценим точность. Для получения усреднённой оценки вычислим для каждого алгоритма средний f1_score на основе 20 независимых выполнений:

    #Считали данные df = pd.read_excel('Выборка.xlsx') #Словарь, куда будем записывать f-score после кждого выполнения f_score_dict ={'NLTK':[], 'NATASHA':[],'STANZA':[],'PULLENTI':[]} #Применяем алгоритмы for i in tqdm(range(0,20)): df['NLTK'] = df.USERNAME.apply(name_recognize_nltk) df['NATASHA'] = df.USERNAME.apply(name_recognize_natasha) df['STANZA'] = df.USERNAME.apply(name_recognize_stanza) df['PULLENTI'] = df.USERNAME.apply(name_recognize_pullenti) #Создаём вспомогательные столбцы для проверки 1 - сущность распознана, 0 - сущность не распознана df['NLTK_mark'] = df.NLTK.apply(lambda x: x if x==0 else 1 ) df['NATASHA_mark'] = df.NATASHA.apply(lambda x: x if x==0 else 1 ) df['STANZA_mark'] = df.STANZA.apply(lambda x: x if x==0 else 1 ) df['PULLENTI_mark'] = df.PULLENTI.apply(lambda x: x if x==0 else 1 ) #Вычисляем f-score на каждой итерации и записываем в словарь f_score_dict['NLTK'].append(f1_score(df.IS_HUMAN,df.NLTK_mark)) f_score_dict['NATASHA'].append(f1_score(df.IS_HUMAN,df.NATASHA_mark)) f_score_dict['STANZA'].append(f1_score(df.IS_HUMAN,df.STANZA_mark)) f_score_dict['PULLENTI'].append(f1_score(df.IS_HUMAN,df.PULLENTI_mark))

    Посмотрим на примеры распознанных и нераспознанных сущностей.

    В этом примере все четыре алгоритма успешно распознали Мстислава Якововича как сущность типа «персона».

    А вот Нону Мефодиевну определить, как человека смогли только два алгоритма из четырёх.

    Интереснее всего посмотреть, как обстоят дела с двумя подводными камнями, о которых говорилось в самом начале эксперимента:

    Видно, что с каждым конкретным примером успешно справились по два алгоритма. Но здесь особенно хочется выделить алгоритмы stanza и pullenti, которые смогли верно распознать и то, что «ЗАО Сила Вернадского» — это «организация», и то, что «IVAN IVANOVICH IVANOV» — это «персона», пусть и написанная транслитом.

    Но это всё частные примеры. Давайте посмотрим на совокупную f1-меру:

    # Вычисляем средние значения f1 nltk_f1 = np.mean(f_score_dict['NLTK']) natasha_f1 = np.mean(f_score_dict['NATASHA']) stanza_f1 = np.mean(f_score_dict['STANZA']) pullenti_f1 = np.mean(f_score_dict['PULLENTI']) print(*['NLTK = ' + str(nltk_f1),'NATASHA = ' + str(natasha_f1), 'STANZA = ' + str(stanza_f1),'PULLENTI = ' + str(pullenti_f1)], sep = '\n')

    Выполнив эту команду, получим следующее:

    NLTK = 0.8

    NATASHA = 0.89

    STANZA = 0.97

    PULLENTI = 0.98

    ВЫВОДЫ

    На данном примере мы увидели, что для решения задачи NER вовсе не обязательно писать нейронную сеть. Более того, мы познакомились с несколькими инструментами, позволяющими решить эту задачу, оценили простоту написания кода и качество результатов.

    В этом ключе особенно хочется выделить пакет pullenty. Написание кода для него кажется наиболее простым. По сути, всё распознавание происходит всего в двух строчках кода:

    processor = Processor([PERSON]) ner_result = processor(text)

    Помимо этого, алгоритм продемонстрировал самый высокий средний f1-score, а также справился и с распознаванием контекста для корректного определения типа сущности, и с транслитом.

    инструмент для извлечения именованных сущностей из русских текстов

    Как извлечь структурированную информацию из текста на русском языке? Знакомы ли вы с Natasha?

    Одной из самых сложных, неоднозначных проблем, которая может встретиться во время работы с данными, является извлечение именованных сущностей (Named‑entity recognition, NER) – слов, обозначающих предмет или явление определенной категории.

    На вход программному модулю подаётся текст, а на выходе получаются структурированные объекты. Например, имеется новостной текст, и необходимо выделить в нем сущности (локации, персоны, организации, даты и так далее). Решая задачу NER, мы сможем понять, что «ООН» – это организация, «8 апреля 1938 года» это дата, а «Kumasi» – локация.

    Задача NER традиционна и хорошо изучена, особенно для английского языка. Существует большое количество как коммерческих так и открытых решений, например, NLTK, Spacy, Stanford NER, OpenNLP и другие. Для русского языка тоже существует довольно много инструментов, но почти все они являются коммерческими (DaData, Pullenti, Abbyy Infoextractor, Dictum).

    Из открытых инструментов отметим Natasha ­– открытая библиотека для языка программирования Python, которая позволяет извлекать структурированную информацию из текстов на русском языке. Natasha отличается лаконичным интерфейсом и включает экстракторы для имён, адресов, сумм денег, дат и некоторых других сущностей.

    Однажды мне нужно было решить следующую задачу: был дан набор строк, в каждой строке была дата договора. Эта дата могла быть в произвольных форматах и стоять в любом месте строки. Я потратил достаточное количество времени, чтобы написать регулярное выражение, которое находит эти даты. Применив же инструмент, решающий задачу NER, время на решение задачи составило не более 2 минут, как на рисунке 1.

    На Рисунках 1 и 2 показаны примеры использования библиотеки Natasha для извлечения дат (рисунок 1) и ФИО (рисунок 2).

    Рисунок 1 – Пример с датами​

    Рисунок 2 – Пример с именами​

    Обработка естественного языка — это весело: как научить компьютер понимать тексты

    Обработка естественного языка (Natural Language Processing, или NLP) — это направление в исследовании искусственного интеллекта, задача которого — обучить машину понимать и обрабатывать язык человека. Разработчик Адам Гитги опубликовал статью, в которой описал принципы NLP и рассказал о создании программы для извлечения информации и текста на языке программирования Python.

    Читать далее

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

    С момента появления компьютеров программисты пытаются научить их понимать «человеческие» языки. Люди изобрели письменность тысячи лет назад, и было бы очень удобно, если бы компьютер мог считать и проанализировать накопленную за этот период информацию.

    Компьютеры пока что не способны воспринимать английский на уровне человека, но и уже достигнутые ими успехи действительно впечатляют. В отдельных областях технологии NLP позволяют сэкономить исследователям массу времени. Последние разработки в сфере обработки естественного языка свободно доступны в таких открытых библиотеках на Python, как spaCy, textacy и neuralcoref. С помощью нескольких строк кода можно буквально творить чудеса.

    Процесс чтения и понимания английского языка очень непрост, не говоря уже о том, что в нём нет жёстко упорядоченных и логичных норм. Например, вот эта фраза:

    «Environmental regulators grill business owner over illegal coal fires»

    В английском языке слово «grill» помимо прямого значения «жарить» имеет значение «допросить с пристрастием». Вызывает сомнение, в каком смысле — прямом или переносном — использовали его авторы заголовка. Понять, что именно сделали с бизнесменом органы охраны природы, компьютеру будет очень проблематично.

    Чтобы выполнить задачи такой сложности с использованием машинного обучения (МО), обычно строится конвейер. Задача разбивается на несколько очень небольших частей, после чего модели МО решают каждую подпроблему по отдельности. Далее модели соединяются в конвейер, по которому обмениваются информацией, что даёт возможность решать задачи очень высокой сложности. Именно так происходит обработка естественных языков.

    Вот отрывок из статьи о Лондоне из Википедии:

    «London is the capital and most populous city of England and the United Kingdom. Standing on the River Thames in the south east of the island of Great Britain, London has been a major settlement for two millennia. It was founded by the Romans, who named it Londinium.»

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

    Шаг 1: сегментация на предложения

    В первую очередь необходимо разбить текст на отдельные предложения:

    1. London is the capital and most populous city of England and the United Kingdom.
       
    2. Standing on the River Thames in the south east of the island of Great Britain, London has been a major settlement for two millennia.
       
    3. It was founded by the Romans, who named it Londinium.

    Можно допустить, что каждое предложение на английском несёт законченную мысль или идею. Гораздо проще написать программу для понимания одного предложения, чем всего абзаца сразу.

    Разработать модель сегментации на предложения несложно: их границы обычно обозначены точками. Но современные NLP-конвейеры часто используют более прогрессивные методы, которые срабатывают даже на плохо отформатированных документах.

    Шаг 2: лексический анализ

    После разделения текста на предложения, нужно по очереди проанализировать их, начиная с первого:

    «London is the capital and most populous city of England and the United Kingdom.»

    Теперь необходимо разложить это предложение на отдельные слова, или лексемы. Этот процесс называется «лексическим анализом». Таким образом, получены элементы:

    «London», «is», «the», «capital», «and», «most», «populous», «city», «of», «England», «and», «the», «United», «Kingdom», «.»

    В естественных языках провести эту операцию тоже нетрудно: слова в предложении отделяются пробелами. Знаки препинания считаются отдельными единицами, так как тоже имеют смысл.

    Шаг 3: частеречная разметка

    Далее нужно попытаться определить, к какой части речи относится каждая из лексем: существительное, глагол, прилагательное и так далее. Знание того, какую роль то или иное слово выполняет в предложении, поможет прояснить значение предложения.

    Это можно сделать, введя каждое из слов (а также несколько стоящих рядом слов, чтобы не вырывать его из контекста) в уже обученный классификатор по частям речи:

    Изначально модель тренировалась на миллионах предложений на английском языке, в которых у каждого слова уже была обозначена часть речи, — она должна была научиться по аналогии выполнять эту операцию самостоятельно.

    Такие модели опираются исключительно на статистику: они не воспринимают значение слов так, как это делают люди. Они просто угадывают части речи на основе схожих предложений и слов, которые они видели раньше.

    Результаты, полученные после анализа всего предложения:

    Эта информация позволяет понемногу восстановить смысл фраз. Очевидно, что в предложении присутствуют слова «London» и «capital» — модель может предположить, что речь идёт о Лондоне.

    Шаг 4: лемматизация текста (определение начальной формы слов)

    В английском, как и в большинстве языков, слова имеют несколько различных форм. Например:

    I had a pony.
    I had two ponies.

    В обоих предложениях употребляется одно существительное «pony», но с разными окончаниями. При обработке текста компьютеру очень полезно знать исходные формы используемых слов, чтобы понимать, что в обоих предложениях речь идёт об одном и том же предмете. Иначе строки «pony» и «ponies» он воспримет как два не связанных между собой слова.

    В NLP этот процесс определения начальной формы, или леммы, каждого слова в предложении имеет название «лемматизация».

    То же делают и с глаголами. Их также можно лемматизировать, то есть выделить исходную, неспрягаемую форму. Поэтому, предложение «I had two ponies» можно представить как «I [have] two [pony]».

    Определение лемм компьютер обычно выполняет по справочным таблицам форм слов, характерных для частей речи, и иногда — набора некоторых правил обработки незнакомых слов.

    Вот так будет выглядеть предложение после лемматизации — выделения основной формы глагола:

    Единственное изменение в том, что «is» превратилось в «be».

    Шаг 5: выявление стоповых слов (слов, которые не несут самостоятельного смысла)

    Далее нужно определить смысловую нагрузку слов в предложении. В английском языке присутствует большое число слов-заполнителей, таких как союзы и артикли («and», «the», «a»). При статистическом анализе текста эти слова создают много шума, потому что встречаются значительно чаще других слов. Некоторые NLP-конвейеры идентифицируют их как «стоповые слова», которые перед проведением анализа необходимо опустить. Вот как после этого будет выглядеть предложение:

    Эти слова обычно просто проверяют по встроенному списку уже знакомых системе стоповых слов. Но универсального списка таких лишних слов, применимого для всех случаев, не существует: они могут различаться в зависимости от ситуации применения.

    Например, при создании поискового движка рок-групп нужно, чтобы система не выбрасывала артикль «the», потому что он встречается во многих названиях. В 1980-х годах была даже группа под названием «The The!»

    Шаг 6: синтаксический анализ на основе грамматики зависимостей

    Теперь необходимо выяснить, как связаны друг с другом слова в предложении. Для этого проводят синтаксический анализ на основе грамматики (или дерева) зависимостей.

    Цель — построить дерево, в котором к каждому слову соответствует одно непроизводное слово. Корень дерева — это ключевой глагол предложения. Вот так будет выглядеть дерево синтаксического анализа вначале:

    Но можно пойти дальше: помимо подбора непроизводных слов, можно вычислить тип взаимосвязи между двумя словами в предложении:

    Из этого дерева видно, что подлежащим в предложении является слово «London», которое связано со словом «capital» через слово «be». Удалось выделить значимую информацию: Лондон — это столица. Продолжив анализ, можно выяснить, что Лондон является столицей Соединённого Королевства.

    Аналогично тому, как выше модель МО угадывала, к какой части речи относятся слова, для парсинга зависимостей в модель вводят слова и получают некий результат на выходе. Анализ связей между словами — весьма трудоёмкая задача, подробного описания которой хватит на целую отдельную статью. Начать можно со статьи «Parsing English in 500 Lines of Python» Мэтью Хоннибала.

    Хотя в 2015 году этот подход считался общепринятым, на сегодняшний день он уже устарел и больше не используется даже самим автором. В 2016 году Google выпустила новый анализатор зависимостей Parsey McParseface, превосходящий остальные инструменты благодаря новому подходу на основе глубокого обучения, который быстро набрал популярность в отрасли. Годом позже компания даже выпустила новую, ещё более совершенную модель ParseySaurus. Таким образом, технологии синтаксического анализа не стоят на месте и постоянно улучшаются.

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

    Шаг 6.1:  поиск именных групп

    Пока что каждое слово в предложении рассматривалось обособленно от остальных. Но иногда гораздо полезнее группировать слова, которые передают единую мысль или понятие. Для автоматической группировки таких слов можно использовать результаты дерева синтаксического анализа.

    Например, вместо

    можно объединить именные группы:

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

    Шаг 7: выделение именованных сущностей (named entity recognition, NER)

    После парсинга грамматики можно перейти к извлечению смысла текста. В предложении есть следующие имена существительные:

    Некоторые из них обозначают объекты реального мира. Например, «London», «England» и «United Kingdom» — это географические названия конкретных мест на карте, которые модель должна уметь опознавать. Зная это, с помощью NLP можно автоматически сгенерировать список реальных мест, упомянутых в предложении.

    Цель выделения именованных сущностей — найти эти существительные и маркировать по типу реальных объектов, с которыми они соотносятся, например, в рассматриваемом предложении — как «географические сущности». Вот так будет выглядеть предложение после того, как каждая лексема пройдёт через NER-модель:

    Однако NER-системы не просто сверяются со словарём. Они используют контекст (окружение слова в предложении) а также статистическую модель, чтобы определить тип объекта, обозначаемого существительным. Качественные NER-системы с учётом контекста способны, например, отличить актрису Бруклин Деккер от района Бруклин в Нью-Йорке.

    Системы выделения именованных сущностей могут присваивать им различные метки, например:

    • имена людей
    • названия компаний
    • географические объекты (на физических и политических картах)
    • наименования продуктов
    • дата и время
    • денежные суммы
    • названия событий

    NER-системы имеют множество применений, потому что позволяют запросто структурировать представленные в тексте данные при обработке естественных языков. Самостоятельно опробовать модели выделения именованных сущностей также можно на сайте spaCy.

    Шаг 8: разрешение кореференции

    На данном этапе уже получено много полезной информации о предложении: части речи каждого слова, взаимосвязи между словами и какие из них являются именами собственными.

    Однако в естественных языках также присутствует множество местоимений. Они помогают сократить речь, заменяя повторяющиеся существительные. Человек понимает, к какому существительному относится то или иное местоимение из контекста. Но модель обработки естественного языка этого не знает, потому что исследует по одному предложению за раз.

    Если проанализировать с помощью NLP-конвейера третье предложение в тексте:

    «It was founded by the Romans, who named it Londinium»,

    то система увидит, что «it» был основан римлянами. Но при этом ей необходимо понять, что римлянами был основан именно Лондон.

    Когда предложение читает человек, он сразу понимает, что местоимение заменяет город. Цель разрешения кореференции — соотнести местоимения в тексте с существительными, на которые они указывают, и найти все местоимения, которые указывают на один и тот же предмет.

    Вот результат этой операции для слова «Лондон» в рассмотренном тексте:

    Если полученную информацию обобщить с деревом синтаксического анализа и данными об именованных сущностях, это значительно приблизит модель к пониманию смысла всего текста.

    Разрешение кореференции — один из наисложнейших этапов всего процесса обработки естественного языка, даже сложнее, чем синтаксический анализ. Новейшие подходы в сфере глубокого обучения обеспечивают более высокую точность, но и они всё же имеют свои недостатки. Подробнее об этом можно прочитать здесь.

    А более детально разобраться с разрешением кореференции можно на сайте Hugging Face.

    Вот так выглядит алгоритм обработки естественного языка:

    входные данные: исходный текст

    1. сегментация на предложения
       
    2. лексический анализ
       
    3. частеречная разметка
       
    4. лемматизация текста
       
    5. выявление стоповых слов
       
    6. синтаксический анализ на основе грамматики зависимостей
       
    7. поиск именных групп
       
    8. выделение именованных сущностей
       
    9. разрешение кореференции

    выходные данные: структуры данных проанализированного текста

    Это базовая модель NLP-конвейера, но в зависимости от целей и способа применения NLP-библиотеки те или иные шаги можно пропускать или менять местами. Например, некоторые библиотеки вроде spaCy проводят сегментацию на предложения на более позднем этапе и используют результаты парсинга зависимостей.

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

    Для начала нужно установить Python 3, после чего установить spaCy с помощью следующего кода:

    # Install spaCy 
 pip3 install -U spacy
 
 # Download the large English model for spaCy
 python3 -m spacy download en_core_web_lg
 
 # Install textacy which will also be useful
 pip3 install -U textacy
 

    Далее следует код для запуска NLP-конвейера на фрагменте текста:

    import spacy
 
 # Load the large English NLP model
 nlp = spacy.load('en_core_web_lg')
 
 # The text we want to examine
 text = """London is the capital and most populous city of England and 
 the United Kingdom. Standing on the River Thames in the south east 
 of the island of Great Britain, London has been a major settlement 
 for two millennia. It was founded by the Romans, who named it Londinium.
 """
 
 # Parse the text with spaCy. This runs the entire pipeline.
 doc = nlp(text)
 
 # 'doc' now contains a parsed version of text. We can use it to do anything we want!
 # For example, this will print out all the named entities that were detected:
 for entity in doc.ents:
 print(f"{entity.text} ({entity.label_})")
 

     

    При его выполнении отобразится список именованных сущностей, найденных в документе, и их типов:

    Полный список сокращений с расшифровкой можно найти здесь.

    Нужно отметить, что здесь модель допустила ошибку: «Лондиний» она интерпретировала как имя человека, а не географическое название. Это могло произойти потому, что в тренировочном наборе данных не было похожих слов, и модель просто выбрала наиболее вероятную догадку. Для выделения именованных сущностей иногда требуется немного преобразовать модель, если в тексте встречается редкая или специальная лексика.

    Принцип выявления сущностей можно применить в создании инструмента для очистки данных. Многие компании хранят у себя тысячи документов с информацией, по которой можно установить личности их владельцев. Чтобы соответствовать требованиям нового регламента о защите данных GDPR, компаниям придётся удалить все имена из файлов.

    Чтобы вручную просмотреть тысячи документов и скрыть все имена, могут уйти годы: намного рациональнее использовать NLP. Вот простой код, который удалит все найденные в документах имена людей:

    import spacy
 
 # Load the large English NLP model
 nlp = spacy.load('en_core_web_lg')
 
 # Replace a token with "REDACTED" if it is a name
 def replace_name_with_placeholder(token):
 if token.ent_iob != 0 and token.ent_type_ == "PERSON":
 return "[REDACTED] "
 else:
 return token.string
 
 # Loop through all the entities in a document and check if they are names
 def scrub(text):
 doc = nlp(text)
 for ent in doc.ents:
 ent.merge()
 tokens = map(replace_name_with_placeholder, doc)
 return "".join(tokens)
 
 s = """
 In 1950, Alan Turing published his famous article "Computing Machinery and Intelligence". In 1957, Noam Chomsky’s 
 Syntactic Structures revolutionized Linguistics with 'universal grammar', a rule based system of syntactic structures.
 """
 
 print(scrub(s))
 

    Вот как будет выглядеть обработанный текст:

    Базовый функционал spaCy позволяет делать удивительные вещи. Но проанализированные выходные данные можно далее использовать в более продвинутых алгоритмах извлечения данных. Несколько таких общих алгоритмов помимо spaCy содержит достойная Python-библиотека textacy.

    В ней есть алгоритм извлечения полуструктурированных высказываний, который можно применять для поиска по дереву синтаксического анализа простых высказываний с подлежащим «London» и глаголом «be» в одной из форм. Это поможет найти факты о Лондоне.

    Пример кода:

    import spacy
 import textacy.extract
 
 # Load the large English NLP model
 nlp = spacy.load('en_core_web_lg')
 
 # The text we want to examine
 text = """London is the capital and most populous city of England and the United Kingdom. 
 Standing on the River Thames in the south east of the island of Great Britain, 
 London has been a major settlement for two millennia. It was founded by the Romans, 
 who named it Londinium.
 """
 
 # Parse the document with spaCy
 doc = nlp(text)
 
 # Extract semi-structured statements
 statements = textacy.extract.semistructured_statements(doc, "London")
 
 # Print the results
 print("Here are the things I know about London:")
 
 for statement in statements:
 subject, verb, fact = statement
 print(f" - {fact}")

     

    На выходе алгоритм сообщит, что Лондон является столицей и самым населённым городом Англии и Соединённого Королевства, а также что его возраст — две тысячи лет:

    На первый взгляд, в этом нет ничего особенного. Но если через этот код пропустить статью о Лондоне из Википедии полностью, а не только три предложения, можно получить гораздо более исчерпывающие результаты:

    Весь этот объём информации извлекается из статьи автоматически.

    Помимо этого, можно загрузить библиотеку neuralcoref и добавить в конвейер разрешение кореференции. Это позволит получить ещё больше фактов за счёт тех предложений, где вместо прямого упоминания Лондона используются местоимения.

    В документации spaCy и textacy есть большое число примеров использования парсированного текста: в этой статье рассмотрен лишь малая доля возможностей библиотек. На их основе можно разработать сайт, который будет содержать информацию о любом городе мира, полученную описанным в последнем примере способом. Если бы в этом сайте был поиск, в него можно было бы добавить функцию автодополнения запросов, как у Google:

    Для этого понадобится список возможных дополнений, которые будут предлагаться пользователю. Чтобы быстро сгенерировать эти данные, можно использовать NLP.

    Вот один из способов извлечь самые частотные отрывки фраз из текста о Лондоне:

    import spacy
 import textacy.extract
 
 # Load the large English NLP model
 nlp = spacy.load('en_core_web_lg')
 
 # The text we want to examine
 text = """London is [.. shortened for space ..]"""
 
 # Parse the document with spaCy
 doc = nlp(text)
 
 # Extract noun chunks that appear
 noun_chunks = textacy.extract.noun_chunks(doc, min_freq=3)
 
 # Convert noun chunks to lowercase strings
 noun_chunks = map(str, noun_chunks)
 noun_chunks = map(str.lower, noun_chunks)
 
 # Print out any nouns that are at least 2 words long
 for noun_chunk in set(noun_chunks):
 if len(noun_chunk.split(" ")) > 1:
 print(noun_chunk)

     

    Если запустить этот код на статье о Лондоне из Википедии, результат будет таким:

    Это далеко не всё, для чего можно использовать NLP. Но прежде чем углубляться в более сложные системы, стоит изучить spaCy или другие библиотеки на других языках, потому что все они имеют схожий принцип работы.

    Nermanual/1 — OpenCorpora Вики

    Общая схема

    Перед Вами текст, разделённый на абзацы. В каждом абзаце необходимо выделить все именованные сущности и указать их тип, после чего нажать кнопку «Сохранить». Обратите внимание, что после нажатия кнопки режим редактирования будет не доступен. Вы можете разметить весь текст целиком или остановиться после любого абзаца.

    Этап 1. Поиск именованной сущности

    Что считать именованной сущностью?

    Именованной сущностью (ИС) считается слово или словосочетание, предназначенное для конкретного, вполне определённого предмета или явления, выделяющее этот предмет или явление из ряда однотипных предметов или явлений.

    Именованная сущность:

    1. Обычно пишется с заглавной буквы

    2. Обязательно имеет референт, то есть того (тех), кому это имя принадлежит

    Например,

    На экраны вышел новый фильм братьев Коэнов. 

    “Коэны” имеют референт (конкретные два Коэна, являющиеся братьями друг другу), а значит перед нами именованная сущность.


    В предложении

    В Воткинске новорождённых девочек родители очень редко именуют Татьянами. 

    cлово «Татьянами» — именованной сущностью являться не будет, так как не имеет референта.

    Этап 2. Определение границ именованной сущности

    Именованной сущностью будет считаться самая длинная цепочка поcледовательных слов, отражающих имя.


    Включение дескриптора

    Дескриптором считается слово или словосочетание, указывающее на родовое понятие именованной сущности. Например, озеро Байкал - 'озеро', Комитет по безопасности - 'комитет'.

    Дескриптор будет частью ИС:

    0. Если является аббревиатурой

    * ОАО “Газпром” * ЗАО “Ай-теко” 

    1. Если дескриптор является вершиной именной группы, но при этом имя не является приложением.

    * Санкт-Петербургский государственный университет * Комитет по безопасности 

    Но:

    * город Тула * патриотическая организация"Мау" 


    Знаки препинания

    Знаки препинания включаются только в том случае, если являются частью имени, а не используется в качестве разделителя. Кавычки входят в название, только в том случае, если в ИС включен дескриптор.

    Например,

    * фильм “Гардемарины, вперёд” * королева Греции Анна-Мария * ОАО "Газпром" 

    Но:

    * Алексей Навальный (@navalny) * Гатчина, Ленинградская область 

    Однородные члены

    Если именованная сущность или дескриптор относится к однородным членам, то ИС выделяется отдельно, а дескриптор не выделяется вообще.

    * Итан и Джоэл Коэны * на станциях метро “Лубянка” и “Парк культуры” * пересечение Невского и Литейного проспектов 

    Этап 3. Определение типа именованной сущности

    На данный момент мы размечаем следующие типы именованных сущностей: Персона, Местоположение, Организация, Название произведения, Событие, Остальное.

    Персона [person]

    Относится к обозначению живых существ.

    Может быть представлена следующими категориями:

    * ФИО во всех вариациях Иван Михайлович, Лопе де Вега * Клички животных Мурзик, Шарик * Имена и обозначения богов Зевс, Аллах, Бог * Имена вымышленных персонажей Амидала, Гарри Поттер, Ктулху * Username/прозвище/псевдоним @navalny * Имя с прозвищем Эразм Роттердамский, Пётр Великий * и т.п. 

    Все дополнения: фамильные приставки, признаки старшинства, родства (в случае написания через тире) и т.п. считаются частью именованной сущности

    * Хуана де ла Морена * Александр Дюма-старший * Муми-мама 

    Прилагательные/причастия, не являющиеся прозвищем или частью имени, не включаются в ИС.

    * Святой Франциск * Солнцеподобный Ким Чен Ын 

    Кроме того, мы не размечаем имена в составе ИС типа loc и org (см. ниже). Это же касается конструкций типа им. Кирова и имени Кирова, даже если им. / имени опущено. Однако имена будут размечаться в названиях, похожих на org/loc, но являющихся типовыми обозначениями сущностей (родовыми понятиями?):

    * детский сад Монтессори 
    Местоположение [loc]

    Относится к обозначениям объектов, указывающих на положение в пространстве

    Может быть представлена следующими категориями:

    * природные объекты Кавказские горы, Баренцево море * территориальные объекты Ростов-на-Дону, Липецкая область, Евразия, парк им. Горького * конструкции Троицкий мост, Биг Бен * астрономические объекты астероид Веста, комета Галлея * вымышленные места Нарния, Атлантида * и т.п. 
    Организация [org]

    Если сущность может быть местом работы человека или в ней можно состоять в качестве члена и цепочка, включает какие-то слова, кроме указания на родовое понятие, то это сущность типа org.

    Имя организации может быть выражено несколькими способами:

    * личное имя компания Apple 

    [с дескриптором как частью имени]

    * заимствованное имя библиотека им. Маяковского * номер школа №13 * название по месту парламент Англии * название по цели/деятельности Центр управления полётами * название по участникам Клуб поклонников Формулы 1 * и т.п. 


    В случае вложенных названий организаций мы размечаем самую длинную цепочку:

    * Комитет по архитектуре и градостроительству города Москвы 

    Важно помнить, что все названия организаций, даже употребленные в значении местоположения, размечаются как org и только как org:

    * Мы встретились в Комитете по культуре. 
    Названия произведений [title]

    Относится к обозначениям результатов деятельности человека

    Может быть представлена следующими категориями:

    * произведения искусства Вино из одуванчиков, Матрица, Мона Лиза, опера Дон Жуан,альбом Nevermind * работа СМИ Ведомости, Аргументы и факты, Вокруг Света * научные тексты диссертация "Влияние поэзии Блока на умы современности" * разработки Ubuntu 14.04 * и пр. 

    В случае, если слова с основным значением "название организации" употребляются в значении "результат деятельности этой организации", им ставится тег title:

    * искать Яндексом 
    События [event]

    Относится к обозначениям общественных акций, событий, праздников, явлений

    * Хэллоуин * Пасха * Великая Отечественная война * вечерний спектакль Зелёный шатёр * флешмоб Замирание 
    Остальное [misc]

    Все то, что является именованной сущностью, но не подходит под описания выше:

    * слоганы Just do it * уникальные природные явления ураган Катрина * эпохи, временные этапы Мезозойская эра * и пр. 
    Фразеологизм [phrase]

    Тип-помета, добавляется к одному из типов выше, если ИС является частью фразеологизма.

    * Бог его знает * болен как Лазарь * язык до Киева доведет 
    Cущности, которые не размечаются как ИС:
    * URL’ы (если не являются названием организации) * должности (папа Римский, Далай-лама, президент РФ) * денежные и временные сущности (даты, количества и пр.) 

    Типичные случаи омонимии

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

    loc/title

    Обычно встречается у произведений архитектуры, ландшафта

    Триумфальная арка выполнена в античном стиле. - [title]

    Мы встретились у Триумфальной арки. - [loc/title]

    org/title

    Обычно встречается у периодических изданий

    Отдел редакторов газеты “Ведомости” устроил протест. - [org]
    Ежедневно тысячи людей читают газету “Ведомости”. - [title]

    Газета “Ведомости” сообщает о сложной политической ситуации. - [org/title]

    Как, читая новости, приносить пользу науке? / Хабр

    В предыдущий раз мы написали на Хабре о краудсорсинге лингвистических данных. Речь шла о морфологической разметке (part of speech tagging) современных текстов на русском языке. С тех пор было размечено около 2.2 млн. заданий, и около 3 тысяч человек приняли в этом участие. Мы с вами прошли чуть больше половины пути. Спасибо, что вы помогаете нам!

    В OpenCorpora мы создаём открытые данные для обучения и тестирования математических моделей анализа текста на русском языке. Таким образом, мы помогаем российской компьютерной лингвистике догнать западную. Потом будем помогать обгонять ;)

    Сегодня мы расскажем о разметке именованных сущностей. Это ещё один слой разметки текстов в Открытом корпусе. Мы будем выделять в тексте имена людей, названия компаний и географических объектов.


    Зачем мы это делаем?

    Морфологическую разметку мы начали и продолжаем по собственной инициативе. Работу над разметкой сущностей мы ведём совместно с оргкомитетом соревнования factRuEval-2016, которое пройдёт в рамках конференции по компьютерной лингвистике Диалог-21. На данном этапе сущности размечаются не во всём корпусе, а только в небольшом его подмножестве, которое станет обучающей и тестовой коллекциями для участников соревнования. В сумме это около 1000 новостных текстов объёмом по 3-4 абзаца. Как обычно, результат разметки будет опубликован на условиях лицензии Creative Commons. Обучающая часть коллекции будет публиковаться по мере её модерации, а разметка тестовой части — не ранее завершения соревнования и подведения его итогов.

    Что такое разметка именованных сущностей?

    Извлечение именованных сущностей из текста — одна из востребованных функций текстовой аналитики (см. об этом подробно, например, в блоге компании Textocat).

    Было бы классно, если бы существовало несколько десятков конкурирующих между собой решений, позволяющих перечислить все упомянутые в тексте объекты, дать их нормализованные названия и соответствующие им идентификаторы объектов. И всё это для русского языка и с открытым исходным кодом. Принимая участие в организации соревнования factRuEval и в подготовке данных для него, мы делаем шаг в эту сторону и приглашаем вас присоединиться.

    Если коротко, то выделение именованных сущностей состоит в том, чтобы найти в текстах имена собственные (ФИО персон, названия организаций и географических объектов), выделить их и пометить соответствующим тегом. Например, для персон нужно отдельно отметить фамилию, имя и отчество, после чего объединить выделенные отрезки в одно упоминание объекта с типом Person. Об этом мы написали подробную инструкцию и записали маленькое видео.

    Что будет дальше?

    Разметка сущностей уже идёт. Следующими этапами разметки коллекции текстов для factRuEval будет идентификация упоминаний объектов между собой, связывание их с WikiData и разметка фактов. Первые два пункта подразумевают, что несколько отдельных упоминаний в тексте одного и того же объекта реального мира (например, Иванов Иван, Иванов и Иванов И.И.) будут объединены друг с другом в одну сущность. Для этой сущности будет указываться идентификатор из WikiData.

    Под фактами имеются в виду описанные в тексте отношения между уже выделенными на предыдущих этапах объектами: отношение Occupation (работать в компании) между персоной и организацией, отношение Ownership (владеть) между персоной и организацией и другие подобные отношения.

    Как нам помочь?

    1. примите участие в разметке.
    Теперь у нас есть два направления работы: именованные сущности и морфология. Для выполнения заданий в обоих направлениях достаточно прочитать инструкции.

    2. напишите об этой работе в социальных сетях и попросите ваших друзей помочь нам.
    Не все читают GeekTimes, но очень многие готовы помогать по чуть-чуть.

    Update: Прямая ссылка на разметку сущностей: http://opencorpora.org/ner.php (она есть в инструкции, пусть будет и тут тоже).

    Распознавание и классификация именованных сущностей

    с помощью Scikit-Learn | by Susan Li

    Как обучать модели машинного обучения для NER с использованием библиотек Scikit-Learn выражения времени, даты, денег и процентов из неструктурированного текста. Цель состоит в том, чтобы разработать практические и независимые от предметной области методы для автоматического обнаружения именованных объектов с высокой точностью.

    На прошлой неделе мы представили введение в распознавание именованных сущностей (NER) в NLTK и SpaCy. Сегодня мы сделаем еще один шаг - обучим модели машинного обучения для NER с использованием некоторых библиотек Scikit-Learn. Давайте начнем!

    Данные представляют собой специально разработанный корпус, аннотированный тегами IOB и POS, которые можно найти на Kaggle. Мы можем быстро просмотреть несколько первых строк данных.

    Рисунок 1

    Существенная информация о сущностях :

    • geo = Географическая сущность
    • org = Организация
    • на человека
    • gpe = Геополитическая сущность
    • tim = Индикатор времени
    • art = Артефакт
    • eve = Событие
    • nat = естественный феномен

    Внутри – снаружи – начало (тегирование)

    IOB (сокращение от «внутри, снаружи, начало») является распространенным форматом тегов для маркировки токенов.

    • Префикс I- перед тегом указывает, что тег находится внутри блока.
    • Префикс B- перед тегом указывает, что этот тег является началом блока.
    • Тег O указывает, что токен не принадлежит ни одному фрагменту (внешнему).
     import pandas as pd 
    import numpy as np
    from sklearn.feature_extraction import DictVectorizer
    from sklearn.feature_extraction.text import HashingVectorizer
    из sklearn.linear_model импортировать Perceptron
    из sklearn.model_selection import train_test_split
    из sklearn.linear_model import SGDClassifier
    из sklearn.linear_model import PassiveAggressiveClassifier
    из sklearn.naive_bayes import MultinomialNB
    из sklearn.metrics import classes_report

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

     df = pd.read_csv ('ner_dataset.csv ', encoding = "ISO-8859-1") 
    df = df [: 100000]
    df.head ()
    Рисунок 2
     df.isnull (). sum () 
    Рисунок 3

    Мы замечаем, что существует много Значения NaN в столбце «Предложение #», и мы заполняем NaN предыдущими значениями.

     df = df.fillna (method = 'ffill') df ['Sentence #']. Nunique (), df.Word.nunique (), df.Tag.nunique () 

    (4544, 10922, 17)

    У нас есть 4544 предложения, которые содержат 10922 уникальных слова и помечены 17 тегами.

    Теги распределены неравномерно.

     df.groupby ('Tag'). Size (). Reset_index (name = 'counts') 
    Рисунок 4

    Следующий код преобразует текстовую дату в вектор с помощью DictVectorizer , а затем разбивает для обучения и тестовые наборы.

     X = df.drop ('Tag', axis = 1) 
    v = DictVectorizer (sparse = False)
    X = v.fit_transform (X.to_dict ('records'))
    y = df.Tag.valuesclasses = np.unique (y)
    classes = classes.tolist () X_train, X_test, y_train, y_test = train_test_split (X, y, test_size = 0.33, random_state = 0)
    X_train.shape, y_train.shape

    ((67000, 15507), (67000,))

    Мы попробуем некоторые из алгоритмов вне ядра, которые предназначены для данные процесса, которые слишком велики для размещения в памяти одного компьютера, поддерживающего метод partial_fit .

    Perceptron

     per = Perceptron (verbose = 10, n_jobs = -1, max_iter = 5) 
    per.partial_fit (X_train, y_train, classes)
    Рисунок 5

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

     new_classes = classes.copy () 
    new_classes.pop ()
    new_classes
    Рис. 6
     print (classification_report (y_pred = per.predict (X_test), y_true = y_test, labels = new_classes)) 
    Рис. с обучением SGD

     sgd = SGDClassifier () 
    sgd.partial_fit (X_train, y_train, classes)
    Рисунок 8
     print (classification_report (y_pred = sgd.predict (X_test), y_true = y_test, labels = 
    Figure_classes)) 9

    Наивный байесовский классификатор для полиномиальных моделей

     nb = MultinomialNB (alpha = 0.01) 
    nb.partial_fit (X_train, y_train, classes)
    Рисунок 10
     print (classification_report (y_pred = nb.predict (X_test), y_true = y_test, labels = new_classes)) 
    Рисунок 11

    Passive Aggressive Classifier

    pa = PassiveAggressiveClassifier ()
    pa.partial_fit (X_train, y_train, classes) Рисунок 12
     print (classification_report (y_pred = pa.predict (X_test), y_true = y_test, labels = new_classes)) 
    Рисунок 13

    Ничего из вышеперечисленного классификаторы дали удовлетворительные результаты.Очевидно, что классифицировать именованные сущности с помощью обычных классификаторов будет непросто.

    CRF часто используются для маркировки или анализа последовательных данных, таких как обработка естественного языка, и CRF находят приложения в тегах POS, распознавании именованных объектов и других.

    sklearn-crfsuite

    Мы обучим модель CRF распознаванию именованных сущностей с помощью sklearn-crfsuite на нашем наборе данных.

     import sklearn_crfsuite 
    from sklearn_crfsuite import scorers
    from sklearn_crfsuite import metrics
    from collection import Counter

    Следующий код предназначен для получения предложений с их POS и тегами.Спасибо Тобиасу за подсказку.

     класс SentenceGetter (объект): 

    def __init __ (self, data):
    self.n_sent = 1
    self.data = data
    self.empty = False
    agg_func = lambda s: [(w, p, t) для w, p, t в zip (s ['Word']. values.tolist (),
    s ['POS']. values.tolist (),
    s ['Tag']. values.tolist ())]
    self.grouped = self.data.groupby ('Sentence #'). Apply (agg_func)
    self.sentences = [s for s in self.grouped]

    def get_next (self):
    try:
    s = self .grouped ['Sentence: {}'. format (self.n_sent)]
    self.n_sent + = 1
    return s
    за исключением:
    return None

    getter = SentenceGetter (df)
    предложения = getter.sentences

    Извлечение функций

    Затем мы извлекаем больше функций (части слова, упрощенные теги POS, нижние / заголовочные / верхние флаги, особенности соседних слов) и конвертируем их в формат sklearn-crfsuite - каждое предложение должно быть преобразовано в список диктов. Следующий код был взят с официального сайта sklearn-crfsuites.

     def word2features (отправлено, i): 
    word = отправлено [i] [0]
    postag = отправлено [i] [1]

    features = {
    'bias': 1.0,
    'word.lower ()': word.lower (),
    'word [-3:]': word [-3:],
    'word [-2:]': word [-2:],
    'word.isupper ()': word .isupper (),
    'word.istitle ()': word.istitle (),
    'word.isdigit ()': word.isdigit (),
    'postag': postag,
    'postag [: 2]' : postag [: 2],
    }
    , если i> 0:
    word1 = отправлено [i-1] [0]
    postag1 = отправлено [i-1] [1]
    функции.update ({
    '-1: word.lower ()': word1.lower (),
    '-1: word.istitle ()': word1.istitle (),
    '-1: word.isupper ()' : word1.isupper (),
    '-1: postag': postag1,
    '-1: postag [: 2]': postag1 [: 2],
    })
    else:
    features ['BOS'] = True
    , если i word1 = отправлено [i + 1] [0]
    postag1 = отправлено [i + 1] [1]
    features.update ({
    '+1: word.lower ( ) ': word1.lower (),
    ' +1: word.istitle () ': word1.istitle (),
    ' +1: word.isupper () ': word1.isupper (),
    ' +1: postag ': postag1,
    ' +1: postag [: 2] ': postag1 [: 2],
    })
    else:
    features [' EOS '] = True

    return featuresdef sent2features (отправлено):
    return [word2features (отправлено, i) для i в диапазоне (len (отправлено))] def sent2labels (отправлено):
    return [метка для токена, postag, метка в отправленных] def sent2tokens (отправлено):
    return [токен для токена, postag, метка в отправленных]

    Разделить поезд и тестовые наборы

     X = [sent2features для s в предложениях] 
    y = [sent2labels (s) для s в предложениях]
    X_train, X_test, y_train, y_test = train_test_split (X, y, test_size = 0.33, random_state = 0)

    Обучить модель CRF

     crf = sklearn_crfsuite.CRF (
    algorithm = 'lbfgs',
    c1 = 0.1,
    c2 = 0.1,
    max_iterations = 100,
    all_possible_transitions = True
    )
    crf.fit (X_train, y_train)
    Рисунок 14

    Evaluation

     y_pred = crf.predict (X_test) 
    print (metrics.flat_classification_report (y_test, y_pred, labels = new_classes))
    Рисунок 15

    Way better! Мы будем придерживаться sklearn-crfsuite и изучать больше!

    Чему научился наш классификатор?

     def print_transitions (trans_features): 
    для (label_from, label_to), вес в trans_features:
    print ("% - 6s ->% -7s% 0.6f "% (label_from, label_to, weight)) print (" Наиболее вероятные переходы: ")
    print_transitions (Counter (crf.transition_features _). Most_common (20)) print (" \ nВерх маловероятных переходов: ")
    print_transitions (Counter ( crf.transition_features _). most_common () [- 20:])
    Рисунок 16

    Интерпретация : Весьма вероятно, что за началом географического объекта (B-geo) будет следовать токен внутри географического объекта (I- geo), но переходы внутри имени организации (I-org) из токенов с другими метками серьезно наказываются.

    Проверить состояние функций

     def print_state_features (state_features): 
    для (attr, label), вес в state_features:
    print ("% 0.6f% -8s% s"% (weight, label, attr)) print ("Верхнее положительное:")
    print_state_features (Counter (crf.state_features _). most_common (30)) print ("\ nTop negative:")
    print_state_features (Counter (crf.state_features _). most_common () [- 30:] )
    Рисунок 17

    Наблюдения :

    1). 5.183603 B-tim word [-3]: day Модель узнает, что если соседним словом было «день», то токен, скорее всего, является частью индикатора времени.

    2). 3,370614 B-per word.lower (): президент Модель узнает, что токен «президент», скорее всего, стоит в начале имени человека.

    3). -3.521244 O postag: NNP Модель узнает, что имена собственные часто являются сущностями.

    4). -3.087828 O word.isdigit () Цифры, скорее всего, являются объектами.

    5). -3.233526 O word.istitle () Заголовок Слова в регистре, вероятно, являются объектами.

    ELI5

    ELI5 - это пакет Python, который позволяет проверять веса моделей sklearn_crfsuite.CRF.

    Проверить вес модели

     import eli5 
    eli5.show_weights (crf, top = 10)
    .Распознавание именованных объектов

    с помощью NLTK и SpaCy | Сьюзан Ли

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

     из nltk.chunk import conlltags2tree, tree2conlltags 
    из pprint import pprintiob_tagged = tree2conlltags (cs)
    pprint (iob_tagged)
    Рис. его именованный тег объекта. Основываясь на этом обучающем корпусе, мы можем построить теггер, который можно использовать для маркировки новых предложений; и используйте nltk.chunk.conlltags2tree () для преобразования последовательностей тегов в дерево фрагментов.

    С помощью функции nltk.ne_chunk () мы можем распознавать именованные сущности с помощью классификатора, классификатор добавляет метки категорий, такие как PERSON, ORGANIZATION и GPE.

     ne_tree = ne_chunk (pos_tag (word_tokenize (ex))) 
    print (ne_tree)
    Рисунок 5

    Google распознается как личность. Это довольно досадно, не правда ли?

    Распознавание именованных сущностей SpaCy было обучено на корпусе OntoNotes 5 и поддерживает следующие типы сущностей:

    Рисунок 6 (Источник: SpaCy)

    Entity

     import spacy 
    from spacy import displacy
    from collection import Counter
    import en_core_web_sm
    nlp = en_core_web_sm.load ()

    Мы используем то же предложение: «Европейские власти оштрафовали Google на рекордную сумму в 5,1 миллиарда долларов в среду за злоупотребление своей властью на рынке мобильных телефонов и приказали компании изменить свою практику».

    Одна из приятных особенностей Spacy заключается в том, что нам нужно применить nlp только один раз, весь фоновый конвейер вернет объекты.

     doc = nlp («Европейские власти оштрафовали Google на рекордную сумму в 5,1 миллиарда долларов в среду за злоупотребление своей властью на рынке мобильных телефонов и приказали компании изменить свою практику») 
    pprint ([(X.text, X.label_) для X в doc.ents])
    Рисунок 7

    Европейский - это NORD (национальности, религиозные или политические группы), Google - это организация, 5,1 миллиарда долларов - денежное выражение, а среда - объект даты. Все они правильные.

    Токен

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

    Рисунок 8 (Источник: SpaCy)
     pprint ([(X, X.ent_iob_, X.ent_type_) для X в doc]) 
    Рисунок 9

    «B» означает, что токен начинает объект, «I» означает, что он находится внутри объекта, «O» означает, что он находится вне entity, а "" означает, что тег объекта не установлен.

    Извлечение именованных сущностей из статьи

    Теперь давайте серьезно займемся SpaCy и извлечем именованные сущности из статьи в New York Times, - «ФБР. Агент Питер Стрзок, критиковавший Трампа в текстах, уволен ».

     из bs4 import BeautifulSoup 
    import requests
    import redef url_to_string (url):
    res = requests.get (url)
    html = res.text
    soup = BeautifulSoup (html, 'html5lib')
    для сценария в супе (["script", "style", 'aside']):
    script.extract ()
    return "" .join (re.split (r '[\ n \ t] +', soup.get_text ())) ny_bb = url_to_string ('https://www.nytimes.com/2018/08/13/us/ policy / peter-strzok-fired-fbi.html? hp & action = click & pgtype = Homepage & clickSource = story-heading & module = first-column-region & region = top-news & WT.nav = top-news ')
    article = nlp (ny_bb)
    len (article .ents)

    188

    В статье 188 объектов, и они представлены в виде 10 уникальных ярлыков:

     label = [x.label_ for x in article.ents] 
    Счетчик (ярлыки)
    Рисунок 10

    Следующие три жетона наиболее часто используются.

     items = [x.text для x в article.ents] 
    Counter (items) .most_common (3)
    Рисунок 11

    Давайте случайным образом выберем одно предложение, чтобы узнать больше.

     предложения = [x вместо x в статье. Представляет] 
    печать (предложения [20])
    .

    Распознавание именованных объектов с использованием Keras Bi-LSTM

    Классифицируйте, понимая контекст предложений с помощью двунаправленного LSTM, без удаления стоп-слов.

    Пример предложения с использованием сущности spaCy, которая выделяет сущности в предложении.

    Распознавание именованных сущностей ( NER ) (также известное как идентификация сущностей , фрагментов и извлечение сущностей ) - это подзадача извлечения информации, которая направлена ​​на поиск и классификацию именованных сущностей, упомянутых в неструктурированном тексте на заранее определенные категории, такие как имена людей, организации, местоположения, медицинские коды, выражения времени, количества, денежные значения, проценты и т. д.

    В этом проекте мы будем работать с набором данных NER, предоставленным Kaggle. Доступ к набору данных можно получить здесь. Этот набор данных представляет собой выдержку из корпуса GMB, который помечен, аннотирован и создан специально для обучения классификатора предсказанию именованных сущностей, таких как имя, местоположение и т. Д. Набор данных также включает одну дополнительную функцию (POS), которая может использоваться при классификации . Однако в этом проекте мы работаем только с одним функциональным предложением.

    Начнем с загрузки и визуализации набора данных.Чтобы скачать ner_dataset.csv, перейдите по этой ссылке в Kaggle.

    Нам нужно будет использовать encoding = ‘unicode_escape’ при загрузке данных. Эта функция принимает параметр для переключения добавления заключительных кавычек и экранирования этой кавычки в строке.

     импортировать панд как pd 
    data = pd.read_csv ('ner_dataset.csv', encoding = 'unicode_escape')
    data.head ()

    Обратите внимание, что предложения в наборе данных размечены в столбце «Word». В столбце «предложение #» один раз отображается номер предложения, а затем выводится NaN до тех пор, пока не начнется следующее предложение.Столбец «Тег» будет нашим ярлыком (y).

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

    • { token } до { token id }: адрес строки в матрице встраивания для текущего токена.
    • { tag } - { tag id }: векторы истинного распределения вероятностей для вычисления потерь на выходе сети.

    Этот шаг желателен в любой модели машинного обучения, включая нейронную сеть, которая требует ввода целых чисел.

     data ['Word_idx'] = data ['Word']. Map (token2idx) 
    data ['Tag_idx'] = data ['Tag']. Map (tag2idx)
    data.head ()

    Мы видим что функция добавила два новых столбца индекса для наших переменных X (Word_idx) и y (Tag_idx). Затем давайте соберем токены в массивы в соответствующей последовательности, чтобы использовать рекуррентную нейронную сеть наилучшим образом.

    Чтобы преобразовать столбцы в последовательные массивы, мы будем

    • Заполнить NaN в столбце «предложение №», используя метод ffill in fillna.
    • После этого запустите groupby в столбце предложения, чтобы получить список токенов и тегов.
     # Заполнить на 
    data_fillna = data.fillna (method = 'ffill', axis = 0) # Группировать и собирать столбцы
    data_group = data_fillna.groupby (
    ['Sentence #'], as_index = False
    ) [' Word ',' POS ',' Tag ',' Word_idx ',' Tag_idx ']. Agg (lambda x: list (x)) # Визуализировать данные
    data_group.head ()
    Преобразованные данные с последовательностью токенов и тегов.

    Padding : Уровни LSTM принимают последовательности только одинаковой длины.Следовательно, каждое предложение, представленное в виде целых чисел («Word_idx»), должно быть дополнено, чтобы иметь одинаковую длину. Мы будем работать с максимальной длиной самой длинной последовательности и дополнять более короткие последовательности, чтобы добиться этого.

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

    Мы также будем преобразовывать переменную y как вектор с горячим кодированием, используя функцию to_categorical в Keras.Давайте импортируем необходимые пакеты.

     из sklearn.model_selection import train_test_split 
    из keras.preprocessing.sequence import pad_sequences
    из keras.utils import to_categorical

    Модели нейронных сетей работают с графической структурой. Поэтому сначала нам нужно спроектировать архитектуру и установить размеры ввода и вывода для каждого слоя. RNN могут обрабатывать различные комбинации ввода и вывода. Для этого мы будем использовать многие и многие архитектуры.Обратитесь к последней архитектуре на изображении ниже. Наша задача - выводить тег (y) для потребляемого токена (X) на каждом временном шаге.

    RNN способны обрабатывать различные комбинации входов и выходов.

    Начнем с загрузки необходимых пакетов.

     import numpy as np 
    import tensorflow
    from tensorflow.keras import Sequential, Model, Input
    from tensorflow.keras.layers import LSTM, Embedding, Dense, TimeDistributed, Dropout, Bidirectional
    from tensorflow.keras.utils import plot_model

    Лучше всего установить начальное число для воспроизводимости.

     из numpy.random import seed 
    seed (1)
    tensorflow.random.set_seed (2)

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

    В этой архитектуре мы в основном работаем с тремя слоями (встраивание, bi-lstm, lstm) и четвертым слоем, который является слоем TimeDistributed Dense, для вывода результата.Мы подробно обсудим слои в следующих разделах.

    • Уровень 1 - Уровень внедрения: Мы укажем максимальную длину (104) дополненных последовательностей. После обучения сети слой внедрения преобразует каждый токен в вектор n измерений. Мы выбрали n измерений равными (64).

    Размеры (?, 104, 64), которые мы видим в слое встраивания данного графика нейронной сети, взяты из этих спецификаций. Обратите внимание, что первое измерение показывает «?» Или «Нет» на графиках.Они представляют собой размеры партии. Если он не указан, отображается «?» Или «Нет», что означает, что модель может принимать партии любого размера.

    • Уровень 2 - Двунаправленный LSTM: Двунаправленный lstm принимает в качестве аргумента повторяющийся уровень (например, первый уровень LSTM). Этот уровень принимает выходные данные из предыдущего слоя внедрения (104, 64).

    Поскольку это двунаправленный lstm, у нас будут прямые и обратные выходы. Объедините эти выходные данные перед передачей ti на следующий уровень, суммируя или взяв среднее значение, объединяя или умножая.Найдите эти функции в аргументе режима слияния в слое bi-lstm. Выходы объединяются в режиме по умолчанию, который, в свою очередь, удваивает количество выходов на следующий уровень. В нашем случае это становится 128 (64 * 2).

    • Уровень 3 - Уровень LSTM: Сеть LSTM - это рекуррентная нейронная сеть, которая имеет блоки ячеек LSTM вместо наших стандартных слоев нейронной сети. Эти ячейки имеют различные компоненты, называемые входным вентилем, вентилем забывания и выходным вентилем.

    Этот уровень берет размер вывода из предыдущего двунаправленного слоя lstm (?, 104, 128) и выводит (?, 104, 256)

    • Уровень 4 - Уровень с распределением по времени: Мы имеем дело с множеством RNN Архитектура, в которой мы ожидаем вывода от каждой входной последовательности.Вот пример, в последовательности (a1 → b1, a2 → b2… an → bn), a и b являются входами и выходами каждой последовательности. Слои TimeDistributeDense позволяют выполнять плотную (полностью подключенную) операцию для каждого вывода на каждом временном шаге. Отсутствие использования этого слоя приведет к одному окончательному результату.

    Этот уровень принимает размер вывода предыдущего уровня lstm (104, 256) и выводит максимальную длину последовательности (104) и максимальные теги (17).

     input_dim = len (list (set (data ['Word']. To_list ()))) + 1 
    output_dim = 64
    input_length = max ([len (s) для s в data_group ['Word_idx'].tolist ()])
    n_tags = len (tag2idx) print ('input_dim:', input_dim, '\ noutput_dim:', output_dim, '\ ninput_length:', input_length, '\ nn_tags:', n_tags)

    Ниже приводится код, использованный для построения архитектуры модели, о которой мы говорили выше. Перед подгонкой модели проверьте график модели, построив модель с помощью функции plot_model или запустите model.summary () для получения сводки модели.

    Мы добавим в модель цикл for, чтобы сохранять и визуализировать потери в каждую эпоху.

     результатов = pd.DataFrame () model_bilstm_lstm = get_bilstm_lstm_model () 
    plot_model (model_bilstm_lstm) results ['with_add_lstm'] = train_model (train_tokens, np.array (train_tags), model_bilstm_lstm)
    . Модель началась с 69. После обработки 25 эпох с размером партии 1000 конечная точность составила 0,9687.

    Поэкспериментируйте с моделью с различными размерами пакетов, значениями исключения, оптимизаторами, показателями и слоями для достижения лучшего результата. Найдите полный код этого проекта в репозитории Github.

    .

    Именованная организация

    • Продукты
      АВТОМАТИЗАЦИЯ ДЛЯ ВСЕЙ КОРПОРАЦИИ
      Лента новостей Process Intelligence для точного обзора процессов.Vantage Платформа, предоставляющая навыки Content IQ, чтобы сделать цифровой персонал умнее. FlexiCapture Собирайте полезные данные из любых документов, от структурированных форм и опросов до неструктурированных документов с большим объемом текста.FineReader Server Разверните серверное решение OCR большого объема для преобразования документов.
    .

    Deep Learning для распознавания именованных сущностей # 1: общедоступные наборы данных и методы аннотации | Максимилиан Хофер

    Как и в любой модели глубокого обучения, вам потребуется ТОНН данных. Высококачественные наборы данных - основа любого проекта глубокого обучения. К счастью, существует несколько аннотированных, общедоступных и в основном бесплатных наборов данных.

    CoNLL 2003

    Этот набор данных включает 1393 английских и 909 немецких новостных статей. Корпус на английском языке предоставляется бесплатно, но, к сожалению, корпус на немецком языке стоит 75 долларов.Это единственный корпус, который чего-то стоит в этом посте. Для создания англоязычного корпуса вам понадобится корпус RCV1 Reuters. Вы получите доступ через пару дней после подачи организационного и индивидуального соглашения бесплатно.

    Объекты снабжены аннотациями LOC (местоположение), ORG (организация), PER (человек) и MISC (разное). Это пример предложения, где каждая строка состоит из [слово] [тег POS] [тег фрагмента] [тег NER]:

    U.№ NNP I-NP I-ORG
    официальный NN I-NP O
    Ekeus NNP I-NP I-PER
    головки VBZ I-VP O
    для IN I-PP O
    Багдад NNP I-NP I-LOC
    . . O O

    Вот полный список того, что означают теги POS. Вот официальный вводный документ CoNLL 2003 и вики GitHub с рейтингом SOTA.

    OntoNotes 5.0 / CoNLL 2012

    OntoNotes Release 5.0 состоит из 1745 КБ текстовых данных на английском, 900 КБ и 300 КБ арабских текстов из различных источников: телефонных разговоров, новостной ленты, широковещательных новостей, трансляций и веб-блогов.Сущности аннотируются, среди прочего, категориями ЛИЦО , ОРГАНИЗАЦИЯ и МЕСТО (полный список из 18 категорий здесь, стр. 21). Проще всего получить доступ через ваш университет / факультет - проверьте здесь при регистрации.

    Задачи i2b2

    Центр информатики для интеграции биологии и прикроватной работы (i2b2) выпустил ряд наборов клинических данных для NER. В частности, 2009 год (добыча лекарств), 2012 год (устранение проблем, лечение и т. Д.) и 2014 г. (выявление болезней, факторов риска, лекарств и т. д.) очень актуальны и включают хорошо задокументированные современные реализации. Получение доступа бесплатное - для этого необходимо подписать соглашение, в котором будет указано, что вы относитесь к данным с уважением. i2b2 очень быстро отвечает!

    Дополнительные данные

    Я не рассматривал подробно другие наборы данных, но они все еще могут быть полезны для вашего приложения: NLPBA 2004 с тегами белок / ДНК / РНК / линия клеток / тип клеток (2404 выдержки из MEDLINE) и электронные письма Enron с тегами с именами / датами / временем (~ 500 тыс. сообщений).

    Ознакомление с различными схемами аннотаций может занять некоторое время. Существует множество фреймворков, от подходов к разметке (в стиле HTML) до пар ключ-значение.

    Разметка (например, OntoNotes 5.0)

    В этом методе аннотации используются теги разметки с использованием угловых скобок для определения именованных объектов, например организаций:

    Disney - глобальный бренд.

    IOB (например, CoNLL 2003)

    IOB (или BIO) обозначает B начальное, I внутреннее и O внешнее.Слова с тегами O находятся вне именованных сущностей, а тег I-XXX используется для слов внутри именованных сущностей типа XXX . Когда два объекта типа XXX находятся непосредственно рядом друг с другом, первое слово второго объекта будет помечено тегом B-XXX , чтобы подчеркнуть, что он запускает другой объект. Вот пример предложения, где каждая строка состоит из [word] [POS tag] [NER tag]:

    We PRP B-NP
    saw VBD O
    the DT B-NP
    желтый JJ I-NP
    dog NN I-NP

    BIOES

    Более сложный метод аннотации различает конец названного объекта и отдельные объекты.Этот метод называется BIOES для B egin, I nside, O utside, E nd, S ingle. Ни один из вышеперечисленных наборов данных не использует BIOES из коробки, но он показал значительное улучшение производительности по сравнению с BIO (например, Chiu and Nichols, 2016 ).

    Дополнительные методы и подробности

    Вот отличное сообщение в блоге о других методах аннотации и их сложности.

    .

    python - быстрое удаление именованных сущностей с помощью NLTK

    Переполнение стека
    1. Около
    2. Товары
    3. Для команд
    1. Переполнение стека Общественные вопросы и ответы
    2. Переполнение стека для команд Где разработчики и технологи делятся частными знаниями с коллегами
    .

    Смотрите также