Поділ тексту на речення за допомогою Томіта-парсера

Щоб підготувати російськомовні тексти для подальшого аналізу, мені одного разу знадобився розбити їх на пропозиції. Зрозуміло, автоматично. Що спадає на думку в першу чергу, якщо потрібно розділити текст на речення? Розбити по точках — вгадав?

Якщо ви почнете це робити, то досить швидко зіткнетеся з тим, що точка — це не завжди роздільник пропозицій («тому що», «тощо», «тощо», "інші", «S. T. A. L. K. E. R.»). Причому ці токени не завжди будуть винятками при розбивці тексту на речення. Наприклад, «тощо» може бути в середині речення, а може і в кінці.

Питальний і окличний знак теж не завжди поділяють текст на речення. Наприклад, «Yachoo!». Пропозиції можуть розділяти і інші знаки, наприклад, двокрапка (коли слід список з окремих тверджень).

Тому я довго не думаючи пошукав готовий інструмент і зупинився на Томіта-парсере від Яндекса. Про нього і розповім.


Взагалі, Томіта-парсер — це потужний інструмент для вилучення фактів з тексту. Сегментатор (розбивка тексту на речення) у ньому — лише частина проекту. Томіта-парсер можна завантажити відразу у вигляді бінарника і запускати з командного рядка. Мені ця система сподобалася тим, що вона працює на основі правил, не вибаглива до ресурсів і дає можливість налаштовувати процес сегментації. А також за моїми спостереженнями, в більшості випадків відмінно справляється із завданням.

Ще мені сподобалося, що при виникненні запитань можна поставити їх на github і іноді навіть отримати відповідь.

Запуск
Запускається Томіта-парсер таким чином

$ echo "Парсер, Розбий ці... літери, знаки і т. п. на пропозиції. І покажи пож. як зі словом S. T. A. L. K. E. R. вийде." | ./tomita-linux64 config.proto

Тобто читання відбувається з stdin, висновок — в stdout.
Результат отримуємо приблизно такий:

[10:01:17 17:06:37] - Start. (Processing files.)
Парсер , Розбий ці . . . літери , знаки і т. п . на пропозиції . 
І покажи пож . як зі словом S. T. A. L. K. E. R. вийде . 
[10:01:17 17:06:37] - End. (Processing files.)


Одна рядок — речення. На цьому прикладі видно, що розбивка пройшла коректно.

Особливості
На що звертаємо увагу.
  • результат додаються пропуски перед знаками пунктуації.
  • Зайві прогалини видаляються.
  • Відбувається автоматична корекція деяких помилок (наприклад, у вихідному тексті остання буква в слові «Парсер» — це англійська «пі», а в обробленому тексті — це вже російська «ер»).
Ці особливості можуть бути як плюсами, так і мінусами в залежності від того, що ви далі будете робити з отриманим текстом. Я, наприклад, далі по отриманому тексту строю синтаксичні дерева з допомогою SyntaxNet, а там як раз розділові знаки повинні бути відокремлені пропусками, так що для мене це плюс.

Налаштування
Я зіткнувся з тим, що при аналізі речень, що містять адреси, система розбиває їх некоректно. Приклад:

$ echo "Я живу на вул. Леніна і мене зарубает час від часу." | ./tomita-linux64 config.proto
[10:01:17 18:00:38] - Start. (Processing files.)
Я живу на вул . 
Леніна і мене зарубает час від часу . 
[10:01:17 18:00:38] - End. (Processing files.)

Як бачимо, розбивка пройшла некоректно. На щастя, такі речі можна настроювати. Для цього в gzt файлі прописуємо

TAbbreviation "вул." {
key = { "abbreviation_г." type = CUSTOM }
text = "вул."
type = NewerEOS
}

Тобто просимо вважати, що після «вул.» пропозиція завжди триває. Пробуємо:

$ echo "Я живу на вул. Леніна і мене зарубает час від часу." | ./tomita-linux64 config.proto
[10:01:17 18:20:59] - Start. (Processing files.)
Я живу на вул. Леніна і мене зарубает час від часу . 
[10:01:17 18:20:59] - End. (Processing files.)

Тепер все добре. Приклад налаштувань я виклав на github.

Які мінуси
Про деякі особливості я згадав вище. Пару слів про мінуси інструменту на даний момент.

Перше — це документація. Вона є, але в ній описано не всі. Спробував зараз пошукати налаштування, яку описав вище — не знайшов.

Друге — це відсутність легкої можливості роботи з парсером в режимі демона. Обробка одного тексту за 0.3-0.4 секунди з урахуванням завантаження всієї системи в пам'ять для мене не критична, так як вся обробка йде в фонових процесах і серед них є набагато більш жирні завдання. Для когось це може стати вузьким місцем.

Приклад виклику з PHP
Як і говорив вище, подаємо вхідні дані в stdin, читаємо з stdout. Приклад нижче зроблений на основі github.com/makhov/php-tomita:
<?php

class TomitaParser
{
/**
* @var string Path to Yandex's Tomita-parser binary
*/
protected $execPath;

/**
* @var string Path to Yandex's Tomita-parser configuration file
*/
protected $configPath;

/**
* @param string $execPath Path to Yandex's Tomita-parser binary
* @param string $configPath Path to Yandex's Tomita-parser configuration file
*/
public function __construct($execPath, $configPath)
{
$this->execPath = $execPath;
$this->configPath = $configPath;
}

public function run($text)
{
$is invalid = array(
0 => array('pipe', 'r'), // stdin
1 => array('pipe', 'w'), // stdout
2 => array('pipe', 'w') // stderr
);

$cmd = sprintf('%s %s', $this->execPath, $this->configPath);
$process = proc_open($cmd, $is invalid, $pipes, dirname($this->configPath));

if (is_resource($process))
{

fwrite($pipes[0], $text);
fclose($pipes[0]);

$output = stream_get_contents($pipes[1]);

fclose($pipes[1]);
fclose($pipes[2]);

return $this->processTextResult($output);
}

throw new \Exception('proc_open fails');
}

/**
* Обробка текстового результату
* @param string $text
* @return string[]
*/
public function processTextResult($text)
{
return array_filter(explode("\n", $text));
}

}

$parser = new TomitaParser('/home/mnv/tmp/tomita/tomita-linux64', '/home/mnv/tmp/tomita/config.proto');
var_dump($parser->run('Пропозиція раз. Пропозиція два.'));

Перевіряємо:

$ php example.php 
/home/mnv/tmp/tomita/example.php:66:
array(2) {
[0] =>
string(32) "Пропозиція раз . "
[1] =>
string(32) "Пропозиція два . "
}


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

Буду радий дізнатися з коментарів, яким інструментом для розбивки тексту на речення користуєтеся ви?
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.