画像処理を例にして畳み込みの機能を理解する

#はじめに
　
　koshian2さんが書かれた「モザイク除去から学ぶ　最先端のディープラーニング」内の演習問題として、畳み込みの機能を例に取り上げられていました。
今回は、この畳み込みの機能について私が理解した内容をまとめたいと思います。
https://qiita.com/koshian2/items/aefbe4b26a7a235b5a5e

　こちらの本ですが、私のような機械学習を始めたような人間でも理解できる内容になっています。基本的なところから順序だてて分かりやすく解説されています。特に、最新の論文をレビューされているところが良かったです。一般的な本屋で流通しているものでは最新といっても１、２年程度前の論文になります。ディープラーニングへの熱い思いを感じられる本になっており、買ってよかったなと思います。


#畳み込みとは何か

　畳み込みとは、深層学習で有名な畳み込みニューラルネットワーク（Convolutional Neural Network）で用いられています。なんとなく厨2的な言葉で、とりあえず言葉に発してみたい語感があります。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/567823/720d84c4-1245-5e1d-b557-218d77a98f8f.png)



　畳み込みの計算は画像のような方法で行います。
　入力する行列から3×3のマスを取り出して、畳み込みカーネル(kernel)と呼ぶ行列と掛け合わせ、足したものを出力として和にします。
　
　これは、いわゆる順伝搬型ネットワークのように、隣接層のユニットが全結合されていることとは異なります（下図）。

![002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/567823/bfdb9e94-9076-f2cf-bb53-5543e1c44928.png)



#畳み込みの計算

```math

X=\left(
\begin{array}{cc} 
0 & 1 & 2 & 3 & 4 \\
5 & 6 & 7 & 8 & 9 \\
10 & 11 & 12 & 13 & 14 \\
15 & 16 & 17 & 18 & 19 \\
20 & 21 & 22 & 23 & 24 \\
\end{array}
\right)

\\
kernel=\left(
\begin{array}{cc} 
0 & 1 & 2  \\
3 & 4 & 5 \\
6 & 7 & 8 \\

\end{array}
\right)
```

　今回は、入力を5×5行のX、カーネル係数を3×3行のkernelとして定義します。


```python:c.ipynp

import numpy as np

def conv(inputs, kernel):
  outputs = np.zeros((3,3),inputs.dtype)
  for i in range(3): #3回行方向に計算します。
    for j in range (3):　#3回列方向に計算します。
      patch= X[i:i+3,j:j+3]
      prod = patch * kernel #マスとカーネルを掛けます。
      sum =  np.sum(prod)　#掛けたものを足し合わせます。
      outputs[i,j] = sum　#出力層に値を入れます。
  return outputs

#Xの行列を定義します。reshapeによって5×5に変換することがポイントです。
X = np.arange(25,dtype=np.float32).reshape(5,5)

#出力層に値を入れます。
kernel = np.arange(9, dtype = np.float32).reshape(3,3)
conv(X,kernel)

```
```math
Outputs=\left(
\begin{array}{cc} 
312 & 348 & 384  \\
492 & 528 & 564 \\
672 & 708 & 744 \\

\end{array}
\right)


```

計算することができました。

#Tensorflowを用いた画像の畳み込み処理

　画像処理により画像を色褪せたり、白黒にさせたり、エッジを強調したりすることができます。これは、今まとめた畳み込みの処理によって行われています。

　今回元にする写真はこちらです。昨年水族館に行った時に撮った写真です。

![004.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/567823/ceb574c2-e917-8ac4-8492-93cde85249cb.png)


　画像をエッジ強調フィルターによって処理をします。
　Tensorflowを使用する場合、テンソルの軸の順番には意味があります。画像では、基本的にバッチ、縦解像度、横解像度、チャンネルという順番になるように取り決めています。
　従って、軸（次元）を追加する場合はこの順番に注意する必要があります。プログラム上でも下記のように、

```python:c.ipynp
float_img[　:,　:,　:,　i:i+1]

```

カラー画像のため3チャンネル（R,G,B)あることから、4つ目のチャンネルについてiを回していくことが必要です。

```python:c.ipynp
kernel = []
kernel = np.array([0,0,0,0,10,0,0,0,0]).reshape(3,3,1,1)
kernel = 0.5* kernel.astype(np.float32)


outputs = []
float_img = tf.cast(img,tf.float32)/255.0
for i in range(3):
  conv_result = tf.nn.conv2d(float_img[:,:,:,i:i+1],kernel,1,'SAME')
  outputs.append(conv_result)
outputs = tf.concat(outputs, axis = -1)

fig = plt.figure(figsize=(14,14))
ax = fig.gca()
ax.imshow(outputs[0].numpy())

```
使用するエッジ強調フィルターのカーネルは以下になります。

```math
kernel=\frac{1}{2}\left(
\begin{array}{cc} 
-1 & -1 & -1  \\
-1 & 10 & -1 \\
-1 & -1 & -1 \\

\end{array}
\right)
```


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/567823/923861c8-149d-38ab-65b0-7a717df537fb.png)


　こうして、画像処理で良く用いられるライブラリのPILにおけるImageFilter.EDGE_ENHANCEと同じ効果を得ることができました。このことから、畳み込み操作と画像処理で行われていることが同様のことであることを確認することができました。
　カーネルは指定の仕方によってぼかしたり、白黒にしたり出力を変えることができます。また、入力の位置によってもこの値を変えればまた別の効果を得られることでしょう。このカーネルについてはもっと深堀して学んでいきたいと思います。



プログラム全文はこちらに置いてあります。
https://github.com/Fumio-eisan/convol_20200307