ROC曲線や累積反応曲線について
前回の記事で、モデルの性能評価として期待利益という値を使った。
データサイエンスの基本コンセプトのひとつに「モデルの性能の比較対象となる適切な基準は何であるかを考察することは重要である」というものがあるらしい。ビジネスとしては予想精度の最大化よりもモデルが利益を向上させているかがモデルの性能の改善として見るからかな?
今回はモデルの性能を可視化する方法をいくつか試したのでメモにする。コードはここ
まずは利益曲線。これはクラスの事前分布とコストと利益の推定が両方明らかな場合に利用するといいらしい。
前回の損益行列を使うと曲線は以下のようになった。
横軸はデータの数(キノコの数ともいえる)。食用と判断したキノコのうち、スコア(安全度)が高いと判断したものほど左に、スコアが低いものほど右にある。
精度をわざと悪くしたため、少し下がっている部分がある。これは偽陰性(食用だと思ったらほんとは毒キノコ)と判断したものがあるからだな。
お客さんにキノコを売るとしては食用と判断した内の半分ほどは大丈夫、黒字ということで判断していいのだろうか。
現実ではモデルを作成する前にクラスの事前分布、利益・コストがわからない場合もある。そんな不確実性を許容できる取り得る性能の全体を示す方法がある。
これはROC(受信者動作特性)曲線と呼ばれるそうだ。(x,y)軸の組として(偽陽性,真陽性)または(偽陰性,真陰性)いずれかの組を取る。
つまり利益(tp,tn)とコスト(fp,fn)の間に生まれるトレードオフを示す。グラフはこんな感じになった。
青い点が今回作ったモデルを示す。また組としては(偽陰性,真陰性)をとった。
ROC空間内においてはいくつか大事な座標がある。(0,0)は陰性の分類を全く行わないことを表し、(1,0)では無条件に陰性として分類を行う分類器ということを示す。
そして(0,1)が全て真陰性に分類する完璧な分類器ということになるんだな。
赤の対角線は半分の確率で陰性と分類するランダムな分類器を表す。そのためこの対角線より上にあるほどランダムではない、データ内の情報を活用できている分類器であることを意味して、逆にこの対角線より下にあるほど何でも陰性と判断する「ゆるい」分類器、ランダム分類器より悪いともいえる。
今回は座標の位置からしてランダムよりそこそこ良いといえるモデルができたかな。
データの数を一定の数で増やし、その時のモデルの性能としてROCグラフに点でプロットして線でつなげると曲線ができる。この曲線の下の面積を全体の面積で割った割合をAUCと呼び、モデルの性能を1つの数値で評価したい時や分類器の使用条件以外は何もわからないときに便利だそうだ。
社会人ではないのでわからないが、現実だとデータ分析がよくわからない営業の人にモデルの性能を説明しなくてはいけないときがあるそうで。そんなとき利益曲線やROC曲線を見せても、よくわからんヾ(`Д´)ノ" といった感じでコミュニケーションが取れないこともあるとか。
そのため人に説明しやすい一般的な可視化の手段として累積反応曲線がある。これはROC曲線と密接に関連しているがこっちの方が直感的でわかりやすいらしい。
累積反応曲線は縦軸(y軸)にヒット率(tpまたはtn率)正しく分類されたデータの割合をとる。横軸(x軸)には対象としている母集団の割合をとる。
ここだとスコアが0.5未満のものは陽性(毒キノコ)としているため、途中でグラフの変化がなくなってしまった。
それでも食用と判断している x = 50 あたりまでのとこだと、青い対角線(ランダム分類器)よりも上にあることから何らかの長所があると見てもいいはず。
ここでモデル性能を表す曲線が、ランダム推定より優れているということを表すリフト値なるものがある。
10個のデータのうち5個が食用、残りの5個が毒キノコとする。このときランダム分類器で5個選んだら、陰性である食用キノコが半分あると判断してもいい。
このときリフト値は (食用キノコの割合) / (データの割合) より 0.5 / 0.5 = 1 となる。
次に自分が作ったモデルでスコア(安全度)が高い上位5個のキノコを選ぶ。このとき食用キノコがほぼ占めていると判断してもいい。
このときリフト値は 1.0 / 0.5 = 2 となる。
このリフト値をプロットしたものがリフト曲線となる。各モデルの(ランダムより)相対的な優位性を示す。
実際には累積反応曲線上にある x座標の値を同じ対角線でのx座標の値で割ったもの。
累積反応曲線とリフト曲線は対象とするデータセットのうち陰性、または陽性の割合が明確でないと使えない。
本通りのいい感じな曲線がうまいこと引けなかったかも。プヨグラミング力が足りない。
混同行列、期待利益からのモデルの評価について
その2で終わるはずだったが続いた。
期待値としての評価をキノコの分類モデルで試して見た。コードはここ。
前回3つのモデルを作成し、性能の計測については損失関数や以下のような精度を使って評価した。
精度は分類器の性能を1つの値で表すことができ、また簡単に計算ができるためとても便利。
これらの評価の方法は単純すぎていくつか問題があるようだ。分類器を正しく評価するためにはクラスの混同という考え方と、分割表の一種である混同行列を理解する必要が大切らしい。
混同行列は n個のクラスを含む分類問題の場合、 (n,n) 行列になる。その行列の列はデータの実際のクラスを表し、行は分類器が予測したクラスを表す。
そのためテストデータに対して混同行列を用いると、分類器によって行われた決定を混同行列内の該当するセルに当てはめ、あるクラスの中に誤って別のクラスがどれくらい混ざっているかが一目でわかる。すごい!!!
2クラス分類での混同行列は以下のように定義される。
- 真陽性(実際のクラス、予測したクラスがどちらとも陽性のとき)
- 偽陽性(実際のクラスは陰性だが、予測したクラスは陽性のとき)
- 真陰性(実際のクラス、予測したクラスがどちらとも陰性のとき)
- 偽陰性(実際のクラスは陽性だが、予測したクラスは陰性のとき)
各セルからわかる通り、対角成分には正しく分類されたデータの数が入り、非対角成分には間違った予測をおこなったものが入る。
混同行列で誤った予測の数がわかるからなんぞや、となるが現実のビジネスだとモデルが誤った判断、偽陽性、偽陰性となった場合のコストを考慮しなくてはいけない。また偽陽性、偽陰性でのエラーは同じ重要度であるとは限らない。
真陽性・真陰性での利益、偽陽性・偽陰性でのコストが明確であれば、混同行列を組み合わせることで分類器を期待利益(期待値)という点で評価することができる。
さっそく前回のロジスティック回帰のモデルで期待利益を計算してみる。
まず混同行列を求める(テストデータ 2438件)。
この時モデルの損失としての精度は 0.68 ほど。陽性、陰性どちらとも五分五分な感じで分割されている。。
各セルは以下のように定義した。
次に期待利益を計算するため利益とコストを定義し、損益行列を作る。
近くの山で適当にキノコをたくさん拾って、そしてそのキノコを分類モデルにかけ、食用(陰性)と分類されたならそのキノコを焼いて客に売り、1000円の利益
を得るとする。また食用と分類されたが毒キノコを客に売ってしまった場合、代金を払い戻してさらに500円支払う(コスト)
ことにする。
毒キノコ売った時のコストがかなり軽い気がするが舌が痺れるくらいの症状しか起きない毒キノコとすれば軽くてもいいや。
上記を背景として以下のように損益行列を作った。(100円=1ドルみたいな感じに変換してる)
真陽性は毒キノコを分類するだけなので利益には影響しない 0 の値が、偽陽性も食用だったけど毒キノコと分類して売らないだけなので 0 の値が入る。
偽陽性と偽陰性の深刻度の違いが見て取れる。
期待利益はこのような数式で得ることができる。
確率にはこんな性質があるようで。
これを用いて変形すると、クラスの事前確率でくくり出すことができ、クラス配分が不均衡である影響を分離することができるらしい。かしこい。
ちなみにp(Y | p)などは混同行列を割合へ変換することで求めることができるので、こんな感じに表すこともできる。
事前確率などを混同行列から求め、期待利益を計算する。5000兆円いくかな。
一人当たり約200円期待できるとのこと。やっす。。。
次は利益曲線やROC曲線などモデルの性能の可視化を試す。
Kaggleのデータセットを使って特徴量を観察する その2
前回の続き
特徴量の選択を終えたのでキノコが食用か毒ありかの判別を行うモデルを構築していく
コードはここ github.com
モデリング
作成したモデルは
Tensorflowを使ったNNのロジスティック回帰モデル
決定木を用いたツリーのモデル
流行りのXGboostを用いたツリーのモデル
3種類を作った
決定木は前回の記事で紹介した本で知ったので、理解も兼ねてメモを残す
ざっくりいうとこんな形式の構造(木)になっている
△の木が逆さまで根が上にある。ブランチ(枝、図で言うと矢印)は常に四角で囲ってある属性
ノードから出ている。このノードから出ているブランチは属性の値を表す。ブランチを最後まで辿ると必ず葉(ラベル、目的変数)が割り当てられる。
ここでは矢印には特に何も書かなかったが、あるデータのある属性の値が x
以上なら左に、x
未満なら右に移動する、といったような設定ができる。また葉の値の割り当てはラベルでもいいし確率としての数値を割り当てることもできる。
基本的には情報価値の高い属性から上(根の場所から)に割り当てていくようだ。
特徴的な部分として決定境界の定め方がある。
こんな感じのデータ分布があるとする。
クラス x とクラス △ を分離するような境界を引けと言われたら
上の図のように引くのが多いはず。線形分類器のSVMや線形回帰、ロジスティック回帰とかもこんな風に引く。
決定木の場合はこのような直線では引かず、属性に対して垂直になるように境界線を引く
超平面と言うらしい。理解は難しいが n-1次元で境界が表せると言うことでいいのかな。
線形分類器が任意の直線を引けるのはf(x) = w1*x1 + w2*x2^2 + ...
で境界が定義できることからわかるように一度に全ての属性を使うから表現力の高い直線が引けるけど逆に決定木では一度に一つの属性しか使わないから'y = 10' のような垂直な直線しか引けないわけだ。
決定木の深さに関しては特に制限はないけど深すぎると過適合を起こすっぽい。深すぎるとある一つのデータに適した葉がたくさんできてしまうからかな。それだと訓練データを丸暗記しているのと同じやん。
以上が決定木のメモで、次は噂のXGBoost
完璧には理解できなかったが勾配ブースティング
とランダムフォレスト
を組み合わせたものだそうで。
ランダムフォレスト
は訓練データをn分割して一つ一つを利用し、n個の決定木を作る。そして予測の時にn個の決定木の出力から分類だったら多数決、回帰だったら平均を取った値を予測値とする。アンサンブル学習と考えが似ている。決定木を複数作るから森ってつけたセンスむせび泣くほど好き。
勾配ブースティング
は勾配とついているように勾配降下法などで損失関数の値が小さくなるように重みを更新していくっぽい。つまり木が進化していく。
ここでいう重みは決定木のブランチの値のことであっているかな。
XGBoost
はこの2つを組み合わせたと言うことは複数の木を作って、それぞれを勾配降下法で調節していく決定木と言うことか。中々やるじゃん・・・。
C++でブースティングの方を高速化しているので速いそうで。
評価
そんなこんなで各モデルを作ってテストデータの損失の値を見ていく。
見ればわかる通り二乗誤差での損失の値。二値分類だから本当はLog_lossを用いたかったがエラーが出てしまう。困った・・・。
一応クリッピングして無理やりLog_lossでの値を出すと
二乗誤差ではロジスティック回帰のが損失値は小さかったがLog_lossでは決定木の方が小さくなった。
どっちにしてもモデル構築に必要な特徴量を選べたと言う点では成功したからおけおけおっけ。
ロジスティック回帰は柔軟性に乏しいのでデータセットが少ない時は適していて、逆に決定木は柔軟性が高いので少ないデータだと過適合を起こしやすくデータセットが多い時のが適している特徴があるようだ。
最後に決定木のツリー構造を可視化する。
かなり複雑な木になっていた。これをコンピューターが自動で計算するんだからすごいな。。
Kaggleのデータセットを使って特徴量を観察する その1
最近、特徴量の観察、選択がとても大事だと実感した。
それもあって深層学習の勉強とは別に、データサイエンスの勉強もしようと思ってこの本を読んでいる。
まだ7章ほどだけどもためになることが結構書いてあっていい。自分の言語処理能力が低いのか翻訳が少し難しい気がする。
この本で得た現状の知識を残したいのとインターンでの経験を忘れないようにするため自分で特徴を選択してモデルを作るということをやって見た。
まずは何のデータセットを使うか。成り行きでKaggleのアカウントを作成したのでKaggleにあるダウンロードして良いデータセットを探してくる。
選んだのはキノコの分類のデータセット。食用か毒ありかの2クラス分類のようだ。
https://www.kaggle.com/uciml/mushroom-classification
本によるとデータマイニングは以下のようなサイクルで行われる。
折角なのでこのサイクルの流れに沿ってく
ビジネスの理解
解決すべき問題を理解する。今回このステップでは特に悩む必要もないかな。
あるキノコのデータからそれが食用か、毒ありかを判定する分類問題を解くくらいでいいだろう。
データの理解
データは問題を解決するための材料になる。でもこのステップでは問題に必要なデータ取得にかかるコスト、そこから得られる利益を見積もるなどを考えるステップっぽいので今回は利益として分類モデルを解決することで毒キノコに当たる人が減って(・∀・)イイ!! ということにしよう。
データの準備
分析ができるようにデータを表形式に変換する処理や欠損値を除外する処理などを行うステップ。ここが一番大事なステップ。
データの中から重要な情報価値のある変数や属性を見つけたり選択することがデータマイニングの基本らしい。情報価値のある属性とは予測値をより確実なものにすることができる属性のこと。相関に似ている。
というわけで分析していこう。コードはいつも通りここ
目的は2クラス分類に使えそうな特徴を発見、選ぶこと。
ノートブックを見ればわかる通り、キノコのCSVファイルを読み込むと以下のようなデータセットがあることがわかる。
cap-shapeとかが何を表すのかはこのサイトを参考にすれば理解できる。
https://edu.deepanalytics.jp/datasets/19
最初つぼとかつばとかわからなかった。そういえばつぼ焼きとかありましたね・・・
データセットは全8124件で、そのうち食用が4208件、毒ありが3916件。あまり偏りがなくてよかった。
数値データとして扱いたいので数値データに直しつつ、散布図で相関を見ていく。
cap-shape
,bruises
, odor
, gill-spacing
, stalk-surface-below-ring
, ring-type
, spore-print-color
, population
あたりが使えそうなことがわかったのでこれらにまずは絞ってみることにする。
絞ったのはいいが、選んだ特徴量がどれだけ情報価値を持っているかのわからない。
この問題に対しては情報価値の指標として、エントロピーを用いることができるようだ。
エントロピーといえば最近リリースしたアプリのマスコットキャラクターしか思い浮かばないが、ここでのエントロピーはある属性で絞ったデータの集まりにおいての乱雑さを測る尺度として使われる。乱雑さというのは絞ったデータの集まりにおいて、クラスAとクラスBがどれだけ別れているかの具合。
上の画像だとある属性で分けた結果、ほぼ綺麗にクラスが分けられているのでエントロピーが小さくなる。
数式ではこう定義される
logの底は2。p_i はデータセットに存在するクラスの目的変数(ラベル)が i という値である確率(相対的な割合)。
あるデータセットに10個のデータが存在して、全てラベルが同じなら、 p_i = 10 / 10 = 1 になりこの時、log(p_i)は最小の値 0 となる。
計算したエントロピーから情報価値(IG)は次のように計算する。
元のデータセットを親として、属性で分割したデータセットを子と表すとして、
IG(親、子) = エントロピー(親) - { p(c1)エントロピー(c1)+p(c2)エントロピー(c2)+.. }
ある子セット c_i のエントロピーに、その子セット中のインスタンスの数(割合)に応じた重み付けを行っている。
この重みによって分割したデータセットのデータが1個の場合、重みは小さくなるからIGにはあまり影響を与えなくなるんだな。
これらの式に従い、選んだ各特徴のIGを計算していくと次のようになった
IG値を見る限りcap-shape
、gill-spacing
は特徴として使わなくても良さそうなことがわかった。
またodor
などがかなり分類に対して貢献できそうなこともわかる。
次はcap-shape
、gill-spacing
を除いた選んだ特徴でモデルを構築して見る。
機械学習よりのインターンに行ってきた感想と特徴量について
タイトル通り機械学習よりのインターンに行ってきた。理由は3年生だからそろそろインターンの体験を積みたいというのが一番でかかったりする。
どこのインターンに行ったのかは言えません。東京湾に沈められますからね。
オフィスは綺麗でリラックマのひよこのぬいぐるみがふかふかしてたのが印象に残ってます。おやつが出て、ま○泉というお店の小さいバーガーが個人的にめっちゃくちゃ美味しくて感動した。東京はすごい。
インターン内容
データセットを渡されて何かを出力するモデルを構築して精度を競争しました。
何のデータセット、何を出力するモデルなのかは言えません。NDAもありますが、東京湾にコンクリートで詰められますからね。
モデルの構築、試行錯誤について
最初はデータの観察方法の講義があったので、それを参考にして各データが何を表すのか、散布図を表示して相関はあるのかなどを観察した。
散布図見ても理屈ではなく直感でこれとこれは相関ありそう!!!と判断したりしてた。後はモデルを構築するにおいてデータセットにおけるどの特徴量が一番出力に貢献しているかの確認もしたりした。
あまり識別には関係ないデータを特徴として渡しても次元が増えてモデルが無駄に複雑になるだけな可能性もありうる。いらないと思っていたデータが実は他のデータと組み合わせると精度が向上した、なんてケースもありえそうだし難しそうなとこだ思う。
そんなこんなで使う特徴を選択しつつモデルの構築に取り掛かった。最初はTensorflowを使ってロジスティック回帰のモデルを作った。
結果としては精度は悪かった。多層にしたり、単層にしたり、重み減衰、L2正則化などを追加したりしたり試行錯誤した。
精度が上がらなくてしょんぼりして帰り道を歩いていた時、S○nS○nのCMが店のモニターで流れていた。有名な白いTシャツの人たちがスタイリッシュな動きをするやつ。
実はそこの会社の勉強会の生放送を視聴していて、その時にXGBoost
、Stacking
という単語が出たのをCMを見たその時思い出した。
ニューラルネットのモデルで精度が出ないなら、線形SVMだったりランダムフォレストとやらを使えばいいじゃないかと閃いたので早速XGBoostを使ってモデルを構築した。
そしたらめっちゃ順位が上がった。XGBoostすげえ!!!!!
次に悩むのは過適合についてだった。高い順位に行くためには汎化能力の高さも大事だと思ったからである。
ブログ記事にもしたが自分はSPPnetの論文を読んだ。そこで
「XGBoost,SVM,ロジスティック回帰など複数のモデルからそれぞれ予測値を出して、その平均をとったものを出力として渡したらより精度が向上するかもしれないし、一つのモデルが過適合起こしても平均をとるからいい感じに過適合な予測値を避けることができるんじゃないか!?」
とSPPでのプーリング処理もどきをやってみようと思いついた。
そこでXGBoost
、Elasticnet
,MLP
,単層のロジスティック回帰
この4つのモデルを最終的に使用することにした。
この考えが後の思考を鈍らせるとはこの時の自分は思いもよらなかった・・・
最終順位
結構下でした^^
とても残念。しかし1位の人もXGBoostを使用しているらしかったので
「自分もXGBoost使ってるから実質優勝じゃん」
3秒で開き直りました。
実はXGBoost
単体と複数のモデルの組み合わせではXGBoost
単体のが精度が出ているのはわかっていました^^
しかし過適合を恐れて後者の方を提出してしまった。結果として平均を取った自分の作戦は他のモデルが足を引っ張ったという感じか。
特徴の整形や選択において、形態素解析を行なっている人もいたらしく、何というかモデルの構築って芸術センスがものを言うところあるよな・・・と思った。
振り返り
特徴の観察、抽出、選択の大切さが一番身にしみた。MNISTとか加工せずそのままモデルに渡せばいいデータセットしか使用していなかったため自分で特徴量を観察、抽出という行為をあまりしたことがなかったからである。
モデルのハイパーパラメータをいじるのではなく、もっと特徴量の観察をすればよかった。
あとは機械学習のインターンってメンターからのフィードバックを得るのが難しいと感じた。モデル構築においてこれが正解、というのがないからアドバイスを得難い。。自分からガシガシ質問に行けばよかったのかなという反省もあった。
機械学習と深層学習の違いって個人的には
データからパターンを人が見つけて、モデルを構築したりするのが機械学習
データからパターンを機械が見つけて、モデルを構築するのが深層学習
こんな感じだと判断している。だから機械学習のインターンでは良い特徴量、パターンを見つけることができるかが一番大切だと思った。
深層学習の勉強もいいが、特徴量エンジニアリング、データセットの可視化、観察あたりの知識もつけるべきということに気づけてとてもよかった。
外れ値検出、OneClassSVMについて
統計データにはよく外れ値なるものが含まれていることが多いそう。
外れ値とはデータの分布において他の観測値から大きく外れた値のことで、異常値とやらもあるけどそれとは異なるっぽい
外れ値検出、処理することは機械学習を行う上で学習の妨げをなくす大切な過程だそうで、行う必要があるので試してみた。
一つ目は単純にデータの集合から、第一四分位数、第三四分位数、四分位範囲(IQR)を求めて外れ値を検出する方法を試す。
大体のことはこのサイトを参考にした。
結果としてはこんな感じに
青が元のデータ集合、赤が外れ値の集合
こうしてみると外れ値じゃない範囲がとても狭い・・・
次に scikit-learn というライブラリにあるOneClassSVMを試す
SVMってRCNNの論文とか読んだ限り、クラス分類に使われているイメージなので最低でも2クラス以上で使用するんじゃないとかと思ったのですが調べる限りSVMの性質を使った外れ値のようだ。
実装についてはまずscikit-learnの公式にあるチュートリアルを理解することに。
Outlier detection on a real data set — scikit-learn 0.19.0 documentation
チュートリアルで使用するデータをまずは表示してみる
# Get data X1 = load_boston()['data'][:, [8, 10]] # two clusters X2 = load_boston()['data'][:, [5, 12]] # "banana"-shaped plt.xlim((-8, 28)) plt.ylim((3, 40)) plt.scatter(X1[:, 0], X1[:, 1], color='red') plt.xlim((3, 10)) plt.ylim((-5, 45)) plt.scatter(X2[:, 0], X2[:, 1], color='blue')
目で見てもわかるくらいの外れ値が確認できる
次にOCSVMを行うための下準備
# Define "classifiers" to be used classifiers = {"OCSVM": OneClassSVM(nu=0.261, gamma=0.05)} colors = ['m', 'g', 'b'] legend1 = {} legend2 = {} xx1, yy1 = np.meshgrid(np.linspace(-8, 28, 500), np.linspace(3, 40, 500)) xx2, yy2 = np.meshgrid(np.linspace(3, 10, 500), np.linspace(-5, 45, 500))
下2つのnp.meshgrid
は外れ値を除いたデータの集合を囲むための方眼紙のようなグリッド線を定義しているはず
赤い方のデータセット X1 からOCSVMを試す
for i, (clf_name, clf) in enumerate(classifiers.items()): plt.figure(1) # OCSVMにサンプル集合X1を与え、サンプル集合の境界を検出する clf.fit(X1) # xx1.ravel(), yy1.ravel() : 配列を1次元配列に変換する [[1,2,3],[1,2,3]] -> [1,2,3,4,5,6] # np.c_() : axis=1の配列の値で連結を行う # 各データとの距離が最大となる超平面を求める Z1 = clf.decision_function(np.c_[xx1.ravel(), yy1.ravel()]) Z1 = Z1.reshape(xx1.shape) plt.title("Outlier detection on a real data set (boston housing)") plt.scatter(X1[:, 0], X1[:, 1], color='red') bbox_args = dict(boxstyle="round", fc="0.8") arrow_args = dict(arrowstyle="->") plt.annotate("several confounded points", xy=(24, 19), xycoords="data", textcoords="data", xytext=(13, 10), bbox=bbox_args, arrowprops=arrow_args) plt.scatter(X1[:, 0], X1[:, 1], color='red') # 境界の輪郭を描写する legend1[clf_name] = plt.contour( xx1, yy1, Z1, levels=[0], linewidths=2, colors=colors[i])
OneClassSVM()
にあるfit()
にデータを渡して検出を行っている
plt.contour
にて描いた輪郭は以下のような結果になった
青のデータ X2 についてはこのような結果になった
どっちもデータが密集しているような場所を囲っているから大体学習は成功しているのかな
四分位数で使ったデータに対して同じようにOCSVMを試して見たところこんな感じになった
赤の外れ値をみる限りはいい感じかな囲みがちょっと狭いかも?
外れ値を気にするようなデータを扱ったことがないから実感わかないが、魑魅魍魎跋扈するこの地獄変、外れ値を含む実データなんて腐る程あるんだろうしそんなデータセットを用いてお仕事してる人がたくさんいるんだろう。統計についても勉強すべきだ・・・
SPPnetについてとTensorflowでの畳み込みフィルターの観察
前回の続き
SPPnetを利用した物体検出では入力画像から1回だけ畳み込みの操作を行うことで、大幅な計算時間の削減が可能ということで感動した。
他のサイトの情報や、論文の内容を読み直したりして以下のような手順でSPPnetによる物体検出器を作成しているのかなと考えた。
- 入力画像をCNNに渡して畳み込みの操作まで行い、特徴マップを得る。
- 入力画像からSelectiveSearch法などで、Region Proposal(物体の候補位置、領域)を得る。
- step1で得た特徴マップの対応するRegion Proposal の部分でSPP層の操作を行う。
- 各Region Proposalのプーリング結果を出力層に渡して分類結果を得る。
こんな感じかな。
いざTensorflowで実装するぞと意気込んだけど、step3をどう実装すればいいか詰まった。
入力画像を 224x224 サイズ、特徴マップのサイズを 14x14 とすると入力画像のRegionProposalの座標をどう特徴マップのサイズ内で変換すればいいかわからない。特徴マップのある始点と終点での切り抜きもできるかわかってないけれども。
単純に 224 -> 14 みたいに変換すればいいのかな?特徴マップはフィルター数も多いので悩む。設計するための知識が圧倒的に足りない。。
Fast-RCNNの論文を読み進めてまた悩むことに。
それとは別によく参考書などに畳み込みフィルターなどの小さいマップの画像がよく載っている。
観察してみたかったのでSPPnetでのネットワークで畳み込みフィルターの観察をしてみることにした。
コードはここ
github.com
大体はフィルター適用後の結果を出力してしまった。最後にフィルター自体を出力した。
フィルター自体は
モザイクばかりでエッジなどは見た限りわからない。こんなフィルターでも適用後は
入力画像の形が残っているので不思議だ。。