web-dev-qa-db-ja.com

行列-行列乗算としての2次元畳み込み

1Dの場合、2つのベクトルabの間のたたみ込みはconv(a, b)として計算できますが、T_aおよびb。ここで、T_aaに対応するテプリッツ行列です。

このアイデアを2Dに拡張することは可能ですか?

a = [5 1 3; 1 1 2; 2 1 3]b=[4 3; 1 2]が与えられた場合、テプリッツ行列でaを変換し、T_abの間の行列行列積を次のように計算できます。 1次元の場合?

32
no_name

はい、可能です。また、二重ブロック循環行列( Toeplitz 行列の特殊なケース)を使用する必要があります。カーネルと入力のサイズが小さい例を紹介しますが、どのカーネルでもテプリッツ行列を作成できます。したがって、2d入力xと2dカーネルkがあり、コンボリューションx * kを計算したいとします。また、kがすでに反転していると仮定します。また、xのサイズはn×nであり、km×mであると仮定します。

したがって、kをサイズ(n-m+1)^2 × n^2のスパース行列に展開し、xを長いベクトルn^2 × 1に展開します。この疎行列とベクトルの乗算を計算し、結果のベクトル(サイズ(n-m+1)^2 × 1を持つ)をn-m+1正方行列に変換します。

読んだだけではわかりにくいと思います。したがって、2×2カーネルと3×3入力の例を次に示します。

enter image description here * enter image description here

これは、ベクトルを使用して作成された行列です。

enter image description here

これは enter image description here

そして、これはkに対してxのスライディングウィンドウを実行した場合と同じ結果です。

34
Salvador Dali

1-入力とフィルターの定義

[〜#〜] i [〜#〜]を入力信号とし、[〜#〜] fとする[〜#〜]はフィルタまたはカーネルです。

2d input signal and filter

2-最終的な出力サイズを計算する

Iがm1 x n1で、Fがm2 x n2の場合、出力のサイズは次のようになります。

enter image description here

3-フィルターマトリックスのゼロパッド

フィルターをゼロパッドして、出力と同じサイズにします。

enter image description here

4-ゼロパッドフィルターの各行のテプリッツ行列を作成する

enter image description here

5-二重にブロックされたテプリッツ行列を作成する

これで、これらすべての小さなテプリッツ行列は、大きな二重にブロックされたテプリッツ行列に配置されます。 enter image description here

enter image description here

6-入力行列を列ベクトルに変換する

enter image description here

7-ベクトル化された入力信号で二重にブロックされたテプリッツ行列を乗算

この乗算により、畳み込み結果が得られます。

8-最後のステップ:結果を行列形式に再形成します

enter image description here

詳細とpythonコードについては、私のgithubリポジトリを見てください:

Pythonでテプリッツ行列を使用して行列乗算として実装された2D畳み込みの段階的な説明

10
Ali Salehi

Kをm ^ 2ベクトルに展開してXを展開すると、次のようになります。

  • a 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
3
geoffn91

上記のコードは、適切な次元の展開された行列を生成しません。次元は(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

詳細については、私のブログ投稿を参照してください:

http://www.telesens.co/2018/04/09/initializing-weights-for-the-convolutional-and-fully-connected-layers/

0
user8697906