snuffkinの遊び場

IT関係、スポーツ、数学等に関することを、気が向いたときに書いてます。

Jubatus Casual Talks #1に行ってきました & Jubabaは便利!

Jubatus Casual Talks #1に行ってきました

今日は日産スタジアムでAKB48の総選挙が開催されていました。私の最寄り駅は日産スタジアムのある駅と同じなので、混雑を心配したのですが、混む時間より前に無事帰宅できました。
指原さんがセンターを取ったとのことで思わぬ結果となったそうですが、総選挙の結果って、Jubatusでtwitterを分析したら事前に分かるのでしょうか?そんなことを考えつつ、AKB48の総選挙を横目に、ブログ書いてます^^

ところで、Jubatusのロゴはカワイイですね。ゆるきゃらとか作ったら面白いと思います。

さて、もう開催から一週間経ってしまいますが、6/2(日)にJubatus Casual Talks #1に行ってきました。98名の申し込みに85名の参加だったそうで、こういう会の中では高出席率だったのではないかと思います。

開発チームの方だけでなくJubatusの利用者からの発表もあり、様々な発表を聞くことができ、勉強になりました。私の趣味では、@komiya_atsushiさん「Java で楽して Jubatus したい!」@shimtoruさん「世界征服を目指す Jubatus だからこそ期待する 5 つのポイント」が特に興味深かったです。

Java で楽して〜」はJavaクライアントが使いづらいので、使いやすくしたJubabaというラッパーを作った発表でした。私が携わる案件はJavaを利用することが多く、Javaには馴染み深いです。Jubatus Javaクライアントを触ったことある人なら「ちょっとイケてないよね」と思う所を、「これは使いやすい」にしてくれるのがJubabaです。このエントリの後半で詳しく書きますが、Jubabaいいですね。

「世界征服を目指す〜」は実システムでJubatusを利用するために越えなくてはならない点を、実際に利用した立場から分析しています。SIする側からすると、こういう点はとても重要ですよね。

Jubatus Blogにまとまっていますが、その他の発表も、いろんな立場のいろんな人が発表しており、興味深かったです。また、懇親会で開発チームの方々から興味深い話を聞けたのは良かったです。これだけの規模の運営は大変かと思いますが、是非第2回も開催して欲しいと思います。

Jubabaは便利!

Jubatus Casual Talks #1で興味をもったJubabaを使ってみました。現状はClassifierのみに対応、とのことですので、Jubatusのチュートリアルに載っているClassifier(Java版)をJubabaで書き直してみました。
動かす前にJubatusをインストールする必要がありますが、手っ取り早く使いたい場合は第1回 Jubatus ハンズオンのページからJubatusインストール済みのVirtualBoxVMWareのイメージをダウンロードすると便利です。(Jubatus動かすには困らないのですが、私にはこのVMイメージのrootのパスワードが分かりませんでした。。。)

で、チュートリアルのShogun.javaをJubabaを使って書き直したソースが以下になります。「Javaのコーディング作法的にどうよ」ってツッコミもあるとは思いますが、元のソースを極力尊重しましたので、その辺りはご容赦ください。

package jp.gr.java_conf.snuffkin.sandbox.jubatus.classifier;

import java.util.Collections;
import java.util.List;

import jubaba.Configuration;
import jubaba.Configuration.ConfigurationBuilder;
import jubaba.classifier.BulkClassifier;
import jubaba.classifier.BulkClassifierTrainer;
import jubaba.classifier.ClassificationResult;
import jubaba.classifier.Classifier;
import us.jubat.classifier.Datum;

public class Shogun {
    public static final String HOST = "127.0.0.1";
    public static final int PORT = 9199;
    public static final String NAME = "shogun";

    String[] data1 = {
            "家康", "秀忠", "家光", "家綱", "綱吉",
            "家宣", "家継", "吉宗", "家重", "家治",
            "家斉", "家慶", "家定", "家茂"
    };
    String[] data2 = {
            "尊氏", "義詮", "義満", "義持", "義量",
            "義教", "義勝", "義政", "義尚", "義稙",
            "義澄", "義稙", "義晴", "義輝", "義栄"
    };
    String[] data3 = {
            "時政", "義時", "泰時", "経時", "時頼",
            "長時", "政村", "時宗", "貞時", "師時",
            "宗宣", "煕時", "基時", "高時", "貞顕"
    };

    private Classifier classifier;

    private final void start() throws Exception {
        // 1.Jubatus Serverへの接続設定
        Configuration conf = new ConfigurationBuilder().host(HOST).port(PORT)
            .timeoutSeconds(10).build();
        classifier = new Classifier(conf, NAME);
        
        // 2.学習用データの準備
        BulkClassifierTrainer trainer = makeStudyData();
        
        // 3.データの学習(学習モデルの更新)
        trainer.train();
        
        // 4.予測用データの準備
        BulkClassifier bulkClassifier = makeExData();
        
        // 5.学習データに基づく予測
        List<ClassificationResult> result = bulkClassifier.classify();
        
        // 6.結果の出力
        output(bulkClassifier.data, result);
        return;
    }

    private final BulkClassifierTrainer makeStudyData() {
        String familyName = "";
        
        // ループ処理にて、各将軍の姓と名のセットを作成
        BulkClassifierTrainer trainer = classifier.newBulkClassifierTrainer();
        for (int labelIndex = 0; labelIndex < 3; labelIndex++) {
            String[] nameList = null;
            switch (labelIndex) {
            case 0:
                familyName = "徳川";
                nameList = this.data1;
                break;
            case 1:
                familyName = "足利";
                nameList = this.data2;
                break;
            case 2:
                familyName = "北条";
                nameList = this.data3;
                break;
            }
            
            for (String name : nameList) {
                trainer.newFeatures().label(familyName).add("name", name);
            }
        }
        // 学習用データをシャッフル
        Collections.shuffle(trainer.data);
        
        return trainer;
    }

    private final BulkClassifier makeExData() {
        // ループ処理にて、各将軍の姓と名のセットを作成
        BulkClassifier bulkClassifier = classifier.newBulkClassifier();
        String name = null;
        for (int labelIndex = 0; labelIndex < 3; labelIndex++) {
            switch (labelIndex) {
            case 0:
                name = "慶喜";
                break;
            case 1:
                name = "義昭";
                break;
            case 2:
                name = "守時";
                break;
            }
            bulkClassifier.newFeatures().add("name", name);
        }
        return bulkClassifier;
    }

    private void output(List<Datum> exData, List<ClassificationResult> result) {
        // 結果の出力
        for (int index = 0; index < result.size(); index++) {
            System.out.print(result.get(index).maximumScoredLabel() + " "
                    + exData.get(index).string_values.get(0).second + "\n");
        }
        System.out.println();
    }

    public static void main(String[] args) throws Exception {
        new Shogun().start();
        System.exit(0);
    }
}

チュートリアルでは168行あったソースが127行になりました。約25%削減ですね。といっても、相変わらず長いか。
ここで、主張したいポイントは「約25%削減」ではなくて、「Datum周りの実装量の削減」です。チュートリアルの81〜96行目、123〜136行目、147〜160行目をそれぞれ数行に短縮することができました。Jubabaによって、今までJubatusに渡すエンティティの構築に行数を割かれていたのが解消されます。生産性は上がるし、エンジニアのやる気(精神衛生)も上がりますね。

ただ、本家Jubatusが改善されないと、どうにもならないこともあります。実はエンティティクラスにtoStringメソッドがありません。正直、デバッグするのがツライっす。是非、toStringメソッドの実装をお願いします。

という訳でまとめると、JavaでJubatusを使いたい人には、Jubabaはオススメです。使うべきです。Classifier以外の実装も楽しみにしています!