Python, Подготовка данных

Решение задачи определения границ предложений

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

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

Задача определения границ предложений или Sentence boundary detection не является тривиальной по нескольким причинам:

  1. Мы не можем однозначно понимать конец предложения по точке.
  2. В текстах тематических областей используются разнообразные сокращения.
  3. Неоднозначность определения границ из-за использования в текстах инициалов и сокращений, которые могут находиться как в середине предложения, так и в конце.

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

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

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

!pip install pysbd
import pysbd
text = "Меня зовут Иванов А.А. Я работаю над задачей определения границ предложений. Хочу протестировать работу библиотеки PySBD. Сложное предложение см. п. 23. Адрес г. Москва, ул. Школьная, д.5."
seg = pysbd.Segmenter(language="ru", clean=False)

Смотрим результат:

print(seg.segment(text))
['Меня зовут Иванов А.А. ', 'Я работаю над задачей определения границ предложений. ', 'Хочу протестировать работу библиотеки PySBD. ', 'Сложное предложение см. п. 23. ', 'Адрес г. Москва, ул. Школьная, д.5.']

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

['Подпунктом 10 п. 7 ст. ', '28 НК РФ определено … проверки.]

Здесь мы видим явную ошибку в первом же предложении.

Попробуем следующий алгоритм, он использует SpaCy:

!pip install spacy
!pip install pymorphy2
import spacy
from pysbd.utils import PySBDFactory
nlp = spacy.blank('ru')
nlp.add_pipe(PySBDFactory(nlp))
doc = nlp('Меня зовут Иванов А.А. Я работаю над задачей определения границ предложений. Хочу протестировать работу библиотеки PySBD. Сложное предложение см. п. 23. Адрес г. Москва, ул. Школьная, д.5.')

Смотрим результат:

print(list(doc.sents))
[Меня зовут Иванов А.А., Я работаю над задачей определения границ предложений., Хочу протестировать работу библиотеки PySBD., Сложное предложение см., п., 23., Адрес г., Москва, ул., Школьная, д.5.]

Этот алгоритм не справится ни с одним из сокращений.

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

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

!pip install rusenttokenize
from rusenttokenize import ru_sent_tokenize
ru_sent_tokenize()

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

shortenings – список сокращений;

joining_shortenings – список сокращений после разделения этого предложения невозможен (например, ‘ул’);

paired_shortenings – список парных сокращений (например, ‘т. д., т.е.’).

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

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

Для сравнения текстовых последовательностей вы можете использовать класс SequenceMatcher библиотеки difflib. Он позволяет сравнить 2 строки с заданным процентом совпадения.

import difflib
difflib.SequenceMatcher(x='', y='') .ratio(b) # где x и у сравниваемые строки, а b-порог схожести.

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

 Метрика acсuracy на 10 тыс. предложений по решениям представлена в таблице ниже:

PySBDSpaCyru_sent_tokenize
54%53%73%

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

Советуем почитать