Современные подходы к NER
Введение
В этой статье мы обсудим задачу распознавания именованных сущностей (NER) и познакомимся с разными современными методами её решения.
Рассмотрим популярные готовые библиотеки для NER, расскажем, как и когда стоит обучить свою модель, а также затронем использование больших языковых моделей (LLM) для этой задачи.
Кроме того, поговорим и о том, в каких случаях простых эвристик достаточно, чтобы получить хорошие результаты.
Итак, поехали! 😎
Что такое NER?
Начнём с постановки задачи. Задача Named Entity Recognition (NER), или распознавание именованных сущностей, заключается в автоматическом выделении и классификации в тексте таких объектов, как имена людей, названия организаций, географические объекты, даты, суммы денег и другие специфические категории.
Решение задачи NER позволяет превратить неструктурированный текст в структурированные данные, что значительно облегчает понимание и улучшает дальнейшую обработку информации и её поиск. Например, на маркетплейсе пользователь вводит в поисковую строку запрос: «Чёрные кожаные кроссовки Adidas с гелевой подошвой для бега 42 размер», а NER-система извлекает из запроса сущности цвета («чёрный») и размера обуви («42») и использует соответствующие фильтры для отображения более релевантных результатов поиска.
Задача NER сводится к присвоению меток каждому токену, где метка указывает либо на конкретный тип сущности, либо на её отсутствие.
Давайте посмотрим на решение задачи NER на примере запроса: «Вакуумный упаковщик KITFORT KT-1502-1 белый». Из этого запроса выделяются такие сущности:
{
'ATTR': ['вакуумный'],
'PRODUCT': ['упаковщик'],
'BRAND': ['kitfort'],
'MODEL': ['kt-1502-1'],
'COLOR': ['белый']
}
Когда говорят о NER, обычно вспоминают ещё и о смежных задачах:
- POS (Part-of-speech) tagging: разметка каждого слова в предложении в соответствии с частью речи (существительное, глагол, прилагательное и др.).
В качестве примера возьмём предложение: «Съешьте еще этих мягких французских булок, да выпейте чаю». В нём определяются части речи для каждого слова:
[
('съешьте', 'глаг.'),
('еще', 'нареч.'),
('этих', 'местоим. прил.'),
('мягких', 'прил.'),
('французских', 'прил.'),
('булок', 'сущ.'),
('да', 'союз'),
('выпейте', 'глаг.'),
('чаю', 'сущ.')
]
2. Выделение токенов (Chunking): определение токенов, относящихся к одной сущности. Эта задача (комбинируемая с POS или NER) заключается в маркировке токенов метками в формате IOB2.
Формат данных IOB2 для задачи NER — способ разметки текста, при котором каждому токену (словоформе) присваивается метка, указывающая, является ли он частью именованной сущности и если да, то какой именно. Расшифровка аббревиатуры IOB2:
- B- (Beginning) — начало именованной сущности,
- I- (Inside) — продолжение именованной сущности,
- O (Outside) — токен не относится к именованной сущности.
Не будем уходить от нашего примера с вакуумным упаковщиком и рассмотрим на нём результат решения задачи Chunking:
{'iob2': [
('B-ATTR', (0, 9)),
('B-PRODUCT', (10, 19)),
('B-BRAND', (20, 27)),
('B-MODEL', (28, 30)),
('I-MODEL', (30, 31)),
('I-MODEL', (31, 35)),
('I-MODEL', (35, 36)),
('I-MODEL', (36, 37)),
('B-COLOR', (38, 43))
],
'tags_dict': {
'ATTR': ['вакуумный'],
'PRODUCT': ['упаковщик'],
'BRAND': ['kitfort'],
'MODEL': ['kt-1502-1'],
'COLOR': ['белый']},
}
Объекты tags_dict
и iob2
в примере представляют два взаимосвязанных способа представления извлечённых сущностей из текста.
tags_dict по-прежнему содержит агрегированные результаты извлечения сущностей в виде словаря, а объект iob2 — пословную разметку текста в формате IOB2 со структурой списка кортежей вида (метка, (начальный_индекс_токена, конечный_индекс_токена))
.
💡Важно отметить: все эти задачи сводятся к классификации токенов предложения. Существует множество других прикладных задач, связанных с классификацией токенов, но мы сосредоточимся именно на NER как на типичном представителе этого класса задач.
NER из коробки
Задача NER не новая, и люди научились достаточно хорошо её решать. Есть несколько известных фреймворков:
- nltk. Базовая база, когда приходится работать с английскими текстами. К сожалению, в основном функционале поддерживаются только такие сущности, как Location, Person, Organization.
import nltk
nltk.download('punkt_tab')
nltk.download('averaged_perceptron_tagger_eng')
nltk.download('maxent_ne_chunker_tab')
nltk.download('words')
text = "At the conference on March 19, Donald Trump claimed that London is the capital of Great Britain."
for sent in nltk.sent_tokenize(text):
for chunk in nltk.ne_chunk(nltk.pos_tag(nltk.word_tokenize(sent))):
if hasattr(chunk, 'label'):
print(chunk)
(PERSON Donald/NNP Trump/NNP)
(GPE London/NNP)
(GPE Great/NNP Britain/NNP)
Для лучшего качества стоит добавить к nltk классическую Java-библиотеку от Stanford CoreNLP.
2. Spacy предлагает 18 меток, которыми отмечаются именованные сущности. Также авторы библиотеки добавили возможность несложно обучить модель под свои данные, что сделало это решение довольно популярным за последние пару лет.
import spacy
text = "At the conference on March 19, Donald Trump claimed that London is "
"the capital of Great Britain."
model_sp = spacy.load('en_core_web_md')
for ent in model_sp(text).ents:
print(ent.text.strip(), ent.label_)
March 19 DATE
Donald Trump PERSON
London GPE
Great Britain GPE
3. DeepPavlov. Создатели ориентируют эту библиотеку преимущественно на применение в диалоговых системах и чат-ботах, однако она также отлично подходит для исследовательских задач. Использование DeepPavlov в продакшене без значительной доработки и кастомизации, по всей видимости, не предусмотрено разработчиками, но при достаточном времени на её изучение и на страдания по установке и разрешению зависимостей она демонстрирует хорошие результаты.
4. Natasha. Один из главных проектов NLP для русского языка. Есть весь необходимый функционал для решения задач обработки естественного языка, плюс библиотека ещё и удобная в установке и использовании.
from natasha import (
Segmenter,
NewsEmbedding,
NewsNERTagger,
Doc
)
segmenter = Segmenter()
emb = NewsEmbedding()
ner_tagger = NewsNERTagger(emb)
text = "Главы МИД Великобритании, Франции, Германии и Украины, вероятно, "
"перенесли запланированную встречу в Лондоне, сообщает Sky News."
doc = Doc(text)
doc.segment(segmenter)
doc.tag_ner(ner_tagger)
doc.ner.print()
Главы МИД Великобритании, Франции, Германии и Украины, вероятно,
ORG LOC─────────── LOC──── LOC───── LOC────
перенесли запланированную встречу в Лондоне, сообщает Sky News.
LOC──── ORG─────
Учим свой NER!
Если в прикладной задаче мы имеем дело с сущностями, с которыми готовые фреймворки не работают из коробки, или со специфическим доменом данных — нужно разрабатывать модель для NER на своих данных! Допустим, мы разрабатываем модель для выделения именованных сущностей для маркетплейса, и нам нужно выделить из пользовательских запросов и названий товаров такие паттерны, как бренд, модель, тип продукта, цвет, назначение и целевая аудитория продукта. В этом случае у нас несколько вариантов:
- Воспользоваться фреймворками для быстрого обучения модели, некоторые из вышеперечисленных библиотек в том или ином виде предоставляют такой функционал.
- Разработать свою модель, используя современные подходы для обучения языковых моделей.
- Применить большие языковые модели (LLM).
Сейчас мы сосредоточимся на втором варианте. В современном NLP для задачи NER традиционно подходят модели, основанные на архитектуре BERT (Bidirectional Encoder Representations from Transformers). На это есть несколько причин.
1. Двунаправленный контекст
BERT анализирует текст сразу в двух направлениях — слева направо и справа налево, что позволяет учитывать контекст до и после каждого слова. Это особенно важно для NER, где значение слова часто определяется его окружением. Например, слово «Apple» может быть как фруктом, так и компанией, и только контекст позволяет правильно его классифицировать.
2. Механизм внимания (attention)
BERT основан на архитектуре трансформера, использующей механизм внимания, который позволяет модели фокусироваться на наиболее значимых частях текста при анализе каждого токена. Это помогает точнее определять границы и типы сущностей, даже если они встречаются в сложных или длинных предложениях.
3. Контекстно-зависимые эмбеддинги
В отличие от классических моделей (например, word2vec), которые создают одно представление для слова вне зависимости от контекста, BERT формирует уникальное векторное представление каждого слова с учётом его окружения. Это позволяет различать омонимы (например, смартфон «Apple» и фрукт) и правильно определять сущности в разных ситуациях.
4. Предобучение на больших корпусах
BERT предварительно обучается на огромных объёмах текстов с помощью задач Masked Language Modeling (MLM) и Next Sentence Prediction (NSP), что обеспечивает глубокое понимание языковых структур и связей между словами. После этого модель можно дообучить (fine-tune) на конкретной задаче NER — это приводит к высокой точности даже при относительно небольшом количестве размеченных данных.
5. Гибкость и масштабируемость
BERT легко адаптируется к разным языковым задачам, в том числе NER, путём добавления небольшого выходного слоя для классификации токенов. Это позволяет использовать одну и ту же базовую архитектуру для различных типов текстов и доменов.
Все эти пункты делают архитектуру BERT «швейцарским ножом» в мире языковых моделей для решения задач классификации токенов.
Как дообучить BERT на NER
Для обучения своей модели на основе библиотеки transformers важно знать о нескольких ключевых моментах.
- Решайте задачу классификации токенов
При использовании такой encoder архитектуры, как BERT, выходной слой модели имеет размерность (batch_size, sequence_length, embedding_size).
Это означает, что каждый токен в предложении представляется вектором размерности embedding_size
. Поэтому для решения задачи классификации вектор каждого токена должен передаваться в линейный слой, который отображает скрытую размерность embedding_size
в количество классов.
2. Делайте предобработку токенизированных данных
Для корректного соответствия токенов с разметкой слов при использовании таких моделей, как BERT, текст разбивается на токены, а не на слова целиком. Чтобы правильно сопоставить метки сущностей с токенами, требуется дополнительная обработка, например, распространение метки первого подслова на все подслова или игнорирование меток для вспомогательных токенов.
Например, входной текст выглядит как «Fischler proposed EU-wide measures after reports», а разметка для текста — следующим образом:
Токенизированный текст при этом получается такой:
На этом моменте возникает алгоритмическая задача — соотнести разметку для слов с разметкой для токенов, чтобы получить следующий результат:
O
означает, что токен не соответствует какой-либо сущности;B-PER
/I-PER
означает, что токен соответствует началу / находится внутри сущности персоны person;B-MISC
/I-MISC
означает, что токен соответствует началу / находится внутри сущности miscellaneous;B-LOC
/I-LOC
означает, что токен соответствует началу / находится внутри сущности location.
3. Выбирайте оптимальные для ваших данных модели
После токенизации данных переходим к выбору модели. Тут все просто — на Hugging Face Model Hub есть специальный фильтр по задачам, включая NER, где собраны готовые модели для распознавания именованных сущностей. Если модель с Hugging Face подходит для нашего домена, то её можно использовать без дополнительного дообучения, иначе лучше дотюнить модель на своих данных.
Критерии выбора модели
- Язык: выбирайте модель, обученную на нужном языке (английский, русский и др.).
- Размер и скорость: DistilBERT и другие облегчённые модели подходят для приложений с ограниченными ресурсами, они сохраняют при этом высокую точность.
- Датасет и домен: модели, дообученные на специфичных датасетах (например, CoNLL-03), лучше подходят для общих задач, но для узкоспециализированных доменов может потребоваться дополнительное дообучение.
- Метрики качества: обращайте внимание на F1-score, точность и скорость инференса, которые обычно указаны в описании модели.
4. Правильно оценивайте качество модели
Для оценки модели NER традиционно используется библиотека seqeval
, которая вычисляет метрики accuracy, precision, recall, F1-score для каждого класса именованных сущностей и строит classification report. Важно оценивать качество модели на отложенной выборке, предварительно исключив утечку данных в тренировочное множество.
from seqeval.metrics import classification_report, f1_score, precision_score, recall_score
y_true = [['B-PER', 'O', 'O', 'B-LOC', 'I-LOC', 'O']]
y_pred = [['O', 'O', 'O', 'B-LOC', 'I-LOC', 'O']]
print("F1:", f1_score(y_true, y_pred))
print("Precision:", precision_score(y_true, y_pred))
print("Recall:", recall_score(y_true, y_pred))
F1: 0.6666666666666666
Precision: 1.0
Recall: 0.5
precision recall f1-score support
LOC 1.00 1.00 1.00 1
PER 0.00 0.00 0.00 1
micro avg 1.00 0.50 0.67 2
macro avg 0.50 0.50 0.50 2
weighted avg 0.50 0.50 0.50 2
Чтобы правильно оценить качество модели, важно определиться с целевой метрикой: в каких-то задачах нужно максимизировать precision, в каких-то — recall или их гармоническое среднее — f1-score.
Например, в задачах, где ложноположительные срабатывания могут привести к серьёзным последствиям, precision выходит на первый план. Когда мы хотим выделить именованные сущности для финансовых или юридических документов, если система ошибочно выделяет несуществующие имена или организации — это может привести к неправильным действиям или юридическим ошибкам. А в задачах, где пропуск правильных сущностей критичен, важно максимизировать recall. Например, если система пропустит упоминание болезни или лекарства в медицинских данных — это приведёт к потере важной информации для анализа или исследования.
И, конечно же, во многих практических задачах NER важно находить баланс между precision и recall, чтобы система была и точной, и полной. Поэтому о выборе метрики нужно помнить всегда, потому что оценка ML модели — одна из основных вещей, влияющих на успешность решения задачи.
Датасет
Последняя, но самая важная составляющая при обучении модели, — это данные. Ни для кого не секрет, что современные языковые модели нуждаются в большом объёме данных для обучения, однако помимо размера датасета важно помнить и о других моментах:
- Качество данных. Это базовое требование к любому датасету. В случае решения задачи NER это означает, что одинаковые паттерны в похожих контекстах должны размечаться одинаковыми сущностями, иначе такие примеры в данных только запутают модель. Например, в запросах «купить Roborock белый» и «белый робот-пылесос Roborock» сущность «Roborock» должна быть размечена одинаково — как BRAND.
- Полнота данных. Все именованные сущности, которые требуется распознавать, должны быть представлены в датасете, при чём в той же пропорции, в которой они встречаются в данных применения модели. Если в обучающей выборке было мало примеров (или не было вовсе) с сущностью, обозначающей цвет товара, на тестовой выборке модель будет больше ошибаться на таких примерах. Естественное решение — обогащение обучающей выборки с нужным типом именованной сущности. Для стабильного качества желательно, чтобы по каждому типу сущности было не менее 1000 примеров, иначе возможны проблемы с точностью распознавания.
- Очистка и нормализация данных. Размеченные данные нужно привести к единому формату, удалить дубликаты, исправить ошибки, устранить шум и неоднозначности.
В итоге, обучение BERT для NER — это не просто запуск модели на размеченных данных. Ключ к успеху — в качестве и согласованности разметки, тщательной подготовке токенизированных данных и правильном выборе модели под задачу. Даже самая мощная архитектура не даст хороших результатов, если данные размечены небрежно или не покрывают все нужные сущности. Важно не только достичь высокой метрики на тесте, но и понять, какие ошибки для вашей задачи критичны, и подобрать метрику под реальные бизнес-цели. Использование предобученных моделей и современных библиотек значительно упрощает старт, но качество решения всегда определяется качеством данных и осознанным подходом к оценке результата.
LLM для NER
Ажиотаж вокруг использования больших языковых моделей (LLM) во всевозможных задачах растёт с космической скоростью, поэтому ML-инженеры применяют LLM даже там, где не нужно. Давайте разберёмся, насколько большие языковые модели применимы для NER.
Несомненно, большие языковые модели показывают отличные результаты во многих задачах. Давайте посмотрим, как справится, например, дистилированная от DeepSeek R1 Llama 3.3 instruct с 70 миллиардами параметров в режиме zero-shot с нашим запросом про вакуумный упаковщик. Для решения задачи сформируем промпт:
query = "Вакуумный упаковщик KITFORT KT-1502-1 белый"
prompt_template = """Выдели из названия товара такие сущности как тип товара, бренд, модель, серию, цвет, для чего предназначается.
Если сущности нет в запросе, не выделяй ее.
Запрос: {query}"""
prompt = prompt_template.format(query=query)
Дополнительно внесём в промпт информацию для модели о том, что не все перечисленные в промпте именованные сущности могут встречаться в тексте.
Ответ LLM:
**Выделенные сущности:**
- **Тип товара:** Вакуумный упаковщик
- **Бренд:** KITFORT
- **Модель:** KT-1502-1
- **Цвет:** Белый
Итак, LLM отлично справилась!
Рассмотрим другой пример и попробуем извлечь информацию из описания товара: «EMERSON легкий держатель для паспорта, дорожный кошелек, подвесная сумка для паспорта».
Ответ LLM:
- **Бренд:** EMERSON
- **Тип товара:** аксессуары для путешествий
- **Модель:** отсутствует
- **Серия:** отсутствует
- **Цвет:** отсутствует
- **Для чего предназначается:** для паспорта, дорожного кошелька, подвесная сумка для паспорта
Здесь мы сталкиваемся с одной из типичных проблем больших языковых моделей — галлюцинации. LLM в качестве типа товара выделила «аксессуары для путешествий», тем самым изменив информацию о товаре. Такая ошибка более критична, потому что ложные сущности приводят к ошибкам в каталогах, фильтрах и аналитике, а также могут вызывать юридические и репутационные проблемы. Их труднее обнаружить и исправить, они искажают данные и подрывают доверие пользователей. В то время, как пропуск отдельных сущностей лишь ограничивает полноту информации, но не нарушает её достоверность. Поскольку LLM — это генеративная модель, основная задача которой генерировать следующий токен, мы не можем быть уверены в достоверности информации её ответов.
Галлюцинации — основная проблема, из-за которой LLM не так хорошо подходит для решения задачи NER. Мы можем нивелировать явные ошибки модели и незнание домена с помощью few-shot или даже с помощью SFT и Alignment на наших данных, но застрахованы от галлюцинаций модели на 100% мы не будем.
Что же тогда делать? Можно использовать ответы LLM в комбинации с различными методами валидации ответов модели.
- Реализация нескольких этапов проверки. Сначала модель генерирует разметку, затем другая модель или набор правил проверяет корректность сущностей. Например, если модель выделила имя компании — проверяем его наличие в базе данных или по внешним источникам. Это позволяет повысить точность и уменьшить количество ложных срабатываний.
Проиллюстрируем этот подход на примере:
prompt_template = """Выдели из текста название организации. И напиши только организации через ;
Текст: {query}"""
query = "Сотрудничаем с Яндексом и вымышленной компанией ТестООО"
prompt = prompt_template.format(query=query)
Ответ LLM:
Яндекс; ТестООО
Проверяем выделенные сущности по нашей небольшой базе организаций в виде словарика:
company_database = {"Яндекс", "Сбербанк", "Газпром", "Тинькофф"}
def validate_company(entity_text):
return entity_text in company_database
for ent in llm_response.split(";"):
ent = ent.strip()
print(f"{ent} -> {'Valid' if ent in company_database else 'Invalid'}")
Яндекс -> Valid
ТестООО -> Invalid
2. Согласованность разных моделей. Использование подхода self-consistency, при котором запускаются несколько моделей или разные версии LLM, и затем объединяются их ответы, позволяет выявлять и отфильтровывать сомнительные сущности. Поскольку генерация ответа LLM основана на вероятностном выборе следующего токена с помощью алгоритма нечёткого лучевого поиска, разные прогоны модели с различными параметрами могут приводить к различным результатам. Однако если несколько моделей или запусков сходятся на одном и том же ответе (демонстрируют self-consistency), вероятность ошибки существенно снижается.
Вернёмся к нашему примеру «EMERSON легкий держатель для паспорта, дорожный кошелек, подвесная сумка для паспорта». Напомним ответ дистилированной Llama DeepSeek R1 70B:
- **Бренд:** EMERSON
- **Тип товара:** аксессуары для путешествий
- **Модель:** отсутствует
- **Серия:** отсутствует
- **Цвет:** отсутствует
- **Для чего предназначается:** для паспорта, дорожного кошелька, подвесная сумка для паспорта
Давайте посмотрим, что скажет Llama 3 70B:
Here is the extracted information:
**Тип товара:** держатель для паспорта, дорожный кошелек, подвесная сумка для паспорта
**Бренд:** EMERSON
**Модель:** не указана
**Серия:** не указана
**Цвет:** не указан
**Для чего предназначается:** для паспорта
Уже лучше! А теперь ответ Qwen QwQ 32B:
- **Бренд**: EMERSON
- **Тип товара**: держатель для паспорта, дорожный кошелек, подвесная сумка
- **Назначение**: для паспорта, дорожное использование
Другие сущности (модель, серия, цвет) в запросе не указаны.
Как видно из примеров, две из трёх LLM корректно выделили ключевые сущности. Используя механизм голосования между ответами разных моделей, мы можем достоверно определять тип продукта.
3. Ручная разметка сложных случаев. Можно автоматически выделять примеры с высокой степенью неопределённости или потенциальными ошибками и отправлять их на ручную разметку. Этот способ полезен, потому что он упрощает процесс разметки асессору — скорректировать разметку сильно проще, чем составить её с нуля. Данный способ дополнительно экономит деньги на разметку 🙂
Таким образом, комплексный подход, сочетающий обучение, автоматическую и ручную валидацию, а также использование внешних источников позволит значительно снизить влияние галлюцинаций и повысить качество NER на базе LLM.
Главное — не полагаться только на одну модель, а строить систему с обратной связью и контролем качества на каждом этапе.
Заключение
Итак, давайте подведём итоги 😊
В статье мы подробно рассмотрели основные фреймворки для распознавания именованных сущностей, обсудили их преимущества и недостатки, а также возможности быстрого внедрения готовых решений.
Особое внимание уделили моделям на основе BERT: разобрали, почему именно этот подход стал стандартом для NER, и как его можно дообучить под свои задачи для высокого качества даже при ограниченных данных.
Затронули современные тенденции, связанные с применением больших языковых моделей (LLM) для NER — это открывает новые возможности для работы с неструктурированными и многоязычными текстами.
Резюмируя вышесказанное, мы можем вывести современный подход для решения задачи NER:
- При работе со стандартными типами сущностей и распространёнными языками (английский, испанский и др.) используйте готовые решения из открытых библиотек или модели из Hugging Face.
- Если сущности и домен данных специфичны (например, медицинские термины), или язык малораспространён (например, турецкий), готовые решения могут не подойти. Тогда стоит пойти в обучение собственной модели на основе архитектуры BERT или воспользоваться LLM.
В заключение, хотим поделиться небольшим анонсом к лекции на курсе по LLM 😊
На занятии мы подробно разберём, когда для задачи NER стоит использовать LLM, а в каких случаях предпочтительнее применять BERT. Также поговорим, как модели для решения задачи NER интегрируются в более крупные ML-системы и каким образом они помогают решать прикладные задачи, например, улучшать поиск на маркетплейсе, делая его более точным и персонализированным для пользователей.