Tensorflowで作った学習モデルをandroidで利用する
物体検出を勉強中です。Tensorflowにはネットワークのグラフ情報やパラメータなどを保存するcheckpoint
というものがありますよね。
checkpoint
ファイルをロードすれば一旦学習をストップして、再度学習を続行することもできて非常に便利。
それとは別でcheckpoint
ファイルからProtocolBuffer
と呼ばれるグラフ情報とパラメータが格納されたファイルを作成できるらしく、それを使ってAndroid上で動かしてみた。
まずはProtocolBuffer
ファイルの作成から。コードはいつも通りここにあげた。
出力結果でネットワークに使用したConv2d
などのパラメータが出てきているのがわかります。
利用としてはtf.Session
での初期化をProtocolBuffer
ファイルに入っているグラフ情報で初期化して使うという感じかな?
モルガンさんのこの記事を参照した。
ProtocolBuffer
ファイルの作成もできたので、Android上で読み込んで使ってみる。
アプリの設計としては
- OpenCVのライブラリを導入して、カメラのフレームを入力にする。
- カメラのフレームを取得して、その画像でOpenCVライブラリにあるテンプレートマッチングを行う。
- マッチングしたところを切り抜きリサイズして
ProtocolBuffer
ファイルを読み込み、学習にかける
こんな感じ こちらのコードはここ
Androidアプリの開発はそんな経験はなくThreadやListenerなどはあまりわかってないので、頭悪いコードになってしまった。
ProtocolBuffer
ファイルが400MBとデカすぎるのでSDカードから読み込むことに。それでもかなり重いが。。
結果としては以下のようになった
欠点として、端末の電池の減りが早くなるのと、端末自体が激アツになってしまう。
作るにおいて躓いたのは、OpenCVの導入だった。
android用のTensorflowの導入は簡単だったけど、OpenCVを手順通りにやるとLinkerErrorが出てしまい困った。
原因としては、OpenCVの導入の手順では「sdk > native > libs」の libs を 自プロジェクトの jniLibs に置くと書いてある。
libsの中身としては、armeabi
やx86
とかAndroid端末のCPU?の名前のディレクトリが何個かある。
自分のプロジェクトにはTensorflowを利用するために、「jniLibs -> armeabi-v7a -> libtensorflow_inference.so」ファイルが置いてある。
jniLibsに armeabi-v7a
以外のx86
などのディレクトリを置いてしまうと、Tensorflowのライブラリを読み込むときに、libtensorflow_inference.soをx86
とかから参照してしまいエラーが出るということだった。OpenCVを利用するためには libsのarmeabi-v7a
だけをコピーして置けば解決した。
RCNNも作ってみたかったけどjavaでのSelectiveSearchのライブラリが見つからなかったので諦めた。
ProtocolBuffer
ファイルの利用例が作れたのでよしとする。
Tensorflowを用いて物体検出の実装を目指す-RCNN
前回に続いて今回はRCNNです。
まずは論文を頑張って読みました。個人的にまとめたスライドを上げておきます。
英語が苦手なので翻訳に頼ってしまった。。BB回帰のところの実装がわからなかったので探したがなかったので今回は無しで実装することに。
RCNNでは要約すると3つのモジュールが必要になるらしい。
一つ目の提案領域の生成には論文通りSelectiveSearch
を使うことにした
SelectiveSearchを実装できるほどアルゴリズムに強いわけでもコーディングスキルがあるわけでもないのでアルパカさんのライブラリを利用することに。大変便利だ・・・
二つ目のCNNにはAlexNetと呼ばれるCNNを使用した。コードはこちら
流石に層が深すぎて学習に時間がとてもかかった。いいインスタンスを借りれる機会があって良かった。
AlexNetの論文を読んでいないので、実装はクラスキャットさんの記事やネットワークのイメージ図を見たりして構築した。
実装しててどうしてもわからなかったのはtf.nn.local_response_normalization
の部分だった。今度ゆっくり調べたい。
三つ目のSVMは、この論文でSVMの存在を知ったレベルで知識がないのでこの部分は二つ目のCNNに識別の部分も任せることにしてみた
モジュールの説明はここまでで、結果はこちらにおいておきます。
結果はこんな感じになった
CNNのモデルが悪いのか変なところも囲んでしまっていてイマイチ。。。
論文ではNon-Maximum-Suppressionと呼ばれるアルゴリズムを導入してるよと書いてあり、このアルゴリズムによって各クラスの四角の重複をいい感じに抑制してくれる。アルゴリズムの内容についてはこちらのサイトを参考に
まだ勉強不足のせいかBoundingBox回帰の実装ができなかった。訓練データにground-truth boxを付与してないからそれもあるのだが。
BoundingBox回帰について詳しい説明などがあるサイトあれば教えて欲しいです。
次はSPP-net
?とやらを調べつつFast-RCNN
を目指す。
Tensorflowを用いて物体検出の実装を目指す-テンプレートマッチング
最近アルバイトのこともあって物体検出のことについて調べていました。
有名なところではRCNN
に続きFast RCNN
、Faster RCNN
、YOLO
、最近だとSSD
というのが熱い感じ?
Faster RCNN
やSSD
はすでにTensorflow版のがGithubで公開されてますが、自分で実装して見たいなと思った。
最終的にはSSD
の実装を目指したいけどRCNN
からYOLO
まで芋づる式的に論文を読まないと実装は難しいとのこと。
まずはRCNN
から実装を目指す。
道路標識を認識する学習モデルを作っていたので、画像から道路標識を検出する物体検出器を作る。
RCNN
はできたけど、その前にOpenCV
にはテンプレートマッチングなるものが用意されているらしいのでそれを使って道路標識っぽいものを探してその部分を切り取って画像認識にかける。という物体検出もどきを作ったのでそれを記事にする。
コードはここ
少し前の記事で道路標識のモデルを作った。
この道路標識のモデルは「止まれ」と「速度制限(10~80まで)」の2クラスだったが、今思えばかなりアホなことをしている。
出力層の活性化でSoftmaxを使用しているということは出力が確率になる。予測結果として出力の0.5以上の値を採用するとして、新しい入力として猫の画像を渡したら少なくとも出力結果は[0.5... ,0.5...]
になるはず。これはいけない
猫の画像で「止まれ」と「制限速度」のそれっぽさが0.5以上になるってかなりおかしいはず。そもそも2クラス分類でSoftmax使うのがおかしいのは?
なので物体検出で使う画像認識のモデルは、「止まれ」、「速度制限10」、「速度制限20」、「速度制限30」の4クラス分類の学習モデルにしました。
次はRCNN
の実装についてを記事にする。
Processingで画像の切り抜きプログラムを作った
お久しぶりです。
自作のデータセットで画像認識を行うにあたって、MNISTのように 28x28 サイズで特徴を切り抜かないといけない問題にあたってしまう。
というわけでProcessingで画像の切り抜き、リサイズして保存するプログラム作った。
Processingにしたのは元の画像をみながら切り抜きたいだけ
コードはここ
あまり例外処理とか入れてないので(苦手)プログラムを実行する前に、LOAD_DATA_PATH
とSAVE_DATA_PATH
を設定しておかないとエラー出ます。
最初に作った時は右下にボタンを作ってそれを押して一枚ごとにファイル選択や保存する設定にしてたんですが、その動作がだるすぎたのでキー操作とファイル選択をリスト表示して選択する形にした。結構快適。
スクレイピングしたことないからあとは元の画像データをいかに時間かけずに集めてくるかが問題。右クリ保存は時間がかかってしまう。。。。。
物体検出であるRCNNをまずは目指してはいるけども、分類において画像データがたくさんないといいモデルは作れないからやっぱりデータ集めは大事。
畳み込み層について その2
前回の続き
前回をまとめると
- 画像は基本3次元の形状で、形状には大切な空間的情報が含まれている。
- 入力の形状を意識したニューラルネットワークの構築には、「畳み込み層」と「プーリング層」を用いる。
- 畳み込み層で行われる畳み込み演算の処理に使われるフィルターが重みパラメータに相当する。
次はプーリング層。プーリングは縦・横方向の空間を小さくする演算。
プーリングには最大値を取るMaxプーリング、平均値を取るAveプーリングがある。
上の画像ではMaxプーリングを行なっている。地味に色んなサイト見てもMaxプーリングしか見たことがない。。。。。
またこの場合 プーリングのウィンドウサイズは 2x2 であり、2x2 の領域に対して最大値となる要素を入力の値から取り出している。またストライド値は 2 である。一般的にはプーリングのウィンドウサイズとストライドの値は同じ値に設定するらしい。
プーリング層の特徴として
- 学習するパラメータを持たない(フィルターなどを持たない。最大値を取る処理などを行うだけ)
- チャンネル数は変化しない(入力と出力のチャンネル数は変化しないということ)
- 微小な位置変化に対して頑健(入力データの小さなズレに対してプーリングは同じような値を返す。)
3つめに関しては、プーリングの処理で見る領域内で入力の値がシャッフル(ズレが起こる)されても、最大値は変わらないので出力はシャッフルする前と後で同じになる、という感じだろうか。
畳み込みとプーリングの処理でよく見かける「パディング」と「ストライド」について
パディング
パディングは、畳み込みの処理を行う前に、入力データの周囲に固定の値(0など)を埋めること。
画像では、幅1のパディングを適用している。幅1のパディングとは入力の周囲を幅1ピクセルの 0 で埋めることを言う。
入力は (4,4) から幅1のパディングで (6,6) になってるのがわかる。
パディングの処理を行う理由としては、出力サイズの調整に使われる。と言うのも
input -> Conv1_1 -> Conv1_2 -> Conv1_3 -> ... Conv1_n -> MaxPool1
上のような第一層の畳み込みを考えるとき、パディングを行わないと、出力サイズが1回の畳み込みを終えるごとに小さくなっていくのでConv1_nに到達する前に出力サイズが 1 になることもありえてしまう。出力サイズが 1 になると言うことはそれ以上畳み込み演算を行えなくなってしまうということなのでこれを回避するためにパディングという処理を行う必要が出てくる。
ストライド
ストライドはフィルターの適用する位置の間隔(ずらす間隔)の値のことをさす。
畳み込みにおいて入力サイズが (7,7) のデータに対し、ストライドが 2 のとき出力サイズは (3,3) になる。
ストライドの設定値を大きくすると、出力サイズは小さくなる。逆にパディングの幅の値を大きくすると、出力サイズは大きくなる。
入力とフィルターサイズ、ストライドとパディングの値による出力サイズの関係式として
上が成り立つ。入力サイズを(H,W)、フィルターサイズを(FH,FW)、出力サイズを(OH,OW)、パディングの値を P、ストライドの値を Sとしている。
フィルターサイズなどの値の設定としては、上の式が必ず割り切れるようにしないといけない。
どうでもいいけどこの式自作のデータセットで画像認識を行う際必要だった。
Tensorflowで道路標識の画像認識をしてみる
勉強ばっかりだと頭に残らないので、実際に自分でデータセット作りtensorflowを使って画像認識をしてみる。
データは道路標識の「制限速度」と「止まれ」の2クラス分類にする。
画像検索のAPIは貧乏で使えないので手作業でデータは各100枚ほど集めた。
80枚を訓練に、20枚をテストデータに使った。
まずは全結合のみのネットワークで。コードはここ
学習ステップが2500ほどで
loss: 0.000067, acc: 1.000000, [Test] acc : 1.000000
こんな感じの損失関数の値が出たけど、精度が1.0なのが怖い。
新しいデータでの出力で正解が何個かでたのでいい感じのパラメータにはなってるはず、多分。
次は畳み込み層ありのネットワークで。コードはここ
ステップ毎の損失関数の値から、学習が停滞している?感じがする。精度も上がったり下がったり不安定
層が浅かったり、フィルターサイズや重みのノード数とかハイパーパラメータの設定など改善できることはたくさんありそう。
精度が上がったり下がったりするのはミニバッチ学習をしているからかな?
まだまだ勉強するべきことは多い。。
畳み込み層について その1
ディープラーニングを用いた画像認識の記事で「畳み込みネットワーク(CNN)」という単語をよく見かける。最近CNNを勉強してたのでメモ。
CNNでは「畳み込み層(Conv)」と「プーリング層(Pool)」の2つの層がニューラルネットワークに登場する。
全結合のニューラルネットワークでは、データの形状を無視してしまう問題が実はある。形状に意味のあるデータに関してはそれを生かすことができない学習モデルを作成することになってしまう。
例えば入力データが画像の場合を考える。画像は基本、縦・横・チャンネル の3次元の形状を持っている。これを全結合層に渡すとき、3次元の形状を1次元の形状に直して渡す必要がある。(平らに直すとも考えられる)
入力画像が「チャンネル・縦・横」の順番で(1,28,28)の形状の時、一列に並べた 1x28x28 = 784個のデータに直して渡す。
tensorflowの公式ページにあるMNISTのチュートリアル(Beginner)の方でも、入力層が 1x28x28のx = tf.placeholder(tf.float32, [None, 784])
と定義されている。
画像の形状には、大切な空間的情報が含まれていることが大半。例えば空間的に近いピクセルの値は似ていたり、RGBの各チャンネルの間には密接な関係性があったりなど。全結合層では形状を無視してしまう(一列に直してしまう)のでこれらの情報を生かすことができない。
畳み込み層は形状を維持する(入力データを3次元のデータとして受け取る)ニューラルネットワークの構築が可能となる。形状の情報を生かした画像認識などの学習モデルの作成ができる。
畳み込み層では「畳み込み演算」という処理を行う。画像処理では「フィルター演算」として有名らしい。全くわからん。。
画像が荒っぽいが、フィルターの行列をカラフルな色で囲った入力の枠の行列と演算を行う。フィルターの行列を一定の間隔でスライドさせながら演算(適用)していく感じ。フィルターの各要素と入力の対応する要素の値同士を乗算し、和を求める。和の値を出力の対応する場所へ当てはめる。
CNNでは、フィルターのパラメータ、すなわち行列の値がニューラルネットワークでいう重みに相当する。
もちろんバイアスも存在して、
フィルター適用後の行列の各要素に対してバイアスの値を加算する。
とりあえずここまで。