livedoorグルメのデータで機械学習してみる⑥

ようやく協調フィルタリングに再挑戦できる状況に。
Pythonで変換した入力ファイルをHDFSにputして「Item-Based Recommendations」を実行。

$ hadoop fs -put ldgourmet_ratings_for_ibr2.csv ldgourmet

$ mahout recommenditembased -s SIMILARITY_LOGLIKELIHOOD -i ldgourmet/ldgourmet_ratings_for_ibr2.csv -o ldgourmet/output --numRecommendations 3
MAHOUT_LOCAL is not set; adding HADOOP_CONF_DIR to classpath.
Running on hadoop, using /usr/lib/hadoop/bin/hadoop and HADOOP_CONF_DIR=/etc/hadoop/conf
MAHOUT-JOB: /usr/lib/mahout/mahout-examples-0.9-cdh5.3.1-job.jar
15/03/17 23:07:52 INFO common.AbstractJob: Command line arguments: {--booleanData=[false], --endPhase=[2147483647], --input=[ldgourmet/ldgourmet_ratings_for_ibr2.csv], --maxPrefsInItemSimilarity=[500], --maxPrefsPerUser=[10], --maxSimilaritiesPerItem=[100], --minPrefsPerUser=[1], --numRecommendations=[3], --output=[ldgourmet/output], --similarityClassname=[SIMILARITY_LOGLIKELIHOOD], --startPhase=[0], --tempDir=[temp]}
15/03/17 23:07:52 INFO common.AbstractJob: Command line arguments: {--booleanData=[false], --endPhase=[2147483647], --input=[ldgourmet/ldgourmet_ratings_for_ibr2.csv], --minPrefsPerUser=[1], --output=[temp/preparePreferenceMatrix], --ratingShift=[0.0], --startPhase=[0], --tempDir=[temp]}
15/03/17 23:07:53 INFO Configuration.deprecation: mapred.input.dir is deprecated. Instead, use mapreduce.input.fileinputformat.inputdir
15/03/17 23:07:53 INFO Configuration.deprecation: mapred.compress.map.output is deprecated. Instead, use mapreduce.map.output.compress
15/03/17 23:07:53 INFO Configuration.deprecation: mapred.output.dir is deprecated. Instead, use mapreduce.output.fileoutputformat.outputdir
15/03/17 23:07:54 INFO client.RMProxy: Connecting to ResourceManager at /0.0.0.0:8032
15/03/17 23:07:55 WARN security.UserGroupInformation: PriviledgedActionException as:take (auth:SIMPLE) cause:org.apache.hadoop.mapred.FileAlreadyExistsException: Output directory temp/preparePreferenceMatrix/itemIDIndex already exists
Exception in thread "main" org.apache.hadoop.mapred.FileAlreadyExistsException: Output directory temp/preparePreferenceMatrix/itemIDIndex already exists
        at org.apache.hadoop.mapreduce.lib.output.FileOutputFormat.checkOutputSpecs(FileOutputFormat.java:146)
        at org.apache.hadoop.mapreduce.JobSubmitter.checkSpecs(JobSubmitter.java:554)
        at org.apache.hadoop.mapreduce.JobSubmitter.submitJobInternal(JobSubmitter.java:430)
        at org.apache.hadoop.mapreduce.Job$10.run(Job.java:1295)
        at org.apache.hadoop.mapreduce.Job$10.run(Job.java:1292)
        at java.security.AccessController.doPrivileged(Native Method)
        at javax.security.auth.Subject.doAs(Subject.java:415)
        at org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1642)
        at org.apache.hadoop.mapreduce.Job.submit(Job.java:1292)
        at org.apache.hadoop.mapreduce.Job.waitForCompletion(Job.java:1313)
        at org.apache.mahout.cf.taste.hadoop.preparation.PreparePreferenceMatrixJob.run(PreparePreferenceMatrixJob.java:77)
        at org.apache.hadoop.util.ToolRunner.run(ToolRunner.java:70)
        at org.apache.mahout.cf.taste.hadoop.item.RecommenderJob.run(RecommenderJob.java:164)
        at org.apache.hadoop.util.ToolRunner.run(ToolRunner.java:70)
        at org.apache.mahout.cf.taste.hadoop.item.RecommenderJob.main(RecommenderJob.java:322)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:606)
        at org.apache.hadoop.util.ProgramDriver$ProgramDescription.invoke(ProgramDriver.java:72)
        at org.apache.hadoop.util.ProgramDriver.run(ProgramDriver.java:145)
        at org.apache.hadoop.util.ProgramDriver.driver(ProgramDriver.java:153)
        at org.apache.mahout.driver.MahoutDriver.main(MahoutDriver.java:195)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:606)
        at org.apache.hadoop.util.RunJar.main(RunJar.java:212)

あれ、、、またエラーに。
今度は中間データと思われるファイルの出力先が既に存在している、と。
恐らく前回エラーになったときに残ったのだと思われるが、それにしても指定したパス以外に勝手にディレクトリを作っておいて、残したままにしておくとは。もうちょっと後処理ちゃんとせえよ…。
とぼやいていても仕方ないのでディレクトリを消して再度。

$ hadoop fs -rmr temp/preparePreferenceMatrix

$ mahout recommenditembased -s SIMILARITY_LOGLIKELIHOOD -i ldgourmet/ldgourmet_ratings_for_ibr2.csv -o ldgourmet/output --numRecommendations 3
(省略)
15/03/17 23:18:37 INFO driver.MahoutDriver: Program took 347306 ms (Minutes: 5.788433333333334)

MapReduceが9回ほど動いて5分ちょいで無事終了。
出力データの中身を見てみる。

$ hadoop fs -ls ldgourmet/output
Found 2 items
-rw-r--r--   1 take supergroup          0 2015-03-17 23:18 ldgourmet/output/_SUCCESS
-rw-r--r--   1 take supergroup     245633 2015-03-17 23:18 ldgourmet/output/part-r-00000
$ hadoop fs -tail ldgourmet/output/part-r-00000
9923823]
4278205127      [1050:4.0119233,138:4.007087,586:4.007003]
4278971698      [277:1.0,1637:1.0]
4279952571      [6645:2.50514,581:1.0,4244:1.0]
4281265025      [311518:4.0,370162:4.0,332770:4.0]
4281634113      [300933:4.4904943,335499:4.0,373547:4.0]
4281870486      [11824:4.500969,21348:4.488718,3729:4.487594]
4282442439      [2358:4.0,313:4.0]
4282703212      [384403:4.2505198,334805:4.20008,380188:4.20008]
4283170779      [7910:5.0,8796:5.0]
4283926049      [12555:4.4908667]
4284165778      [8728:5.0,2701:5.0,2302:5.0]
4285085889      [300933:5.0,301515:5.0,323026:4.673149]
4288106213      [406041:4.0870314,396687:4.087023,359265:4.083425]
4288708981      [2945:4.504145,4365:4.502466,8206:4.502196]
4289424931      [2297:5.0,11250:4.5070863,20130:4.498961]
4289532598      [308708:5.0,300489:5.0,308715:5.0]
4291952257      [5343:4.0,5433:4.0,2044:4.0]
4291973773      [2855:5.0,891:5.0,3574:5.0]
4292027381      [352331:4.0,374319:4.0,341967:4.0]
4292733388      [3240:4.514431,10312:4.4899635,20040:4.0136404]
4293060625      [4836:5.0,25064:5.0,3074:5.0]
4294950334      [7207:4.494212,6595:4.493965,12624:4.0]

それっぽく出ている感じ。
データはユーザーIDに対しておすすめレストランのIDと点数のセットが3つ(パラメータで3つレコメンドするようにしたので)出ているよう。元の評価データが5点満点だからか見た感じ5点満点で点数が出ている。

ちょっと気になるのは、3つとも満点で出ているユーザーが結構いること。そんなにお勧めなのか、あるいは自分が評価して満点を付けたレストランが出ているのか。

試しに元データを追ってみる。

$ grep 4293060625 ldgourmet_ratings_for_ibr2.csv
4293060625,433,5
4293060625,6839,5
4293060625,302354,5
$ grep 4291973773 ldgourmet_ratings_for_ibr2.csv
4291973773,4243,4
4291973773,6404,5
4291973773,6925,5
4291973773,11719,4
4291973773,13890,5
4291973773,17502,4
4291973773,19616,3

どうやら、自分が評価したのとは違うレストランがちゃんと選ばれている様子。
ほんとに適切なレコメンドがされているのか気になるが、今日はここまで。
次回、レコメンド結果を定性的に評価してみよ。

livedoorグルメのデータで機械学習してみる⑤

データクレンジングにあたり必要な機能のPythonでの実装方法の確認が一通り終わったので組み合わせてデータ変換プログラムを完成させる。

convert_ratings.py

# -*- coding: utf-8 -*-
 
import sys
 
if __name__ == "__main__":
    arg = sys.argv
 
    input_file = arg[1]
    output_file = arg[2]
 
    with open(input_file, 'r') as in_file:
        with open(output_file, 'w') as out_file:
            rows = 0
            for line in in_file:
                rows += 1

                # 先頭行はヘッダなのでスキップ
                if rows == 1:
                    continue

                list = line.split(",")

                restaurant_id = list[1]

                # ユーザーIDは16進->10進変換
                user_id = long(list[2], 16)

                total = list[3]

                out_file.write(str(user_id) + "," + restaurant_id + "," + total + "\n")

                if rows % 1000 == 0:
                    print(str(rows) + "行変換しました。")

            print(str(rows) + "行変換しました。")

まあ、こんな感じで。
では、ようやく変換を。

$ python convert_ratings.py ratings.csv ldgourmet_ratings_for_ibr2.csv
1000行変換しました。
2000行変換しました。
(省略)
205833行変換しました。

$ head ldgourmet_ratings_for_ibr2.csv
3993170538,310595,5
4240577537,10237,1
104934135,3334,2
104934135,15163,5
1290717597,567,3
1290717597,1026,5
1290717597,1058,5
1290717597,2569,3
1290717597,3309,4
1290717597,3648,4

バッチリ。
とりあえず今日はここまでにして、次回いよいよ協調フィルタリングに再挑戦。

カンマ区切り文字列の分割

ファイルの読み書きもできたし、16進を10進に変換できたし、いよいよMahout用データを作るプログラムを書こうと思ったら、カンマ区切りの入力データを分割する方法が未確認だということが発覚。調べて試してみる。

comma_split.py

# -*- coding: utf-8 -*-
 
if __name__ == "__main__":
    comma_del_str = "aaa,bbb,ccc,ddd"

    list = comma_del_str.split(",")

    for column in list:
        print(column)

文字列型のオブジェクトに対してsplit(“区切り文字”)するだけだった。

$ python comma_split.py
aaa
bbb
ccc
ddd

16進表記の文字列を数値に変換

いよいよメインイベント。
16進表記されたユーザーIDを数値型に変換する。

ちょっとGoogle先生に聞いてみたところ、なんとlong(“文字列”, 基数)だけでOKっぽい。
とりあえず対話型のインタプリタで試してみる。

>>> print(long("ee02f26a", 16))
3993170538
>>> print(long("fcc21401", 16))
4240577537

できちゃった。恐るべしPython。
メインイベントなのにHello Worldより短い。(笑)

Pythonでファイル入出力

続いてファイルの読み込みと書き出し。
とりあえずは入力データをそのまま書き出してみる。

fileio_sample.py

# -*- coding: utf-8 -*-

import sys

if __name__ == "__main__":
    arg = sys.argv

    input_file = arg[1]
    output_file = arg[2]

    with open(input_file, 'r') as in_file:
        with open(output_file, 'w') as out_file:
            for line in in_file:
                out_file.write(line)

ファイルを開くにはopen()という関数を使えばいいらしい。引数はいくつかあって、今回は「ファイル名,読み書きモード」というパターンを使用。ファイル名だけならモードが’r’扱いになるよう。あと、3番目にエンコーディングも指定可能。

11行目と12行目のwith文は説明が難しいけど初期処理とか終了処理とかを自動的にやってくれるものらしい。どんな初期処理や終了処理が動くかは関数によって違い、例えばファイルを開くopen関数の場合だと、withを抜ける時に終了処理として自動的にcloseしてくれるよう。

今回は1行読みながら1行書くので入力と出力の2つのファイルを同時にopenする。順番はどちらが先でもいいと思うが、何となく読む方を先に。

では動かしてみる。
livedoorグルメのデータはサイズが大きいので、テスト用に抜粋して使用。

$ head ratings.csv > test_input.csv
$ python fileio_sample.py test_input.csv test_output.csv
$ cat test_output.csv
id,restaurant_id,user_id,total,food,service,atmosphere,cost_performance,title,body,purpose,created_on
156445,310595,ee02f26a,5,0,0,0,0,,"名前は忘れましたが、札幌で食べたお店よりも、全然こっちの方が美味 しかったので、載せました。お店も綺麗(新規オープン・・)でランチは結構混んでいます。個人的にはゆったりと食事できるので夜の方がオススメです。  辛さが0倍から50倍まで選べるのもGOOD!、スープも2種類みたいで、友達は黄色がオススメと言っていましたが、自分は赤の方を食べました。かなり美味しかったです。店長も好感のもてるお兄さんでした。  駅近くなので一度お試しあれです!",0,"2006-10-07 05:06:09"
3842,10237,fcc21401,1,0,0,0,0,,"味的には別に取り立てておいしいと言うこともない。けどまずくもない。  ギリシア料理といもの自体があまり特徴がないということもあると思う。  でも★1を付けてしまったのはサービスの悪さから。  夜に行ったのですけど、何の説明もなくパンを出された。  本当はおかわり自由らしいけど、それについても何も言ってくれなかったので  当然こちらは催促しないし、「パンのおかわりいかがですか?」の一言もなかった。  女二人で料理3品とドリンク1つづつで¥7000ってかなり高いんじゃない?  場所はいいけど、かなり後悔。せめて笑顔と最低限の説明は欲しいところ。",0,"2004-10-20 00:34:28"
144379,3334,06412af7,2,0,0,0,0,,"意外と昔からあるお店で  近江牛、特選黒毛和牛など高級肉を扱ってるお 肉屋さん  ランチも、きっとおいしいと思い駐車場もあるので行ってみました  ステーキ焼ランチ1050円。とてもリーズナブル  炭火かなと思ったら、ガスでした    でてきたランチのお肉は、ロース肉  サシが入ってなく、見た目オージービーフっぽい  食べてみたら、うーんこれはやっぱ。。。  外国産のお肉みたいな味がする  高くておいしいのは当たり前  1000円ランチといえども肉の質は落として欲しくないな~と思った    他にもお客さん(サラリーマン系)が来てたけど  ランチでも、和牛サーロインステーキとか高い値段のを注文してました  ↑  焼き方を聞いていたので、和牛サーロインステーキ等の場合は  焼いてもらえるみたいですね    ",0,"2006-06-03 16:07:43"
144377,15163,06412af7,5,0,0,0,0,,"ランチがおすすめです  お肉自体の旨み、そしてあまみが  お口にじゅわぁ~と広がります。  あのお値段で、ここまでおいしかったお店は初めてです  駐車場は30台くらいとめられて  周りに別のお店はナイので、近くに看板もあるし場所もわかりやすい",0,"2006-06-03 15:14:45"
75967,567,4ceec99d,3,0,0,0,0,,"おいしいと言う友達のすすめでケーキを2種類、プリン系を2種類買ってみ ました。  ケーキはちょっと当たりハズレがあるのかな?  二つ買ったうちの一つ(苺がのったケーキ)はおいしかった。    プリンはクリームブリュレ風のと洋ナシがのったプリン。  両方とも超美味しい!!プリンはリピーター間違いなし。",0,"2004-12-01 23:12:29"
104898,1026,4ceec99d,5,0,0,0,0,,"ランチに利用しました。1,000円のカレー2種+ナンのセット。飲み物付   カレーがのったお皿にミニサラダとチキンも付いてました。    チキンカレーと野菜カレーを頼みました。カレーはスパイスが効いています。  でもだいぶ日本人向けに作ってあるように感じます。  私が頼んだチキン、野菜カレーはさらさらのタイプのカレーでは無く、少しとろみがあるタイプでした。  ナンが大きくて最初は食べきれるかな?と思ったのですが、全然いらぬ心配でした。  パリパリで、中はふっくらなナンはおかわりしたいぐらい美味しかったです。  夜のメニューを見せてもらったら、カレーはかなり種類があるみたいです。  激辛からマイルドと種類も豊富なので、辛いもの苦手な方も行ってみる価値ありです。",0,"2005-01-04 03:57:02"
86073,1058,4ceec99d,5,0,0,0,0,,"神楽坂付近に行く時は必ず寄る店です。  私はいつも抹茶ババロアを食べます。  甘さ控えめなババロア、上品な味の餡、濃厚な生クリーム  この3つがとってもいいバランス!!    注文してから待っている間に出るおせんべいも美味しい。  お茶をすすりながら待っている時間も堪能できる。  いつ行っても混んでいる理由は分かります。純粋においしいですもんね。    納得のお値段なんだけど‥  ただ持ち帰りにすると少し割高感を感じるんですよね。",0,"2004-11-09 00:34:17"
13968,2569,4ceec99d,3,0,0,0,0,,"タンタン麺を食べました。味は濃いめでしっかりとした味。  とっても美味しかったです。  お客さんはみんな地元の常連さんが多いと思いました。  店員の方も対応すごくいいです。",0,"2004-09-22 23:29:57"
97833,3309,4ceec99d,4,0,0,0,0,,"土曜日のランチで利用しました。  席が隣の人と近くてちょっとドキドキ。煙草吸う人来たら嫌だな~と。    ランチセットはメインが数種類から選べて、ピラフ、サラダ、デザート、コーヒーで1000円。  今回は+500円増のメインサイコロステーキを頂きました。  マスタードのソースとすごく合っていて美味しい。  連れのお皿からマッシュポテトを頂戴する。少し甘めで美味し~い!!    私が行った時は常に満員状態でした。  ‥にも関わらず、店員の方はよく気のきく感じでしたね。  コーヒーも気兼ねなくおかわりできますし。  また是非行きたいですね。",0,"2005-05-28 23:17:16"

今日のところはこのへんで。

はじめてのPython

Javaのpublic static void main()的なやつすら知らないので、まずはHelloWorldでも。
最終的にそこそこのステップがあるプログラムを作るので対話型ではなくモジュール(Pythonではプログラムを記述したファイルをこう呼ぶらしい)を作って実行。

helloworld.py

# -*- coding: utf-8 -*-

if __name__ == "__main__":
    print "Hello World."

1行目の「# -*- coding: utf-8 -*-」はソースのエンコード指定。上記ソースでは特に非ASCII文字が記述されているわけではないが、日本語を記述するときのためにおまじないのように書いておけばよさそう。

3行目の「if __name__ == “__main__”:」がJavaでいうところのmain()メソッドのようなもの。「__name__」という特殊な変数にはモジュール名(通常はファイル名の.pyの前の部分)が格納されるのだが、コマンドラインから実行された場合のみ「__main__」となり、このifブロックの中はコマンドラインから実行された場合のみ処理されることになる。

上記ソースの場合は他のモジュールにimportされるようなものではなくスクリプトとして実行(コマンドラインから動かすことをそう言うらしい)するためのものなので全ての処理をトップの階層に記述しても動きは同じだが、勉強なので何となく明示的に記述してみた。(mainなしでベタに書いたらbashのシェルスクリプトのようなイメージ。お気軽にやるならそれで全然いいと思う。)

実行してみる。

$ python helloworld.py
Hello World.

次に、コマンドライン引数を受け取る方法を試す。
とりあえず受け取った文字列をそのまま標準出力に表示。

cmdarg_sample.py

# -*- coding: utf-8 -*-

import sys

if __name__ == "__main__":
    arg = sys.argv

    print "input file = " + arg[1]
    print "output file = " + arg[2]

コマンドライン引数はsysというモジュールのargvという配列に格納されるらしい。sysを使うために3行目でimport。Javaの場合Systemクラスなどはimport不要だが、Pythonは何でもimportが必要なのかな。
引数の配列はarg[0]から始めたくなるところだが、arg[0]にはモジュールのファイル名が入ってくるよう。よって実質的にarg[1]から。(確かにpythonコマンドから見たらモジュールファイル名が第一引数か…)

$ python cmdarg_sample.py input.csv output.csv
input file = input.csv
output file = output.csv

とりあえず今日はここまで。

livedoorグルメのデータで機械学習してみる④

Mahoutに読み込ませるデータのユーザーIDをlong型にしなければならない。
Hiveの関数を見てみたが文字列を数値に変換できそうなものがなかったので別の手段を使うことに。
簡単なプログラム書くだけなので何でもいいのだけれど、せっかくなので勉強してみようと思っていたPythonを使ってみる。

開発環境でコマンドを叩いてみると幸いデフォルトでインストール済みのよう。CentOS6のデフォルトで2.6.6とちょっと古めだが、大したことをするわけではないので特に問題はないだろうと思ってバージョンアップはしないまま使う。

さて、必要なプログラムの要件はとりあえず以下のような感じ。
・コマンドライン引数で入力ファイルと出力ファイルのパスを受け取る
・入力ファイルを1行ずつ読み込み、必要な項目を抜き出してレイアウトを整え、出力ファイルに出力する
・ユーザーID(データを見た感じ16進表記の数値っぽい)はJavaのlong型に変換可能な数値にして出力

Python初体験で構文も全く知らないド素人なので一歩ずつ進める。
参考にしたのは↓こちら。(微妙にバージョン違うけど…)

Python 2.7ja1 documentation
Python-ism

続きは別記事にて。

livedoorグルメのデータで機械学習してみる③

いよいよ口コミデータを協調フィルタリングで処理してみる。
まずは、Hiveで作った入力データをHDFSに置く。(HDFS上のHiveの出力データそのままでもよかったのだけど)

$ hadoop fs -mkdir ldgourmet
$ hadoop fs -put ldgourmet_ratings_for_ibr.csv ldgourmet

次に、下記のサイトのサンプルを参考に「Item-Based Recommendations」を実行。

Introduction to Item-Based Recommendations with Hadoop

$ mahout recommenditembased -s SIMILARITY_LOGLIKELIHOOD -i ldgourmet/ldgourmet_ratings_for_ibr.csv -o ldgourmet/output --numRecommendations 3
(省略)
Error: java.lang.NumberFormatException: For input string: "ee02f26a"
        at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
        at java.lang.Long.parseLong(Long.java:441)
        at java.lang.Long.parseLong(Long.java:483)
        at org.apache.mahout.cf.taste.hadoop.ToEntityPrefsMapper.map(ToEntityPrefsMapper.java:60)
        at org.apache.mahout.cf.taste.hadoop.ToEntityPrefsMapper.map(ToEntityPrefsMapper.java:30)
        at org.apache.hadoop.mapreduce.Mapper.run(Mapper.java:145)
        at org.apache.hadoop.mapred.MapTask.runNewMapper(MapTask.java:784)
        at org.apache.hadoop.mapred.MapTask.run(MapTask.java:341)
        at org.apache.hadoop.mapred.YarnChild$2.run(YarnChild.java:168)
        at java.security.AccessController.doPrivileged(Native Method)
        at javax.security.auth.Subject.doAs(Subject.java:415)
        at org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1642)
        at org.apache.hadoop.mapred.YarnChild.main(YarnChild.java:163)
(省略)
Exception in thread "main" java.io.FileNotFoundException: File does not exist: /user/take/temp/preparePreferenceMatrix/numUsers.bin
        at org.apache.hadoop.hdfs.server.namenode.INodeFile.valueOf(INodeFile.java:65)
        at org.apache.hadoop.hdfs.server.namenode.INodeFile.valueOf(INodeFile.java:55)
        at org.apache.hadoop.hdfs.server.namenode.FSNamesystem.getBlockLocationsUpdateTimes(FSNamesystem.java:1878)
        at org.apache.hadoop.hdfs.server.namenode.FSNamesystem.getBlockLocationsInt(FSNamesystem.java:1819)
        at org.apache.hadoop.hdfs.server.namenode.FSNamesystem.getBlockLocations(FSNamesystem.java:1799)
        at org.apache.hadoop.hdfs.server.namenode.FSNamesystem.getBlockLocations(FSNamesystem.java:1771)
        at org.apache.hadoop.hdfs.server.namenode.NameNodeRpcServer.getBlockLocations(NameNodeRpcServer.java:527)
        at org.apache.hadoop.hdfs.server.namenode.AuthorizationProviderProxyClientProtocol.getBlockLocations(AuthorizationProviderProxyClientProtocol.java:85)
        at org.apache.hadoop.hdfs.protocolPB.ClientNamenodeProtocolServerSideTranslatorPB.getBlockLocations(ClientNamenodeProtocolServerSideTranslatorPB.java:356)
        at org.apache.hadoop.hdfs.protocol.proto.ClientNamenodeProtocolProtos$ClientNamenodeProtocol$2.callBlockingMethod(ClientNamenodeProtocolProtos.java)
        at org.apache.hadoop.ipc.ProtobufRpcEngine$Server$ProtoBufRpcInvoker.call(ProtobufRpcEngine.java:587)
        at org.apache.hadoop.ipc.RPC$Server.call(RPC.java:1026)
        at org.apache.hadoop.ipc.Server$Handler$1.run(Server.java:2013)
        at org.apache.hadoop.ipc.Server$Handler$1.run(Server.java:2009)
        at java.security.AccessController.doPrivileged(Native Method)
        at javax.security.auth.Subject.doAs(Subject.java:415)
        at org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1642)
        at org.apache.hadoop.ipc.Server$Handler.run(Server.java:2007)
(省略)

エラーになった。
いくつかExceptionが出ているが、根本はユーザーIDのデータをlongに変換しようとしてエラーになっているのが原因と思われる。ていうか何故long?ユーザーIDが数値でなければならない理由が分からない。。。

よく分からないがGitHub上のRecommender.javaItemBasedRecommender.javaあたりを見てみると確かにuserIDがlongになっている。でも理由は不明。

理由は不明だがとりあえずlongに変換したデータを作ってやってみる。
でも、今日はここまで。

livedoorグルメのデータで機械学習してみる②

結局Hiveを使ってデータのクレンジングをすることに。

まずはテーブルを作ってダウンロードした元データを取り込む。

create table ldgourmet_ratings_org (
    id                  int,
    restaurant_id       int,
    user_id             string,
    total               int,
    food                int,
    service             int,
    atmosphere          int,
    cost_performance    int,
    title               string,
    body                string,
    purpose             string,
    created_on          string
) row format delimited fields terminated by ',';

load data local inpath 'ratings.csv' into table ldgourmet_ratings_org;    

ちゃんと取り込めたかどうかHiveQLで抽出してみる。

select count(distinct restaurant_id), count(distinct user_id), count(*) from ldgourmet_ratings_org;
(省略)
OK
72181   17771   205833
Time taken: 45.217 seconds, Fetched: 1 row(s)

次に、Mahoutに食べさせるデータ形式のテーブルを作成し、そこに抽出。
必要なデータは「ユーザーID,アイテムID,評価」なので、「user_id,restaurant_id,total」とする。
取り込んだデータの1行目がヘッダ行だったので、ついでにそれも除く。

create table ldgourmet_ratings_for_ibr (
    user_id             string,
    restaurant_id       int,
    total               int
) row format delimited fields terminated by ',';

insert overwrite table ldgourmet_ratings_for_ibr
select user_id, restaurant_id, total from ldgourmet_ratings_org
where user_id <> 'user_id';

select * from ldgourmet_ratings_for_ibr limit 10;
OK
ee02f26a        310595  5
fcc21401        10237   1
06412af7        3334    2
06412af7        15163   5
4ceec99d        567     3
4ceec99d        1026    5
4ceec99d        1058    5
4ceec99d        2569    3
4ceec99d        3309    4
4ceec99d        3648    4
Time taken: 0.056 seconds, Fetched: 10 row(s)

できあがったテーブルのデータをファイルとして取得。

$ hadoop fs -ls /user/hive/warehouse/ldgourmet_ratings_for_ibr/
Found 1 items
-rwxr-xr-x   1 take supergroup    3476773 2015-03-04 00:10 /user/hive/warehouse/ldgourmet_ratings_for_ibr/000000_0

$ hadoop fs -get /user/hive/warehouse/ldgourmet_ratings_for_ibr/000000_0 ldgourmet_ratings_for_ibr.csv

今日はここまで。

CDH5でHiveをインストール

Mahoutでlivedoorグルメのデータを機械学習するにあたりデータの変換が必要になったのでHiveをインストール。

# yum install -y hive

一般ユーザー(今回はtake)でhiveコマンド叩いてみる。

$ hive
Logging initialized using configuration in file:/etc/hive/conf.dist/hive-log4j.properties
Exception in thread "main" java.lang.RuntimeException: java.lang.RuntimeException: Unable to instantiate org.apache.hadoop.hive.metastore.HiveMetaStoreClient
(省略)
Caused by: java.sql.SQLException: Failed to create database '/var/lib/hive/metastore/metastore_db', see the next exception for details.
(省略)

げ、、、メタストア作れないとかいうエラーが。
パーミッション問題かな。

$ ls -l /var/lib/hive
合計 4
drwxr-xr-x. 2 hive hive 4096  2月 25 05:58 2015 metastore

あ、やっぱりhiveユーザーにしか書き込み権限がないや。
試しにhiveユーザーで動かしてみる。

# sudo -u hive hive

動いた。で、メタストアもできたっぽい。
ただ、takeユーザーでhiveコマンド叩くと固まるように。
どうやら作成されたメタストアのファイルのオーナーがhiveになり、takeユーザーのアクセス権もないのが原因のよう。

やっぱりメタストアにPostgreSQL使おうかな、、、と思ったが、面倒なのでとりあえずパーミッションを変えて対応。一旦メタストアを消して、/var/lib/hive/metastoreにアクセス権限を与えてからtakeユーザーでhiveコマンドを実行。

# rm -rf /var/lib/hive/metastore/metastore_db
# chmod 777 /var/lib/hive/metastore

これでtakeユーザーでhiveコマンドが起動できたが、今度はtakeユーザーのオーナーでメタストアの各種ファイルができてしまい、他のユーザーでアクセス不能に。Derby不便…。

まあ、いずれPostgreSQLでやるとして、とりあえず今のところはこれでいくことに。