LoginSignup
52
46

More than 1 year has passed since last update.

numpy.pad関数完全理解

Last updated at Posted at 2020-06-05

対象者

深層学習で、特に畳み込みニューラルネットワーク(CNN)を勉強中に見かけるnumpy.pad関数について、いまいち動作が掴めないという方へ。
公式ドキュメントを噛み砕いて和訳していきます。

目次

pad関数とは

CNNで登場するpad関数、結構わかりづらい動作をしますよね?
大抵の書籍ではメインではないのでさらっと

pad_example.py
x = np.pad(x, [(0, 0), (0, 0), (pad, pad), (pad, pad)], "constant")

とすればOK、ぐらいしか書いてないのではないでしょうか。
ということで、この関数を徹底解剖します。
公式ドキュメントには

numpy.pad(array, pad_width, mode='constant', **kwargs)

のように引数が指定されると書いていますね。まずはそれぞれ見ていきましょう。

第一引数について

まずは公式ドキュメントを見てみましょう。

array : array_like of rank N
The array to pad.

和訳すると、

array : ランクNの配列かそれに類するもの
パディング対象の配列

となります。ランク(階数)とは線形代数の専門用語で、まあここでは次元数という認識でいいんじゃないでしょうか...詳しくはこちらこちらをご覧ください。

とりあえず、これについてはお分かりかと思います。パディング対象の配列を指定します。

第二引数について

さて、問題は第二引数ですね。

pad_width : {sequence, array_like, int}
Number of values padded to the edges of each axis. ((before_1, after_1), ..., (before_N, after_N)) unique pad widths for each axis. ((before, after),) yields same before and after pad for each axis. (pad,) or int is a shortcut for before = after = pad width for all axes.

和訳してみます。

pad_width : {シーケンス、配列かそれに類するもの、整数}のいずれか
それぞれの次元の端にパディングされる数字の数。
((before_1, after_1), ..., (before_N, after_N)) : それぞれの次元特有のパディング幅(before_i, after_i)を指定します。
((before, after),) : それぞれの次元に同じパディング幅(before, after)を指定します。
(pad,) または整数 : 全ての次元に対して同じパディング幅(before = after = pad)を指定します。

さて、意味がわかりにくいですね。実装も交えて見てみましょう。

pad_example.py
import numpy as np


x_1d = np.arange(1, 3 + 1)
print(x_1d)

1次元の場合

まずは1次元配列から見てみましょう。それぞれのドキュメントで指定されているようにやってみます。
まずは

((before_1, after_1), ..., (before_N, after_N))

ですね。

pad_example.py
print(np.pad(x_1d, ((1, 1))))
print(np.pad(x_1d, ((2, 1))))
print(np.pad(x_1d, ((1, 2))))
pad_1d_tuple_i.png

これならなんとなくわかりますよね?1次元ですので指定可能なtupleは1つだけで、before_1で指定している数だけ配列の左に、after_1で指定している数だけ配列の右に、それぞれ$0$でパディングされていますね。
ちなみに二重タプルで書いているつもりですが、実はPythonでは単タプルと同様の扱いをされています。

続いて

((before, after),)

でやってみましょう。

pad_example.py
print(np.pad(x_1d, ((1, 1),)))
print(np.pad(x_1d, ((2, 1),)))
print(np.pad(x_1d, ((1, 2),)))
pad_1d_tuple.png

はい、結果は一緒ですね。こちらでは明示的に二重タプルとして引数を送っています。

最後に

(pad,)または整数

でやってみましょう。

pad_example.py
print(np.pad(x_1d, (1,)))
print(np.pad(x_1d, (2,)))
print(np.pad(x_1d, 1))
print(np.pad(x_1d, 2))
pad_1d_pad_int.png

両端に指定した数だけ$0$が埋められていますね。この指定方法だと両端とも同じ数だけパディングされます。

2次元の場合

次は2次元配列でやってみます。

pad_example.py
x_2d = np.arange(1, 3*3 + 1).reshape(3, 3)
print(x_2d)

print(np.pad(x_2d, ((1, 1), (2, 2))))
print(np.pad(x_2d, ((2, 2), (1, 1))))
print(np.pad(x_2d, ((1, 2), (1, 2))))
print(np.pad(x_2d, ((2, 1), (1, 2))))

print(np.pad(x_2d, ((1, 1),)))
print(np.pad(x_2d, ((1, 2),)))
print(np.pad(x_2d, ((2, 1),)))
print(np.pad(x_2d, ((2, 2),)))

print(np.pad(x_2d, (1,)))
print(np.pad(x_2d, (2,)))
print(np.pad(x_2d, 1))
print(np.pad(x_2d, 2))

((before_i, after_i))の結果
pad_2d_tuple_i_1122.pngpad_2d_tuple_i_2211.pngpad_2d_tuple_i_1212.pngpad_2d_tuple_i_2112.png
((before, after),)の結果
pad_2d_tuple_11.pngpad_2d_tuple_12.pngpad_2d_tuple_21.pngpad_2d_tuple_22.png
(pad,)の結果
pad_2d_pad_1.pngpad_2d_pad_2.png
整数の結果
pad_2d_int_1.pngpad_2d_int_2.png
さて、2次元の場合は1次元目である行(上下)にまずパディングされ、その次に2次元目の列(左右)にパディングされています。それ以外は1次元の時と同じですね。

4次元の場合

もう大体お分かりかと思いますので、3次元は飛ばして4次元で実験します。
一つずつコメント外して実行することをお勧めします。出力が縦に長くなってしまうので大変見づらいです。

pad_example.py
def print_4darray(x):
    first, second, third, fourth = x.shape
    x_str_size = len(str(np.max(x)))
    for i in range(first):
        for k in range(third):
            for j in range(second):
                str_size = len(str(np.max(x[i, j, k, :])))
                if x_str_size != str_size:
                    add_size = "{: " +str(x_str_size - str_size)+ "d}"
                    np.set_printoptions(
                        formatter={'int': add_size.format})
                else:
                    np.set_printoptions()
                print(x[i, j, k, :], end=" ")
            print()
        print()

x_4d = np.arange(1, 3*3*3*3 + 1).reshape(3, 3, 3, 3)
print_4darray(x_4d)

print_4darray(np.pad(x_4d, ((1, 1), (2, 2), (0, 0), (0, 0))))
print_4darray(np.pad(x_4d, ((0, 0), (0, 0), (2, 2), (1, 1))))
print_4darray(np.pad(x_4d, ((1, 1), (0, 0), (2, 2), (0, 0))))
print_4darray(np.pad(x_4d, ((0, 0), (1, 1), (0, 0), (2, 2))))
print_4darray(np.pad(x_4d, ((0, 0), (1, 1), (2, 2), (0, 0))))
print_4darray(np.pad(x_4d, ((1, 1), (0, 0), (0, 0), (2, 2))))

#print_4darray(np.pad(x_4d, ((1, 1),)))
#print_4darray(np.pad(x_4d, ((1, 2),)))
#print_4darray(np.pad(x_4d, ((2, 1),)))
#print_4darray(np.pad(x_4d, ((2, 2),)))

#print_4darray(np.pad(x_4d, (1,)))
#print_4darray(np.pad(x_4d, (2,)))
#print_4darray(np.pad(x_4d, 1))
#print_4darray(np.pad(x_4d, 2))
`np.pad(x_4d, ((1, 1), (2, 2), (0, 0), (0, 0)))`の結果 pad_4d_tuple_i_11220000.png
`np.pad(x_4d, ((0, 0), (0, 0), (2, 2), (1, 1)))`の結果 pad_4d_tuple_i_00002211.png
`np.pad(x_4d, ((1, 1), (0, 0), (2, 2), (0, 0)))`の結果 pad_4d_tuple_i_11002200.png
`np.pad(x_4d, ((0, 0), (1, 1), (0, 0), (2, 2)))`の結果 pad_4d_tuple_i_00110022.png
`np.pad(x_4d, ((0, 0), (1, 1), (2, 2), (0, 0)))`の結果 pad_4d_tuple_i_00112200.png
`np.pad(x_4d, ((1, 1), (0, 0), (0, 0), (2, 2)))`の結果 pad_4d_tuple_i_11000022.png
`np.pad(x_4d, ((1, 1),))`の結果 pad_4d_tuple_11.png
`np.pad(x_4d, ((1, 2),))`の結果 pad_4d_tuple_12.png
`np.pad(x_4d, ((2, 1),))`の結果 pad_4d_tuple_21.png
`np.pad(x_4d, ((2, 2),))`の結果 pad_4d_tuple_22.png
`np.pad(x_4d, (1,))`の結果 pad_4d_pad_1.png
`np.pad(x_4d, (2,))`の結果 pad_4d_pad_2.png
`np.pad(x_4d, 1)`の結果 pad_4d_int_1.png
`np.pad(x_4d, 2)`の結果 pad_4d_int_2.png
ご覧の通り、縦・横・縦・横の順にパディングが施されています。高次元になるとパディングは数値ではなく配列になっていますね。

print_4darray関数ですが、1次元目、3次元目、2次元目の順にループを回し、4次元目をprint関数で出力しています。この時end=" "で改行ではなく半角スペースを出力しています。
あとは調整用の改行とかnp.set_printoptions関数で出力時の空白を制御していたりとかしています。
numpyの標準出力では見づらいので作成しました。

ちなみに、コードを実行すると多分いくつか画面内に収まらないものがあると思います。画像は複数枚のスクショを一生懸命重ねました。笑
あと、jupyter notebookのセル幅を広げたりもしました

第三引数について

第三引数についても見てみましょう。長いので部分ごとに。

modestr or function, optional
One of the following string values or a user supplied function.

モードを指定する文字列または関数を指定するオプション引数です。
下記の文字列から一つ、またはユーザが関数を指定します。

引数自体の説明は特に問題ありませんね。ユーザが指定する関数というのは後述します。

`constant`の説明

‘constant’ (default)
Pads with a constant value.

constant (デフォルト)
定数(0のこと)でパディングします。

`constant`は0埋めのことを表します。`im2col`関数ではこれを用います。 [第二引数](#第二引数の場合)までの例ではこれが用いられていたわけですね。なので例は省きます。
`edge`の説明

‘edge’
Pads with the edge values of array.

edge
行列の端の値でパディングします。

こちらは例を見たらわかると思います。
pad_example.py
print(np.pad(x_2d, 1, "edge"))
pad_2d_edge.png

2次元配列でのパディング例です。
中心部分の$3 \times 3$要素が元々の配列でしたね。パディング後の配列の端から縦・横・斜めに中心に向かっていき、一番最初に出会った値をコピーしています。

`linear_ramp`の説明

‘linear_ramp’
Pads with the linear ramp between end_value and the array edge value.

linear_ramp
最後の値と端の値との間のランプ関数でパディングします。

和訳してもわかりませんね。実際に動かしてみましょう。
pad_example.py
print(np.pad(x_2d, 3, "linear_ramp"))
pad_2d_linear_ramp.png

実際に動かしてもぱっと見ではわかりづらいですね笑。ブロックで分けてみましょう。
pad_2d_linear_ramp.png

赤色のブロックは動作が読めませんが、他の色のブロックおよびブロックの間はわかるのではないでしょうか?
例として水色のブロックに注目します。
$3$の縦横に注目すると、端の値$0$に向かって$3210$となっていますね。イメージとしては$0 \le x \le 3$を4等分して切り捨てる感じです。ここでは$0, 1, 2, 3$のように等分されますので、それがそのまま現れていますね。
緑色のブロックに注目します。ここも同じルールが適用できます。$0 \le x \le 7$を4等分すると$0, 2.\dot3, 4.\dot6, 7$となり、$0, 2, 4, 7$が現れています。
また、各ブロックの他の要素は上記の例で決定された値を斜め方向に帯状に並べていますね。端の値は$0$となっています。

`maximum`の説明

‘maximum’
Pads with the maximum value of all or part of the vector along each axis.

maximum
軸ごとの全体または一部のベクトルの最大値でパディングします。

言いたい事はなんとなくわかりますね。最大値でパディングする感じです。しかしこれが意外とややこしかったりします。 pad_2d_maximum.png

微妙にんん??となる感じですが、ルールが分かれば納得します。
pad_2d_maximum.png

紫色のブロックが元々の配列ですね。これにパディングを施していきます。
まずはの値がそれぞれのブロックの中の最大値でパディングされます。そしてそれらが全て終わったあとの値がブロック内の最大値でパディングされます。

`mean`の説明

‘mean’
Pads with the mean value of all or part of the vector along each axis.

mean
軸ごとの全体または一部のベクトルの平均値でパディングします。

これも`maximum`と同じ動作原理で動きます。違いは最大値か平均値かという点だけですね。
pad_example.py
print(np.pad(x_2d, 1, "mean"))
pad_2d_mean.png
`median`の説明

‘median’
Pads with the median value of all or part of the vector along each axis.

median
軸ごとの全体または一部のベクトルの中央値でパディングします。

`maximum`や`median`と同じ動作原理です。特筆すべきことはないでしょう。
pad_example.py
print(np.pad(x_2d, 1, "median"))
pad_2d_median.png
`minimum`の説明

‘minimum’
Pads with the minimum value of all or part of the vector along each axis.

minimum
軸ごとの全体または一部のベクトルの最小値でパディングします。

`maximum`などと同じ動作原理でパディングします。
pad_example.py
print(np.pad(x_2d, 1, "minimum"))
pad_2d_minimum.png
`reflect`の説明

‘reflect’
Pads with the reflection of the vector mirrored on the first and last values of the vector along each axis.

reflect
軸ごとのベクトルの、最初と最後の値を写したベクトルの反射でパディングします。

全然意味がわかりませんね。実際に動かしてみましょう。
pad_example.py
print(np.pad(x_2d, 2, "reflect"))
pad_2d_reflect_2.png

わかりにくい...けどなんとなくわからなくもないですね。例の如くブロック分けします。
pad_2d_reflect_2.png

これでどうでしょう。同じ色のブロックの間に位置する値に対して対称的にパディングされていますね。
水色のブロックに注目すると、$1$を中心としてその下の$4, 7$が「反射したベクトル」でパディングしていますね。
緑色のブロックでは中心が$3$で、その左側の$1, 2$が「反射したベクトル」でパディングされていますし、赤色のブロックでは中心を$1$として$5, 6, 8, 9$が「反射したベクトル」でパディングされています。

`symmetric`の説明

‘symmetric’
Pads with the reflection of the vector mirrored along the edge of the array.

symmetric
配列の端にそって写したベクトルの反射でパディングします。

`reflect`と文言が似ていますね。どう違うのか見てみましょう。
pad_example.py
print(np.pad(x_2d, 2, "symmetric"))
pad_2d_symmetric.png

reflectとの最大の違いは、元々の配列の端の値を「含んで反射する」か「含まずに反射する」かですね。

`wrap`の説明

‘wrap’
Pads with the wrap of the vector along the axis. The first values are used to pad the end and the end values are used to pad the beginning.

wrap
軸に沿ったベクトルのラップでパディングします。最初の値は最後をパディングするために使われ、最後の値は最初をパディングするために使われます。

??って感じですね。動かしましょう。
pad_example.py
print(np.pad(x_2d, 2, "wrap"))
pad_2d_wrap.png

こうして見ると明白ですね。reflectの反射しないバージョンです。もはや公式ドキュメントとしてそう書いて欲しい...

`empty`の説明

‘empty’
Pads with undefined values.
New in version 1.17.

empty
不定の値でパディングします。
numpyのバージョン1.17で追加されました。

これはパディングのメモリだけ確保して、初期化をしないバージョンですね。`numpy.empty`関数などと同じような感じです。一応確認しておきましょう。
pad_example.py
import numpy as np


print(np.pad(np.arange(1, 3*3+1).reshape(3, 3), 2, "empty"))
print(np.pad(np.arange(1, 3*3+1).reshape(3, 3), 5, "empty"))
pad_2d_empty_2.png pad_2d_empty_5.png

実験のため新しいノートブックを作成しています(新しいセルではありません)。
ぼくの環境では上記のようになりました。emptyコマンドでのパディング幅4までは$0$パディングで、$5$以上では不定の、おそらく確保したメモリ先に残っていた値が出力されています。ちなみにパディング幅$5$の画像は全体を写しても仕方ないので一部のみです。

\の説明

<function>
Padding function, see Notes.

Notes
New in version 1.7.0.
For an array with rank greater than 1, some of the padding of later axes is calculated from padding of previous axes. This is easiest to think about with a rank 2 array where the corners of the padded array are calculated by using padded values from the first axis.

The padding function, if used, should modify a rank 1 array in-place. It has the following signature:

padding_func(vector, iaxis_pad_width, iaxis, kwargs)
where

vector: ndarray
A rank 1 array already padded with zeros. Padded values are vector[:iaxis_pad_width[0]] and vector[-iaxis_pad_width[1]:].

iaxis_pad_width: tuple
A 2-tuple of ints, iaxis_pad_width[0] represents the number of values padded at the beginning of vector where iaxis_pad_width[1] represents the number of values padded at the end of vector.

iaxis: int
The axis currently being calculated.

kwargs: dict
Any keyword arguments the function requires.

<function>
パディング関数。ノートを参照。

ノート
numpyのバージョン1.7.0で追加。
ランク1以上の配列のため、高次のいくつかのパディングは低次のパディングから計算されます。これは2次元配列に対してパディングを施した配列の角の要素を、既に施したパディングの値を用いて決定することを考えるともっともわかりやすいでしょう。

パディング関数を用いる場合は1次元配列を所定の方法で変更する必要があります。次のような感じです:

padding_func(vector, iaxis_pad_width, iaxis, kwargs)
それぞれの引数について、

vector: ndarray
1次元配列は既に0でパディングされています。パディングされる値はvector[:iaxis_pad_width[0]]vector[-iaxis_pad_width[1]:]です。

iaxis_pad_width: tuple
整数の二重タプルで、iaxis_pad_width[0]はベクトルの最初にパディングされる値の数を表し、iaxis_pad_width[1]はベクトルの最後にパディングされる値の数を表します。

iaxis: int
現在計算されている次元。

kwargs: dict
関数が要求するいくつかのキーワード引数。

`padding_func`に則った関数を定義して渡せばその通りにパディングしてくれるみたいですね。実際に公式サイトのコードで実験してみます。
pad_example.py
def pad_with(vector, pad_width, iaxis, kwargs):
    pad_value = kwargs.get('padder', 10)
    vector[:pad_width[0]] = pad_value
    vector[-pad_width[1]:] = pad_value

print(np.pad(x_2d, 2, pad_with))
print(np.pad(x_2d, 2, pad_with, padder=100))
pad_2d_function.png

vectorpad_widthiaxisは自動で渡されます。他にユーザが引数を指定したい場合はキーワード引数としてnumpy.pad関数に渡しておき、パディング関数内で取り出すようにします(pad_value = kwargs.get('padder', 10))。

キーワード引数`stat_length`の説明

stat_length: sequence or int, optional
Used in ‘maximum’, ‘mean’, ‘median’, and ‘minimum’. Number of values at edge of each axis used to calculate the statistic value.
((before_1, after_1), … (before_N, after_N)) unique statistic lengths for each axis.
((before, after),) yields same before and after statistic lengths for each axis.
(stat_length,) or int is a shortcut for before = after = statistic length for all axes.
Default is None, to use the entire axis.

stat_length: シーケンスまたは整数、オプションです。
maximummeanmedianminimumで指定できるオプションです。それぞれの次元の端の値の数が統計値の計算に用いられます。
((before_1, after_1), … (before_N, after_N))ではそれぞれの次元で統計幅を個別に指定しています。
((before, after),)ではそれぞれの次元に対して同じ統計幅が用いられます。
(stat_length,)または整数は全ての次元に対してbefore = afterな統計幅を用いるためのショートカットです。
デフォルトはNoneで、全ての次元に用います。

長いですね...しかし、ちゃんと読めばどういうことかわかりますね。 `maximum`、`mean`、`median`、`minimum`ではそれぞれの統計値を取りその値でパディングする訳ですが、このキーワード引数を指定することで統計を取るベクトルのサイズ(統計幅と勝手に呼びました)を指定できます。
pad_example.py
print(np.pad(x_2d, 1, "maximum", stat_length=2))
pad_2d_maximum_stat_length=2.png

maximumでの出力結果と見比べていただけるとわかりますが、最大値を取るベクトルのサイズが$3$(全体)ではなく$2$となっています。
pad_2d_maximum_stat_length=2.png

キーワード引数`constant_values`の説明

constant_values: sequence or scalar, optional
Used in ‘constant’. The values to set the padded values for each axis.
((before_1, after_1), ... (before_N, after_N)) unique pad constants for each axis.
((before, after),) yields same before and after constants for each axis.
(constant,) or constant is a shortcut for before = after = constant for all axes.
Default is 0.

constant_values: シーケンスまたは実数、オプションです。
constantで指定できるオプションです。それぞれの次元にパディングする値を設定することができます。
((before_1, after_1), ... (before_N, after_N))では、各次元に対してそれぞれ個別にパディング用の定数を設定します。
((before, after),)では、それぞれの次元に対して同じパディング用の定数を設定します。
(constant,)か定数はbefore = afterな定数を全ての次元に適用するショートカットです。
デフォルトでは$0$です。

こちらはわかりやすいと思います。`constant`では$0$埋めしますが、この埋める値を自由に設定できるということですね。
pad_example.py
print(np.pad(x_2d, 1, "constant", constant_values=(-1, -2),))
pad_2d_constant_constant_values=((-1, -2),).png
キーワード引数`end_values`の説明

end_values: sequence or scalar, optional
Used in ‘linear_ramp’. The values used for the ending value of the linear_ramp and that will form the edge of the padded array.
((before_1, after_1), ... (before_N, after_N)) unique end values for each axis.
((before, after),) yields same before and after end values for each axis.
(constant,) or constant is a shortcut for before = after = constant for all axes.
Default is 0.

end_values: シーケンスまたは実数、オプションです。
linear_rampで指定できるオプションです。linear_ramp関数での最後の値を設定し、また端の値を指定の値で埋めます。
((before_1, after_1), ... (before_N, after_N))では、各次元に対してそれぞれ個別に設定します。
((before, after),)では、それぞれの次元に対して同じものを設定します。
(constant,)か定数はbefore = afterな値を全ての次元に適用するショートカットです。

こちらもわかりやすいかと思います。`linear_ramp`で端の値を$0$以外にも色々と変えられる訳です。
pad_example.py
print(np.pad(x_2d, 3, "linear_ramp", end_values=((-1, -2), (-3, -4))))
pad_2d_linear_ramp_end_value.png こんな感じですね〜
キーワード引数`reflect_type`の説明

reflect_type: {‘even’, ‘odd’}, optional
Used in ‘reflect’, and ‘symmetric’. The ‘even’ style is the default with an unaltered reflection around the edge value. For the ‘odd’ style, the extended part of the array is created by subtracting the reflected values from two times the edge value.

reflect_type: evenoddか、オプションです。
reflectsymmetricで指定できるオプションです。evenはデフォルトのスタイルで、端の値周りに不変な反射をします。oddスタイルでは、配列のパディング部分の値が、端の値の2倍から反射された値を引くことで決定されます。

うーん...まあやってみましょうか。
pad_example.py
print(np.pad(x_2d, 2, "reflect", reflect_type="odd"))
pad_2d_reflect_odd.png

この結果を説明文と見比べるとわかりますね。
pad_2d_reflect_odd.png

水色のブロックに注目すると、$1$を中心としている点はeven(デフォルト)と同じですが、パディングの値が全く異なりますね。
説明文に則って計算してみましょう。
説明文には「端の値の2倍から反射された値を引くことで決定されます」とあるので、端の値$1$の2倍である$2$から、反射された値$7, 4$を引くと$-5, -2$となり、出力画像と一致しますね!
赤色のブロックでも同様です。
端の値$3$の2倍である$6$から、反射された値$2, 1$を引くと$4, 5$となりますね。

ちなみに`numpy.pad`関数の戻り値はビューではなく新しい配列ですね。
pad_example.py
print(np.pad(x_2d, 2, "constant").base)
# 出力は None となります。

base属性は配列がオリジナル(何ともメモリを共有していない)の場合はNoneを返し、そうでない場合は配列の値を返します。

im2colでの動作

さて、この記事im2col関数について徹底的に説明しているのですが、ここに登場するコードには

im2col.py
    pad_zero = (0, 0)
    
    O_h = int(np.ceil((I_h - F_h + 2*pad_ud)/stride_ud) + 1)
    O_w = int(np.ceil((I_w - F_w + 2*pad_lr)/stride_lr) + 1)
    
    pad_ud = int(np.ceil(pad_ud))
    pad_lr = int(np.ceil(pad_lr))
    pad_ud = (pad_ud, pad_ud)
    pad_lr = (pad_lr, pad_lr)
    images = np.pad(images, [pad_zero, pad_zero, pad_ud, pad_lr], \
                    "constant")

という部分があります。
ここでのpad関数が何をしているか、もうお分かりかと思います。
1,2次元目はpad_zeroですのでパディングなし、3,4次元目はそれぞれpad_ud, pad_lrだけパディングしています。全体の括りは実はタプル型でなくても大丈夫なんですね。
1,2次元目はバッチ、チャンネル数で、3,4次元目というのは画像データですので、画像まわりだけパディングしていることが理解できると思います。

おわりに

pad関数、奥が深い...

参考

深層学習シリーズ

52
46
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
52
46