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

Mahoutを使って機械学習の勉強するにあたって適当な題材がないか探していたところ、livedoorグルメ(今はロケタッチグルメというサービスに変わっている様子)のデータが学術用に公開されていることを発見。

利用規約に「学術研究の目的に限ります」と書いてあるが、個人の勉強も「学術研究」の範囲内だろうということで使わせていただく。(少なくとも商用利用するわけではないし)

livedoor グルメの DataSet を公開

データを見てみると「口コミデータ」と「口コミへの投票データ」がトランザクションデータで、後はそれに関連するマスタのよう。「口コミへの投票」の意味がよく分からなかったが、実際のサイトを見てみるとその口コミに対して閲覧者が「参考になった」ボタンを押下したログだと思われる。livedoorグルメのAPIだと「口コミデータ」のレイアウトの中に「投票数」という項目があるので、恐らくこれの元ネタだろう。

さて、ではこのデータを使ってどんな機械学習をしてみようか…
いろいろ考えてみたが、そもそもデータマイニングに詳しくないのであまり思いつかない。ということで、ベタだがレコメンデーションでもやってみよう。

で、レコメンデーションといえば代表的なのが協調フィルタリング。簡単に言うと買物する商品が似ているAさんとBさんを探し、Aさんが買っていなくてBさんが買っているものをAさんにお勧めする、というようなもの。

今回は口コミデータを元に、評価が似ているAさんとBさんを探し、Aさんが評価していなくてBさんが高い評価をしている店をAさんにレコメンドする、ということを目標にしてみる。たった20万件しか口コミデータがなく、しかも全国に範囲が散らばっていて、そもそも似た傾向の人を探すこと自体が可能なのか疑問だが、とりあえずやってみよう。

Mahoutの協調フィルタリングについて調べてみると、「Item-Based Recommendations」というのが上記に該当するアルゴリズムのよう。Mahout公式サイトの以下のページの解説によると必要なデータは「ユーザーID,アイテムID,評価」という形式だとわかる。

Introduction to Item-Based Recommendations with Hadoop

livedoorグルメの口コミデータは以下のレイアウトなので、「user_id,restaurant_id,total」を抜き出せば良さそう。(本当は評価の細かい内訳を使った方が精度が高そうだが、Mahoutのアルゴリズムが複数の評価項目を受け取れなさそうなのと、データをざっと見た限り総合評価以外の点数がまともに入っていなさそうなのとで諦める)

  • id 口コミID
  • restaurant_id 対象お店ID
  • user_id ユーザID
  • total 総合評価(0-5)
  • food 料理評価(0-5)
  • service サービス評価(0-5)
  • atmosphere 雰囲気評価(0-5)
  • cost_performance コストパフォーマンス評価(0-5)
  • title 口コミコメントタイトル
  • body 口コミコメント
  • purpose 利用目的
  • created_on 投稿日時

ということで、Mahoutに読み込ませるためにデータの加工が必要になった。
20万件ならExcelでも何とかなりそうなレベルだが、せっかくなのでプログラムで処理する。Hadoopの勉強なのでMapReduceでやるか、あるいはHiveでお気軽にやるか、はたまたちょっと触ってみようかと思ってずっと放置しているPythonを試してみるか。

悩んだので今日はここまで。

Mahoutのインストールとサンプル実行

Hadoop上で動く機械学習ライブラリMahoutを入れてみる。

# yum -y install mahout
(省略)
====================================================================================================
 Package                 Arch     Version                                     Repository       Size
====================================================================================================
Installing:
 mahout                  noarch   0.9+cdh5.3.1+18-1.cdh5.3.1.p0.17.el6        cloudera-cdh5    64 M
Installing for dependencies:
 hadoop-0.20-mapreduce   x86_64   2.5.0+cdh5.3.1+791-1.cdh5.3.1.p0.17.el6     cloudera-cdh5    28 M
 hadoop-client           x86_64   2.5.0+cdh5.3.1+791-1.cdh5.3.1.p0.17.el6     cloudera-cdh5    31 k
 hbase                   x86_64   0.98.6+cdh5.3.1+74-1.cdh5.3.1.p0.17.el6     cloudera-cdh5    47 M

Transaction Summary
====================================================================================================
Install       4 Package(s)
(省略)

依存関係のあるパッケージも含めて完了。
hadoop-0.20-mapreduceがインストールされたのはちょっと気になる。
なぜMRv1に依存?YARNでは動かないということだろうか。

ただ、MRv1がインストールされてもJobTrackerやTaskTrackerはサービス登録されていない様子。もしこのまま動くならYARN上で動いているということに。

サンプルを動かしてみる。
とりあえず以下のサイトに載っているものを。
Mahoutで体感する機械学習の実践 第3回 Mahoutの環境構築とFP-Growthによるマーケットバスケット分析

$ wget http://image.gihyo.co.jp/assets/files/dev/serial/01/mahout/0003/gihyo-mahout-fpg-sample.csv

$ mahout fpg -i gihyo-mahout-fpg-sample.csv -o output/gihyo-mahout-fpg-sample
(省略)
15/02/25 23:20:00 WARN driver.MahoutDriver: Unable to add class: fpg
java.lang.ClassNotFoundException: fpg
(省略)

エラー。調べたところ、どうやらMahout 0.9からFP-Growthはなくなったらしい。
そんな簡単にアルゴリズムが消えるなんて、怖くて使えない。。。

仕方ないので、別のアルゴリズムのサンプルでも。
今度は以下のサイトのものを。

Mahoutで体感する機械学習の実践 第5回 K-MeansとCanopyクラスタリングでセグメンテーション分析を行う

$ wget http://image.gihyo.co.jp/assets/files/dev/serial/01/mahout/0005/gihyo-mahout-kmeans-sample.arff
$ mahout arff.vector --input gihyo-mahout-kmeans-sample.arff --output  gihyo-mahout-kmeans-sample.vector --dictOut gihyo-mahout-kmeans-sample.dict
$ mahout kmeans --input gihyo-mahout-kmeans-sample.vector --output gihyo-kmeans-output --maxIter 50 --numClusters 10 --clusters gihyo-kmeans-null-cluster --clustering -dm org.apache.mahout.common.distance.EuclideanDistanceMeasure
(省略)
15/02/26 00:14:39 INFO driver.MahoutDriver: Program took 684308 ms (Minutes: 11.405133333333334)

–maxIter(クラスタ重心点の最大計算回数)に50をセットしたせいか、MapReduceが33段も動いてえらく時間がかかった。でも、結局MRv1ではなくYARN上のMRv2で動いたよう。
出力結果を見てみる。

$ hadoop fs -ls -R gihyo-kmeans-output
(省略)
drwxr-xr-x   - take supergroup          0 2015-02-26 00:14 gihyo-kmeans-output/clusters-33-final
-rw-r--r--   1 take supergroup          0 2015-02-26 00:14 gihyo-kmeans-output/clusters-33-final/_SUCCESS
-rw-r--r--   1 take supergroup        194 2015-02-26 00:14 gihyo-kmeans-output/clusters-33-final/_policy
-rw-r--r--   1 take supergroup       2633 2015-02-26 00:14 gihyo-kmeans-output/clusters-33-final/part-r-00000
(省略)

finalが付いているのが最終出力らしい。
中身を見てみる。

$ mahout clusterdump --input gihyo-kmeans-output/clusters-33-final --output ./gihyo-kmeans-dump.txt
$ cat gihyo-kmeans-dump.txt
VL-699{n=92 c=[74.815, 69.804, 13.402] r=[13.741, 16.057, 9.035]}
VL-733{n=95 c=[23.189, 83.537, 25.747] r=[13.245, 11.736, 13.585]}
VL-124{n=103 c=[18.505, 75.049, 74.388] r=[11.747, 14.355, 14.764]}
VL-669{n=106 c=[21.613, 25.462, 24.311] r=[14.588, 14.396, 14.659]}
VL-992{n=93 c=[50.312, 45.806, 43.602] r=[11.221, 12.955, 11.853]}
VL-90{n=98 c=[23.133, 21.051, 79.714] r=[13.375, 13.353, 13.786]}
VL-172{n=104 c=[66.683, 72.288, 84.462] r=[16.433, 15.380, 10.757]}
VL-977{n=98 c=[80.327, 81.388, 49.092] r=[13.999, 12.359, 13.020]}
VL-329{n=105 c=[73.524, 21.086, 83.914] r=[14.420, 13.686, 11.711]}
VL-864{n=106 c=[79.698, 17.934, 31.972] r=[12.508, 12.466, 16.247]}

無事に動いたようなので今回はこれで終わり。

MapReduceのサンプル(WordCount)を実行

HDFSとYARNのインストールが終わったので、MapReduceのサンプルプログラム(WordCount)を実行してみる。

(1)ユーザーディレクトリの作成
HDFS上にユーザーのホームディレクトリを作成する。
/userはhdfsユーザーで作成し、それ以下は個々のユーザーで。
以下、takeユーザーの想定。

$ sudo -u hdfs hadoop fs -mkdir /user
$ hadoop fs -mkdir /user/take

(2)データをHDFSにアップ
せっかくなので公開されているデータを取得する。今回は著作権切れの書籍の公開サイトから「シャーロックホームズの冒険(The Adventures of Sherlock Holmes)」を。日本語の文章をサンプルのWordCountが正しく処理できるか不明なので、とりあえず英語版で。

$ wget http://www.gutenberg.org/ebooks/1661.txt.utf-8
$ hadoop fs -put 1661.txt.utf-8

(3)サンプルプログラムの実行
いよいよWordCountの実行。

$ hadoop jar /usr/lib/hadoop-mapreduce/hadoop-mapreduce-examples.jar wordcount 1661.txt.utf-8 output
15/02/20 23:32:50 INFO client.RMProxy: Connecting to ResourceManager at /0.0.0.0:8032
15/02/20 23:32:51 INFO mapreduce.JobSubmitter: Cleaning up the staging area /tmp/hadoop-yarn/staging/take/.staging/job_1424358523005_0002
15/02/20 23:32:51 WARN security.UserGroupInformation: PriviledgedActionException as:take (auth:SIMPLE) cause:org.apache.hadoop.security.AccessControlException: Permission denied: user=take, access=EXECUTE, inode="/tmp":mapred:supergroup:drwxrwx---
        at org.apache.hadoop.hdfs.server.namenode.DefaultAuthorizationProvider.checkFsPermission(DefaultAuthorizationProvider.java:257)
(以降省略)

うーん、エラーになってしまった。
どうやらHDFS上の/tmpがmapredユーザーの所有権になっていてtakeユーザーでは書き込みができないことが原因の様子。

こんなことにならないようにHDFSのパーミッション設定をfalseにしておいたつもりなんだが…

とりあえず、Hadoopのユーザー管理やパーミッションについては後日調べるとして、今回は/tmp以下のパーミッションを777にして再トライ。

$ sudo -u hdfs hadoop fs -chmod -R 777 /tmp
$ hadoop jar /usr/lib/hadoop-mapreduce/hadoop-mapreduce-examples.jar wordcount 1661.txt.utf-8 output
(省略)
15/02/20 23:42:42 INFO mapreduce.Job:  map 0% reduce 0%
15/02/20 23:42:50 INFO mapreduce.Job:  map 100% reduce 0%
15/02/20 23:42:56 INFO mapreduce.Job:  map 100% reduce 100%
15/02/20 23:42:56 INFO mapreduce.Job: Job job_1424358523005_0003 completed successfully
15/02/20 23:42:56 INFO mapreduce.Job: Counters: 49
(省略)
        File Output Format Counters
                Bytes Written=158018

今度は無事に終了。
/user/take/output/以下に結果が出ているはずなので見てみる。

$ hadoop fs -ls output
Found 2 items
-rw-r--r--   1 take supergroup          0 2015-02-20 23:42 output/_SUCCESS
-rw-r--r--   1 take supergroup     158018 2015-02-20 23:42 output/part-r-00000
$ hadoop fs -get output/part-r-00000 wordcount-result.txt
$ head wordcount-result.txt
"'A     1
"'About 1
"'Absolute      1
"'Ah!'  2
"'Ah,   2
"'Ample.'       1
"'And   10
"'Arthur!'      1
"'As    1
"'At    1

サンプルのWordCountは結構適当な作りらしく、クォートや感嘆符なども含んだままカウントしてしまっている様子。
でも、とりあえず動いたので今回はここまで。

CDH5でHadoop擬似分散環境を構築② – CDHインストール

CDH5でHDFSとYARNをインストール。

(1)yumリポジトリのインストール
Clouderaからyumリポジトリをダウンロードしてリポジトリディレクトリにインストール

# wget http://archive.cloudera.com/cdh5/redhat/6/x86_64/cdh/cloudera-cdh5.repo
# mv cloudera-cdh5.repo /etc/yum.repos.d/

(2)CDH5のインストール
擬似分散環境用の設定ファイルpseudoをyumでインストールする。
依存関係のあるモジュールも自動でインストールされるため、これでHDFSやYARNも入る。
(CentOSをMinimalでインストールしてあるため、依存関係のあるglibcなどのライブラリ類もここで入る。)

# yum install -y hadoop-conf-pseudo

(3)設定
本当はデータの保存先などちゃんと設定した方がいいが、とりあえず遊び用なのでデフォルトのままで。

各種設定ファイルの中のホスト名をlocalhostから実際のホスト名に変えておく。(擬似分散環境なので変えなくても動作するが、なぜかHistoryServerのWebUIが設定ファイルで指定したホスト名でしかアクセスさせてくれないので。NameNodeのWebUIなどはlocalhostに設定したままでもホスト名やIPアドレスでも受け付けてくれるのに…)

・/etc/hadoop/conf/core-site.xml

<property>
  <name>fs.defaultFS</name>
  <value>hdfs://[hostname]:8020</value>
</property>

・/etc/hadoop/conf/mapred-site.xml

<property>
  <name>mapred.job.tracker</name>
  <value>[hostname]:8021</value>
</property>
<property>
  <name>mapreduce.jobhistory.address</name>
  <value>[hostname]:10020</value>
</property>
<property>
  <name>mapreduce.jobhistory.webapp.address</name>
  <value>[hostname]:19888</value>
</property>

それと、HDFSのパーミッションを無効にしておく。これをやっておかないと色々なところでPermission deniedが発生する。(本当はちゃんとパーミッション管理をした方がいいと思うが、遊び用なので…)

・/etc/hadoop/conf/core-site.xml
以下を追加。

<property>
  <name>dfs.permissions.enabled</name>
  <value>false</value>
</property>

(4)HDFSのフォーマット

# sudo -u hdfs hdfs namenode -format

(5)HDFSの起動
SecondaryNamenodeは起動しなくても動くので遊び用環境ではメモリ節約のため起動しない。

# service hadoop-hdfs-namenode start
# service hadoop-hdfs-datanode start

正常に起動できたらWeb管理画面にアクセスできるので以下のURLを叩いてみる。

http://[ホスト名orIPアドレス]:50070/

hadoop-hdfs-webui

(6)YARNの起動
YARNのデーモンを起動。

# service hadoop-yarn-resourcemanager start
# service hadoop-yarn-nodemanager start
# service hadoop-mapreduce-historyserver start

正常に起動できたらWeb管理画面にアクセスできるので以下のURLを叩いてみる。
・ResourceManager

http://[ホスト名orIPアドレス]:8088/

hadoop-resourcemanager-webui

・NodeManager

http://[ホスト名orIPアドレス]:8042/

hadoop-nodemanager-webui

・HistoryServer

http://[ホスト名orIPアドレス]:19888/

hadoop-historyserver-webui

(7)自動起動の設定
CDHでインストールすると各種デーモンが自動起動するように設定されるので、使わないSecondaryNamenodeの自動起動設定を無効にしておく。

# chkconfig hadoop-hdfs-secondarynamenode off

CDH5でHadoop擬似分散環境を構築① – 事前準備

CentOS 6.5にCDH5の擬似分散環境をインストールするための事前準備。

(1)OS
CentOS 6.5をMinimalインストールする。

(2)JDK
Oracle JDK 1.7の最新版(投稿時点で1.7.0_75)をRPMでインストール。

# rpm -ivh jdk-7u75-linux-x64.rpm

(3)その他モジュール
Minimalで不足している下記のモジュールをyumでインストール。

# yum install wget

(4)ホスト名の設定
Hadoopはホスト名参照をするので設定しておく。
/etc/hostsに以下のような感じで。

192.168.0.1    hostname

(5)ファイアウォールの無効化
サーバの外からHadoopのWeb管理画面にアクセスするためiptablesを無効にしておく。
好みでちゃんと設定したい場合は50070と8088と8042と19888を空けておく。

# service iptables stop
# chkconfig iptables off
# chkconfig --list iptable
iptables        0:off   1:off   2:off   3:off   4:off   5:off   6:off

続・CentOSにSSH接続すると1分くらいで切断される

ESET Smart SecurityのファイアウォールがSSH接続を切断してしまう件の続報。

ファイアウォールを無効にしておくのは嫌なのでサポートに問い合わせたところ、詳細設定の「ネットワーク – パーソナルファイアウォール – IDSと詳細オプション」の中の「アクティブでないTCP接続を維持」のチェックを入れて試してみてほしいとの回答が。

残念ながらこのオプションにチェックを入れても解消せず。

続いてサポートからログを取って送るように指示があったのでログを取ってみたところ、「TCPパケットはどのオープン接続にも属していません」というメッセージが記録されていることを発見。試しにこのキーワードについてGoogle先生に質問してみたところ、下記のサイトを発見。

http://www.mysql.gr.jp/mysqlml/mysql/msg/15995

このサイトに書かれていた、「ネットワーク – パーソナルファイアウォール – IDSと詳細オプション」の中の「TCP接続状態のチェック」のチェックを外したところ、無事に解消。

なお、上記サイトでは最初にサポートに指示された「アクティブでないTCP接続を維持」にもチェックを入れているが、試したところこのオプションは関係なかった。(チェックを外しても切断されなかった)

CentOSにSSH接続すると1分くらいで切断される

TeraTermからVMWare Player上のCentOS 6.5にSSHで接続すると、なぜか1分くらいで応答がなくなり切断される。(TeraTerm自体も落ちる)

以下のサイトの通りにしてみたが解消せず。
(Minimalインストールしたからなのか、バージョンが違うからなのか、そもそもpcnet32やvmxnetが入っていない)

<参考サイト>
【Linux】VMwarePlayerのCentOSでSSHが切れまくる件。

結局、クライアントOS(Windows7)側のファイアウォール(ESET Smart Security)を無効にしたら解消。