Chainerで中間層の出力値を取得する方法について

お久しぶりです。またまたPythonでの深層学習用フレームワーク「Chainer」についての記事です。過去の記事で、CNN(畳み込みニューラルネットワーク)の中間層を可視化することについて触れましたが、今回はどんなニューラルネットワークを構築した場合にも共通の内容になります。

前回の中間層の可視化とは?

過去記事の「CNNにおける中間層を可視化する」記事で紹介していた内容と今回はいったい何が異なるかについて説明しておきます。

過去記事では、あくまでCNNの中間層の話で、ネットワークの層が畳み込み層【Chainerでは、L.Convolution2D(1, 64, 16, stride=4)】で構成されている場合にのみ適応できる内容になっています。詳しく説明すると、具体的に過去記事で可視化をしているのは、中間層である畳み込み層で処理に使っているフィルタを可視化したに過ぎません。

フィルタは入力画像に対して、出力チャンネル数分が作成され、画像に畳み込み処理されるため、各畳み込み層で可視化されるフィルタの数は、入力チャンネル数×出力チャンネル数となります。ネットワークの組み方次第では膨大な数のフィルタが生成されるので、一枚一枚見ていくのは大変かもしれません。

ただし、フィルタを見ることで画像中のどのような形状が抽出されようとしているのかを知ることができる場合もあるため、中間層のフィルタの可視化はやってみてそんなことではないと思います。

中間層の出力を取り出す方法について・・・

さて、ここからが本題です。中間層の出力を取り出す方法について説明します。そもそも中間層の出力というのは、中間層の各ユニットでの値という意味です。ニューラルネットの各ユニットでは、入力に対しての重みを学習していきますが、その重みを利用してユニットで実際に出力された値を今回は取り出します。フィルタの可視化とは違い、ユニットはどんなネットワークでも存在するため、RNNでもCNNでも、DNNでも共通の方法になっています。

実際に取り出す方法はいたって簡単で、中間層の出力をコンソールに出力するプログラムの一部を以下に示します。

・dnn_out.py

# -*- coding: utf-8 -*-
from sklearn.datasets import fetch_mldata
from sklearn.cross_validation import train_test_split
import chainer
import chainer.functions as F
import chainer.links as L
from chainer import training
from chainer.training import extensions
from chainer.datasets import tuple_dataset
from PIL import Image
import glob
import numpy as np
from chainer import serializers
from chainer import optimizers
from chainer.dataset.convert import concat_examples

class CNN(chainer.Chain):

    def __init__(self, train=True):
        super(CNN, self).__init__(
            conv1=L.Convolution2D(1, 64, 16, stride=4),
            conv2=L.Convolution2D(None, 64,  5, pad=2),
            conv3=L.Convolution2D(None, 8,  3, pad=1),
            l1=L.Linear(128, 2),
        )
        self.train = train

    def __call__(self, x):

        h1 = F.max_pooling_2d(F.relu(self.conv1(x)), 2)
        h2 = F.max_pooling_2d(F.relu(self.conv2(h1)), 2)
        h3 = F.max_pooling_2d(F.relu(self.conv3(h2)), 2)

        #出力のための部分
        for data in h3:
           print data
        end

                for data in F.relu(self.conv3(h2)):
           print data
        end

        return self.l1(h3)

def main():
    model=CNN()
    gpu_id = -1

    optimizer = chainer.optimizers.Adam()
    optimizer.setup(model)
・・・・・
・・・・・
・・・・・




 

少し解説をしていくと、def __call__の中にprint文を記述すれば良いだけです。def __init__とdef __call__は、学習を行うためのネットワークモデルを記述する部分ですが、ここにはネットワーク構造以外の構文も記述していいということに気づかず使っている人がいるように見受けられます。

各層の出力は次の層への入力として与えらえているので、上の例でいうと、h1,h2,h3や、F.relu(self.conv1(x)),F.relu(self.conv2(h1)),F.relu(self.conv3(h2))にユニットで出力された値が実際に格納されています。たいていの場合、ミニバッチの設定などによって複数の配列の形になっていることが多いので、for文を使ってやることで綺麗に出力することができます。別ファイルに値を落とし込みたい場合も、同じ箇所にファイル書き込み処理のプログラムを書き加えれば、中間層の出力値を取得することができるはずです。

まとめ

今回はChainerでの中間層の出力値を取得する方法について説明しました。やり方はとても簡単なのでぜひやってみてください。CNNの場合、中間層の出力を取得し画像に落とし込むことで、フィルタ適応後の画像を得ることができます。前回のフィルタの可視化と同時にこちらも試してみると、より特徴抽出の過程がわかるかもしれませんね。


にほんブログ村 IT技術ブログへ
にほんブログ村
にほんブログ村 IT技術ブログ IT技術メモへ
にほんブログ村

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です