1Dの場合、2つのベクトルa
とb
の間のたたみ込みはconv(a, b)
として計算できますが、T_a
およびb
。ここで、T_a
はa
に対応するテプリッツ行列です。
このアイデアを2Dに拡張することは可能ですか?
a = [5 1 3; 1 1 2; 2 1 3]
とb=[4 3; 1 2]
が与えられた場合、テプリッツ行列でa
を変換し、T_a
とb
の間の行列行列積を次のように計算できます。 1次元の場合?
はい、可能です。また、二重ブロック循環行列( Toeplitz 行列の特殊なケース)を使用する必要があります。カーネルと入力のサイズが小さい例を紹介しますが、どのカーネルでもテプリッツ行列を作成できます。したがって、2d入力x
と2dカーネルk
があり、コンボリューションx * k
を計算したいとします。また、k
がすでに反転していると仮定します。また、x
のサイズはn×n
であり、k
はm×m
であると仮定します。
したがって、k
をサイズ(n-m+1)^2 × n^2
のスパース行列に展開し、x
を長いベクトルn^2 × 1
に展開します。この疎行列とベクトルの乗算を計算し、結果のベクトル(サイズ(n-m+1)^2 × 1
を持つ)をn-m+1
正方行列に変換します。
読んだだけではわかりにくいと思います。したがって、2×2カーネルと3×3入力の例を次に示します。
これは、ベクトルを使用して作成された行列です。
そして、これはk
に対してx
のスライディングウィンドウを実行した場合と同じ結果です。
[〜#〜] i [〜#〜]を入力信号とし、[〜#〜] fとする[〜#〜]はフィルタまたはカーネルです。
Iがm1 x n1で、Fがm2 x n2の場合、出力のサイズは次のようになります。
フィルターをゼロパッドして、出力と同じサイズにします。
これで、これらすべての小さなテプリッツ行列は、大きな二重にブロックされたテプリッツ行列に配置されます。
この乗算により、畳み込み結果が得られます。
詳細とpythonコードについては、私のgithubリポジトリを見てください:
Kをm ^ 2ベクトルに展開してXを展開すると、次のようになります。
m**2
vector k
((n-m)**2, m**2)
のunrolled_X
行列ここで、unrolled_X
は、次のPythonコードによって取得できます。
from numpy import zeros
def unroll_matrix(X, m):
flat_X = X.flatten()
n = X.shape[0]
unrolled_X = zeros(((n - m) ** 2, m**2))
skipped = 0
for i in range(n ** 2):
if (i % n) < n - m and ((i / n) % n) < n - m:
for j in range(m):
for l in range(m):
unrolled_X[i - skipped, j * m + l] = flat_X[i + j * n + l]
else:
skipped += 1
return unrolled_X
XではなくXをアンロールすると、各Xに対して他の方法よりもコンパクトな表現(小さな行列)が可能になりますが、各Xをアンロールする必要があります。目的に応じて、kをアンロールすることもできます。
ここで、unrolled_X
はスパースではありませんが、unrolled_k
はスパースですが、@ Salvador Daliが言及したように、サイズは((n-m+1)^2,n^2)
です。
k
の展開は次のように実行できます:
from scipy.sparse import lil_matrix
from numpy import zeros
import scipy
def unroll_kernel(kernel, n, sparse=True):
m = kernel.shape[0]
if sparse:
unrolled_K = lil_matrix(((n - m)**2, n**2))
else:
unrolled_K = zeros(((n - m)**2, n**2))
skipped = 0
for i in range(n ** 2):
if (i % n) < n - m and((i / n) % n) < n - m:
for j in range(m):
for l in range(m):
unrolled_K[i - skipped, i + j * n + l] = kernel[j, l]
else:
skipped += 1
return unrolled_K
上記のコードは、適切な次元の展開された行列を生成しません。次元は(n-k + 1)*(m-k + 1)、(k)(k)でなければなりません。 k:フィルター次元、n:入力行列の行数、m:列数。
def unfold_matrix(X, k):
n, m = X.shape[0:2]
xx = zeros(((n - k + 1) * (m - k + 1), k**2))
row_num = 0
def make_row(x):
return x.flatten()
for i in range(n- k+ 1):
for j in range(m - k + 1):
#collect block of m*m elements and convert to row
xx[row_num,:] = make_row(X[i:i+k, j:j+k])
row_num = row_num + 1
return xx
詳細については、私のブログ投稿を参照してください: