Segmentation.
Сегментацию текста из Википедии предлагалось осуществить с помощью двух программ: Pragmatic Segmenter и NLTK Punkt. Первая из них — сегментатор с открытым исходным кодом, разработанный TM-Town. Он основан на правилах и реализован на Ruby. В Pragmatic Segmenter не используются алгоритмы машинного обучения — а значит, не требуются никакие тренировочные данные. Как пишут его создатели, их целью было разработать такой инструмент, который бы отвечал потребностям реального мира, был бы способен обрабатывать текст на многих языках и делал бы свою работу хорошо, даже если язык ему незнаком и контент плохо отформатирован (например, текст, импортированный из PDF, в котором разбиение строк происходит в середине предложения).
Pragmatic Segmenter был разработан в том числе с целью сегментирования текстов для создания памятей переводов, поэтому для поддержания связности и осмысленности предложение в скобках, вложенное в другое предложение, трактуется вместе с ним как единый сегмент. Даже если в подобном случае возникнет неопределенность, двусмысленность в определении границ предложения, алгоритм проигнорирует её и не станет разбивать предложение.
TM-Town также разработала набор тестов для проверки точности и аккуратности сегментатора — несколько golden rules определены для различных языков, в том числе для немецкого, текст на котором и был скачан мной из хранилища текстов Википедии. В предложенном коде я специфицировала язык, изменив значение lang на ‘de’:
require 'pragmatic_segmenter'
lang = "de"
if ARGV[0]
lang=ARGV[0]
end
STDIN.each_with_index do |line, idx|
ps = PragmaticSegmenter::Segmenter.new(text: line, language: lang)
ps.segment
for i in ps.segment
print(i,"\n")
end
end
С помощью команды
$ ruby -I. segmenter.rb < wiki_1.txt > segmentation_pragmatic.txt
текстовые данные, содержащиеся в файле, были разбиты на предложения сегментатором Pragmatic Segmenter, а получившиеся предложения записаны в новый файл.
Также работа алгоритма была проверена на тестовых данных:
$ echo "This is a test.This is a test. This is a test." | ruby -I . segmenter.rb
Результат получился таким:
This is a test.
This is a test.
This is a test.
Как видим, Pragmatic Segmenter в данном случае успешно справился с задачей, сумев отделить друг от друга два предложения, несмотря на отсутствие пробела после точки.
Далее предстояло познакомиться с еще одним сегментатором — NLTK Punkt. Сегментатор NLTK Punkt работает по другому принципу, нежели Pragmatic Segmenter: он делит текст на предложения, используя алгоритм машинного обучения без учителя, чтобы построить модель для аббревиатур, коллокаций и слов, с которых начинаются предложения. Таким образом, перед тем, как он может быть использован, его нужно натренировать на обширном корпусе целевого языка. Можно применить и некоторые уже предустановленные модели. Если использовать непосредственно ‘PunktTrainer’, это даст возможность применить инкрементное обучение и изменять гиперпараметры, которые позволяют определить, например, что считать аббревиацией.
С помощью программы алгоритмом NLTK был обработан все тот же файл wiki_1.txt, результаты были записаны в новый файл segmentation_nltk.txt:
import codecs, nltk
nltk.download('punkt')
from nltk.tokenize import sent_tokenize
path = ‘/home/gugutse/Documents/segmentation/wiki_1.txt'
sent_tokenize_list = []
with codecs.open(path, 'r', encoding='utf-8', errors='ignore') as f:
for line in f:
sent_tokenize_list += sent_tokenize(line)
with open('segmentation_nltk.txt', 'w') as f:
for i in sent_tokenize_list:
print(i)
Проверка на тестовых данных:
$ echo "This is a test.This is a test. This is a test." | python3 sent_tokenize.py
Результат не соответствовал ожиданиям:
This is a test.This is a test.
This is a test.
NLTK Punkt, в отличие от Pragmatic Segmenter, не смог правильно сегментировать предложения, сбившись на точке, после которой отсутствовал пробел.
Затем нужно было сравнить результат работы двух сегментаторов. Мне понравилась идея построчного сравнения выходных данных каждого сегментатора с помощью diff:
gugutse@gugutse-VirtualBox:~/Documents/segmentation/pragmatic_segmenter/lib$ while read l; do diff -u <(echo "$l" | ruby -I. segmenter.rb) <(echo "$l" | python3 sent_tokenize.py); echo -------; done <wiki_1.txt
Многие предложения были идентичны, но встречались и интересные отличия:
--- /dev/fd/63 2018-11-02 12:40:15.105105368 +0300
+++ /dev/fd/62 2018-11-02 12:40:15.105105368 +0300
@@ -1 +1,3 @@
-Heinrich Detjen (* 30. Januar 1899 in Saarbrücken; † 27. Februar 1968) war ein saarländischer Politiker (KPD/DFU).
+Heinrich Detjen (* 30.
+Januar 1899 in Saarbrücken; † 27.
+Februar 1968) war ein saarländischer Politiker (KPD/DFU).
Здесь Pragmatic Segmenter трактовал информацию в скобках и само предложение, внутри которого она находится, как единый сегмент (вспомним, что он разработан в том числе с целью сегментирования текстов для создания памятей переводов — в этом случае удобнее не отделять клаузу в скобках). Также мы видим, что Pragmatic Segmenter отлично справился с обозначением дат, не трактуя точку после числа в качестве конца предложения (к слову, подобное написание дат — одно из golden rules для немецкого языка). NLTK Punkt хуже провел сегментацию: точки после чисел он посчитал за концы сегментов, наличие изолированных скобок его «не смутило».
--- /dev/fd/63 2018-11-02 12:41:06.309105368 +0300
+++ /dev/fd/62 2018-11-02 12:41:06.309105368 +0300
@@ -1,2 +1,3 @@
-Die Stalwarts (engl. Feste, Starke, Mutige) waren eine Gruppe konservativer US-amerikanischer Republikaner, die im späten 19.
+Die Stalwarts (engl.
+Feste, Starke, Mutige) waren eine Gruppe konservativer US-amerikanischer Republikaner, die im späten 19.
Pragmatic Segmenter адекватно провел сегментацию, вновь включив в сегмент информацию в скобках. NLTK Punkt не распознал сокращение engl., трактовав точку в качестве конца предложения.
--- /dev/fd/63 2018-11-02 12:43:55.545105368 +0300
+++ /dev/fd/62 2018-11-02 12:43:55.549105368 +0300
@@ -1,3 +1,3 @@
-Georg Burckhardt (*
-5. Januar 1539 in Wettelsheim/Franken;
-† 6. Februar 1607 in Wildbad bei Mörnsheim) war ein deutscher Philosoph und Professor der Logik und Rhetorik an der Eberhard-Karls-Universität Tübingen.
+Georg Burckhardt (* 5.
+Januar 1539 in Wettelsheim/Franken; † 6.
+Februar 1607 in Wildbad bei Mörnsheim) war ein deutscher Philosoph und Professor der Logik und Rhetorik an der Eberhard-Karls-Universität Tübingen.
Этот пример представляет особенный интерес, и не только потому, что оба сегментатора не вполне корректно справились с задачей. NLTK Punkt, как и в первом примере, разбил текст по точкам после чисел, обозначающих даты, несмотря на то, что скобки в таком случае оказались «разорваны». Куда любопытнее понять, по какой причине Pragmatic Segmenter тоже разделил информацию в скобках, правда, по-иному: первое предложение окончилось звездочкой, был осуществлен перенос даты на следующую строку, которая завершилась точкой с запятой, вторая дата была перенесена на следующую — третью — строку. В первом примере в аналогичной ситуации Pragmatic Segmenter справился с задачей: никакого разделения информации в скобках не произошло, точка с запятой не трактовалась как конец сегмента. Дело по всей видимости в том, что в этом случае числа с точкой, находящиеся в скобках, идут друг за другом с точки зрения нумерации — и, вероятно, сегментатор интерпретирует это как пункты списка, numbered list (отсюда и это нетипичное разделение сегментов внутри скобок, и окончание одного сегмента знаком точка с запятой).
А вот, кажется, еще одно подтверждение этой особенности Pragmatic Segmenter:
--- /dev/fd/63 2018-11-02 13:06:35.388181680 +0300
+++ /dev/fd/62 2018-11-02 13:06:35.388181680 +0300
@@ -1,6 +1,5 @@
-Die Hauptburg wird im
-15. bis
-16. Jahrhundert auf mittelalterlichen Grundmauern errichtet.
+Die Hauptburg wird im 15. bis 16.
+Jahrhundert auf mittelalterlichen Grundmauern errichtet.
Вновь разделение происходит по числам, которые идут при счете подряд, то есть информация воспринимается как отдельные нумерованные пункты. NLTK Punkt все также в неведении относительно порядковых числительных в немецкой письменной речи (они пишутся с точкой).
В некоторых случаях Pragmatic Segmenter хуже, чем NLTK Punkt, справляется с аббревиатурами — например, u.a. и k.u.k. (это если информация не идет в скобках или кавычках — текст в кавычках Pragmatic Segmenter также не разъединяет):
--- /dev/fd/63 2018-11-02 12:51:33.838108681 +0300
+++ /dev/fd/62 2018-11-02 12:51:33.838108681 +0300
@@ -1,6 +1,4 @@
-Toporow war u.
- a.
-Träger des Solschenizyn-Preises.
+Toporow war u. a. Träger des Solschenizyn-Preises.
--- /dev/fd/63 2018-11-02 12:54:04.364181680 +0300
+++ /dev/fd/62 2018-11-02 12:54:04.364181680 +0300
@@ -1,6 +1,3 @@
-Die anfängliche Begeisterung der Kleinbürger für den Ersten Weltkrieg wird vorstellbar heraufbeschworen und kann als Kabinettstück österreichischen Galgenhumors, den Untergang der k.
-u.
-k.
-Monarchie betreffend, gelten.
+Die anfängliche Begeisterung der Kleinbürger für den Ersten Weltkrieg wird vorstellbar heraufbeschworen und kann als Kabinettstück österreichischen Galgenhumors, den Untergang der k. u. k. Monarchie betreffend, gelten.
Общее количество предложений в файле wiki_1.txt составило 322 (подсчитано вручную). С помощью команд
$ wc -l segmentation_pragmatic.txt
345
$ wc -l segmentation_nltk.txt
355
выясняем, что Pragmatic Segmenter нашел 346 предложений, NLTK Punkt — 356 предложений ($ wc -l — это подсчет перевода строки, поэтому нужно прибавить еще последнее предложение). Я вручную выписала по строкам 152 предложения из оригинального файла, а потом сравнила полученную сегментацию с результатами обеих программ. Pragmatic Segmenter успешно справился со 150 предложениями из 152 (98.7%), NLTK Punkt — со 144 из 152 (94.7%); но выборка, безусловно, была невелика.
Можно сказать, что Pragmatic Segmenter, основанный на правилах, в целом лучше справляется с задачей: он не разделяет по точкам даты и числительные в немецком языке, включает информацию в скобках в единый сегмент с тем предложением, в которое она инкорпорирована. Если в тексте в скобках встречаются числа/даты/числительные, идущие друг за другом при счете, сегментатор анализирует их и идущие за ними слова как элементы нумерованного списка. Что касается NLTK Punkt, то алгоритм не осведомлен об особенности немецкого языка, касающейся оформления чисел, так что делит предложения с числами по точке. И Pragmatic Segmenter, и NLTK Punkt делают ошибки, связанные с аббревиацией (причем, по-разному; но, например, var. оба анализируют неверно). Оба сегментатора корректно анализируют инициалы и предложения с цитированием (они анализируются как один сегмент).