Recommender チュートリアル (Groovy)

ここではGroovy版のRecommenderサンプルプログラムの解説をします。

ソースコード

このサンプルプログラムでは、学習アルゴリズム等の設定をするnpb_similar_player.jsonと野手データの学習を行うupdate.groovy、推薦を実行して結果を出力するanalyze.groovy、また学習用データとしてbaseball.csvを使用します。 以下にnpb_similar_player.jsonとupdate.groovyおよびanalyze.groovyのソースコードを記載します。

npb_similar_player.json

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
{
  "method": "inverted_index",
  "converter": {
    "string_filter_types": {},
    "string_filter_rules": [],
    "num_filter_types": {},
    "num_filter_rules": [],
    "string_types": {},
    "string_rules": [],
    "num_types": {},
    "num_rules": [
      {"key" : "*", "type" : "num"}
    ]
  },
  "parameter": {}
}

update.groovy

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@GrabResolver(name='jubatus', root='http://download.jubat.us/maven')
@GrabResolver(name='msgpack', root='http://msgpack.org/maven2/')
@Grab('us.jubat:jubatus:[0.6,)')
import us.jubat.recommender.RecommenderClient
import us.jubat.common.Datum

HOST = "127.0.0.1"
PORT = 9199
NAME = "recommender_baseball"
TIMEOUT = 5

columns = ["打率", "試合数", "打席", "打数", "安打", "本塁打", "打点", "盗塁", "四球", "死球", "三振", "犠打", "併殺打", "長打率", "出塁率", "OPS", "RC27", "XR27"]
csv = new File("dat/baseball.csv")
recommender = new RecommenderClient(HOST,PORT,NAME,TIMEOUT)

csv.eachLine{ line ->
  datum = new Datum()
  splitted = line.split(",")
  datum.addString( "チーム", splitted[1] )
  columns.eachWithIndex{ it, idx ->
    datum.addNumber( it, splitted[idx+2] as Double )
  }
  println splitted[0]
  recommender.updateRow(splitted[0], datum)
}

println "done!"

analyze.groovy

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
@GrabResolver(name='jubatus', root='http://download.jubat.us/maven')
@GrabResolver(name='msgpack', root='http://msgpack.org/maven2/')
@Grab('us.jubat:jubatus:[0.6,)')
import us.jubat.recommender.RecommenderClient

HOST = "127.0.0.1"
PORT = 9199
NAME = "recommender_baseball"
TIMEOUT = 5

columns = ["打率", "試合数", "打席", "打数", "安打", "本塁打", "打点", "盗塁", "四球", "死球", "三振", "犠打", "併殺打", "長打率", "出塁率", "OPS", "RC27", "XR27"]
csv = new File("dat/baseball.csv")
recommender = new RecommenderClient(HOST,PORT,NAME,TIMEOUT)

csv.eachLine{ line ->
  splitted = line.split(",")
  sr = recommender.similarRowFromId(splitted[0],4)
  println "player ${splitted[0]} is similar to : ${sr[1].id} ${sr[2].id} ${sr[3].id}"
}

println "done!"

解説

npb_similar_player.json

設定は単体のJSONで与えられます。JSONの各フィールドは以下の通りです。

  • method

    分類に使用するアルコリズムを指定します。

    今回は、転置インデックスを利用したいので、"inverted_index"を指定します。 Recommenderで指定できるアルゴリズムは上記以外に、"minhash"、"lsh"、"euclid_lsh"があります。

  • converter

    特徴変換の設定を指定します。

    ここでは、"num_rules"を設定をしています。

    "num_rules"は数値特徴の抽出規則を指定します。 "key"は"*"つまり、すべての"key"に対して、"type"は"num"なので、指定された数値をそのまま重みに利用する設定です。 具体的には、打率が"0.33"であれば"0.33"を、打点が"30"であれば"30"を重みとします。

    "string_rules"は文字列特徴の抽出規則を指定します。 今回は文字列は使用しないので指定していません。

  • parameter

    アルゴリズムに渡すパラメータを指定します。 methodに応じて渡すパラメータは異なります。

    methodで“inverted_index”を指定していますので、設定不要です。

update.groovy

学習の手順を説明します。

Recommenderのクライアントプログラムは、us.jubat.recommender.RecommenderClientを利用して作成します。 使用するメソッドは、1データ分の学習を行うupdateRow()メソッドです。

  1. Jubatus Clientライブラリの準備

    Grapeを利用してJubatusのMavenリポジトリからJubatus Clientライブラリを取得します(1-3行目)。 Jubatusの依存するmsgpack-apiのMavenリポジトリも指定しておきましょう(2行目)。 JavaDocが必要なら、Mavenリポジトリ http://download.jubat.us/maven/us/jubat/jubatus/ から *-javadoc.jar が取得できます。

  2. Jubatus Serverへの接続設定

    Jubatus Serverへの接続を行います(14行目)。

    Jubatus ServerのIPアドレス,Jubatus ServerのRPCポート番号, タスクを識別するZookeeperクラスタ内でユニークな名前, タイムアウト秒数を設定します。

  3. 学習用データの準備

    Jubatus Serverに学習させるデータDatumを作成します。

    RecommenderClientでは、Datumを学習用データとして作成し、RecommenderClientのupdateRow()メソッドに与えることで、学習が行われます。 今回はプロ野球データfreakというサイトの野手データ(CSVファイル)を元に学習用データを作成していきます。 野手データの要素として、"名前"、"チーム"、"打率"、"打数"、"安打"などがあります。 下図に、今回作成する学習用データの構造を示します。

    ID(String) Datum
    Array<Array<String, String>> Array<Array<String, Float>>
    key(String) value(String) key(String) value(Float)
    "大島洋平" "チーム" "中日"
    "打率"
    "試合数"
    "打席"
    "打数"
    "安打"
    "本塁打"
    "打点"
    "盗塁"
    "四球"
    "死球"
    "三振"
    "犠打"
    "併殺打"
    "長打率"
    "出塁率"
    "OPS"
    "RC27"
    "XR27"
    0.31
    144
    631
    555
    172
    1
    13
    32
    46
    13
    80
    17
    7
    0.368
    0.376
    0.744
    5.13
    4.91
    "高橋由伸" "チーム" "巨人"
    "打率"
    "試合数"
    "打席"
    "打数"
    ・・・
    ・・・
    0.239
    130
    442
    368
    ・・・
    ・・・

    Datumとは、Jubatusで利用できるkey-valueデータ形式のことです。 Datumには3つのkey-valueが存在します。

    1つはキーも値も文字列の文字列データ(string_values)です。 1つはキーは同様に文字列で、値は数値の数値データ(num_values)です。 もう1つは、キーは同様に文字列で、値は文字列のバイナリデータ(binary_values)です。

    Datumのコンストラクタ引数で指定したvalueが文字列の場合は、string_valuesに、valueが数値の場合はnum_valuesに値がセットされます。

    表の1つ目のデータを例に説明すると、"チーム"は文字列なのでstring_valuesとしてkeyに"チーム"、valueに"中日"をセットします。
    それ以外の項目は数値なので、num_valuesに
    keyに"打率"、valueに'0.31'、
    keyに"試合数"、valueに'144'、
    keyに"打席"、valueに'631'、
    keyに"打数"、valueに'555'と
    "XR27"の項目までをセットします。

    これらの情報を保持したDatumをCSVの1行ずつ、つまり選手1人ずつ作成します。 学習用データとして選手の"名前"をDatumのID使用します。

    このサンプルでの学習用データ作成の手順は下記の流れで行います。

    まず、学習用データの元となるCSVファイルを読み込みます(13行目)。 eachLineにて1行ずつループで読み込んで処理します(16-25行目)。 CSVファイルなので、取得した1行を要素ごとに分け、リストとして保持します(18行目)。 それぞれの要素を設定しDatumを作成します(19-21行目)。

    これで、1人分の選手のデータが入ったDatumの作成が完了しました。

  4. データの学習(学習モデルの更新)

    2.の工程で作成した学習用データを、updateRow()メソッドに渡すことで学習が行われます(20行目)。 第1引数には、学習データ内でユニークな名前をIDとして指定します。 ここでは選手の"名前"をIDとして使用します。 第2引数として、先ほど 2. で作成したDatumを指定します。

    これで、選手1人分のデータの学習が完了しました。 ループ処理で 2. と 3. をCSVの行数分繰り返し実行すれば、データの学習は完了します。

analyze.groovy

推薦の手順を説明します。

学習と同様にクライアントプログラムは、us.jubat.recommender.RecommenderClientを利用して作成します。 使用するメソッドは、与えられたデータから推薦を行うsimilarRowFromId()メソッドです。

  1. Jubatus Clientライブラリの準備
    update.groovyと同様のため省略。
  2. Jubatus Serverへの接続設定
    update.groovyと同様のため省略。
  3. 推薦用データの準備
    推薦で必要なデータは先ほど学習でIDに指定した選手の"名前"になります。 学習時と同じ要領で、カラムの1番目である"名前"を取得し、RecommenderClientのsimilarRowFromId()メソッドに与えることで、推薦が行われます。
  4. 学習モデルに基づく推薦
    2.で取得した選手の"名前"を、similarRowFromId()メソッドに渡すことで、推薦結果のリストを得ることができます(17行目)。 第1引数に、"名前"を指定します。 第2引数は、似ているタイプを近傍順にいくつ出力するかを指定します。 ここでは、トップ3まで出力するので"4"を指定します。 なぜ"4"かというと、トップには自身が出力される為です。
  5. 結果の出力
    3.で取得した、推薦結果のリストはsimilarRowFromId()メソッドの第2引数に"4"を指定したので、4 つの要素を持ったリストです。 リストの1番目は自分自身なので、2番目から4番目までを結果として出力します。 update.groovyと同様、選手1人ずつループで処理し 2. ~ 4. を繰り返します。

サンプルプログラムの実行

  • Jubatus Serverでの作業

    jubarecommenderを起動します。

    $ jubarecommender --configpath npb_similar_player.json
    
  • Jubatus Clientでの作業

    下記のコマンドで実行します。

    $ groovy update.groovy
    $ groovy analyze.groovy
    

    実行結果:

    player 長野久義 is similar to : 糸井嘉男 ミレッジ 栗山巧
    player 大島洋平 is similar to : 本多雄一 石川雄洋 荒波翔
    player 鳥谷敬 is similar to : サブロー 糸井嘉男 和田一浩
    player 坂本勇人 is similar to : 角中勝也 稲葉篤紀 秋山翔吾
    player 中田翔 is similar to : 井口資仁 新井貴浩 中村紀洋
    ...
    ...(以下略)
    done!