可視化系はほぼ必須なので、僕も chainer で SmoothGrad をやってみました。簡単に言うと、ディープラーニングというブラックボックスに対して、推論時にどの画素の寄与度が大きいかを可視化するようなものです。
https://pair-code.github.io/saliency/
そういえば Python/chainer のバージョンは以下です。
- Python 3.6.0 / chainer ver 2.0.0
理論
[1706.03825] SmoothGrad: removing noise by adding noise
元論文を読んでみると、やっていることは以下の式、
元の画像にガウシアンでノイズを入れて勾配の平均をとると、やってることはめちゃくちゃ単純なのにこんなうまくいくもんなんか。
すでにやっている方がいらっしゃっいましたが、勾配の計算はまとめてやってしまった方がすっきりすると思うのでそんな感じで実装してみます。
実装
画像データを使いたいので、ここでは cifar10 の1個目の画像で検証してみることにします。この間作った chainer 2.0 用の雛形を流用しつつ、MNISTを呼んでいたところを以下の感じで。
train, test = chainer.datasets.get_cifar10() self.N_train = len(train) self.N_test = 1 self.train_iter = chainer.iterators.SerialIterator(train, batchsize, repeat=True, shuffle=True) self.test_iter = chainer.iterators.SerialIterator(test, self.N_test, repeat=False, shuffle=False)
乗せるノイズの割合 sigma, 平均をとるサンプル数 N_sample として、以下のようにガウシアンを発生させてみます。
stdev = self.noise * (np.max(x) - np.min(x)) x_tile = np.tile(x, (self.N_sample,1,1,1)) noise = np.random.normal(0, stdev, x_tile.shape).astype(np.float32) x_tile = x_tile + noise t = np.tile(t, self.N_sample)
tile でサンプル分複製して、まとめて以下のように backpropagation させたら完成 (合ってますよね…?)
with chainer.using_config('train', False): x_tile = chainer.Variable(x_tile) y, loss = self.model.loss(x_tile, t) x_tile.zerograd() loss.backward() total_grad = np.sum(np.absolute(x_tile.grad),axis=(0,1))
検証
N_sample=100 として、noise=0, 10, 30% で SmoothGrad をやってみます。
うまくいってんのかな… そもそもCIFAR-10 の1番目のこの画像何なんですか…。とりあえず動いてるっぽいので、もう少し解像度のしっかりした画像を使って今度検証してみよう。
あと論文中にある Integrated Grad と Guided Backpropgation というのと組み合わせたやつが面白そうなんで、ちょっと論文をチェックしてみたい。
[1703.01365] Axiomatic Attribution for Deep Networks
[1412.6806] Striving for Simplicity: The All Convolutional Net
今回作ったソースコードを以下に公開しました。

カゴメ 野菜生活100 Smoothie グリーンスムージーMix 330ml×12本
- 出版社/メーカー: カゴメ
- 発売日: 2017/02/28
- メディア: 食品&飲料
- この商品を含むブログを見る