からっぽのしょこ

読んだら書く!書いたら読む!同じ事は二度調べ(たく)ない

7.2.3:パディングの可視化:2次元データ版【ゼロつく1のノート(実装)】

はじめに

 「プログラミング」初学者のための『ゼロから作るDeep Learning 1』の攻略ノートです。『ゼロつくシリーズ』の補助となるように解説を加えます。本と一緒に読んでください。
 関数やクラスとして実装される処理の塊を細かく分解して、1つずつ実行結果を見ながら処理の意図を確認していきます。

 この記事では、パディングの処理や影響をPythonで可視化します。

【前節の内容】

www.anarchive-beta.com

【他の節の内容】

www.anarchive-beta.com

【この節の内容】

7.2.3 パディングの可視化:2次元データ版

 2次元データ(1データ・1チャンネルのピクセルデータ)に対するパディング(padding)を確認します。パディングの処理は、畳み込み層(convolution layer)で行われます。
 畳み込み演算については「7.2.2:畳み込み演算の可視化:2次元データ版【ゼロつく1のノート(実装)】 - からっぽのしょこ」、ストライドについては「ストライドの可視化:2次元データ版」を参照してください。

 利用するライブラリを読み込みます。

# ライブラリを読込
import numpy as np
import matplotlib.pyplot as plt


数式で確認

 まずは、パディングの処理を数式で確認します。ここでは、Pythonのインデックスに合わせて、0から添字を割り当てます。

 入力データの高さを  H、横幅を  W として、 (H, W) の行列を入力データ  \mathbf{X} とします。

 \displaystyle
\mathbf{X}
    = \begin{bmatrix}
          x_{0,0}   & x_{0,1}   & \cdots & x_{0,W-1} \\
          x_{1,0}   & x_{1,1}   & \cdots & x_{1,W-1} \\
          \vdots    & \vdots    & \ddots & \vdots \\
          x_{H-1,0} & x_{H-1,1} & \cdots & x_{H-1,W-1}
      \end{bmatrix}

 パディングの幅を  P として、 \mathbf{X} の周囲を  0 で埋めます。パディングした入力データを  \mathbf{X}^{\mathrm{pad}} で表します。

 \displaystyle
\begin{aligned}
\mathbf{X}^{\mathrm{pad}}
   &= \underbrace{
      \begin{bmatrix}
          0      & \cdots & 0      & 0         & 0         & \cdots & 0           & 0      & \cdots & 0 \\
          \vdots & \ddots & \vdots & \vdots    & \vdots    & \ddots & \vdots      & \vdots & \ddots & \vdots \\
          0      & \cdots & 0      & 0         & 0         & \cdots & 0           & 0      & \cdots & 0 \\
          0      & \cdots & 0      & x_{0,0}   & x_{0,1}   & \cdots & x_{0,W-1}   & 0      & \cdots & 0 \\
          0      & \cdots & 0      & x_{1,0}   & x_{1,1}   & \cdots & x_{1,W-1}   & 0      & \cdots & 0 \\
          \vdots & \ddots & \vdots & \vdots    & \vdots    & \ddots & \vdots      & \vdots & \ddots & \vdots \\
          0      & \cdots & 0      & x_{H-1,0} & x_{H-1,1} & \cdots & x_{H-1,W-1} & 0      & \cdots & 0 \\
          0      & \cdots & 0      & 0         & 0         & \cdots & 0           & 0      & \cdots & 0 \\
          \vdots & \ddots & \vdots & \vdots    & \vdots    & \ddots & \vdots      & \vdots & \ddots & \vdots \\
          0      & \cdots & 0      & 0         & 0         & \cdots & 0           & 0      & \cdots & 0
      \end{bmatrix}
      }_{P+W+P}
      \left.
          \vphantom{
              \begin{array}
                  0 \\ \vdots \\ 0 \\ \vdots \\ x_{0,0} \\ x_{1,0} \\ \vdots \\ x_{H-1,0} \\ 0 \\ \vdots \\ 0
              \end{array}
          }
      \right\} {P+H+P}
\\
   &= \begin{bmatrix}
          x_{0,0}      & x_{0,1}      & \cdots & x_{0,W+2P-1} \\
          x_{1,0}      & x_{1,1}      & \cdots & x_{1,W+2P-1} \\
          \vdots       & \vdots       & \ddots & \vdots \\
          x_{H+2P-1,0} & x_{H+2P-1,1} & \cdots & x_{H+2P-1,W+2P-1}
      \end{bmatrix}
\end{aligned}

  (H, W) の行列の上下左右に  P 個ずつ要素を追加するので、パディング後のサイズは  (H + 2 P, W + 2 P) になります。

 パディング前後の関係を詳しく見ます。

 \displaystyle
\mathbf{X}^{\mathrm{pad}}
    = \underbrace{
      \left[
      \begin{array}{ccc|cccc|ccc}
          x_{0,0}       & \cdots & x_{0,P-1}       & x_{0,P}       & x_{0,P+1}       & \cdots & x_{0,P+W-1}       & x_{0,P+W}       & \cdots & x_{0,P+W+P-1} \\
          \vdots        & \ddots & \vdots          & \vdots        & \vdots          & \ddots & \vdots            & \vdots          & \ddots & \vdots \\
          x_{P-1,0}     & \cdots & x_{P-1,P-1}     & x_{P-1,P}     & x_{P-1,P+1}     & \cdots & x_{P-1,P+W-1}     & x_{P-1,P+W}     & \cdots & x_{P-1,P+W+P-1} \\
          \hline
          x_{P,0}       & \cdots & x_{P,P-1}       & x_{P,P}       & x_{P,P+1}       & \cdots & x_{P,P+W-1}       & x_{P,P+W}       & \cdots & x_{P,P+W+P-1} \\
          x_{P+1,0}     & \cdots & x_{P+1,P-1}     & x_{P+1,P}     & x_{P+1,P+1}     & \cdots & x_{P+1,P+W-1}     & x_{P+1,P+W}     & \cdots & x_{P+1,P+W+P-1} \\
          \vdots        & \ddots & \vdots          & \vdots        & \vdots          & \ddots & \vdots            & \vdots          & \ddots & \vdots \\
          x_{P+H-1,0}   & \cdots & x_{P+H-1,P-1}   & x_{P+H-1,P}   & x_{P+H-1,P+1}   & \cdots & x_{P+H-1,P+W-1}   & x_{P+H-1,P+W}   & \cdots & x_{P+H-1,P+W+P-1} \\
          \hline
          x_{P+H,0}     & \cdots & x_{P+H,P-1}     & x_{P+H,P}     & x_{P+H,P+1}     & \cdots & x_{P+H,P+W-1}     & x_{P+H,P+W}     & \cdots & x_{P+H,P+W+P-1} \\
          \vdots        & \ddots & \vdots          & \vdots        & \vdots          & \ddots & \vdots            & \vdots          & \ddots & \vdots \\
          x_{P+H+P-1,0} & \cdots & x_{P+H+P-1,P-1} & x_{P+H+P-1,P} & x_{P+H+P-1,P+1} & \cdots & x_{P+H+P-1,P+W-1} & x_{P+H+P-1,P+W} & \cdots & x_{P+H+P-1,P+W+P-1}
      \end{array}
      \right]
      }_{P+W+P}
      \left.
          \vphantom{
              \begin{array}
                  x_{0,0} \\ \vdots \\ x_{P-1,0} \\ \vdots \\ x_{P,0} \\ x_{P+1,0} \\ \vdots \\ x_{P+H-1,0} \\ x_{P+H,0} \\ \vdots \\ x_{P+H+P-1,0}
              \end{array}
          }
      \right\} {P+H+P}

 入力データ(パディング前)の要素を  x_{h,w}^{\mathrm{in}}、パディング後の要素を  x_{h,w}^{\mathrm{pad}} で表すと、次の関係が成り立ちます。

 \displaystyle
x_{h,w}^{\mathrm{pad}}
    = \begin{cases}
          0  &\quad (h \lt P) \\
          0  &\quad (w \lt P) \\
          x_{h-P,w-P}^{\mathrm{in}}
             &\quad (P \leq h \lt H+P, P \leq w \lt W+P) \\
          0  &\quad (h \geq H+P) \\
          0  &\quad (w \geq W+P)
      \end{cases}

  x_{h,w}^{\mathrm{in}} の行インデックスは  h = 0, 1, \dots, H-1、列インデックスは  w = 0, 1, \dots, W-1 であり、また  x_{h,w}^{\mathrm{pad}} の行インデックスは  h = 0, 1, \dots, H+2P-1、列インデックスは  w = 0, 1, \dots, W+2P-1 です。

図で確認

 次は、パディングの処理を図で確認します。

パディングの処理

 入力データとフィルター(重み)の形状、パディングのサイズを設定して、パディング後の形状を計算します。

# 入力データのサイズを指定
IH = 3
IW = 4

# パディングのサイズを指定
P = 2

# パディングデータのサイズを計算
PH = IH + 2*P
PW = IW + 2*P
print(PH, PW)
7 8

 入力サイズ  (H_{\mathrm{I}}, W_{\mathrm{I}})、パディングサイズ  P を指定して、パディング後サイズ  (H_{\mathrm{P}}, W_{\mathrm{P}}) を計算します。

 パディングにおける各要素の対応関係をアニメーションで確認します。

 入力データの要素を塗りつぶしで示しています。
 入力サイズが  (H, W) の入力データの周囲(上下左右)に  P 個ずつ  0 を追加するので、パディング後のサイズが  (H + 2 P, W + 2 P) になるのを確認できます。

 入力データを作成します。

# 入力データを作成
X = np.arange(IH*IW).reshape((IH, IW))
print(X)
print(X.shape)
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]
(3, 4)

 入力データ  \mathbf{X} を作成します。

 パディングした入力データを作成します。

# 入力データをパディング
X_pad = np.pad(X, pad_width=[(P, P), (P, P)], mode='constant')
print(X_pad)
print(X_pad.shape)
[[ 0  0  0  0  0  0  0  0]
 [ 0  0  0  0  0  0  0  0]
 [ 0  0  0  1  2  3  0  0]
 [ 0  0  4  5  6  7  0  0]
 [ 0  0  8  9 10 11  0  0]
 [ 0  0  0  0  0  0  0  0]
 [ 0  0  0  0  0  0  0  0]]
(7, 8)

 np.pad() でパディングできます。第1引数に入力データ  \mathbf{X}、パディング幅の引数(第2引数) pad_width にパディングサイズ  P、パディング設定の引数(第3引数) mode'constant' を指定します。
 pad_width 引数に、軸ごとに前と後のパディング幅を指定できます。
 定数でパディングする場合は、mode 引数に 'constant'constant_values 引数に値を指定します。デフォルトは 0 です。

 パディング前後のグラフを作成します。

・作図コード(クリックで展開)

# グラデーションの中心の調整用の範囲を設定
X_max = np.ceil(max(X.max(), abs(X.min())))

# グラフサイズの調整値を指定
r = 1.0

# パディングを作図
fig, axes = plt.subplots(nrows=1, ncols=2, constrained_layout=True, 
                         figsize=((IW+PW)*r, PH*r), width_ratios=[IW, PW], 
                         facecolor='white', dpi=100)
fig.suptitle('padding', fontsize=20)

# 入力データを描画
ax = axes[0]
ax.pcolor(X, cmap='Spectral', vmin=-X_max, vmax=X_max, edgecolor='gray') # 入力データ
for h in range(IH):
    for w in range(IW):
        ax.text(x=w+0.5, y=h+0.5, s=f'{X[h, w]:.1f}', 
                size=15, ha='center', va='center') # 要素ラベル
ax.set_xticks(ticks=np.arange(IW)+0.5, labels=np.arange(IW))
ax.set_yticks(ticks=np.arange(IH)+0.5, labels=np.arange(IH))
ax.invert_yaxis() # 軸の反転
ax.set_xlabel('width: $W$')
ax.set_ylabel('height: $H$')
ax.set_title('input data: $X$', loc='left')
ax.set_aspect('equal', adjustable='box')

# パディングデータを描画
ax = axes[1]
ax.pcolor(X_pad, cmap='Spectral', vmin=-X_max, vmax=X_max, edgecolor='gray') # パディングした入力データ
for h in range(PH):
    for w in range(PW):
        ax.text(x=w+0.5, y=h+0.5, s=f'{X_pad[h, w]:.1f}', 
                size=15, ha='center', va='center') # 要素ラベル
ax.set_xticks(ticks=np.arange(PW)+0.5, labels=np.arange(PW))
ax.set_yticks(ticks=np.arange(PH)+0.5, labels=np.arange(PH))
ax.invert_yaxis() # 軸の反転
ax.set_xlabel('width: $W + 2P$')
ax.set_ylabel('height: $H + 2P$')
ax.set_title('padding data: $X^{pad}$'+f', padding size: $P = {P}$', loc='left')
ax.set_aspect('equal', adjustable='box')

plt.show()

パディングによる変化

 パディングサイズを調整することで入力サイズを調整できます。

パディングサイズと出力データの関係

 パディングによる出力データへの影響をアニメーションで確認します。

 入力データ  \mathbf{X} に対するパディングの幅  P と出力データ  \mathbf{Y} の形状の関係を確認できます。ストライドの幅は  S = 1 です。
 サイズがフィルターのサイズを超えたパディングの範囲では、フィルター(重み)  \mathbf{W} の影響は受けず、バイアス  b のみが影響します。

 この記事では、パディングを確認しました。次の記事では、ストライドを確認します。

参考文献

おわりに

 7章の内容が全て書き終わってから1つずつ投稿していくつもりだったのですが、嬉しい出来事が重なりまして、記事をストックせずに放出しています。表現や処理の統一などですぐに細かな修正をすることになるのですが、素敵な楽曲を聴きながら作業を進めます。まぁ自分では基本的に手元の音源で聴くんですがね。サブスクが開始して布教しやすくなったぞーーーーー。

 投稿日の0時に公開されたSIOOMのMV(LIVE映像)をどうぞ♪

 2010年代後半に活躍したハロプロOGメンバー5名によるアヴェンジャーズのような夢のユニットです。是非とも聴いてください!!!!!
 時を同じくして、この5名や道重さゆみさんなどの音楽サブスクが一斉解禁されたのでそちらでも聴いてみてください♬

【次節の内容】

www.anarchive-beta.com