Dans un CNN, chaque nouveau filtre a-t-il des poids différents pour chaque canal d'entrée, ou les mêmes poids de chaque filtre sont-ils utilisés sur les canaux d'entrée?

28

Ma compréhension est que la couche convolutionnelle d'un réseau neuronal convolutionnel a quatre dimensions: canaux d'entrée, hauteur de filtre, largeur de filtre, nombre de filtres. En outre, je crois comprendre que chaque nouveau filtre est simplement compliqué sur TOUS les canaux d'entrée (ou les cartes d'entité / d'activation de la couche précédente).

CEPENDANT, le graphique ci-dessous de CS231 montre chaque filtre (en rouge) appliqué à un seul canal, plutôt que le même filtre utilisé sur les canaux. Cela semble indiquer qu'il existe un filtre distinct pour CHAQUE canal (dans ce cas, je suppose que ce sont les trois canaux de couleur d'une image d'entrée, mais la même chose s'appliquerait à tous les canaux d'entrée).

C'est déroutant - existe-t-il un filtre unique différent pour chaque canal d'entrée?

entrez la description de l'image ici

Source: http://cs231n.github.io/convolutional-networks/

L'image ci-dessus semble contradictoire avec un extrait des "Fondamentaux du Deep Learning" d'O'reilly :

"... les filtres ne fonctionnent pas uniquement sur une seule carte d'entités. Ils fonctionnent sur l'intégralité du volume de cartes d'entités qui ont été générées sur une couche particulière ... Par conséquent, les cartes d'entités doivent pouvoir fonctionner sur des volumes, pas seulement des zones "

... De plus, je crois comprendre que ces images ci-dessous indiquent qu'un filtre LE MÊME est juste convolu sur les trois canaux d'entrée (contrairement à ce qui est illustré dans le graphique CS231 ci-dessus):

entrez la description de l'image ici

entrez la description de l'image ici

Ryan Chase
la source
1
arxiv.org/pdf/1707.09725 chapitre 2
Martin Thoma

Réponses:

13

Dans un réseau neuronal convolutif, existe-t-il un filtre unique pour chaque canal d'entrée ou les mêmes nouveaux filtres sont-ils utilisés sur tous les canaux d'entrée?

L'ancien. En fait, il existe un noyau distinct défini pour chaque combinaison canal d'entrée / canal de sortie.

Typiquement pour une architecture CNN, dans un seul filtre comme décrit par votre number_of_filtersparamètre, il y a un noyau 2D par canal d'entrée. Il existe des input_channels * number_of_filtersensembles de poids, chacun décrivant un noyau de convolution. Ainsi, les diagrammes montrant un ensemble de poids par canal d'entrée pour chaque filtre sont corrects. Le premier diagramme montre également clairement que les résultats de l'application de ces noyaux sont combinés en les additionnant et en ajoutant des biais pour chaque canal de sortie.

Cela peut également être considéré comme utilisant une convolution 3D pour chaque canal de sortie, qui se trouve avoir la même profondeur que l'entrée. C'est ce que montre votre deuxième diagramme, et aussi ce que de nombreuses bibliothèques feront en interne. Mathématiquement, c'est le même résultat (à condition que les profondeurs correspondent exactement), bien que le type de couche soit généralement appelé "Conv2D" ou similaire. De même, si votre type d'entrée est intrinsèquement 3D, comme des voxels ou une vidéo, alors vous pouvez utiliser une couche "Conv3D", mais en interne, il pourrait bien être implémenté comme une convolution 4D.

Neil Slater
la source
merci pour cette explication. Il semble que chaque filtre ait en fait un certain nombre de input_channelsversions avec des poids différents . Avez-vous une source "officielle" qui confirme cette compréhension?
Ryan Chase
@RyanChase: Oui, c'est correct. Je voudrais simplement vous indiquer le cours d'Andrew Ng sur les CNN - en commençant ici avec la façon dont l'image couleur serait traitée: coursera.org/learn/convolutional-neural-networks/lecture/ctQZz/…
Neil Slater
Je voudrais noter que, dans cette source ( cs231n.github.io/convolutional-networks ), les filtres (poids ou kernesl) sont des volumes (c'est-à-dire en 3 dimensions), et ils ont la même 3ème dimension a celle de l'entrée le volume. De plus, comme il est (au moins) maintenant indiqué dans cette source, les volumes ont été découpés à travers la 3ème dimension afin de mieux visualiser l'application du filtre au volume d'entrée. Je ne pense pas qu'en général, "il existe un noyau distinct défini pour chaque combinaison canal d'entrée / canal de sortie". est correct.
nbro
Notez que les filtres (ou noyaux) sont les poids qui doivent être appris (c'est-à-dire qu'ils ne sont pas fixes, mais ce sont en fait les paramètres du CNN). Il se peut qu'ils soient (c'est-à-dire les tranches du filtre), à ​​la fin, les mêmes à travers la 3ème dimension.
nbro
@nbro: Oui, vous pouvez implémenter une convolution 2D sur plusieurs tranches 2D en une seule convolution 3D avec une profondeur de noyau identique au nombre de canaux. Mathématiquement, c'est identique à ma description. Vous pouvez également le visualiser comme un réseau à action directe tronqué entièrement connecté avec des poids partagés (dont beaucoup sont nuls). Cette réponse se concentre sur la vue des filtres 2D, car l'OP demande comment les filtres 2D sont disposés. Ils peuvent en fait être organisés en un noyau 3D plus grand, mais ils sont toujours appliqués en tant que noyaux 2D en utilisant le "truc" que la convolution 3D est équivalente.
Neil Slater
12

L'image suivante que vous avez utilisée dans votre question décrit très précisément ce qui se passe. N'oubliez pas que chaque élément du filtre 3D (cube gris) est composé d'une valeur ( 3x3x3=27valeurs) différente. Ainsi, trois filtres 2D différents de taille 3x3peuvent être concaténés pour former ce seul filtre 3D de taille 3x3x3.

convnet2D

Le 3x3x3bloc RVB de l'image est multiplié par élément par un filtre 3D (représenté en gris). Dans ce cas, le filtre a des 3x3x3=27poids. Lorsque ces poids sont multipliés par élément puis additionnés, cela donne une valeur.


Alors, existe-t-il un filtre séparé pour chaque canal d'entrée?

OUI , il y a autant de filtres 2D que de canaux d'entrée dans l'image. Cependant , cela aide si vous pensez que pour les matrices d'entrée avec plus d'un canal, il n'y a qu'un seul filtre 3D (comme indiqué dans l'image ci-dessus).


Alors pourquoi cela s'appelle-t-il une convolution 2D (si le filtre est 3D et la matrice d'entrée est 3D)?

Il s'agit d'une convolution 2D car les enjambées du filtre se situent uniquement sur les dimensions en hauteur et en largeur ( PAS en profondeur) et, par conséquent, la sortie produite par cette convolution est également une matrice 2D. Le nombre de directions de mouvement du filtre détermine les dimensions de la convolution.

Remarque: Si vous développez votre compréhension en visualisant un seul filtre 3D au lieu de plusieurs filtres 2D (un pour chaque couche), alors vous aurez un temps facile à comprendre les architectures CNN avancées comme Resnet, InceptionV3, etc.

Mohsin Bukhari
la source
c'est une bonne explication, mais plus précisément la question que j'essaie de comprendre est de savoir si les filtres qui fonctionnent sur chaque canal d'entrée sont des copies des mêmes poids, ou des poids complètement différents. Ce n'est pas réellement montré dans l'image et en fait, pour moi, cette image suggère que ce sont les mêmes poids appliqués à chaque canal (car ils sont de la même couleur) ... Selon la réponse de @neil slater, cela ressemble à chacun le filtre a en fait un certain nombre de input_channelsversions avec des poids différents . Si c'est aussi votre compréhension, existe-t-il une source "officielle" qui le confirme?
Ryan Chase
Oui, en effet, c'est aussi ma compréhension. Pour moi, c'était clair quand j'ai essayé de penser à ce cube gris composé de 27 valeurs de poids différentes. Cela signifie qu'il y a 3 filtres 2D différents plutôt que le même filtre 2D appliqué à chaque couche d'entrée.
Mohsin Bukhari
Je n'ai trouvé aucune source officielle pour le confirmer. Cependant, lorsque j'essayais de comprendre ce même concept, j'ai créé une entrée factice et un filtre de poids dans Tensorflow et observé la sortie. J'étais content de ça. Si je trouve une explication officielle . Je modifierai ma réponse ci-dessus.
Mohsin Bukhari
Si vous suivez le chemin Tensorflow. Vous pouvez imprimer votre filtre de poids après avoir montré à votre couche CNN factice un échantillon d'entrée.
Mohsin Bukhari
@Moshsin Bukhari Je vais certainement essayer d'explorer les filtres de TensorFlow. Seriez-vous prêt à partager votre code pour savoir comment vous avez procédé pour explorer ce qui est contenu dans les filtres? Êtes-vous capable d'imprimer les valeurs du filtre à chaque étape du réseau par exemple?
Ryan Chase
3

Je poursuis les réponses ci-dessus avec un exemple concret dans l'espoir de clarifier davantage le fonctionnement de la convolution en ce qui concerne les canaux d'entrée et de sortie et les poids, respectivement:

Soit l'exemple comme suit (par rapport à 1 couche convolutionnelle):

  • le tenseur d'entrée est 9x9x5, soit 5 canaux d'entrée, donc input_channels=5
  • la taille du filtre / noyau est 4x4 et la foulée est 1
  • le tenseur de sortie est 6x6x56, soit 56 canaux de sortie, donc output_channels=56
  • le type de rembourrage est 'VALIDE' (c'est-à-dire sans rembourrage)

On remarque que:

  • comme l'entrée a 5 canaux, la dimension du filtre devient 4x4x5, c'est-à-dire qu'il y a 5 filtres 2D distincts et uniques de taille 4x4 (c'est-à-dire chacun a 16 poids); afin de convoluer sur l'entrée de taille 9x9x5 le filtre devient 3D et doit être de taille 4x4x5
  • par conséquent: pour chaque canal d'entrée, il existe un filtre 2D distinct avec 16 poids différents chacun. En d'autres termes, le nombre de filtres 2D correspond au nombre de canaux d'entrée
  • comme il y a 56 canaux de sortie, il doit y avoir 56 filtres tridimensionnels W0, W1, ..., W55 de taille 4x4x5 (cf. dans le graphique CS231 il y a 2 filtres tridimensionnels W0, W1 pour tenir compte des 2 sorties canaux), où la 3ème dimension de taille 5 représente le lien vers les 5 canaux d'entrée (cf. dans le graphique CS231 chaque filtre 3D W0, W1 a la 3ème dimension 3, qui correspond aux 3 canaux d'entrée)
  • donc: le nombre de filtres 3D est égal au nombre de canaux de sortie

Cette couche convolutive contient donc:

56 filtres tridimensionnels de taille 4x4x5 (= 80 poids différents chacun) pour tenir compte des 56 canaux de sortie où chacun a une valeur pour la 3e dimension de 5 pour correspondre aux 5 canaux d'entrée. Au total, il y a

number_of_filters=input_channel*output_channels=5*56=280

Filtres 2D de taille 4x4 (soit 280x16 poids différents au total).

Lukas Z.
la source
0

Il n'y a que des restrictions en 2D. Pourquoi?

Imaginez une couche entièrement connectée.

Ce serait terriblement énorme, chaque neurone serait connecté à peut-être 1000x1000x3 entrées de neurones. Mais nous savons que le traitement du pixel voisin est logique, nous nous limitons donc à un petit voisinage 2D, de sorte que chaque neurone n'est connecté qu'à 3x3 neurones proches en 2D. Nous ne savons rien de tel sur les canaux, nous nous connectons donc à tous les canaux.

Pourtant, il y aurait trop de poids. Mais en raison de l'invariance de translation, un filtre fonctionnant bien dans une zone est très probablement utile dans une zone différente. Nous utilisons donc le même ensemble de poids sur 2D. Encore une fois, il n'y a pas une telle invariance de traduction entre les canaux, donc il n'y a pas une telle restriction là-bas.

maaartinus
la source
0

Reportez-vous à la section "Connectivité locale" dans http://cs231n.github.io/convolutional-networks/ et à la diapositive 7-18.

L'hyperparamètre "Champ réceptif" du filtre est défini uniquement par la hauteur et la largeur, car la profondeur est fixée par la profondeur de la couche précédente.

NOTEZ que "L'étendue de la connectivité le long de l'axe de profondeur est toujours égale à la PROFONDEUR du volume d'entrée" -ou- PROFONDEUR de la carte d'activation (dans le cas de couches ultérieures).

Intuitivement, cela doit être dû au fait que les données des canaux d'image sont entrelacées et non planes. De cette façon, l'application du filtre peut être obtenue simplement par multiplication des vecteurs de colonne.

NOTEZ que Convolutional Network apprend tous les paramètres du filtre (y compris la dimension de profondeur) et qu'ils sont totaux "h w input_layer_depth + 1 (polarisation)".

sunil4data
la source
0

Je recommande le chapitre 2.2.1 de ma thèse de maîtrise comme réponse. Pour ajouter aux réponses restantes:

Keras est votre ami pour comprendre ce qui se passe:

from keras.models import Sequential
from keras.layers import Conv2D

model = Sequential()
model.add(Conv2D(32, input_shape=(28, 28, 3),
          kernel_size=(5, 5),
          padding='same',
          use_bias=False))
model.add(Conv2D(17, (3, 3), padding='same', use_bias=False))
model.add(Conv2D(13, (3, 3), padding='same', use_bias=False))
model.add(Conv2D(7, (3, 3), padding='same', use_bias=False))
model.compile(loss='categorical_crossentropy', optimizer='adam')

print(model.summary())

donne

_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_1 (Conv2D)            (None, 28, 28, 32)        2400      
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 28, 28, 17)        4896      
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 28, 28, 13)        1989      
_________________________________________________________________
conv2d_4 (Conv2D)            (None, 28, 28, 7)         819       
=================================================================
Total params: 10,104

Essayez de formuler vos options. Qu'est-ce que cela signifierait pour les paramètres si quelque chose d'autre était le cas?

2400=32(355)

Cette approche vous aide également avec d'autres types de couches, pas seulement les couches convolutives.

Veuillez également noter que vous êtes libre d'implémenter différentes solutions, qui peuvent avoir d'autres nombres de paramètres.

Martin Thoma
la source
0

Juste pour clarifier deux détails:

NN3×3N2N

N2N3×3×

Le moyen le plus simple de s'en convaincre est d'imaginer ce qui se passe dans d'autres scénarios et de voir que le calcul devient dégénéré - c'est-à-dire que si vous n'entrelacez pas et ne recombinez pas les résultats, alors les différentes sorties ne feraient rien du tout - elles aurait le même effet qu'une sortie unique avec des poids combinés.

user2465201
la source
0

Pour quiconque essaie de comprendre comment les convolutions sont calculées, voici un extrait de code utile dans Pytorch:

batch_size = 1
height = 3 
width = 3
conv1_in_channels = 2
conv1_out_channels = 2
conv2_out_channels = 2
kernel_size = 2
# (N, C_in, H, W) is shape of all tensors. (batch_size, channels, height, width)
input = torch.Tensor(np.arange(0, batch_size*height*width*in_channels).reshape(batch_size, in_channels, height, width))
conv1 = nn.Conv2d(in_channels, conv1_out_channels, kernel_size, bias=False) # no bias to make calculations easier
# set the weights of the convolutions to make the convolutions easier to follow
nn.init.constant_(conv1.weight[0][0], 0.25)
nn.init.constant_(conv1.weight[0][1], 0.5)
nn.init.constant_(conv1.weight[1][0], 1) 
nn.init.constant_(conv1.weight[1][1], 2) 
out1 = conv1(input) # compute the convolution

conv2 = nn.Conv2d(conv1_out_channels, conv2_out_channels, kernel_size, bias=False)
nn.init.constant_(conv2.weight[0][0], 0.25)
nn.init.constant_(conv2.weight[0][1], 0.5)
nn.init.constant_(conv2.weight[1][0], 1) 
nn.init.constant_(conv2.weight[1][1], 2) 
out2 = conv2(out1) # compute the convolution

for tensor, name in zip([input, conv1.weight, out1, conv2.weight, out2], ['input', 'conv1', 'out1', 'conv2', 'out2']):
    print('{}: {}'.format(name, tensor))
    print('{} shape: {}'.format(name, tensor.shape))

L'exécution de ceci donne la sortie suivante:

input: tensor([[[[ 0.,  1.,  2.],
          [ 3.,  4.,  5.],
          [ 6.,  7.,  8.]],

         [[ 9., 10., 11.],
          [12., 13., 14.],
          [15., 16., 17.]]]])
input shape: torch.Size([1, 2, 3, 3])
conv1: Parameter containing:
tensor([[[[0.2500, 0.2500],
          [0.2500, 0.2500]],

         [[0.5000, 0.5000],
          [0.5000, 0.5000]]],


        [[[1.0000, 1.0000],
          [1.0000, 1.0000]],

         [[2.0000, 2.0000],
          [2.0000, 2.0000]]]], requires_grad=True)
conv1 shape: torch.Size([2, 2, 2, 2])
out1: tensor([[[[ 24.,  27.],
          [ 33.,  36.]],

         [[ 96., 108.],
          [132., 144.]]]], grad_fn=<MkldnnConvolutionBackward>)
out1 shape: torch.Size([1, 2, 2, 2])
conv2: Parameter containing:
tensor([[[[0.2500, 0.2500],
          [0.2500, 0.2500]],

         [[0.5000, 0.5000],
          [0.5000, 0.5000]]],


        [[[1.0000, 1.0000],
          [1.0000, 1.0000]],

         [[2.0000, 2.0000],
          [2.0000, 2.0000]]]], requires_grad=True)
conv2 shape: torch.Size([2, 2, 2, 2])
out2: tensor([[[[ 270.]],

         [[1080.]]]], grad_fn=<MkldnnConvolutionBackward>)
out2 shape: torch.Size([1, 2, 1, 1])

Remarquez la somme de chaque canal de la convolution sur toutes les sorties des canaux précédents.

Simon Alford
la source