Learningの学習結果(重み)はブラックボックスで、隠れ層のユニット(特に深い層の!)が一体何を学習したのかがよくわからないと長年言われてきた。今回紹介する方法を使うとニューラルネットが何を学習したのか目で見える形で表現できる。畳み込みニューラルネットで学習したフィルタの可視化というと以前やったように学習した第1層のフィルタの重みを直接画像として可視化する方法がある。畳み込みフィルタのサイズは基本的に数ピクセル(MNISTの例では5x5ピクセル程度)のとても小さな画像なのでこれを直接可視化しても何が学習されたか把握するのはとても難しい。MNISTを学習した畳み込みニューラルネット(2016/11/20)のフィルタを可視化しても各フィルタがどの方向に反応しやすいかがわかる程度だ。各フィルタが何を学習したかを可視化する別のアプローチとして各フィルタの出力を最大化するような入力画像を生成する手法が提案された。この生成画像はニューラルネットの入力画像と同じサイズなのでフィルタが最終的に何を学習したのかより把握しやすいというメリットがある。今回は畳み込みニューラルネットの一種であるVGG16を対象に学習したフィルタを可視化してみたい。あとでMNISTやCIFAR-10を学習したCNNや最新のResNetでもやってみるつもり。この記事はkeras.applications.vgg16モジュールが導入される前に書かれている正則化などの工夫が入っておらず生成される画像が美しくないという問題がある。そこでこの記事ではKerasのvgg16モジュールを使って書き換えるとともに、よりきれいな画像が生成されるようにいくつか工夫した。基本的なアプローチは、畳み込みニューラルネットの指定したフィルタの出力を最大にする入力画像を勾配法を用いて更新することである。入力したときのi番目のフィルタの出力(activation)だ。どれくらい変化するかを表している。ascent)を用いてフィルタの出力を最大化する入力画像を求めている。この式はニューラルネットの重みの更新式と比較するとわかりやすい。ニューラルネットの重みの更新式はこれはよく目にする式だ。どれくらい変化するかを表している。descent)を用いて損失を最小化する重みを求めている。こんな感じで比較してみると両者の基本原理はよく似ていることがわかる。ニューラルネットの重み更新では重みの初期値としてランダムな値を使った。フィルタの可視化でも同様に入力画像の初期値として下記のようなランダムな画像を使う。Dreamはこの画像の初期値として任意の画像を使う。Kerasで書くと下のようになる。imgこの関数の二つの引数は畳込みニューラルネットのどの層の何番目のフィルタを最大化したいかを指定する。畳み込みニューラルネット内の畳込み層または出力層を指定できる。出力層の65番目のフィルタを最大化したいときは下のように指定する。65)層の名前はmodel.summary()で表示される。predictionsはVGG16の出力層に付けられた名前である。snake(ウミヘビ)クラスを意味する。1個だけではつまらないので16個まとめて画像化すると下のようになる。左上にフィルタ番号に対応するクラス名を表示した。この可視化画像を見てもはっきり言ってよくわからない。ウミヘビは何かウネウネしてるぞ、ゴリラに何か目があるぞとかそれくらい(笑)でもこれらの画像をニューラルネットに入力するとウミヘビである確率が99%、ゴリラである確率が99%ととんでもない結果が出てくる・・・人間にはまったくウミヘビやゴリラと認識できない画像なのに、ニューラルネットは99%の確率でウミヘビ・ゴリラと判定してしまうのだ。ここら辺は参考文献で挙げた「ニューラルネットは簡単にだませる」という論文を参照。このナイーブな方法ではあまりはっきりとした画像が生成できなかったが、いくつかの改良を加えるとよりはっきりとした鮮やかな画像が生成できることが知られている。priors)を正則化項として導入すればよい。VGG16の出力をsoftmaxからlinearに正則化項を導入する前に一つ重要な改善を入れよう。VGG16の出力層の出力は分類のためにsoftmax活性化関数を適用しており、1000クラスの合計が1.0になるように正規化されている。他のクラスの出力を下げるという方法もとれてしまう。そこで、最初の工夫としてsoftmaxをlinearにしてみた。VGG16のsoftmax活性化関数をかける前の出力(つまり線形出力)が取れれば簡単なのだがKerasにそのようなプロパティは用意されていない。VGG16のsoftmax活性化関数がmodel.add(Dense(1000))model.add(Activation('softmax'))のように独立した層として追加されていれば、一つ前のsoftmaxをかける前の出力は簡単に取れたのだが、VGG16は下のように層ではなく引数として実装されていた。activation='softmax'))これだとsoftmaxをかける前の出力は取り出せない・・・仕方ないのでKerasのvgg16.pyをコピーして一部修正したVGG16クラスを用意した。121行目だけ下のように書き換えればOK。学習した重みはそのまま使える。name='predictions')(x)この工夫を入れてもう一度可視化すると下のようにコントラストが少し強くなる。何が何だかよくわからない。次の工夫はLpノルム正則化である。Lpノルムの定義はである。発散させると最大ノルムになる。Kerasで実装すると下のようになる。生成画像であることに注意。activationは最大化を目指しているためペナルティである正則化項は差し引くことにも注意。noramlize()は画像のサイズによらないようにする正規化である。この正則化項は画像のノルムを求めているのでピクセル値が極端に小さな値や大きな値になったときにペナルティをかけるような正則化だと考えられる。そのようなピクセル値が極端な値になる画像は自然ではないという事前知識を表している。Lpノルムのpや正則化項の重み(lpnorm_weight)はハイパーパラメータなのだが参考文献を参考に設定した。いくつか異なる設定を試してみるとよいかも。Lpノルム正則化を入れると下の画像が生成された。Variation正則化を導入しよう。Variationはで定義される。Variationが大きい画像ほど隣通しのピクセルの差分が大きな画像と解釈できる。tv)配列のスライスがややこしいが具体的な行列(5x5くらいの)で計算してみると隣通しのピクセルの差を取っているのがすぐにわかる。aは画像の行方向の差分でbは画像の列方向の差分を意味する。Variationを小さく(=ピクセル間の差を小さく)する方向に正則化がはたらく。Variation正則化は隣通しのピクセルの輝度差が小さい滑らかな画像にする正則化であると考えられる。この正則化を導入すると下の画像のようにきれいな画像が出てくる。ここまでくると元のクラスが何なのかうっすらと見えてくる!ナイーブな方法に比べると正則化の導入によってかなり改善しているのがわかる。他のクラスもはってみる。隠れ層のフィルタの可視化これまでは出力層のフィルタの可視化をしていたが隠れ層のフィルタの可視化も同様にできる。いうことで浅い層(block1_conv1)から深い層(block5_conv3)に向かってランダムに16個のフィルタを選んで可視化してみよう!block1_conv1block2_conv1block3_conv1block4_conv1block5_conv1block5_conv2block5_conv3こんな感じで浅い層ほど単純な特徴が深い層ほど複雑な特徴が学習されていることがわかる。これを自動獲得できる点が深層学習のすごいところなのだ。隠れ層の可視化で細かい注意点をいくつか。Dreamを実装してみたい。Dreamも基本的に今回紹介した可視化技術に基づいている。(2017/1/8)のつづき。»プロフィールid:aidiary人工知能とその関連分野に興味があります。)はてなブログをはじめよう!aidiaryさんは、はてなブログを使っています。ブログを報告するスターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。