能動学習システムDUALISTを日本語テキストに適用する

機械学習手法に基づくテキスト分類は十分な学習データがあれば高い精度が期待できますが、分類ラベルを人手でつける作業に手間がかかります。そこで、効率的に分類器を学習させる手法として、効果的な分類対象を優先的にラベル付けさせる能動学習(active learning)というアプローチがあります。

DUALISTは、アノテータに対象のラベル付けと同時に、素性であるキーワードが適切かどうかの判定を委ねる能動学習システムで、7月に開催されるEMNLP 2011に採択された論文で提案されており、実装も公開されています。
Google Code Archive - Long-term storage for Google Code Project Hosting.

DUALISTのインストールと実行は簡単です。システムはJavaで実装されていて、機械学習パッケージのMALLETが同梱されています。他に、WebフレームワークのPlay!をインストールする必要があります。その上で、DUALISTを展開したディレクトリ内でplay runを実行すると、ブラウザでhttp://localhost:9000/にアクセスしてシステムを試すことができます。

しかし残念ながら、そのままでは日本語のテキストを扱うことができません。そこで、MeCabJavaバインディングを利用してDUALISTを日本語テキストに適用することにします。

まず、MeCabを使って名詞と未知語を抽出するMALLETのPipeを定義します。

package dualist.ja;

import cc.mallet.pipe.Pipe;
import cc.mallet.extract.StringSpan;
import cc.mallet.extract.StringTokenization;
import cc.mallet.types.Instance;
import cc.mallet.types.TokenSequence;

import org.chasen.mecab.Tagger;
import org.chasen.mecab.Node;

public class SimpleMecabPipe extends Pipe
{
    static {
        try {
            System.loadLibrary("mecab-java");
        } catch (UnsatisfiedLinkError e) {
            System.err.println("ERROR: Failed to load mecab-java native code.");
            System.err.println(e);
            System.exit(1);
        }
    }

    public Instance pipe (Instance carrier)
    {
        CharSequence input = (CharSequence) carrier.getData();
        String string = input.toString();
        Tagger tagger = new Tagger();
        Node node = tagger.parseToNode(string);
        int cursor = 0;
        TokenSequence ts = new StringTokenization(input);
        while (node != null) {
            node = node.getNext();
            if (node == null) break;
            String[] f = node.getFeature().split(",");
            if (f[0].equals("名詞") &&
                !f[1].equals("数") && !f[1].equals("サ変接続") && !f[1].equals("接尾") ||
                f[0].equals("未知語")) {
                String surface = node.getSurface();
                cursor = string.indexOf(surface, cursor);
                ts.add (new StringSpan(input, cursor, cursor + surface.length()));
            }
        }
        carrier.setData(ts);
        return carrier;
    }
}

次に、dualist/app/guts/pipes/DocumentPipe.javaでSerialPipesを生成する引数のうち、CharSequence2TokenSequenceのところを、先ほどのSimpleMecabPipeに置き換えます。なお、Play!フレームワークJavaベースでありながら、スクリプト言語よろしくコンパイルせずに実行できる優れものですが、JNIに必要なネイティブライブラリの読み込みができないようです。そこで、SimpleMecabPipeクラスはあらかじめコンパイルしておき、dualist/lib/dualist-ja.jarに格納して参照することにします。また、MeCabのJNI用jarファイルも同じディレクトリに置きます。

さて、動作確認ですが、JNI用ネイティブライブラリのパスを指定してplayコマンドを実行します。ブラウザでhttp://localhost:9000/にアクセスして、日本語のデータサンプルを読み込めば、画面右側の素性リストに日本語の単語が表示されるはずです。

以上の内容をMacPortsの形にまとめて現在登録申請中です。このportを使うと、sudo port install dualist +mecabでインストールができて、dualist-mecabを実行することで、日本語テキストを対象としたシステムが利用できます。