ホワイトノイズを使って機械学習されたモデルを推定する

## 概要

機械学習の課題では、入出力からモデルを作成し、出力を推定することが目的となることが多くあります。例えば、下記のように花が描かれている絵を入力として与えたときに、それが花であるという出力を出すモデルを作ったりします。

<img width="697" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/42108/82430af2-28f3-2049-9940-754a44fca74d.png">


一方で、自然界などではすでにモデルが存在しており、そのモデルを深く調査したいというケースが存在します。例えば花を見たときに、それが花だと認識できる脳の仕組みを調べたいケースなどです。

<img width="681" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/42108/801f315e-57e1-fc99-9574-86cb4c7b34b4.png">

- 出力を推定したいケース(前者)の例
    - 画像を入力としたときに、映っているものを知りたい
    - 音声を入力としたときに、別の言語ではどのように表現されるかを知りたい
    - 過去の株価データを入力したときに、将来の株価データが知りたい
- モデルを推定したいケース(後者)の例
    - 人間が花を見たときになぜ花だとわかるのか、脳の仕組みを知りたい
    - 工場の機械が想定通りに動作しているのか知りたい
    - 開発したデバイスが理論通りの動作をしているのかを知りたい

今回は、後者のモデルを推定する方法を試してみました。
手順として、
1. 推定するためのモデルを作成する
2. ホワイトノイズをモデルに入力し、入出力のマッピングを行い、モデルを推定する
3. 推定されたモデルと、1で作成したモデルを比較する
という流れを行います。

## 推定されるモデルを作成する

今回はTensorFlowの手書き文字チュートリアル[MNIST For ML Beginners](https://www.tensorflow.org/versions/r0.10/tutorials/mnist/beginners/#mnist-for-ml-beginners)のモデルを使用しました。このモデルは線形・非線形の処理を組み合わせた比較的シンプルな下記のようなモデルです。

![image](https://qiita-image-store.s3.amazonaws.com/0/42108/ff5007d7-940f-09fa-8dce-2816617cc235.png)
(TensorFlowのページより転載)

[TensorFlowチュートリアルをそのまま使用](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/examples/tutorials/mnist/mnist_softmax.py)し、上記モデルを作成しました。

このモデルは、手書き文字を入力した時にその文字が0-9のどれに対応するかを推定します。

<img width="696" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/42108/5eea70c7-7f67-28e8-844c-4953735ec5f8.png">

## ホワイトノイズをモデルに入力し、入出力のマッピングを行い、モデルを推定する

上記モデルの重み付けパラメータ`W`を推定したいと思います。
モデルの仕組みが全くわからない場合、どのような入力を与えたときに適切な出力が得られるのかがわかりません。そのため、ランダムな値を与えたときの出力を多数積み重ねる事によりモデルを推定します。

たとえば、下記入力のようなホワイトノイズを与えたときに、モデルがどのように応答するかを何度も試します。

<img width="682" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/42108/761f0839-1016-e1ee-17be-ba56f210a0f9.png">

次に、`y = Wx`というシンプルなモデルを記述し、実際のシステムにホワイトノイズを入力した際の入出力を使って`W`の重み付けを行います。

<img width="369" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/42108/3a04606c-b1d9-f282-d2c1-18ad802d4561.png">

以下のような感じです。

```py
import numpy as np

input_vector_size = 28 * 28
output_vectors = np.zeros((input_vector_size, 10))

for i in range (10000):
    if i % 1000 == 0:
        print("in progress... [%d]" % i)
    # ホワイトノイズの作成
    noise= np.random.randn(input_vector_size)
    # 未知のモデルに対してホワイトノイズを入力し、出力を得る
    result = y.eval(feed_dict={x: [noise]})
    index = np.argmax(result)
    # モデルが選択した出力に正の重み付けを、選択しなかった出力に負の重み付けをする
    for i in range(10):
        if (i==index):
            output_vectors[:,i] += noise * 0.9
        else:
            output_vectors[:,i] -= noise * 0.1
```

10000試行繰り返したあとの、重み付け関数`W`(上記コードだと`output_vectors`)は以下のようになりました。左上が手書き文字`1`に対応した関数で、以降2,3...と続きます。

```
fig = plt.figure(figsize=(10, 4))
for i in range(10):
    ax = fig.add_subplot(2, 5, i + 1)
    ax.imshow(output_vectors[:,i].reshape([28, 28]), cmap="seismic")
```

![image](https://qiita-image-store.s3.amazonaws.com/0/42108/7677be1b-46a3-f508-62e9-e0e71ac6c39b.png)

## 推定されたモデルと、1で作成した正解モデルを比較する

TensorFlowチュートリアルを使って作成されたモデルは以下のようになっていました。

![image](https://qiita-image-store.s3.amazonaws.com/0/42108/14fbacda-659a-49cd-9e8d-c05e152e4e08.png)

見た目で比較するとなかなか良く推定できているのではないでしょうか。
試行を10000から100000に増やすと、より近くなります。

ホワイトノイズを100000回入れた場合
![image](https://qiita-image-store.s3.amazonaws.com/0/42108/09d68ed3-d2cd-1840-ccff-b3d455448565.png)

## まとめ

上記のように、単純な線形モデルを仮定しただけでも、内部関数を推定・記述することができました。今後はより非線形なモデルに対して線形モデルだけでどの程度記述できるか・非線形をアプリオリでない形で組み込むにはどうすればよいのか等も試してみたいと思います。

## 補遺

制御工学等で使用される[インパルス応答](https://ja.wikipedia.org/wiki/%E3%82%A4%E3%83%B3%E3%83%91%E3%83%AB%E3%82%B9%E5%BF%9C%E7%AD%94)の分析はこの手法と同様の手法です。

また、神経科学・認知科学分野でも`reverse correlation`と呼ばれる手法で神経細胞のモデル化等に使用されています。
[Reverse correlation in neurophysiology](http://www.maths.tcd.ie/~mnl/store/RingachShapley2004a.PDF)
[受容野の定量的測定法](http://ohzawa-lab.bpe.es.osaka-u.ac.jp/ohzawa-lab/rf/method.htm)