Что такое NER, зачем он нужен и когда не поможет

в 13:37, , рубрики: named entity recognition, natural language processing, ner, nlp, spacy, нейросети python, обучение моделей, примеры кода

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

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

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

Что такое NER, зачем он нужен и когда не поможет - 1

Краткий экскурс

Named Entity Recognition (NER) — это технология извлечения именованных сущностей из текста: значимых фрагментов вроде имен, организаций, городов, дат, медицинских кодов и т. д.

Пример: «Илон Маск анонсировал запуск Starship в Техасе весной 2025 года».

NER выделит:

  • Илон Маск → персона (PER)

  • Starship → продукт / проект (PRODUCT)

  • Техас → гео (LOC)

  • весна 2025 года → дата (DATA)

NER широко используется в:

  • банках и страховых для выделения названий компаний, ИНН, адресов, сумм, реквизитов;

  • юридических документах для поиска дат, номеров договоров, участников;

  • чат‑ботах и техподдержке для извлечения имен, аккаунтов, заявок, местоположения;

  • медицине — извлекает коды диагнозов, названия препаратов, даты процедур;

  • логистике: выделение адресов, номеров накладных, получателей, грузов.

Звучит просто, но за этим стоит довольно сложная инженерия.

На практике я столкнулся с рядом проблем. Например, название организации может быть связано с географией — ООО «Москва». Также одно словосочетание порой содержит в себе две сущности — эта задача не решаема в рамках spaCy, так как сущности не могут накладываться друг на друга и определяться к двум разным категориям. Нередки случаи слипания слов друг с другом или с предлогами (решается постпроцессингом). И, наконец, данные могут попадать из многошумовых каналов, тогда проблем будет сразу несколько: «Клиент ООО АльфаБизнесСнаб прислал счет из Питера. Не уверен, что юр. лицо корректно, уточнить по ИНН 7 812 345 678» — тут и слитные слова, и пропущенные кавычки, и обрывки сущностей.

На чем основан NER

Исторически NER развивался в несколько этапов:

  1. Правила и регулярные выражения

    • Быстро, дешево, работает только если данные строго формализованы.

    • Подходит исключительно для строго формализованных сущностей: email'ов, номеров телефонов, паспортов.

  2. Классическое машинное обучение

    • Conditional Random Fields, SVM.

    • Требуют ручной инженерии признаков (features), работают лучше, чем регулярки.

  3. Глубокое обучение

    • BiLSTM‑CRF, Transformer‑модели (BERT и пр.)

    • Дает лучшие результаты, особенно при обучении на больших объемах данных.

Следующим этапом решении задач можно назвать применение LLM. Но в реальности из практических соображений обычно применяется золотая середина — что‑то лучше регулярки, но не требующее GPU. Именно здесь и приходит на помощь практичный NER на CPU.

Когда NER — хорошее решение

Использовать NER стоит, если:

  • у вас структурированный или полуформализованный текст (новости, документы, соцсети),

  • требуется извлекать сущности вроде имен людей, компаний, дат, локаций,

  • вы не хотите или не можете использовать глубокие модели,

  • нужно решение, работающее быстро на CPU,

  • нет возможности использовать любую LLM.

Также можно быстро протестировать применимость — разметьте до 100 примеров одного класса, и уже станет понятно, можно ли строить модель или нет.

Когда NER не подходит

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

Причина проста — нет ярко выраженных паттернов, нет повторяемости, нет контекста для обобщения. Модель не понимает, что объединяет эти сущности, особенно при маленьком датасете.

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

Аннотаторы для NER

Разметка данных — самая трудозатратная часть NER. К счастью, есть удобные инструменты.

Мне больше всего нравится NER Annotator

  • Прост в использовании

  • Позволяет разметить текст вручную, выбрать тип сущности

  • Можно экспортировать данные в JSON / IOB

Важные нюансы:

  1. Можно выбрать разделители, чтобы исходный текст был разбросан на задания.

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

  3. Работать командой неудобно — нельзя сохранить прогресс и выйти.

Пример разметки в NER Annotator

Пример разметки в NER Annotator

Еще несколько хороших аннотаторов:

  • Label Studio — open‑source аннотация

  • Prodigy — коммерческий, но очень быстрый

  • doccano — легкий веб‑аннотатор

Проблемы spaCy в контексте NER

spaCy популярен благодаря простоте и скорости — удобная вещь из коробки, запустить можно демо за 30 минут.

Дока по установке https://spacy.io/usage

Генерация конфига под GPU/CPU https://spacy.io/usage/training

Внимательно проверьте пути, подстройте под свои!

spaCy имеет ограничения:

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

  • Проблемы с кастомными сущностями, особенно если сущность длинная или переменная по структуре.

Аналоги spaCy

https://medium.com/ubiai‑nlp/mastering‑named‑entity‑recognition‑with‑bert‑ca8d04b67b18 — BERT

https://colab.research.google.com/github/NielsRogge/Transformers‑Tutorials/blob/master/BERT/Custom_Named_Entity_Recognition_with_BERT_only_first_wordpiece.ipynb — BERT

https://github.com/flairNLP/flair — FLAIR

https://stanfordnlp.github.io/CoreNLP/ner.html — StanfordCoreNLP

Давайте обучим свою первую NER-модель

Обучение модели NER из spaCy

Eстановим все необходимые либы:

!pip install -U pip setuptools wheel
!pip install -U 'spacy[cuda11x,transformers,lookups]'
!python3 -m spacy download ru_core_news_lg


import spacy
from spacy.tokens import DocBin
from tqdm import tqdm
import json



cv_data = json.load(open('path to your json','r'))

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

!python3 -m spacy init fill-config /home/alex/ner/base_config.cfg /home/alex/ner/config.cfg

Функция, которая обрабатывает аннотации в формат библиотеки spaCy:

# Define a function to create spaCy DocBin objects from the annotated data
def get_spacy_doc(file, data):
  # Create a blank spaCy pipeline
  nlp = spacy.blank('ru')
  db = DocBin()

  # Iterate through the data
  for text, annot in tqdm(data):
    doc = nlp.make_doc(text)
    annot = annot['entities']
    print(annot)
    ents = []
    entity_indices = []

    # Extract entities from the annotations
    for start, end, label in annot:
      skip_entity = False
      for idx in range(start, end):
        if idx in entity_indices:
          skip_entity = True
          break
      if skip_entity:
        continue

      entity_indices = entity_indices + list(range(start, end))
      try:
        span = doc.char_span(start, end, label=label, alignment_mode='strict')
      except:
        continue

      if span is None:
        # Log errors for annotations that couldn't be processed
        err_data = str([start, end]) + "    " + str(text) + "n"
        file.write(err_data)
      else:
        ents.append(span)

    try:
      doc.ents = ents
      db.add(doc)
    except:
      pass

  return db

Делим датасет на треин и тест 80/20:

from sklearn.model_selection import train_test_split
train, test = train_test_split(cv_data, test_size=0.2)

Избавляемся от пустых строк:

train = [x for x in train if x is not None]
test = [x for x in test if x is not None]
file = open('/home/alex/ner/train_file.txt','w')

Cоздаем spaCy DocBin объект для тренировочного и тестового датасета:

db = get_spacy_doc(file, train)
db.to_disk('/home/alex/ner/trained_models/train_data.spacy')

db = get_spacy_doc(file, test)
db.to_disk('/home/Ageev/ner/trained_models/test_data.spacy')

# Close the error log file
file.close()

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

!python3 -m spacy train /home/Ageev/ner/config.cfg  --output /home/Ageev/ner/trained_models/dir  --paths.train /home/Ageev/ner/trained_models/train_data.spacy  --paths.dev /home/Ageev/ner/trained_models/test_data.spacy --gpu-id 0

Что дальше?

Оцените качество на тестовом наборе, при необходимости — добавьте данные и улучшите разметку. При обучении должны получить примерно такую табличку с метриками:

Что такое NER, зачем он нужен и когда не поможет - 3

В случае ошибок — дебаггер:

!python3 -m spacy debug data /home/alex/ner/config.cfg --paths.train /home/Ageev/ner/trained_models/train_data.spacy  --paths.dev /home/Ageev/ner/trained_models/test_data.spacy

Проверка модели:

text =”тут ВАШ текст для проверки ВАШЕЙ модели”



import spacy

# Load the trained spaCy NER model from the specified path
nlp = spacy.load('/home/alex/ner/model-last') 
#
# /home/Ageev/ner/trained_models/15000epochs/model-last
doc = nlp(text)

# Iterate through the named entities (entities) recognized by the model
for ent in doc.ents:
  # Print the recognized text and its corresponding label
  print(ent.text, "  ->>>>  ", ent.label_)

Постобработка

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

  1. Очистка мусора (лишние символы, цифры и тд).

  2. Правильная обрезка границ сущности.

  3. Выправление слов до начальных форм (через тот же GPT или LLM).

  4. Нормализация (например, приведение к строчным буквам).

  5. Фильтрация по длине.

  6. Удаление дубликатов.


Спасибо всем, кто дочитал до конца. Приглашаю всех начинающих специалистов, интересующихся AI и ML, в мой телеграм-канал.

Автор: Alexandr1997ag

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js