web-dev-qa-db-fra.com

Convolution 2D comme multiplication matrice-matrice

Je sais que, dans le cas 1D, la convolution entre deux vecteurs, a et b, peut être calculée comme conv(a, b), mais aussi comme le produit entre les T_a et b, où T_a est la matrice de Toeplitz correspondante pour a.

Est-il possible d'étendre cette idée à la 2D?

Donné a = [5 1 3; 1 1 2; 2 1 3] et b=[4 3; 1 2], est-il possible de convertir a dans une matrice de Toeplitz et de calculer le produit matrice-matrice entre T_a et b comme dans le cas 1-D?

32
no_name

Oui, c'est possible et vous devez également utiliser une matrice circulante à double bloc (qui est un cas particulier de la matrice Toeplitz ). Je vais vous donner un exemple avec une petite taille de noyau et l'entrée, mais il est possible de construire une matrice Toeplitz pour n'importe quel noyau. Vous avez donc une entrée 2d x et un noyau 2d k et vous voulez calculer la convolution x * k. Supposons également que k est déjà retourné. Supposons également que x ait la taille n×n et k est m×m.

Vous déroulez donc k dans une matrice clairsemée de taille (n-m+1)^2 × n^2, et déroulez x dans un long vecteur n^2 × 1. Vous calculez une multiplication de cette matrice clairsemée avec un vecteur et convertissez le vecteur résultant (qui aura une taille (n-m+1)^2 × 1) dans une n-m+1 Matrice Carrée.

Je suis presque sûr que c'est difficile à comprendre rien qu'en lisant. Voici donc un exemple pour le noyau 2 × 2 et l'entrée 3 × 3.

enter image description here * enter image description here

Voici une matrice construite avec un vecteur:

enter image description here

ce qui est égal à enter image description here .

Et c'est le même résultat que vous auriez obtenu en faisant une fenêtre coulissante de k sur x.

34
Salvador Dali

1- Définir l'entrée et le filtre

Soit [~ # ~] i [~ # ~] le signal d'entrée et [~ # ~] f [~ # ~] être le filtre ou le noyau.

2d input signal and filter

2- Calculez la taille de sortie finale

Si I est m1 x n1 et F est m2 x n2, la taille de la sortie sera:

enter image description here

3- Mettre à zéro la matrice du filtre

Mettez le filtre à zéro pour lui donner la même taille que la sortie.

enter image description here

4- Créer une matrice Toeplitz pour chaque ligne du filtre à remplissage nul

enter image description here

5- Créer une matrice Toeplitz doublement bloquée

Maintenant, toutes ces petites matrices de Toeplitz doivent être disposées dans une grande matrice de Toeplitz doublement bloquée. enter image description here

enter image description here

6- Convertir la matrice d'entrée en vecteur colonne

enter image description here

7- Multipliez la matrice toeplitz doublement bloquée avec un signal d'entrée vectorisé

Cette multiplication donne le résultat de la convolution.

8- Dernière étape: remodeler le résultat en une forme matricielle

enter image description here

Pour plus de détails et python jetez un œil à mon dépôt github:

Explication étape par étape de la convolution 2D implémentée comme multiplication matricielle en utilisant des matrices de toeplitz en python

10
Ali Salehi

Si vous démêlez k en un vecteur m ^ 2 et déroulez X, vous obtiendrez alors:

  • une m**2 vecteur k
  • une ((n-m)**2, m**2) matrice pour unrolled_X

unrolled_X peut être obtenu par le code Python code:

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

Dérouler X et non k permet une représentation plus compacte (matrices plus petites) que l'inverse pour chaque X - mais vous devez dérouler chaque X. Vous pourriez préférer dérouler k en fonction de ce que vous voulez faire.

Ici le unrolled_X n'est pas rare, alors que unrolled_k serait rare, mais de taille ((n-m+1)^2,n^2) comme l'a mentionné @Salvador Dali.

Dérouler k pourrait se faire comme ceci:

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

Le code ci-dessus ne produit pas la matrice déroulée des bonnes dimensions. La dimension doit être (n-k + 1) * (m-k + 1), (k) (k). k: dimension du filtre, n: num lignes dans la matrice d'entrée, m: num colonnes.

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

Pour plus de détails, consultez mon article de blog:

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

0
user8697906