RとH2Oを使って Deep Learning でキャラクター判定をやらせてみた
何かとDeepLearningがブームになっています。うちでもやってみました。
学習させること
画像認識させてみたいと思います。手始めに、ミッフィー、キティ、マイメロのキャラクターを判別させてみます。人間が見てもおばあちゃんくらいになると厳しいこの3者が区別できるのか?
結論
3つのキャラのうちどれ?って判定をさせると、6割くらいまでの精度にはなりました。普通は背景を取り除いて、キャラの顔だけを切り抜いてから分類させるとは思うのですが、背景込でキャラ判別させた割にはまぁまぁの精度かなと思います。キャラの顔だけをくり抜くことをさせるには、OpenCVとかを使うはずですが、OpenCVをいじりまくるのはまた今度ということで。
以下の図は、活性化関数の種類別に、横軸が繰り返し回数、縦軸が正答率です。20回のRectifierが一番正答率が高いという感じですね。キティは9割がた当たるのですが、ミッフィーとマイメロは区別しにくいようです。単に輪郭を区別しただけという気もしますが、一次元のベクトルから二次元の形を当てるというのもまた一興。
ただ、画像認識でやりたいことがはっきりしている場合には、OpenCVで行けるところまでいくのが定石。
以下は、手順のメモです。
用意するもの
DeepLearning専用のフレームワークを使わず、ここは手軽にRとH2Oを使ってみます。Rであれば、統計な人たちにも馴染み深いですね。
通常はRはRstudioで手元のPCで使うことが多いかと思いますが、機械学習には時間がかかるので、Rをクラウドサーバに入れたいと思います。クラウドサーバだと、特定の時間だけ、サーバースペックをあげることができるので便利ですね。
centOSにRを入れる
wget "http://cran.ism.ac.jp/src/base/R-3/R-3.2.2.tar.gz" tar xvzf R-3.2.2.tar.gz cd R-3.2.2 yum install gcc-gfortran libgfortran ./configure --with-readline=no --with-x=no yum install java-1.7.0-openjdk-devel make make install
centOSの RにH2Oを入れる
> yum install libcurl-devel #パッケージインストールするのに必要 >R >>install.packages("h2o") 接続先を選択する画面 HTTPS CRAN mirror 1: 0-Cloud [https] 2: Austria [https] 3: China (Beijing 4) [https] 4: China (Hefei) [https] 5: Colombia (Cali) [https] 6: France (Lyon 2) [https] 7: Iceland [https] 8: Russia (Moscow 1) [https] 9: Switzerland [https] 10: UK (Bristol) [https] 11: UK (Cambridge) [https] 12: USA (CA 1) [https] 13: USA (KS) [https] 14: USA (MI 1) [https] 15: USA (TN) [https] 16: USA (TX) [https] 17: USA (WA) [https] 18: (HTTP mirrors) ←これ選択 新たに選択肢が増えるので48:Japan(Tokyo)を選択
ダウンロード完了
用意する画像
画像検索を使って、ミッフィー、キティ、マイメロの画像をそれぞれ落としてきます。イラストのこともあれば、グッズの写真ってこともあります。そういうのも一旦お構いなしに、インプットとしてみます。
実際に学習するのは、一次元のベクトル
画像を学習させるとは言うものの、機械学習にかけるのは、1次元のベクトルです。画像をグレースケールにして(この時点でマイメロの色がなくなり輪郭がミッフィーとおなじになる)、100×100のピクセルを1万の一次元の要素にしちゃいます。ホントは色付きでもっとたくさんのピクセル数でやりたいところですが、めちゃくちゃ時間がかかるので、この辺で許さないと厳しかったです。
こんなcsvができあがります。
一番左の列がラベルで、ここにキティかミッフィーかマイメロが入ります。それ移行の列は1万個のピクセルの値ですね。0~255までのグレースケールの値が入ります。
このデータを、学習用と判定用の両方作ります。
h2oのパラメータ
以下のパラメータを指定しました。
パラメータ | 説明 | 今回のモデル |
---|---|---|
x | モデルの文字列変数を含むベクトル 100*100ピクセルだったら2列目から1001列目 |
2:10001 |
y | モデル内の応答変数の名前。ラベルが付いている列(たぶん) | 1 |
training_frame | 学習用データフレーム | 略 |
activation | 活性化関数を選択 Tanh TanhWithDropout Rectifier RectifierWithDropout Maxout MaxoutWithDropout |
Tanh TanhWithDropout Rectifier RectifierWithDropout Maxout MaxoutWithDropout|Tanh TanhWithDropout Rectifier RectifierWithDropout |
hidden | 隠れレイヤーのユニット数をベクトルで並べる 入力次元の1/10 or 1/100ユニットが普通 例:rep(20,5) 20ユニット×5層 |
rep(1000,4) |
epochs | 繰り返し回数 デフォルトだと1回 |
10~40 |
グレースケールに変換してベクトルに変換するRスクリプト
ImageMagicとRubyとかの組み合わせでもできちゃうけど、Rでもここまでできるってことで。
library(raster) #グレースケール変換関数 as.grayscale.array<-function(file,flag){ image<-brick(file) image.rgb<-getValues(image) rgbcol<-ncol(image.rgb) #元画像がカラーであればRGB値をグレースケールに変換 if (rgbcol>1){ image.bw<-image.rgb[,1]*0.21+image.rgb[,2]*0.72+image.rgb[,3]*0.07 return (c(flag,image.bw)) #元画像が白黒であればそのまま }else{ return (c(flag,image.rgb)) } } #各画像のパスをキャラごとに格納 kity <- list.files("C:/rooter/pic_resize100/",pattern = "kity",full.names=T) miffy <- list.files("C:/rooter/pic_resize100/",pattern = "miffy",full.names=T) mymelo <- list.files("C:/rooter/pic_resize100/",pattern = "mymelo",full.names=T) #評価用データ pre_kity <- list.files("C:/rooter/pic_eva/",pattern="kity",full.names=T) pre_miffy <- list.files("C:/rooter/pic_eva/",pattern="miffy",full.names=T) pre_mymelo <- list.files("C:/rooter/pic_eva/",pattern="mymelo",full.names=T) #3つのキャラクターを1つのベクトルに結合 res.dl<-rbind(t(sapply(kity, as.grayscale.array , "kity")), t(sapply(miffy, as.grayscale.array , "miffy")), t(sapply(mymelo, as.grayscale.array , "mymelo")) ) #評価用データのベクトル生成 pred.dl<-rbind(t(sapply(pre_kity, as.grayscale.array , "kity")), t(sapply(pre_miffy, as.grayscale.array , "miffy")), t(sapply(pre_mymelo, as.grayscale.array , "mymelo")) ) #学習用・評価用データをデータフレーム化 res.dl<-data.frame(res.dl) pred.dl<-data.frame(pred.dl) #出力 write.csv(res.dl,"C:/rooter/training_data.csv",row.names=FALSE, quote=TRUE) write.csv(pred.dl,"C:/rooter/prediction_data.csv",row.names=FALSE, quote=TRUE)
学習と評価のRスクリプト
library(h2o) #あらかじめJava SE 7をダウンロードしておく localH2O <- h2o.init(ip="localhost" , nthreads = -1) #データファイルをH2Oクラスに変換 #学習用 target<-read.csv("training_data.csv") target<-data.frame(target) act<-"TanhWithDropout" epo<-40 start<-proc.time() #学習 res.dl<-h2o.deeplearning(x = 2:10001, y = 1, training_frame = as.h2o(target), activation = act, hidden = rep(1000,4), epochs = epo, ) end<-proc.time() #評価用 pred.dt<-read.csv("prediction_data.csv") #予測 prediction<-h2o.predict(object = res.dl , newdata = as.h2o(pred.dt)) #精度を算出 pred<-as.data.frame(prediction) result.ar<-t(rbind(pred.dt[,1],pred[,1])) #正答率を計算 kity.num<-0 miffy.num<-0 mymelo.num<-0 num<-0 for(i in 1:nrow(result.ar)){ if(result.ar[i,1]==result.ar[i,2]){ if(result.ar[i,1]==1){ kity.num<-kity.num+1 } if(result.ar[i,1]==2){ miffy.num<-miffy.num+1 } if(result.ar[i,1]==3){ mymelo.num<-mymelo.num+1 } num<-num+1 } } kity.res<-format(kity.num/sum(result.ar[,1]==1), nsmall=5) miffy.res<-format(miffy.num/sum(result.ar[,1]==2), nsmall=5) mymelo.res<-format(mymelo.num/sum(result.ar[,1]==3), nsmall=5) res<-format(num/nrow(result.ar),nsmall=5) #出力 pred.ar<-data.frame(chara=c("kity","miffy","mymelo","all","time"), pre=c(kity.res,miffy.res,mymelo.res,res,as.character(end-start)[3])) write.table(pred.ar,paste("log/",act,"/epo",epo,".txt",sep="")) write.csv(pred,paste("log/",act,"/epo",epo,".csv",sep=""))