2014年12月31日水曜日

CouchDBをMapReduceで処理してみる

さてせっかくなので、MapReduce機能を使ってCouchDBのデータ処理をメモしておきます。
調べているうちに非常にわかりやすい所があり、MapReduceの説明にもちょうどよかったので自分なりに解釈して記しておきます。

まずデータを用意しないといけません。以下のような家計簿もどきを用意します。

date    category      item       price
5/10     果物           バナナ       300
5/10       魚               鯛         1000
5/11     果物           みかん       300
5/11     果物           バナナ       300
5/12       魚               鯛           900

各行が1個のドキュメントにあたります。(とりあえず全ドキュメントが持つFieldは同じにしておきます。その方が説明が楽なので。)

FutonでViewをTemplateに切り替えます。するとMapとReduceのプログラムを入力する欄が表示されます。そこでMapとして以下を入力します。

function(doc) {
  emit(null, doc.price);
}

すると全ドキュメントから価格だけ抜き出して一覧表示してくれます。


Reduceのプログラムをしてないので、データの抜き出しだけした結果です。
後、if文で特定のFieldのドキュメントだけ抜き出すこともできます。


次にReduce欄に以下を入力します。Reduce処理として集めたデータの加算を指示しています。

function(keys, values) {
  return sum(values);
}

すると以下の様になります。(emitの第1引数がkeysになり、第2引数がvaluesになるという関係です)


って言いたいところですが、Reduceが動いていない... よく見たらviewの右上にReduceにチェックを入れるボタンが。


どうもここにチェックを入れないとReduce処理をしてくれないようです。無事合計がでてくれました。

次に日付毎の価格を集計してみます。Map欄に以下を入力します。

function(doc) {
  emit(doc.date, doc.price);
}

key欄にdateを指定し、それ毎の塊を作ります。その時、priceの値だけを集めてきます。それをReduceで集計すれば日付ごとの価格の集計になります。


次にcategory毎の件数を集計してみます。とりあえずkeyにcategoryを指定すれば、category毎の塊は作成してくれます。

function(doc) {
  emit(doc.category, 1);
}

この時、valuesに「1」を固定して指定します。つまり集めた塊ごとに「1」の値が与えられることになります。これを集計すれば塊の件数になります。Reduce欄には例によってvaluesの集計をするようにします。


ということで、categoryの件数が出力されました。

本来、MapReduceはGoogleが考えだしたみたいに多数の計算機で処理するのが本当で、1台のPCでやっても旨味があまりありませんが、非常にわかりやすくドキュメント型のデータを処理してくれます。
後はこのMap欄やReduce欄のコードをアプリ側から自由に実行できないと、CouchDBの本当のいいところがでてこないでしょうが、それはまたメモできる程度、試行できたら。(Map欄のコードを実行した例は2回前のCouchDBのメモでもう記してますから、わかりやすいデータが思いつけば書けるでしょう)

CouchDBをpythonで操作する

さて実際のDB操作のための実装に移ります。例題として、ちょっと理科年表のデータを入れてみます。(地球半径とか高度ごとの気圧です)


ちょっと見にくいですが、Raidus, SeaLevelGravity, StaticPressureの3個のドキュメントを作成しました。これをプログラムで参照、変更してみます。
Futon上で最初にデータベースを作成してて困ったのが以下のStaticPressureです。
「高度:気圧」のペアのリストの構造なので、JSON形式でも扱いやすいしpythonでも辞書型で扱い易いと思ったんですが、Futon上からはどうしても思うように入力できません。結局、valueにはカンマ区切りの数字の羅列(高度1、気圧1、高度2、気圧2、。。。)という形式にしました。(そのうち3次元テーブルもでてくるから、このフォーマットの方が汎用性高いでしょう。javaにもpythonにもsplit()関数があって、valueを文字列で取得、個々の数字を簡単に切り出せますし。)
しかし、どうも辞書形式が登録できないのが気に入りません。頑張って上図のdataのように、「文字列:数字」のペアなら辞書として登録できることがわかりました。(どうもこれはFutonというより、CouchDBの制約のようです。後に実装でpythonから数字ペアの辞書を登録しますが、CouchDB上では最初の数字は文字列と認識されてしまいます)

ということで、pythonから直接、数字ペアを登録してみます。ドキュメント「Radius」にdataというFieldを作成しておいて、以下のコードで実行してみます。


# coding: utf-8
import couchdb

server = couchdb.Server('http://localhost:5984')
db = server['environment']

doc = db['Radius'] # ドキュメントの取得(コピーを作成するので注意)

dl = { 1:1, 2:2, 3:3} # サンプルデータの辞書

doc['data'] = dl # ドキュメントの一部を変更

db[doc.id] = doc # ドキュメントの更新
#db.commit() # なくてもいい

見た目は数字ペアの辞書が登録できたように見えます。


ところが、以下のプログラムで確認してみると、
# coding: utf-8
import couchdb

server = couchdb.Server('http://localhost:5984')
db = server['environment']

doc = db['Radius'] # ドキュメントの取得(コピーを作成するので注意)

print doc['data']

実行結果は以下の様になります。
{u'1': 1, u'3': 3, u'2': 2}
全然ダメです、最初の数字が文字列として認識されてしまっています。

余談:
ところでCouchDBは古いリビジョンを残していて、「適当」なところで自動的にガベージコレクションしてくれるそうです。ただ、プログラムを作って、動かしている方としてはHDDの容量も気になるので、意図的に古いのを削除したくなります。python-couchdbでは以下のメソッドで古いリビジョンを削除します。

# coding: utf-8
import couchdb

server = couchdb.Server('http://localhost:5984')
db = server['environment']

#db.cleanup() # これは古い設計
db.compact() # こっちが今の、古いリビジョンのドキュメントは削除する

説明見てたら同じようなメソッドが2個あったので試してみましたが、やはりcleanup()の方はダメで、そもそもエラーが出てきました。(説明にはそこまで書いてなかった)

2014年12月30日火曜日

CouchDBを設定し、pythonで操作してみる

NoSQLのデータベースで、高速性よりもフォーマットを自由に扱いたい場合、CouchDBの評判が高いようです。ちょっと試しに自PC上に(Ubuntu14.04)CouchDBを設定し、ついでにデータ操作をどうやるかを試すために、pythonでのサンプルを作ってみます。(オリジナルでJavaScriptのAPIは持っているそうです)

まずCouchDBのHP(launchpad.net)にいってインストール方法を調べます。Ubuntu14.04の場合は以下のようにするらしい。

$ sudo apt-get install software-properties-common

(aptのリポジトリの状態を確認、更新するらしい。
自分の場合あ既にこのリポジトリが入っていたらしく、最新の状態ですと言われた)

$ sudo add-apt-repository ppa:couchdb/stable

既に(古いバージョンが)インストールされているとまずいので、一度削除します。

$ sudo apt-get remove couchdb couchdb-bin couchdb-common

(自分は3個とも入ってない言われました)

いよいよインストールします。

$ sudo apt-get install -V couchdb
$ sudo stop couchdb
$ sudo start couchdb

ブラウザでhttp://127.0.0.1:5984/にアクセスします。
動いてるようですが、文字列しか表示されません。
couchdbのユーザー設定ないから?
(でも/etc/passwdで確認するとcouchdbのアカウントは登録されていました)
HPのドキュメントを見たら、現状はこれでいいようです。

http://127.0.0.1:5984/_utils

でFutonが動きます。大丈夫なようです。(データベースをWebベースで参照、変更、作成ができます)



PCを再起動しても自動的にデーモンは起動しています、Futonが動きました。

pythonでcoudbを使いたいので、/etc/couchdb/local.iniに以下を追加して再起動しろとのこと。

[query_servers]
python=/usr/bin/couchpy

念の為に/usr/bin以下を知らべてみたら、couchjs(おそらくJavaScript用のモジュール)はあるが、couchpyは存在していません。(注:この設定は必要なかったかも、なくてもcouchpyさえインストールすれば動きました。でも本当は必要な筈...)しばらくぐぐった結果、以下のパッケージをインストールしたらよかった。

$ sudo apt-get install python-couchdb

しかしcouchdbの再起動方法がわからない。(/etc/init.d以下にcoudbというスクリプトがあるという情報があったが、ubutu14.04だとそんなのない!どうやって最初に起動してるんだ?→後述)
PS:ちなみにこのpython-couchdb、昔はgoogle-Codeで開発されていたんですが、githubに移っていました。googleでも特に制約はないと思うけど、流行りですかね。
というか、再起動は必要なかった。(うまくいかないようなら、インストール直後にやったみたいにコマンドで直接、停止、起動をすればいいでしょう。)

試しにpythonでプログラムを書いてみました。試験用に用意したのは以下の様なデータです。
ぱっと見わかりにくいでしょうが、Fieldにわざと"name"と”名前”と英語、日本語の両方を使っています。


# coding: utf-8
import couchdb

db = couchdb.Server('http://localhost:5984')
sampledb = db['sample_db']

for id in sampledb:
 dbitem = sampledb[id]
 name = dbitem[u'名前']
 print name

日本語のフィールド名でも動きました。(まあそれでも、英語にしといた方が無難だろうね)

ところでCouchDBはデフォルトではPCの外部からのアクセスは禁止してあります。(データの操作にhttp使うくせに、こういうとこはしっかりしてます)
これを許可しないと使いずらいので、以下の場所を編集します。(コメントにデフォルトでなっているのをはずして、全部のIPアドレスを許可する、といったところでしょうか)

/etc/couchdb/local.ini
;bind 127.0.0.1
        ↓
bind 0.0.0.0

特に起動用スクリプトが見当たらないんですが、勝手に再起動してくれるようです。(他のディストリビューションだと起動用スクリプトができるものもあるらしいですが、今度はcouchdbのアカウントを自分で作ってあげないといけないとか)
あとFuton上でもConfigurationというToolがありそこからもできそうですが、、、ちょっとよくわかりません。うっかいこの項目を削除という操作をしてしまったらしく、いきなりWeb上から何もできなってしまって(まあ意味から考えて通信ができなくなりますわな)、あわてて手動で設定ファイルを編集しなおしました。

ところでCouchDBを通常のDBとして使おうと思ったんですが、どうもサンプルを探すとCouchDBの特徴であるMap/Reduceにより全文中の特定のFieldを探してくる、という例ばかりひっかかります。(考えてみたら、フォーマット自由のドキュメント型DBなんだから、そういう使い方が本道なんでしょうね)
でも自分はとりあえず普通に(ユニークな)idで検索するのをやりたいんです。

とりあえず、以下のプログラムでまずDBを作成します。
# coding: utf-8
import couchdb

server = couchdb.Server('http://localhost:5984')
db = server.create('python-tests')

db['johndoe'] = dict(type='Person', name='John Doe', sex='male')
db['maryjane'] = {'type':'Person', 'name':'Mary Jane', 'sex':'female'}  # あえてjson形式で指定してみる

db.commit()

# 特にcloseとかsaveは必要ない?
# 不安なら時にcommitするか
# 他のPCからすぐに編集した結果を参照したいアプリでは、あったほうがいい

次に以下のプログラムでidを直接指定します。

# coding: utf-8
import couchdb

server = couchdb.Server('http://localhost:5984')
db = server['python-tests']

print db['maryjane']

これで以下の出力がでてきます。
<Document u'maryjane'@u'1-6d8ea1b66198a41c398662aa3ae07514' {u'type': u'Person', u'name': u'Mary Jane', u'sex': u'female'}>

うまくいきました。(couchdb-pythonにAPIとかのマニュアルが見当たらないので、試行錯誤です)

せっかくなので、Map機能を使い検索してみます。FutonでTemplate Viewにして、

以下の関数をMap関数(左側の欄)として定義します。

function(doc) {
  emit(doc.sex, doc);
}

デフォルトは、emit(null, doc);でどうも全文対象という意味らしいんですが、sexというFieldにマークをします。
これをapplication/sample_viewという名前で保存します。(このスクリプト自体がDB内に保存されるのに注意。Futonで参照できます。)

これを使ったサンプルを以下に示します。
# coding: utf-8
import couchdb

server = couchdb.Server('http://localhost:5984')
db = server['python-tests']

#print db['maryjane']

result = db.view("application/sample_view", key="female")

for r in result:
 print 'id',r['id']
 print 'key',r['key']
 print 'name', r['value']['name']

keyの与え方はともかく、検索された後の他のタグを知る方法がちょっとクセがあります。
以下の様に出力されます。

id maryjane
key female
name Mary Jane

とりあえず最低限、必要なことはわかりました。

2014年12月28日日曜日

cardboardを作る

他にやりたいことは色々あるんですが、何となく目に留まったのがGoogleさんからでている、お手軽VR体験ができるcardboard。型紙をDLして、自分でカット、作成してもいいんですが、レンズとか手に入れるのがめんどくさい。
Amazonさんを覗いてみたら、今は安い値段でキットが色々でてるんですね。最近でたので¥560というのがあったので、ポチッと。ただし到着予定を見たら来年?なにこれ?
まあ安いし、あんまり来なかったらキャンセルするか、と軽い気持ちでいたらしばらくして発送のメールがきました。ところが、これまた到着予定が来年の1月で、荷物の追跡しようにも情報はないといわれる始末。(中国からDHLで送ってますよ!¥560って送料込みの値段なんですが。)これはやばいかな、と思ったら2週間ほどして日本郵便できました。

開けてみたら、普通にGoogleの型紙を加工してあるだけのようです。特に説明とか一切なし。GoogleのCardboardのサイトみても特に作り方は見つからず、たんに型紙に書いてある番号どうしをはめ込めとだけ...ちょっと困りました。作り方の動画なんかを2,3調べて参考にして作ってみましたがなんとかうまくいきました。

最後までわからなかったのが、磁石の仕組み。どうも2個の磁石を段ボール間にはさんでくっつけているだけのようなんですが、なんでこれでスイッチの仕組みになるのかわからない。(表側の磁石はずらせます。それで変化する磁束をNexus5が読み取っているのかな?)ただ、とくに磁石を固定するものもないので、磁石をスイッチとしてずらそうとするとよく内側のが落ちてしまいます。
しかたなく、内側の磁石はセロテープで固定しました。

さてできあがったので、さっそく手持ちのNexus5にアプリをいれてVR体験です!すごいという評判でしたが、確かにこの値段でここまでできれば十分でしょう。Nexus5のCPUパワーが素晴らしいせいもあるのでしょうが、頭を傾けたり、見回すと忠実に反応してくれます。なんか、また試したいことが増えちゃいました。

ところで自分はメガネしてますが、今回のCardboardはサイズ的にメガネしてるときついようです。もう少し大きめに作ってくれないと。Cardboardには色々亜流ができてるようですから、メガネOKなのを選んだ方がよかったかもしれません。(まあ段ボール製なので耐久性はないでしょうから、次に考えましょう)

2014年12月17日水曜日

Deep learningお試し:Pylearn2を動かす

昔から興味があった人工知能、最近Pylearn2がよく話題になってます。要は多重パーセプトロンらしいんですが、理論が難しくてよく分からん。(微分方程式が何故出てくるのか理解できない。重み付けの解法らしいけど……。)

とりあえず動かしてみようと、まずはvagrantでprecise64の環境で挑戦。しかしvagrantではGUIがなく、よく分からないエラーがでて断念。(最初はvagrantのpylearn2がインストール済みの定義ファイルがあったのですが、vagrantのバージョンがかなり古いらしく、動きませんでした。残念( i _ i ))
続いてVMplayerのUbuntu12.04(32bit)上で挑戦。なんだかんだ頑張って、よくあるcifar-10の学習はできました。
色々苦労はしましたが、自分で認識させる所まではまだ全然なんで、せめて自分で認識させることができるようになってから詳しい所はメモしようと思います。

2014年12月6日土曜日

つらつら思うこと:vagrantとDocker

1年くらい前にvagrantのことを知り、仮想マシンの扱いが飛躍的に便利になったと思いました。これで色々実験がしやすくなると思ったら、Linuxメインな自分にとって今度はDockerが現れ、仕組みは違えど狙うところはほとんど同じ。なんか栄枯盛衰が激しいですね。(自分はWeb系は仕事じゃないんで関係ないですが、業界の人は勉強が大変だ)

ただ自分的には、実験色々しようと思うとまっさらなマシンの方がいいから、しばらくはvagrantの方がいいかな、と思うところです。