nn.Conv2dのpadding_modeを全部試してみる
はじめに
画像や時系列データに対してnn.Conv2dを使用する際、サイズを変更せず、端の情報を欠落させないようにパディングを使用することが多いかと思います。今回はpadding_modeパラメータを利用し、異なるパディング方法が畳み込み結果にどのような影響を与えるかを比較してみます。なお、その他の引数については今回細かくは触れません。以下のブログを参考にしてください。
https://blog.since2020.jp/ai/effect_of_arguments/
目的と背景
padding_modeという引数を指定することで、畳み込みの際に周辺のデータをどのように埋めるかを制御できます。公式のドキュメントでは、このパラメータに指定できるオプションが記載されていますが、その詳細や実際の挙動については明確にされていません。そこで、今回は padding_modeを変更し、簡易的な白黒画像に対して畳み込み処理を実行して、その挙動を観察していきます。
padding_modeについて
padding_modeには、以下の4種類があります。ここでは、それぞれの動作について具体的な例を挙げて解説します。
- padding_mode=’zeros’
- デフォルトの設定です。画像の外側をゼロで埋めます。これが最も一般的なゼロパディングです。
- 例:
padding=2の場合、配列[1, 2, 3, 4, 5]は[0, 0, 1, 2, 3, 4, 5, 0, 0]になります。
- padding_mode=’reflect’
- エッジの少し内側を鏡のように反転させて埋めます。画像の端の情報が反転して繰り返されることで、エッジ部分の情報が自然に補完されます。
- 例:
padding=2の場合、配列[1, 2, 3, 4, 5]は[3, 2, 1, 2, 3, 4, 5, 4, 3]になります。
- padding_mode=’replicate’
- エッジの値をそのまま複製して外側に広げます。これにより、エッジ情報が引き延ばされ、端の情報が自然に繰り返される形になります。
- 例:
padding=2の場合、配列[1, 2, 3, 4, 5]は[1, 1, 1, 2, 3, 4, 5, 5, 5]になります。
- padding_mode=’circular’
- 配列を輪っかのように扱い、外に出た分を反対側から持ってきて埋めます。これにより、端の値が反対側から循環して繋がる形になります。
- 例:
padding=2の場合、配列[1, 2, 3, 4, 5]は[4, 5, 1, 2, 3, 4, 5, 1, 2]になります。
実験
環境設定
今回は Google Colab のノートブックを使用して、畳み込み処理を行います。まずは必要なライブラリのインポートから。
import numpy as np
import torch import torch.nn as nn
import torch.nn.functional as F
import matplotlib.pyplot as plt今回は5×5の白黒モザイク画像を使います。
def show(img, title=""):
plt.figure(figsize=(3, 3))
plt.imshow(img, cmap="gray", vmin=0, vmax=1)
plt.title(title)
plt.axis("off")
plt.show() img = torch.arange(25.0).reshape(1, 1, 5, 5) / 24.0
show(img[0,0].numpy(), "Original 5x5 Image")

以下、padding_modeごとに畳み込み処理を行い、畳み込み前と後の画像を比較していきます。import torch
import torch.nn as nn
import matplotlib.pyplot as plt
x = torch.arange(25.0).reshape(1, 1, 5, 5) / 24.0
conv = nn.Conv2d(1, 1, 3, padding=1, padding_mode="zeros")
conv.weight.data[:] = 1/9
y = conv(x)
fig, axes = plt.subplots(1, 2, figsize=(6, 3))
axes[0].imshow(x[0, 0].detach().numpy(), cmap="gray", vmin=0, vmax=1)
axes[0].set_title("Original 5x5")
axes[0].axis("off")
axes[1].imshow(y[0, 0].detach().numpy(), cmap="gray", vmin=0, vmax=1)
axes[1].set_title("padding_mode=reflect")
axes[1].axis("off")
plt.tight_layout()
plt.show()結果
padding_mode=’zeros’
0で埋めるので外堀は真っ黒になります。確かに全体的にエッジが暗くなっているのが分かるかと思います。

padding_mode=’reflect’
今回はpadding=1のため、エッジの1つ内側の情報で外堀を埋めます。エッジが全体的に平均化されているのが分かるでしょうか。

padding_mode=’replicate’
エッジの情報をそのまま外に複製しているので、畳み込んでもそれほどオリジナルと色合いは変わりません。

padding_mode=’circular’
黒の近辺は白で埋め、白い画素の近辺は黒で埋めます。全セルが近い色合いになっています。

まとめ
- Conv2dにおけるpadding_modeの違いを実例とともに紹介してみました。私も普段ゼロパディングばかり使っていましたが、畳み込むデータによってはpadding_modeの変更で大きな差が出そうです。
- 今回は画像データに使用しましたが、2次元配列であれば何にでも適用できます。画像は端の情報がそれほど重要でないことが多いですが、例えば多変量時系列データの場合、端の方に位置する変数情報が欠落してしまう可能性もあるため、パディングの適切な選択が肝になってくると考えます。
