Lemmatizer UTF-8 対応ライブラリ

ロシア語形態素解析器 Lemmatizer ライブラリ(Лемматизатор европейских языков)を利用した試験プログラムを書いてみた。Lemmatizer はロシア単語の文法構造と見出し語を解析する。このライブラリは C/C++ のアプリケーションから利用できる。UTF-8 テキストを解析できる点で,多国語混在テクスト中のロシア語解析にも応用可能である。このパッケージは先日紹介したものを少し拡張したもののようである。私は FreeBSD で試験したが,Linux,Mac OS X でも動くと思う。Lemmatizer の各ライブラリは静的ライブラリなので,アプリのプログラム・サイズが大きくなってしまうところが残念である。

【インストール】

インストールには CMake が必要である。FreeBSD なら

# cd /usr/ports/devel/cmake && make install clean

で導入可能である。次に Lemmatizer の次の三つのライブラリ・アーカイブをダウンロードページから取得して展開する:libMAFSA-0.2.tar.gz, libturglem-0.2.tar.gz, turglem-russian-0.2.tar.gz。オペレーションは以下のとおり。ただし,turglem-russian-0.2 の make は辞書生成のため,かなりの時間と 100MB 以上のメモリを要するので注意。

% cd libMAFSA-0.2
% cmake .
% make
% sudo make install
% cd ../libturglem-0.2
% cmake .
% make
% sudo make install
% cd ../turglem-russian-0.2
% cmake .
% make
% sudo make install

【試験プログラム】

Lemmatizer 利用アプリケーション・プログラムの流れは,tl::lemmatizer クラスを生成し,辞書・文法ルールをロードし,解析したい語に対して lemmatize<russian_utf8_adapter> を呼び出す。UTF-8 文字列もそのまま char* に入れればよい(UTF-16 にした wchar_t* などを期待しない)ので面倒がない。解析結果は tl::lem_result クラスに格納されるので,必要な情報をこのクラスから取り出して活用する。サンプルプログラムを参考に私が作成したテストプログラムを,以下に示す。テキストファイルを読込んで,単語を分割し,Lemmatizer に解析させる。単語リストの作成にはもう少しきちんとした(句読点を考慮した)tokenizer が必要だが,ここでは,指定文字で語分割するごく単純な自前の split 関数ですませている。

// -*- coding: utf-8; -*-
// lemtest.cpp: Lemmatizer test programm.
//                                    2009 (c) isao yasuda.
 
#include <turglem/lemmatizer.hpp>
#include <turglem/russian/charset_adapters.hpp>
#include <string>
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <cstdio>
 
using namespace std;
 
// Lemmatizer
void lem_analyze(const tl::lemmatizer &lem, const char *s)
{
    tl::lem_result lr;  // instance for result of lemmatizer analyze.
    size_t sz_lem = lem.lemmatize<russian_utf8_adapter>(s, lr);
 
    if (sz_lem) {
        printf("(lemmatize results: %zd)\n", sz_lem);
        for (size_t i = 0; i < sz_lem; i++) {
            printf("    ** %zd: paradigm/form = %04d/%02d", i,
                    lem.get_paradigm(lr, i),
                    lem.get_src_form(lr, i)
                );
            string nform = lem.get_text<russian_utf8_adapter>(lr, i, 0);
            printf("\t\t\t\tNormal Form: '%s'\n", nform.c_str());
        }
    } else {
        cout << "Empty result! Check your text." << endl;
    }
}
 
// split 関数: 指定文字で文字列を分割
void split(const string &s, char c, vector<string> &v) {
    string::size_type i = 0;
    string::size_type j = s.find(c);
 
    while (j != string::npos) {
        v.push_back(s.substr(i, j-i));
        i = ++j; j = s.find(c, j);
        if (j == string::npos)
            v.push_back(s.substr(i, s.length()));
    }
}
 
// main
int main(int argc, char **argv)
{
    tl::lemmatizer lem;  // lemmatizer instance
    string buffer;
    ifstream in(argv[1]);
 
    if (!in) {
        cerr << "usage: " << argv[0] << " file-name" << endl;
        return 1;
    }
    // load dict, etc.
    try {
        lem.load_lemmatizer(
            "/usr/local/share/turglem/russian/dict_russian.auto",
            "/usr/local/share/turglem/russian/paradigms_russian.bin",
            "/usr/local/share/turglem/russian/prediction_russian.auto"
            );
    }
    catch (const exception &e) {
        cerr << "Error: " << e.what() << endl;
    }
 
    while (!in.eof()) {
        vector<string> v;
        getline(in, buffer, '\n');
        if (!buffer.c_str()) continue;
        split(buffer, ' ', v);
        cout << buffer << endl;
 
        for (int i = 0; i < v.size(); i++) {
            cout << "  " << i << " input word: " << v[i] << ' ';
            // lemmatizer analyze.
            try {
                if (v[i].c_str()) lem_analyze(lem, v[i].c_str());
            }
            catch (const exception &e) {
                cerr << "Error: " << e.what() << endl;
            }
        }
    }
    return 0;
}

コンパイルは以下のようにする。ライブラリの指定順序が違うとリンクでエラーとなる。

% g++ -I/usr/local/include -L/usr/local/lib -o lemtest lemtest.cpp \
      -lturglem -lturglem-russian -lMAFSA

lemtest 入力ファイル” で動く。試験入力内容と実行結果を以下に示す。

入力:

Я люблю вас
Саша прочитал книгу на русском языке

実行結果:

Я люблю вас
  0 input word: Я (lemmatize results: 1)
    ** 0: paradigm/form = 2297/00               Normal Form: 'Я'
  1 input word: люблю (lemmatize results: 1)
    ** 0: paradigm/form = 0913/01               Normal Form: 'ЛЮБИТЬ'
  2 input word: вас (lemmatize results: 3)
    ** 0: paradigm/form = 0742/01               Normal Form: 'ВЫ'
    ** 1: paradigm/form = 0742/03               Normal Form: 'ВЫ'
    ** 2: paradigm/form = 0742/05               Normal Form: 'ВЫ'
Саша прочитал книгу на русском языке
  0 input word: Саша (lemmatize results: 1)
    ** 0: paradigm/form = 2485/00               Normal Form: 'САША'
  1 input word: прочитал (lemmatize results: 1)
    ** 0: paradigm/form = 0440/01               Normal Form: 'ПРОЧИТАТЬ'
  2 input word: книгу (lemmatize results: 1)
    ** 0: paradigm/form = 0088/03               Normal Form: 'КНИГА'
  3 input word: на (lemmatize results: 1)
    ** 0: paradigm/form = 0026/00               Normal Form: 'НА'
  4 input word: русском (lemmatize results: 3)
    ** 0: paradigm/form = 0002/06               Normal Form: 'РУССКИЙ'
    ** 1: paradigm/form = 0002/19               Normal Form: 'РУССКИЙ'
    ** 2: paradigm/form = 0313/05               Normal Form: 'РУССКИЙ'
  5 input word: языке (lemmatize results: 2)
    ** 0: paradigm/form = 0010/05               Normal Form: 'ЯЗЫК'
    ** 1: paradigm/form = 0021/05               Normal Form: 'ЯЗЫК'

"paradigm/form" に文法構造が示される。この数値で品詞,性・数・格などが示される。詳細は現在調査中である。"Normal Form" は見出し語である。

自作プログラムから Lemmatizer を呼出す方法の概略を,これで理解できた。そのうち,PCREBoost::regex などの正規表現ライブラリと組合わせて,コンコーダンス・プログラム,ロシア語旧正書法変換プログラムなどを開発したいと思っている。