CORDEA blog

Android applications engineer

【pylearn2】自分のデータセットを使ってカンタンにGRBMしよう

はじめに

pylearn2というdeep learning libraryは、installしていくつかのサンプルを動かすだけなら割と簡単です。

ただ、いざ自分の用意したデータセットを使用してdeep learningさせようと思うと意外に大変。

というわけで可能な限り簡単に自分のデータセットを使ってGRBM(Gaussian restricted Boltzmann machine)を行うためのパイプラインを作成しました。
なんか間違ってたら適当に修正して下さい。

 

hoge_dataset.pyとgrbm.yamlこちらのプログラムにいくつか私が変更を加えたものです。
私が作成したものではないパラメータ等ありますので、元のリポジトリもご参照下さい。


github.com

 

方法

 

pylearn2のinstallはいろんなところで書かれていますので割愛します。
 

自分のデータセットを作成

識別したい画像を用意してこんな感じで配置
配置する場所はPYLEARN2_DATA_PATH内にディレクトリ作ってその中に
今回は ${PYLEARN2_DATA_PATH}/train_test 内に in ディレクトリを作成したものとして書いています
 

in ディレクトリの名前が紛らわしいと感じたならconvert_image.pyの_DIRを修正して下さい。
 

└── in
    ├── class_1
    │   ├── 1.jpg
    │   └── 2.jpg
    ├── class_2
    │   ├── 1.jpg
    │   └── 2.jpg
    └── class_3
        ├── 1.jpg
        └── 2.jpg
% mv in ${PYLEARN2_DATA_PATH}/train_test/

 

下準備

% PYLEARN2_INSTALL_DIR=$HOME # installした場所
% cd $PYLEARN2_INSTALL_DIR/pylearn2/pylearn2/scripts/tutorials/ # 別にどこでもいいです
% git clone https://github.com/CORDEA/use_images_in_pylearn2.git
% cd use_images_in_pylearn2
% mv *.py $PYLEARN2_INSTALL_DIR/pylearn2/pylearn2/datasets/

実行

% train.py grbm.yaml

重みの可視化

% show_weight.py grbm.pkl

 

設定値など

ディレクトリをtrain_testとして書いてありますので、適宜読み替えて下さい。

各種パラメータについて

grbm.yamlにおけるパラメータで大体設定できるようにしてあります。

  • which_set
    • csvの名前になります
  • base_path
    • 自分のデータセットを置いたディレクトリへのPATHです
  • image_to_csv
    • Trueにすると in ディレクトリにあるimageをcsvにしてから学習に移ります
  • image_size
    • imageの大きさです。defaultで128ですがコンピュータの処理性能等や目的に応じて。
  • color
    • defaultでFalseです。Trueにすると色情報を持ったcsvになりますが、次元数が3倍に。
  • save
    • defaultでFalseです。TrueにするとnpyファイルをPYLEARN2_DATA_PATH/train_test/に保存します。既にnpyファイルが存在する場合はcsvファイルではなくnpyファイルを読み込みます。

 

作成されるファイルについて

  • ${PYLEARN2_DATA_PATH}/train_test/train.csv
    • imageから作成したcsvファイル
  • ${PYLEARN2_DATA_PATH}/train_test/comparative_table.name
    • labelとディレクトリ, 画像名の対応表。いらないような気がします
  • ${PYLEARN2_DATA_PATH}/train_test/*.npy
    • numpyのファイル, 詳しくはhoge_dataset.pyを参照して下さい。saveがTrueである場合のみ作成及び読み込みを行います。

Caffe, Pylearn2をそれぞれinstallしたDockerコンテナをDocker Hubに公開した

QiitaにてCaffe, Pylearn2のinstallに関する記事を投稿したところTwitterで次のような反応を頂きました。


これは私も感じていたことで、installが一番の関門ではないにしろ、「ちょっと使ってみたい」というユーザーを阻むには十分すぎる障壁だろうと思います。

 

もちろん、Pylearn2はVagrantに慣れていればVMが公開されているのでそちらを使用する手もあります。

一応Dockerfileも書いてはいる(動作確認はしていない)のですが、そもそもDockerfileを使用するにも時間が掛かるので、Docker Hubにコンテナを公開することにしました。

他の方も同じようなコンテナは公表しておられますので、そこらへんは好みで...

 

使い方とか

Caffe

% docker pull cordea/pycaffe

Caffeのmake, python wrapperに必要なライブラリのinstall, pathを通すところまで終了している状態のコンテナです。
Qiitaの記事で言うと"make"まで終了しています。


Pylearn2

% docker pull cordea/pylearn2

pylearn2のinstallとpathを通すところまで終了している状態のコンテナです。

docker上のUbuntu 14.04にcaffeをinstall

Caffe

Caffeはディープラーニングのフレームワークです。


BVLC/caffe · GitHub


はじめに

今回はDocker上のUbuntuにCaffeをinstallします。
GPUが絡むと面倒なので、今回はCPUモードで使用します。
また、Pythonで使用するための設定やインストールは行いません。
そちらは本家のInstallationを参考にして下さい。

  • 2015/01/26 追記

 CaffeのPython wrapperを使用するための手順を追記したものをQiitaに投稿しました。
 Pythonで使用される方はそちらをご覧ください。

Version

  • Docker version 1.3.2, build 39fa2fa/1.3.2

手順

ユーザーの作成

行わなくても問題ありません

% docker run -it --name="caffe" ubuntu /bin/bash
root@4536d063fc8a:/# adduser --disabled-password --gecos '' cordea
root@4536d063fc8a:/# adduser cordea sudo
root@4536d063fc8a:/# echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers


 

installとか

root@4536d063fc8a:/# su cordea
cordea@4536d063fc8a:/$ cd home/cordea/
cordea@4536d063fc8a:~$ sudo apt-get update
cordea@4536d063fc8a:~$ sudo apt-get install git vim wget make bc libprotobuf-dev libleveldb-dev libsnappy-dev libopencv-dev libboost-all-dev libhdf5-serial-dev libblas-dev libatlas-base-dev libgflags-dev libgoogle-glog-dev liblmdb-dev protobuf-compiler
cordea@4536d063fc8a:~$ mkdir caffe
cordea@4536d063fc8a:~$ cd caffe/


 

CUDA

CPUモードではGraphics Driverは必要ありませんが、CUDA Toolkitは必要なようなのでToolkitだけinstallします。

cordea@4536d063fc8a:~/caffe$ wget http://developer.download.nvidia.com/compute/cuda/6_5/rel/installers/cuda_6.5.14_linux_64.run
cordea@4536d063fc8a:~/caffe$ chmod u+x cuda_6.5.14_linux_64.run 
cordea@4536d063fc8a:~/caffe$ ./cuda_6.5.14_linux_64.run 
Do you accept the previously read EULA? (accept/decline/quit): accept
Install NVIDIA Accelerated Graphics Driver for Linux-x86_64 340.29? ((y)es/(n)o/(q)uit): n
Install the CUDA 6.5 Toolkit? ((y)es/(n)o/(q)uit): y
Enter Toolkit Location [ default is /usr/local/cuda-6.5 ]: 
/usr/local/cuda-6.5 is not writable.
Do you wish to run the installation with 'sudo'? ((y)es/(n)o): y
Do you want to install a symbolic link at /usr/local/cuda? ((y)es/(n)o/(q)uit): y
Install the CUDA 6.5 Samples? ((y)es/(n)o/(q)uit): n
Installing the CUDA Toolkit in /usr/local/cuda-6.5 ...

===========
= Summary =
===========

Driver:   Not Selected
Toolkit:  Installed in /usr/local/cuda-6.5
Samples:  Not Selected

cordea@4536d063fc8a:~/caffe$ sudo ldconfig /usr/local/cuda-6.5/lib64/


 

makeとか

runtestでerrorが出るのはGPUが無いからだ...と思います

cordea@4536d063fc8a:~/caffe$ git clone https://github.com/BVLC/caffe
cordea@4536d063fc8a:~/caffe$ cd caffe/
cordea@4536d063fc8a:~/caffe/caffe$ cp Makefile.config.example Makefile.config         
cordea@4536d063fc8a:~/caffe/caffe$ make all
cordea@4536d063fc8a:~/caffe/caffe$ make test
cordea@4536d063fc8a:~/caffe/caffe$ make runtest
.build_release/test/test_all.testbin 0 --gtest_shuffle
libdc1394 error: Failed to initialize libdc1394
Cuda number of devices: 0
Setting to use device 0
Current device id: 0
Note: Randomizing tests' orders with a seed of 65626 .
[==========] Running 838 tests from 169 test cases.
[----------] Global test environment set-up.
[----------] 7 tests from SyncedMemoryTest
[ RUN      ] SyncedMemoryTest.TestInitialization
[       OK ] SyncedMemoryTest.TestInitialization (0 ms)
[ RUN      ] SyncedMemoryTest.TestAllocationCPU
[       OK ] SyncedMemoryTest.TestAllocationCPU (0 ms)
[ RUN      ] SyncedMemoryTest.TestCPUWrite
[       OK ] SyncedMemoryTest.TestCPUWrite (0 ms)
[ RUN      ] SyncedMemoryTest.TestGPUWrite
F0120 07:23:59.559131 31252 syncedmem.cpp:51] Check failed: error == cudaSuccess (35 vs. 0)  CUDA driver version is insufficient for CUDA runtime version
*** Check failure stack trace: ***
    @     0x2b0c0e68ddaa  (unknown)
    @     0x2b0c0e68dce4  (unknown)
    @     0x2b0c0e68d6e6  (unknown)
    @     0x2b0c0e690687  (unknown)
    @           0x70090b  caffe::SyncedMemory::mutable_gpu_data()
    @           0x5bf8ad  caffe::SyncedMemoryTest_TestGPUWrite_Test::TestBody()
    @           0x65a883  testing::internal::HandleExceptionsInMethodIfSupported<>()
    @           0x651327  testing::Test::Run()
    @           0x6513ce  testing::TestInfo::Run()
    @           0x6514d5  testing::TestCase::Run()
    @           0x654818  testing::internal::UnitTestImpl::RunAllTests()
    @           0x654aa7  testing::UnitTest::Run()
    @           0x41d480  main
    @     0x2b0c114b4ec5  (unknown)
    @           0x4244b7  (unknown)
    @              (nil)  (unknown)
make: *** [runtest] Aborted (core dumped)


 

試す

Tutorialにしたがって動作を確認します。

prototxtを編集しないとエラーが出ますので、"lenet_solver.prototxt"の一番下の行のGPUをCPUに変更して下さい。

cordea@4536d063fc8a:~/caffe/caffe$ vim examples/mnist/lenet_solver.prototxt
cordea@4536d063fc8a:~/caffe/caffe$ ./data/mnist/get_mnist.sh 
cordea@4536d063fc8a:~/caffe/caffe$ ./examples/mnist/create_mnist.sh
cordea@4536d063fc8a:~/caffe/caffe$ ./examples/mnist/train_lenet.sh 

GlueLangを動かしてみる。

とりあえず、私のMacでGlueLangのサンプルを動かすまでの話です。
Ubuntuでは上手くいくのですが、私のMacだと始めにちょっとこけたのでメモ。

  • 2015/01/13 追記

 現在はLLVM 6.0でも動作することを確認しました。

GlueLang

 

ryuichiueda/GlueLang · GitHub


Mac OS X (10.9.5)

 

使ってみる

% git clone https://github.com/ryuichiueda/GlueLang/
% cd GlueLang
% make
% ./glue EXAMPLE/if_then_else.glue

私の場合EXAMPLEのfizzbuzz.glueとif_then_else.glueがCommand errorで動作しなかった。

 

こけた原因と対処法

原因

おそらくgccとg++のversion

対処法
% ./glue EXAMPLE/if_then_else.glue

Execution error at line 5, char 3
        line5: ? /usr/bin/true
                 ^
        Command error

        process_level 1
        exit_status 127
        pid 34374

  ...

% brew tap homebrew/versions
% brew install gcc48
% sudo ln -sf /usr/local/bin/gcc-4.8 /usr/bin/gcc
% sudo ln -sf /usr/local/bin/g++-4.8 /usr/bin/g++

% make clean
rm -f glue Arg.o Command.o CommandLine.o Comment.o Element.o Environment.o Feeder.o IfBlock.o Import.o Pipeline.o Script.o TmpFile.o VarString.o main.o /usr/local/bin/glue

% make
g++ -Wall -O3 --static -std=c++11   -c -o Arg.o Arg.cc
g++ -Wall -O3 --static -std=c++11   -c -o Command.o Command.cc
g++ -Wall -O3 --static -std=c++11   -c -o CommandLine.o CommandLine.cc
g++ -Wall -O3 --static -std=c++11   -c -o Comment.o Comment.cc
g++ -Wall -O3 --static -std=c++11   -c -o Element.o Element.cc
g++ -Wall -O3 --static -std=c++11   -c -o Environment.o Environment.cc
g++ -Wall -O3 --static -std=c++11   -c -o Feeder.o Feeder.cc
Feeder.cc: In member function 'int Feeder::countIndent()':
Feeder.cc:476:20: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
  while(i < p->size()){
                    ^
g++ -Wall -O3 --static -std=c++11   -c -o IfBlock.o IfBlock.cc
IfBlock.cc: In member function 'virtual int IfBlock::exec()':
IfBlock.cc:136:29: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
  for(int i=0;i<m_nodes.size();i++){
                             ^
g++ -Wall -O3 --static -std=c++11   -c -o Import.o Import.cc
g++ -Wall -O3 --static -std=c++11   -c -o Pipeline.o Pipeline.cc
g++ -Wall -O3 --static -std=c++11   -c -o Script.o Script.cc
g++ -Wall -O3 --static -std=c++11   -c -o TmpFile.o TmpFile.cc
g++ -Wall -O3 --static -std=c++11   -c -o VarString.o VarString.cc
g++ -Wall -O3 --static -std=c++11   -c -o main.o main.cc
g++ -o glue Arg.o Command.o CommandLine.o Comment.o Element.o Environment.o Feeder.o IfBlock.o Import.o Pipeline.o Script.o TmpFile.o VarString.o main.o

% ./glue EXAMPLE/if_then_else.glue
OK
a
OK
OK

 

ここには書いておりませんがgcc,g++のVersionを上げた後、fizzbuzz.glueも正常に動くことを確認しています。

gcc, g++ Version

上げる前
% gcc --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/usr/include/c++/4.2.1
Apple LLVM version 6.0 (clang-600.0.56) (based on LLVM 3.5svn)
Target: x86_64-apple-darwin13.4.0
Thread model: posix
% g++ --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/usr/include/c++/4.2.1
Apple LLVM version 6.0 (clang-600.0.56) (based on LLVM 3.5svn)
Target: x86_64-apple-darwin13.4.0
Thread model: posix

 

上げた後
% gcc --version
gcc (Homebrew gcc48 4.8.3) 4.8.3
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
% g++ --version
g++ (Homebrew gcc48 4.8.3) 4.8.3
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

 
 

Ubuntu

普通に動作しているのでversionだけ。

Version

% uname -r
3.16.7-tinycore64
% cat /etc/issue
Ubuntu 14.04.1 LTS \n \l
% gcc --version
gcc (Ubuntu 4.8.2-19ubuntu1) 4.8.2
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
% g++ --version
g++ (Ubuntu 4.8.2-19ubuntu1) 4.8.2
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

niconicoのコンテンツ検索apiを用いたアニメ間距離の可視化 続き

前回は距離の算出やノイズに悩まされ、あまり良い結果が得られなかったのであの後もしばらく続けておりました。
これ以上放っておくと手順を忘れそうなので、ここらでまとめておきます。

とはいえ一ヶ月前くらいにやったことなので既に結構忘れていて余り書くことがない...すみません。

 
とりあえずプリキュアとかの続編がある程度固まってくれているのがちょっと嬉しいです。

 

前回からの変更点

  • 2010-2014作品から、2000-2014年に取得範囲を変更
  • 説明文にアニメタイトルを含むものまで取得していましたが、関係ないものが多くヒットするようなので動画タイトルかタグのいずれかにアニメタイトルを含むものだけを取得するように変更(getdataFromWikipedia.py)
  • 動画件数が200件に満たないものは除外(getSearchResult.py)
  • 取得した動画のタグに「アニメ」タグを含まないものは除外(tagParseJSONforCount.py)
  • タグの出現回数ではなく、アニメ1作品の総タグ数に占める対象タグの割合に変更(tagParseJSONforCount.py)
  • tf-idfを算出し、一定値に満たないタグは除外することで、特異的なタグが距離に反映されやすくなるように機能追加(calcTfidf.py)
  • あまり関係はないですが、出力の関係でTree ViewerをDendroscopeからEPoSに変更
  • 他にもMeCabを使って形態素解析してMDSしてみたり(tagMeCabParseJSONforCount.py)


 

結果

Dendrogram
画像サイズが大きいのでご注意下さい。

距離の算出はピアソンです。


GitHubnewickデータもおいておきます。


 

手順

python getSearchResult.py animes_2000-2014.txt
python tagParseJSONforCount.py animes > tags_anime_dataset.tsv
python calcTfidf.py tags_anime_dataset.tsv > tfidf_anime_dataset.tsv
awk -F "\t" '{if($2>=1.0){print $0}}' tfidf_anime_dataset.tsv > tfidf_1.0_anime_dataset.tsv
gawk -f cut_file.awk tfidf_1.0_anime_dataset.tsv tags_anime_dataset.tsv > tags_1.0_anime_dataset.tsv
R CMD BATCH hclust.R


 

コード類

GitHub


 

【Python】niconicoのコンテンツ検索apiを用いたアニメ間距離の可視化【R】

はじめに

3連休中あまりに暇だったので、動画に付いているタグによってアニメ間の距離はどう見えるかを調べた結果と作ったプログラム群。

今回対象としているアニメ

2010-2014年までのアニメ、823作品
Wikipediaから取得

主に使用しているライブラリ等

  • Python
    • scikit-learn
    • matplotlib
  • R
    • ctc
    • amap

やってみて感じたこと

問題点
  • 1700件までしか取得できないapi仕様
  • モノによってノイズが多く入り込む可能性がある
  • アニメによって数の差がかなりある
利点
  • 勢いのあるサービスであり、今後もデータが増える見込みがある
  • サムネイル画像習得による大規模画像解析が可能
  • 使い方次第でビッグデータになり得る


 
 

手順

ソースコードGitHubにあります。

アニメ一覧の取得(Wikipediaページのparse)

よく考えたらxmlが取得できるのでhtmlなどparseする必要はなかった...

python getDataFromWikipedia.py > animes_2010-2014.txt


 

検索結果の取得

apiにより取得したJSONをファイルに出力する
制限があるため、1秒間に2回、100件ずつ取得する。
また、検索結果が1700件以上あっても仕様上1700件までしか取得できない。

流れとしては以下の通り

  1. 取得対象名がタイトル/説明文/タグのいずれかに含まれているものを取得
  2. 検索結果として得られた動画に付いているタグをJSON形式で100件取得する。
  3. 100件をファイルに保存する

関係無い動画が取得される可能性はありますが、今回はそれもそのアニメ自体の影響力の強さと考えて特にフィルターをかけたりはしていません。

while read line;do;python getSearchResult.py $line;done < animes_2010-2014.txt

 

JSON→tsv 変換処理

変換処理によって横列がタグの出現回数, 縦列がコンテンツとするtsvファイルを作成する。

python tagParseJSON.py animes > animes_tag.tsv


変換処理によって832*354,117のデータが得られた


 

tSNEによる可視化

python mds_plot.py animes_tag.tsv
手法とパラメーターの探索

http://raw.cordea.jp/niconico/act1/explore_1.png


確認して一番良さげなPCA+t-SNEに絞る。

  1. PCAによる次元縮約
  2. t-SNEによる座標算出
  3. プロット


http://raw.cordea.jp/niconico/act1/explore_3.png



ココらへんのimageは全てこちらにあります。

結果

小さい画像
f:id:CORDEA:20141126221336p:plain


大きい画像
約33*33inchの非常に大きな画像ですのでお気をつけ下さい。


 

Hierachical clusteringによる可視化

Dendrogramを描きますが、これに関してはRの方が楽なのでRでやります。
ですがこのままプロットしたところで確認できるようなものにならないのは分かりきっているので、ctcライブラリを使用してDendroscope用のデータを作成します。
ちなみにctcライブラリはFedora 20で上手くインストール出来なかったのでこの部分はDebianを使っています。





出来上がったnewick.txtには以下のように何故かダブルクォーテーションが付いていてエラーを吐いたのでダブルクォーテーションを消します。

"(TIGER:82.6491092539521,(Fate%slZero:16.5373639856817,(Free!:18.3057504854552,
(Angel_Beats!:72.6269302031301,(STEINS%scGATE:69.8680393971481,
(R-15:31.1784166447384,(WORKING!!:46.0964574970961,(TARI_TARI:23.1396305160703,

[中略]

72.6269302031301):18.3057504854552):16.5373639856817):82.6491092539521);"

 

Dendroscopeで確認

ラベル一つ一つまで確認できるようなイメージを作成しようとしたところ、Debianでは強制終了し、WindowsではBusy状態で停止したため、適当な大きさで妥協した。

小さい画像

f:id:CORDEA:20141126221246p:plain



大きい画像
こちらも非常に大きな画像ですのでお気をつけ下さい。

 

まとめ

  • アニメ間の距離は続編が近い傾向にある。しかし続編間の距離が非常に遠くなったものもあった。
  • 動画数が非常に少ないアニメが2~3割あると見られ、それらが正常にプロットされておらず、何らかの閾値を設ける必要がある。
  • 説明変数が多すぎて上手くクラスタが構成されない
    • 次元縮約を行わない場合に、次元の呪いによる球面集中現象が見られた

そして結果をまとめるのが一番大変だった...
結果については色々と考察したのですが、やはり変数が多すぎることと手順に問題があること(距離の算出とか)から、検索数などでフィルターをかけて距離の算出法を見直せば何か見えてくるかもしれません。
いずれにしろかなりやりがいのあるデータですので今後も進めてまいります。

 

optparseでエスケープ文字を受け取りたい時

optparseはエスケープ文字をそのまま入力すると期待した結果と異なる挙動を示すことがあります。
これは一部のコマンド(sortなど)にも言えます。
しばらくしたら忘れてそうなのでメモ。

試しに次のようなプログラムを書いてみました。

from optparse import OptionParser

def optSettings():
    usage="hogehoge"
    version="0.0.1"
    parser = OptionParser(usage=usage, version=version)

    parser.add_option(
            "-a",
            action = "store",
            type   = "str",
            dest   = "test"
            )

    return parser.parse_args()

if __name__ == '__main__':
    options, args = optSettings()
    print >>>,options.test


このプログラムで次のように実行すれば"hogehoge"が帰ってきます。

% python optparse_test.py -a "hogehoge"
>>> hogehoge

'\t'や'\n'はこうなります。

% python optparse_test.py  -a "\t"
>>> \t


'\t'の場合は次のような入力によってタブを出力できます。
'#'はわかりやすいように入れているだけです。

% python optparse_test.py  -a $'#\t#'
>>> #	#


ちなみに始めに書いたsortコマンドの場合はこんな感じで。

% sort -t $'\t' -k 2 -nr

おわり。