深層学習5章の白色化とデノイジング自己符号化器を試してみた

お久しぶりです。

SPPnetの論文をよみつつ深層学習の青い本も読み進めてます。
青い本の5章における白色化とデノイジング自己符号化器が気になったので作って見ることにした。

白色化というのは機械学習における訓練データに偏りがあると学習の妨げになる場合があるので学習前に訓練データに何らかの処理を施し、偏り(相関でいいのかな)を除去する処理のこと。

訓練データの成分間の相関は共分散行列で与えられるっぽい
f:id:Owatank:20170826184205p:plain

この共分散行列の(p,q)成分は、訓練データのサンプルXの(p,q)成分がどの程度同じように変化するか、相関があるかを示す。

ある確率変数 X1,X2 に対しての共分散行列を以下のように定めることにする。
f:id:Owatank:20170826184749p:plain
ρ11,ρ22 はそれぞれ X1の分散、X2の分散を、ρ12 は X1とX2 の共分散を表す。
つまり対角成分に分散が、非対角成分には今日分散が並ぶ行列を共分散行列と呼ぶんだな。

確率変数が互いに独立(無相関)ならば、共分散の値は 0 になるという性質から、白色化はこの共分散行列の非対角成分を 0 にする、つまり対角行列に線形変換する操作を行っている、で合っているかな?。

手順を本通り行い、乱数のデータセットを作り試してみた。
結果はここ

元の訓練データ(正規化済み)の分布が
f:id:Owatank:20170826185721p:plain
こんな感じになる
f:id:Owatank:20170826185743p:plain

線形変換後の共分散行列が対角行列になっているかの確認が取れなかった。

おまけでデノイジング自己符号化器を作った。こっちはTensorflowで実装した。
デノイジング自己符号化器は訓練データをノイズありのもので学習し、出力でノイズなしの結果が得られることを期待する感じ。
訓練データはMNISTのデータを使った。

github.com

学習をガウス分布に従うノイズありのみのデータで行ったため、最後のソルトペッパーノイズでの出力はあまりよろしくなかった。
これを実装している時に、配列を a = b のような形でコピーして、 a の値を変える操作を行うと参照元 b の値も変化するということに気づいた。
ポインタとかそこらへんの問題かな。a = b.copy() のように copy を使うと値だけを得られるということを知った。気をつける。

深層学習5章の自己符号化器をTensorflowで作ってみる

間がかなり空いてしまった。
イルカの深層学習5章にて自己符号化器なるものの説明があった。これは入力層よりノードの少ない中間層で符号化し、入力と同じノード数の出力層にて復号化を行う方法でいいのだろうか。入力のノード数より少ないノード数に落とした時、どれだけ入力と近い結果が得られるかが大事なのかな?
読んでいて「復号できんの!すげえ!!!!!!!!」とワクワクしたのと、誤差関数などが自分でも実装できそうだったので、Tensorflowで構築してみることにした。
本通り、損失関数には二乗誤差を、出力の活性化関数には恒等関数を使用した。

MNISTのデータセットを用いたコードはここ
学習を続けることに、徐々に復号の結果が入力に近くなっていくのがわかった。

次に 64x64 の道路標識の「止まれ」でやってみようと思った。
最初はRGBでやっていたが

f:id:Owatank:20170813143647p:plain

禍々しいものが生まれた。何を学習したのだろうか。

損失の値もいい値にはならなかったので、諦めてグレースケールの方でやってみることにした。
コードはここ

こっちはノード数を 50x50 にした。 32x32 の半分のノード数では上手くいかなかった。
最後の結果からわかるように訓練データの結果は
f:id:Owatank:20170813152429p:plain
そこそこ形がわかるような出力になってくれたが、テストデータの結果は
f:id:Owatank:20170813152508p:plain
モザイクのような結果になってしまった。
訓練データが少なくて過適合を起こしているのかもしれない。

結果を観察するのは楽しかった。

深層学習4章の誤差逆伝播について

深層学習という青いイルカさんが表紙の本がありまして最近読んでいる。
重み上限など自分の知らないことが載っていてまだまだ勉強不足と感じる。

4章の誤差逆伝播について、二乗誤差の第n層の重みの更新を考えるとき
f:id:Owatank:20170801132533p:plain
こんな式が載っていた。右辺の第二項の微分を計算したいが、出力層から第n層までを展開、微分しなくてはいけなくなり計算量が大きくなる問題があるということが書いてあった。そしてその問題を解決するために出てきたのが誤差逆伝播

読み進めていった結果

(;^ω^)
こんな感じの顔になった。連鎖律からどういう微分をするのかはわかった気はするがスッキリしなかった。こっちも微分の計算大変じゃね?という感想。
この資料を参考に実際計算をしてみる。

ネットワークとしては中間層が1つの以下を考える。
f:id:Owatank:20170801133428p:plain
まずは順伝播、h1,z1,h2,z2,p1,p2を求める。
中間層。活性化関数としてシグモイド関数を使うことにする。
f:id:Owatank:20170801135358p:plain
出力層。活性化関数は恒等関数。
f:id:Owatank:20170801135736p:plain

次は逆伝播。重みの更新を試みる。まずは誤差関数の値を計算する
f:id:Owatank:20170801140531p:plain
次に出力層に近い w5 ~ w8 の重みの勾配を求める。
w5はネットワークの図から、p1を通過するのがわかる。連鎖律から以下のように式で表せるはず。
f:id:Owatank:20170801142315p:plain
右辺の第1項は
f:id:Owatank:20170801142403p:plain
第2項は
f:id:Owatank:20170801150506p:plain
右辺が計算できたので、勾配を求めてそのまま重み w5 を更新する。
f:id:Owatank:20170801151130p:plain
元の w5 の値より若干増加した。同様に w6 を更新する。右辺の第1項は共通、計算してあるので
f:id:Owatank:20170801151744p:plain
w7,w8については、この2つは p2 を通過しているので dE/dp2 を計算する。
f:id:Owatank:20170801152158p:plain
w7,w8を更新する
f:id:Owatank:20170801152517p:plainf:id:Owatank:20170801152753p:plain
w7とw8はw5,w6に比べてかなり減少してしまった。あってるのか不安になってきた。

最後に入力と中間の間にある w1~w4の重みを更新する。
w1 は z1とh1 を通過しているので
f:id:Owatank:20170801155009p:plain
右辺の第1項から計算していく。z1はp1とp2に値を伝播しているので以下のような連鎖律で表せるはず。
f:id:Owatank:20170801154532p:plain f:id:Owatank:20170801154616p:plain
dE1/dp1とdE2/dp2はw5~w8の重みの更新をする際に計算したのでやるべき計算は dp1/dz1 と dp2/dz1 のみ

右辺第2項と第3項を計算する。
f:id:Owatank:20170801162018p:plain
こっちは比較的楽。右辺が計算できたので勾配を求め、w1の重みを更新する。
f:id:Owatank:20170801162520p:plain
w2も同様に
f:id:Owatank:20170801162859p:plain
w3はz2,h2を通過してきているので、
f:id:Owatank:20170801164224p:plain
右辺第1項を計算する
f:id:Owatank:20170801164309p:plain
右辺第2項と第3項も計算する
f:id:Owatank:20170801164434p:plain
右辺が全て計算できたので w3 の重みを更新する。
f:id:Owatank:20170801164735p:plain
w4も同様に
f:id:Owatank:20170801165035p:plain

これで w1 ~ w8 全ての重みが更新できた。
誤差逆伝搬における入力と中間の間の w1 ~ w4 の重みに更新については出力層から微分の値を受け取ることで、局所的な計算のみで勾配を計算できる認識でいいのかな?
計算が苦手な自分でも求められたから楽にはなってるだろう。多層になればなおさらな気がした。

Tensorflowで作った学習モデルをandroidで利用する

物体検出を勉強中です。Tensorflowにはネットワークのグラフ情報やパラメータなどを保存するcheckpointというものがありますよね。
checkpointファイルをロードすれば一旦学習をストップして、再度学習を続行することもできて非常に便利。
それとは別でcheckpointファイルからProtocolBufferと呼ばれるグラフ情報とパラメータが格納されたファイルを作成できるらしく、それを使ってAndroid上で動かしてみた。

まずはProtocolBufferファイルの作成から。コードはいつも通りここにあげた。
出力結果でネットワークに使用したConv2dなどのパラメータが出てきているのがわかります。
利用としてはtf.Sessionでの初期化をProtocolBufferファイルに入っているグラフ情報で初期化して使うという感じかな?
モルガンさんのこの記事を参照した。

ProtocolBufferファイルの作成もできたので、Android上で読み込んで使ってみる。
アプリの設計としては

  • OpenCVのライブラリを導入して、カメラのフレームを入力にする。
  • カメラのフレームを取得して、その画像でOpenCVライブラリにあるテンプレートマッチングを行う。
  • マッチングしたところを切り抜きリサイズしてProtocolBufferファイルを読み込み、学習にかける

こんな感じ こちらのコードはここ
Androidアプリの開発はそんな経験はなくThreadやListenerなどはあまりわかってないので、頭悪いコードになってしまった。
ProtocolBufferファイルが400MBとデカすぎるのでSDカードから読み込むことに。それでもかなり重いが。。
結果としては以下のようになった
f:id:Owatank:20170729151250p:plain
欠点として、端末の電池の減りが早くなるのと、端末自体が激アツになってしまう。

作るにおいて躓いたのは、OpenCVの導入だった。
android用のTensorflowの導入は簡単だったけど、OpenCVを手順通りにやるとLinkerErrorが出てしまい困った。
原因としては、OpenCVの導入の手順では「sdk > native > libs」の libs を 自プロジェクトの jniLibs に置くと書いてある。
libsの中身としては、armeabix86とかAndroid端末のCPU?の名前のディレクトリが何個かある。
自分のプロジェクトにはTensorflowを利用するために、「jniLibs -> armeabi-v7a -> libtensorflow_inference.so」ファイルが置いてある。
jniLibsに armeabi-v7a以外のx86などのディレクトリを置いてしまうと、Tensorflowのライブラリを読み込むときに、libtensorflow_inference.soをx86とかから参照してしまいエラーが出るということだった。OpenCVを利用するためには libsのarmeabi-v7aだけをコピーして置けば解決した。
f:id:Owatank:20170729152341p:plain

RCNNも作ってみたかったけどjavaでのSelectiveSearchのライブラリが見つからなかったので諦めた。
ProtocolBufferファイルの利用例が作れたのでよしとする。

Tensorflowを用いて物体検出の実装を目指す-RCNN

前回に続いて今回はRCNNです。

まずは論文を頑張って読みました。個人的にまとめたスライドを上げておきます。
英語が苦手なので翻訳に頼ってしまった。。BB回帰のところの実装がわからなかったので探したがなかったので今回は無しで実装することに。

RCNNでは要約すると3つのモジュールが必要になるらしい。

  • 入力画像から物体っぽい領域を提案してくれるアルゴリズム
  • 各提案領域を一つずつ学習モデルにかけて特徴を抽出するためのCNN
  • 抽出した特徴から何のクラスであるか識別するためのクラス固有の線形SVM

一つ目の提案領域の生成には論文通りSelectiveSearchを使うことにした
SelectiveSearchを実装できるほどアルゴリズムに強いわけでもコーディングスキルがあるわけでもないのでアルパカさんのライブラリを利用することに。大変便利だ・・・

二つ目のCNNにはAlexNetと呼ばれるCNNを使用した。コードはこちら

流石に層が深すぎて学習に時間がとてもかかった。いいインスタンスを借りれる機会があって良かった。
AlexNetの論文を読んでいないので、実装はクラスキャットさんの記事やネットワークのイメージ図を見たりして構築した。
実装しててどうしてもわからなかったのはtf.nn.local_response_normalizationの部分だった。今度ゆっくり調べたい。

三つ目のSVMは、この論文でSVMの存在を知ったレベルで知識がないのでこの部分は二つ目のCNNに識別の部分も任せることにしてみた

モジュールの説明はここまでで、結果はこちらにおいておきます。

github.com

結果はこんな感じになった
f:id:Owatank:20170724175853p:plain
CNNのモデルが悪いのか変なところも囲んでしまっていてイマイチ。。。

論文ではNon-Maximum-Suppressionと呼ばれるアルゴリズムを導入してるよと書いてあり、このアルゴリズムによって各クラスの四角の重複をいい感じに抑制してくれる。アルゴリズムの内容についてはこちらのサイトを参考

まだ勉強不足のせいかBoundingBox回帰の実装ができなかった。訓練データにground-truth boxを付与してないからそれもあるのだが。
BoundingBox回帰について詳しい説明などがあるサイトあれば教えて欲しいです。

次はSPP-net?とやらを調べつつFast-RCNNを目指す。

Tensorflowを用いて物体検出の実装を目指す-テンプレートマッチング

最近アルバイトのこともあって物体検出のことについて調べていました。
有名なところではRCNNに続きFast RCNNFaster RCNNYOLO、最近だとSSDというのが熱い感じ?

Faster RCNNSSDはすでにTensorflow版のがGithubで公開されてますが、自分で実装して見たいなと思った。
最終的にはSSDの実装を目指したいけどRCNNからYOLOまで芋づる式的に論文を読まないと実装は難しいとのこと。
まずはRCNNから実装を目指す。

道路標識を認識する学習モデルを作っていたので、画像から道路標識を検出する物体検出器を作る。
RCNNはできたけど、その前にOpenCVにはテンプレートマッチングなるものが用意されているらしいのでそれを使って道路標識っぽいものを探してその部分を切り取って画像認識にかける。という物体検出もどきを作ったのでそれを記事にする。

コードはここ

github.com

少し前の記事で道路標識のモデルを作った。

owatank.hatenablog.com

この道路標識のモデルは「止まれ」と「速度制限(10~80まで)」の2クラスだったが、今思えばかなりアホなことをしている。
出力層の活性化でSoftmaxを使用しているということは出力が確率になる。予測結果として出力の0.5以上の値を採用するとして、新しい入力として猫の画像を渡したら少なくとも出力結果は[0.5... ,0.5...]になるはず。これはいけない
猫の画像で「止まれ」と「制限速度」のそれっぽさが0.5以上になるってかなりおかしいはず。そもそも2クラス分類でSoftmax使うのがおかしいのは?

なので物体検出で使う画像認識のモデルは、「止まれ」、「速度制限10」、「速度制限20」、「速度制限30」の4クラス分類の学習モデルにしました。
次はRCNNの実装についてを記事にする。

Processingで画像の切り抜きプログラムを作った

お久しぶりです。

自作のデータセットで画像認識を行うにあたって、MNISTのように 28x28 サイズで特徴を切り抜かないといけない問題にあたってしまう。

というわけでProcessingで画像の切り抜き、リサイズして保存するプログラム作った。
Processingにしたのは元の画像をみながら切り抜きたいだけ

コードはここ

f:id:Owatank:20170714134440p:plain

あまり例外処理とか入れてないので(苦手)プログラムを実行する前に、LOAD_DATA_PATHSAVE_DATA_PATHを設定しておかないとエラー出ます。

最初に作った時は右下にボタンを作ってそれを押して一枚ごとにファイル選択や保存する設定にしてたんですが、その動作がだるすぎたのでキー操作とファイル選択をリスト表示して選択する形にした。結構快適。

スクレイピングしたことないからあとは元の画像データをいかに時間かけずに集めてくるかが問題。右クリ保存は時間がかかってしまう。。。。。

物体検出であるRCNNをまずは目指してはいるけども、分類において画像データがたくさんないといいモデルは作れないからやっぱりデータ集めは大事。