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

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

要旨

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…

部分継続チュートリアル

この文書についてこれはCommunity Scheme Wikiで公開されているcomposable-continuations-tutorial(2010年09月30日版)の日本語訳です。誤字脱字・誤訳などがありましたらコメントあるいはメールで御指摘いただけると幸いです。本訳は原文のライセンスに基づきCreative Commons Attribution-ShareAlike 2.0 Genericの下で公開されます。Original text: Copyright© 2006-2010 Community Scheme WikiJapanese translation: Copyright© 2011 SATOH Koichi本文部分継続(Composable continuation)は継続区間を具象化することで制御を逆転させるものです。 ウンザリするほど複雑な概念を表す長ったらしいジャーゴンのように聞こえますが、実際はそうではありません。今からそれを説明します。resetとshiftという2つのスペシャルフォームを導入するところから始めましょう[1]。 (reset expression)は特別な継続を作るなりスタックに目印を付けるなりしてからexpressionを評価します。簡単に言えば、expressionが評価されるとき、あとから参照できる評価中の情報が存在するということです。 実際にはshiftがこの情報を参照します。(shift variable expression)は目印のついた場所、つまりresetを使った場所にジャンプし、その場所からshiftを呼び出した場所までのプログラムの断片を保存します; これはプログラムの区間を「部分継続」として知られる組み合わせ可能な手続きに具象化し、この手続きにvariableを束縛してからexpressionを評価します。組み合わせ可能(Composable)という語はその手続きが呼び出し元に戻ってくるため、他の手続きと組み合わせられることから来ています。 Composable continuationの別名として例えば限定継続(Delimited continuation)や部分継続(Partial continuation)もありますが、ここでは一貫して「組み合わせ可能」という用語を使います(訳注: …