スキップしてメイン コンテンツに移動

京大テキストコーパスのパーサを書いた

要旨

CaboCha やなんかの出力形式であるところの京大テキストコーパス形式のパーサモジュールを Perl で書いたので紹介します。

これを使うと例えば CaboCha の出力した係り受け関係を Perl のオブジェクトグラフとして取得できます。

使用例

単なる文節区切りの例。

#!/usr/bin/env perl

use v5.18;
use utf8;
use IPC::Open3;
use Parse::KyotoUniversityTextCorpus;
use Parse::KyotoUniversityTextCorpus::MorphemeParser::MeCab;
use Symbol qw//;

my ($in, $out, $err);
my $pid;

BEGIN {
  ($in, $out, $err) = (Symbol::gensym, Symbol::gensym, Symbol::gensym);
  $pid = open3($in, $out, $err, cabocha => '-f1');
}

END {
  close $out;
  close $err;
  waitpid $pid => 0 if defined $pid;
}

binmode STDOUT, ':encoding(utf8)';
binmode $in, ':encoding(utf8)';
binmode $out, ':encoding(utf8)';

my $parser = Parse::KyotoUniversityTextCorpus->new(
  morpheme_parser =>
    Parse::KyotoUniversityTextCorpus::MorphemeParser::MeCab->new,
);

say $in '星から出るのに、その子は渡り鳥を使ったんだと思う。';
say $in '出る日の朝、自分の星の片付けをした。';
close $in;
my $sentence_trees = $parser->parse(fh => $out);
for my $sentence_tree (@$sentence_trees) {
  for my $chunk (@{ $sentence_tree->as_arrayref }) {
    printf(
      "\%d: \%s -> \%d\n",
      $chunk->id,
      $chunk->surface,
      $chunk->is_root ? '-1' : $chunk->dependency->id,
    );
  }
  print "\n";
}

実行すると:

0: 星から -> 1
1: 出るのに、 -> 5
2: その -> 3
3: 子は -> 5
4: 渡り鳥を -> 5
5: 使ったんだと -> 6
6: 思う。 -> -1

0: 出る -> 1
1: 日の -> 2 
2: 朝、 -> 6 
3: 自分の -> 4
4: 星の -> 5
5: 片付けを -> 6
6: した。 -> -1

立志

日本語係り受け解析器 CaboCha1 は大変便利ですごく便利ですが (便利なので2回言いました) 公式の Perl バインディングは SWIG 製で API 的にあんまり良い感じじゃないし CPAN にも上がっていないのが悲しい点です。MeCab に対する Text::MeCab のような素敵なバインディングも今のところありません。

簡単に使って幸せになるには cabocha コマンドをパイプで繋いで出力をパースするのが手っ取り早いです。CaboCha の出力形式には人間用のツリー形式の他に京都大学テキストコーパス (以下 KC)2/XML/CoNLL の各形式があります。 この中でパースし易いのは XML 形式ですが、これは CaboCha の独自形式 (だと思う) なので対応してもあまり面白くありません。KC 形式と CoNLL 形式は他のツールでも使われています。例えば日本語の構文解析器として CaboCha の他に KNP3 や J.DepP4 が知られていて、これらも KC 形式で出力ができるので必要になったら CaboCha から切り替えて使えます。多分これを処理する車輪は知の高速道路の路肩に一杯転がっているはずなんですが、見つからなくて辛いので自分で書くことにしました。

導入

Tarball を cpanm で突っこむのが早いです:

cpanm http://sekia.github.io/Parse-KyotoUniversityTextCorpus-0.01.tar.gz

リポジトリから最新版を入れる場合は Dist::Zilla (dzil) でビルドする必要があります:

git clone git@github.com/sekia/Parse-KyotoUniversityTextCorpus.git
cd Parse-KyotoUniversityTextCorpus
dzil install

そのうち CPAN に上げるので気の長い人はそれまで待っててください。

使い方

だいたい perldoc 参照ですが Parse::KyotoUniversityTextCorpusnew して parse したら係り受け関係の木構造の根 (即ち最後の文節) に相当する Parse::KyotoUniversityTextCorpus::Chunk を含んだ配列リファレンスが返ってくるので、ここからトラバースするのが基本的な使い方です。 上のコード例のように as_arrayref を呼ぶと文節が順番に並んだ配列リファレンスが返ってくるので単に文節を分割したいだけの時なんかは便利です。

MorphemeParser について

KC の文節中の形態素の出力形式は形態素解析器 JUMAN のものですが、CaboCha は内部で MeCab を使っているので当然 MeCab の形式になります。また MeCab は出力形式や辞書が設定で変更できるので、KC 形式でも形態素の出力形式は様々あることになります。 なので Parse::KyotoUniversityTextCorpus では形態素のパースはせず、一行一形態素とみなして MorphemeParser というオブジェクトに丸投げするようになっています。 ディストリビューションに含まれている MorphemeParser は Parse::KyotoUniversityTextCorpus::MorphemeParser::MeCab で、これは IPA 辞書を使った MeCab が出力するデフォルトの出力をパースできます。JUMAN とか ChaSen とか Unidic を使った MeCab とかの形式がパースしたい人は頑張って自分で書いてください。

TODO

  • Chunk にもっとメソッドを生やす
  • JUMAN の MorphemeParser も追加する
  • CPAN にアップロード

コメント

このブログの人気の投稿

OCaml で Web フロントエンドを書く

要旨フロントエンド開発に Elm は堅くて速くてとても良いと思う。昨今の Flux 系アーキテクチャは代数的データ型と相性が良い。ところで工数を減らすためにはバックエンドも同じ言語で書いてあわよくば isomorphic にしてしまいたいところだが、Elm はバックエンドを書くには現状適していない。OCaml なら js_of_ocaml でエコシステムを丸ごとブラウザに持って来れるのでフロントエンドもバックエンドも無理なく書けるはずである。まず The Elm Architecture を OCaml で実践できるようにするため Caelm というライブラリを書いている。俺の野望はまだまだこれからだ (未完)Elm と TEA についてElm というプログラミング言語がある。いわゆる AltJS の一つである。 ミニマリスティクな ML 系の関数言語で、型推論を持ち、型クラスを持たず、例外機構を持たず、変数の再代入を許さず、正格評価され、代数的データ型を持つ。 言語も小綺麗で良いのだが、何より付属のコアライブラリが体現する The Elm Architecture (TEA) が重要である。TEA は端的に言えば Flux フロントエンド・アーキテクチャの変種である。同じく Flux の派生である Redux の README に TEA の影響を受けたと書いてあるので知っている人もいるだろう。 ビューなどから非同期に送信される Message (Redux だと Action) を受けて状態 (Model; Redux だと State) を更新すると、それに対応して Virtual DOM が再構築されビューがよしなに再描画され人生を書き換える者もいた——という一方向の流れはいずれにせよ同じである。 差異はオブジェクトではなく関数で構成されていることと、アプリケーション外部との入出力は非同期メッセージである Cmd / Sub を返す規約になっていることくらいだろうか。後者は面白い特徴で、副作用のある処理はアプリケーションの外で起きて結果だけが Message として非同期に飛んでくるので、内部は純粋に保たれる。つまり Elm アプリケーションが相手にしないといけない入力は今現在のアプリケーションの完全な状態である Model と、時系列イベントである Me…

Algorithm::LibLinear の紹介

Notice: This article is outdated. Please refer an updated English tutorial. 要旨かなり前になりますが、Algorithm::LibLinear という Perl モジュールを書きました。CPANGithubこれを使うと線形分類器などが高速に学習できます。テキストや画像の分類が応用として期待されます。LIBLINEAR についてLIBLINEARLIBSVM と同じ台湾国立大学の Chih-Jen Lin 教授のチームが公開しているオープンソースの機械学習パッケージです。 関数のロジスティック回帰、サポートベクター回帰及び線形 SVM による多クラス分類を行うことができます。LIBSVM と違ってカーネル関数を使うことはできませんが、はるかに高速に動作します。Algorithm::LibLinear についてLIBLINEAR には C++ で書かれたライブラリと、その機能を使って機械学習と分類・関数回帰を行うコマンドラインユーティリティが含まれています。 Algorithm::LibLinear はライブラリの機能を Perl からオブジェクト指向的に利用できるようにした上で、コマンドラインユーティリティの一部機能をライブラリ化して Perl で再実装したものです。使い方分類問題を解くときは、訓練データセットの読み込み・スケーリング学習器パラメータの設定分類器の訓練実データの分類という手順で行います。訓練データセットの読み込み正解ラベルのついたデータを大量に用意して学習させます。LIBSVM 形式のデータを読み込むか:my $data_set = Algorithm::LibLinear::DataSet->load(string => <<'EOD'); 1 1:0.1 2:0.1 4:0.1 -1 1:0.1 2:-0.1 3:0.1 ... EOD HashRef として表現されたデータを使います:my $data_set = Algorithm::LibLinear::DataSet->new(data_set => [ +{ feature => +{ 1 => 0.1, 2 => 0.1, 4 =…