初心者データサイエンティストの備忘録

調べたことは全部ここに書いて自分の辞書を作る

【PyTorch】畳み込み演算の次元数変化:チュートリアルの解説

はじめに

 PyTorchのチュートリアルPyTorch60分講座: ニューラルネットワーク入門を勉強しました。本記事では、チュートリアル記載のLeNetがどのように入力画像の次元数を変化させていくのかについてまとめようと思います。
 なお、私もチュートリアルを読んでいる際に混乱したのですが、チュートリアルに記載のLeNetの構造を表した図と、チュートリアル中に書かれているコード(Netクラス)は若干構造が異なっています(図1)。本記事では、コードを正として説明します。

図1:チュートリアル中のLeNetを表した図とコードの差分

畳み込み演算による次元数の変化

 本節では、畳み込み演算による入力画像の次元数の変化について説明します。下記のような設定とします。

  • 入力画像
    • チャネル数:C _ {\rm in}
    • 縦幅:H _ {\rm in}
    • 横幅:W _ {\rm in}
  • フィルタ
    • 縦幅:H _ F
    • 横幅:W _ F
  • ストライド S
  • 出力画像
    • チャネル数:C _ {\rm out}

 このとき、畳み込み演算では次の手順にしたがって計算が行われます。

  1. まず、C _ {\rm in}個のフィルタが用意され、入力画像との積和をとる
  2. 次に、1で計算された出力の画素を全チャネルにわたって和をとる
  3. 上記の作業をC _ {\rm out}回行うことで、出力画像のチャネル数をC _ {\rm out}にする

 図2は上記の作業をまとめたものです。

図2:畳み込み演算の次元数の変化

 以上より、次元数が ({\rm チャネル数}, {\rm 縦幅}, {\rm 横幅}) = (C _ {\rm in}, H _ {\rm in}, W _ {\rm in})の入力画像が畳み込み演算によって、 ({\rm チャネル数}, {\rm 縦幅}, {\rm 横幅}) = \left(C _ {\rm out}, \left\lfloor \frac{ H _ {\rm in}-H _ F}{S} \right\rfloor+1, \left\lfloor \frac{W _ {\rm in}-W _ F}{S}\right\rfloor+1 \right)に変化します。ただし、 \lfloor \cdot \rfloorは小数点以下を切り捨てて、整数に変換する関数です。

プーリングによる次元数の変化

 本節では、プーリングによる入力画像の次元数の変化について説明します。とはいえ、畳み込み演算とほとんど同じです。下記のような設定とします。

  • 入力画像
    • チャネル数:C _ {\rm in}
    • 縦幅:H _ {\rm in}
    • 横幅:W _ {\rm in}
  • ウィンドウサイズ
    • 縦幅:H _ W
    • 横幅:W _ W
  • ストライド S
  • 出力画像
    • チャネル数:プーリングではチャネル数が変化しないので、C _ {\rm in}のまま

 図3はプーリングの計算手順をまとめたものです。

図3:プーリングの手順

 以上より、次元数が ({\rm チャネル数}, {\rm 縦幅}, {\rm 横幅}) = (C _ {\rm in}, H _ {\rm in}, W _ {\rm in})の入力画像がプーリングによって、 ({\rm チャネル数}, {\rm 縦幅}, {\rm 横幅}) = \left(C _ {\rm in}, \left\lfloor \frac{ H _ {\rm in}-H _ W}{S} \right\rfloor+1, \left\lfloor \frac{W _ {\rm in}-W _ W}{S}\right\rfloor+1 \right)に変化します。

LeNetによる次元数の変化

 以上を踏まえて、LeNetが32×32の入力画像の次元数をどのように変化させるのかについて説明します。まず、LeNetの構造を改めて文字で書き起こします。

  1. C _ {\rm in} = 1, C _ {\rm out} = 6, H _ F = W _ F = 3, S = 1の畳み込み演算を行う
  2. C _ {\rm in} = 1, H _ W = W _ W = 2, S = 2の最大値プーリングを行う
  3. C _ {\rm in} = 6, C _ {\rm out} = 16, H _ F = W _ F = 3, S = 1の畳み込み演算を行う
  4. C _ {\rm in} = 16, H _ W = W _ W = 2, S = 2の最大値プーリングを行う
  5. 576次元のベクトルに変換する
  6. 576次元から120次元への線形変換を行う
  7. 120次元から84次元への線形変換を行う
  8. 84次元から10次元への線形変換を行う

 以上の変換のうち、5~8は単純な線形変換です。したがって、次元数の変化は上記に書いてある通りなので説明しません。1~4の変換について次元数がどのように変化するのか説明します。

1の変換

 畳み込み演算によって計算された出力画像の次元数は、 ({\rm チャネル数}, {\rm 縦幅}, {\rm 横幅}) = \left(C _ {\rm out}, \left\lfloor \frac{ H _ {\rm in}-H _ F}{S} \right\rfloor+1, \left\lfloor \frac{W _ {\rm in}-W _ F}{S}\right\rfloor+1 \right)でした。これに、C _ {\rm out} = 6, H _ {\rm in} = W _ {\rm in} = 32, H _ F = W _ F = 3, S = 1を代入することで、


({\rm チャネル数}, {\rm 縦幅}, {\rm 横幅}) = (6, 30, 30)

となります。

2の変換

 プーリングによって計算された出力画像の次元数は、 ({\rm チャネル数}, {\rm 縦幅}, {\rm 横幅}) = \left(C _ {\rm in}, \left\lfloor \frac{ H _ {\rm in}-H _ W}{S} \right\rfloor+1, \left\lfloor \frac{W _ {\rm in}-W _ W}{S}\right\rfloor+1 \right)でした。これに、C _ {\rm in} = 6, H _ {\rm in} = W _ {\rm in} = 30, H _ W = W _ W = 2, S = 2を代入することで、


({\rm チャネル数}, {\rm 縦幅}, {\rm 横幅}) = (6, 15, 15)

となります。

3の変換

 C _ {\rm out} = 16, H _ {\rm in} = W _ {\rm in} = 15, H _ F = W _ F = 3, S = 1なので、1の変換と同様に計算すると


({\rm チャネル数}, {\rm 縦幅}, {\rm 横幅}) = (16, 13, 13)

となります。

4の変換

 C _ {\rm in} = 16, H _ {\rm in} = W _ {\rm in} = 13, H _ W = W _ W = 2, S = 2なので、2の変換と同様に計算すると


({\rm チャネル数}, {\rm 縦幅}, {\rm 横幅}) = (16, 6, 6)

となります。

以上より、1~4の変換によって32x32の画像が16x6x6=576次元のベクトルに変換できました。

LeNetに入力できる画像の範囲

 ここからはおまけです。
 畳み込み演算やプーリングによる出力画像の次元数の計算には、床関数( \lfloor \cdot \rfloor)が含まれていました。したがって、入力画像のサイズが32x32と多少異なっていてもうまいこと床関数が次元数を調整し、576次元の全結合層に入力できるベクトルを得られる可能性があります。本節では、「入力画像のサイズを32x32からどれくらい変えてもLeNetに入力できるのか?」ということを考えようと思います。ただし、簡単のため入力画像は正方形とします。

 まず、LeNetの入力画像の縦幅を Hとします。このとき、入力画像は正方形であることを仮定しているので横幅も Hとなります。この入力画像に1の畳み込み演算による変換を行うと、出力画像のサイズは、


\begin{eqnarray}
({\rm チャネル数}, {\rm 縦幅}, {\rm 横幅}) &=& \left(6, \left\lfloor H-3 \right\rfloor+1, \left\lfloor H-3 \right\rfloor+1 \right) \\
&=& (6, H-2, H-2)
\end{eqnarray}

となります。ただし、 H \in \mathbb{N}なので H-2 \in \mathbb{N}となることを利用しました。
 同様に2のプーリングによる変換を行うと、出力画像のサイズは、


\begin{eqnarray}
({\rm チャネル数}, {\rm 縦幅}, {\rm 横幅}) &=& \left(6, \left\lfloor \dfrac{(H-2)-2}{2} \right\rfloor+1, \left\lfloor \dfrac{(H-2)-2}{2} \right\rfloor+1 \right) \\
&=& \left(6, \left\lfloor \dfrac{H}{2}\right\rfloor-1, \left\lfloor \dfrac{H}{2}\right\rfloor-1 \right)
\end{eqnarray}

となります。ここで、 H _ 1 = \left \lfloor \dfrac{H}{2} \right \rfloor-1としておきます。
 同様の計算方法で、3の畳み込み演算を行うと、出力画像のサイズは、


\begin{eqnarray}
({\rm チャネル数}, {\rm 縦幅}, {\rm 横幅}) =
= (16, H_1-2, H_1-2)
\end{eqnarray}

となります。また、4のプーリング演算を行うと、最終的な出力画像のサイズは、


\begin{eqnarray}
({\rm チャネル数}, {\rm 縦幅}, {\rm 横幅}) =
= \left(16, \left\lfloor \dfrac{H_1}{2}\right\rfloor-1, \left\lfloor \dfrac{H_1}{2}\right\rfloor-1 \right)
\end{eqnarray}

となります。
 このとき、上記の出力画像を576次元ベクトルに変換し全結合層に入力するので、


\begin{eqnarray}
16 \times \left(\left\lfloor \dfrac{H_1}{2}\right\rfloor-1\right) \times \left( \left\lfloor \dfrac{H_1}{2}\right\rfloor-1 \right) = 576
\end{eqnarray}

となります。これより、


\begin{eqnarray}
\left\lfloor \dfrac{H_1}{2}\right\rfloor-1 = 6
\end{eqnarray}

となります。
 一般的に、任意の x \in \mathbb{R}, k \in \mathbb{N}に対して、 \lfloor x \rfloor = k \Leftrightarrow k \leq x \lt k+1が成り立ちます。これを用いると、 \left\lfloor \dfrac{H_1}{2}\right\rfloor-1 = 6から、 14 \leq H _ 1 \lt 16となります。
 次に H _ 1 = \left \lfloor \dfrac{H}{2} \right \rfloor-1を使うと、 15 \leq \left\lfloor \dfrac{H}{2} \right\rfloor \lt 17となり、ここから


\begin{eqnarray}
30 \leq H < 34
\end{eqnarray}

となります。
 以上のことから、LeNetに入力可能な画像のサイズは正方形の場合、30×30、31×31、32×32、33×33の4パターンに限られることがわかります。実際に、入力画像のサイズを変えて実行してみると、図4のような結果になりました。

図4:LeNetに入力可能な画像のサイズ

 コードにおいても入力画像が4パターンしかないことを示せました。

まとめ

 本記事では、LeNetを題材に畳み込み演算とプーリングによって次元数がどのように変わるのかについて説明しました。また、LeNetに入力できる画像の範囲についても考察しました。