Layer-wise Relevance Propagation을 Chainer에서 구현

1. Layer-wise Relevance Propagation(LRP)



원논문은 로, 자세한 것은 이하 참조.

Qiita : 딥 러닝의 판단 근거를 이해하는 방법

나는 LRP를

일단 데이터를 순 전파시켜, 출력층으로부터 각 층의 출력과 가중치를 바탕으로 공헌도를 구해 가는 수법

라고 이해합니다.

 

2. Chainer



국산 신경망을 위한 프레임워크입니다.

일단, 순전파시키면 데이터가 수주 연결되어 남기 때문에, 개인적으로는 사용하기 쉽고 좋아합니다.

Chainer: A flexible framework for neural networks

 

3. 구현 (chainer v2.0.0)



Chainer는 한번, 순전파시키면 데이터와 생성된 레이어의 종류를 참조할 수 있으므로, 그것을 이용해 구현합니다 버그등이 있을지도 모릅니다.

아래의 코드는 최소한의 선형 결합과 컨볼루션, 풀링에만 대응하고 있습니다.
또한 입력 z의 shape는 (데이터 수, 출력 뉴런 수)를 상정하고 있습니다.
import chainer
import numpy as np

def LRP(z, epsilon=0):
    creator = z.creator
    var = z
    # relevance value
    r = np.zeros(z.data.shape)
    for i, d in enumerate(z.data):
        r[i, d.argmax()] = d.max()

    while(creator is not None):
        x = creator.inputs[0].data
        y = creator.outputs[0]().data

        if len(creator.inputs) > 1:
            w = creator.inputs[1].data
        if creator.label == "LinearFunction":
            _y = y + epsilon*np.sign(y)
            r = x.reshape(r.shape[0], -1) * (np.dot(r/_y, w))
        elif creator.label == "Convolution2DFunction":
            _y = y + epsilon*np.sign(y)
            r = x * chainer.functions.deconvolution_2d(r.reshape(y.shape)/_y,
                                                       w).data
        elif creator.label == "MaxPooling2D":
            r = chainer.functions.unpooling_2d(
                r.reshape(y.shape),
                ksize=creator.kh,
                stride=creator.sy,
                outsize=x.shape[2:]).data

        var = creator.inputs[0]
        creator = var.creator
    return r


일단 출력층의 데이터가 사라지지 않도록 아래의 후크를 순전파 시 추가했습니다.
from chainer.function import FunctionHook

class RetainOutputHook(FunctionHook):
    def forward_postprocess(self, function, in_data):
        function.retain_outputs([0])

''' Example
with RetainOutputHook():
    z = model.predict(x)
'''


4. 출력 예



아래의 네트워크(n_units=100)에 MNIST를 학습시켜 입력에 대한 LRP를 시각화했습니다.
class CNN(chainer.Chain):
    def __init__(self, n_units):
        super(CNN, self).__init__()
        with self.init_scope():
            self.conv1 = chainer.links.Convolution2D(in_channels=1, out_channels=n_units//2, ksize=3, stride=1)
            self.conv2 = chainer.links.Convolution2D(in_channels=None, out_channels=n_units, ksize=3, stride=1)
            self.l = chainer.links.Linear(None, 10)

    def __call__(self, x):
        x = chainer.functions.relu(self.conv1(x))
        x = chainer.functions.max_pooling_2d(x, ksize=2, stride=2)
        x = chainer.functions.relu(self.conv2(x))
        x = chainer.functions.max_pooling_2d(x, ksize=2, stride=2)
        return self.l(x)

왼쪽: 입력 이미지 오른쪽: LRP

좋은 웹페이지 즐겨찾기