Czy w CNN każdy nowy filtr ma różne wagi dla każdego kanału wejściowego, czy też te same wagi każdego filtra są używane w kanałach wejściowych?

28

Rozumiem, że warstwa splotowa splotowej sieci neuronowej ma cztery wymiary: kanały wejściowe, wysokość filtru, szerokość filtru, liczba filtrów. Co więcej, rozumiem, że każdy nowy filtr jest po prostu zawijany przez WSZYSTKIE kanały wejściowe (lub mapy funkcji / aktywacji z poprzedniej warstwy).

JEDNAK grafika poniżej z CS231 pokazuje, że każdy filtr (na czerwono) jest stosowany do POJEDYNCZEGO KANAŁU, a nie ten sam filtr stosowany w kanałach. To wydaje się wskazywać, że istnieje KAŻDY filtr dla KAŻDEGO kanału (w tym przypadku zakładam, że są to trzy kolorowe kanały obrazu wejściowego, ale to samo dotyczy wszystkich kanałów wejściowych).

Jest to mylące - czy dla każdego kanału wejściowego istnieje inny unikalny filtr?

wprowadź opis zdjęcia tutaj

Źródło: http://cs231n.github.io/convolutional-networks/

Powyższy obraz wydaje się być sprzeczny z fragmentem „Podstawy głębokiego uczenia się” O'reilly :

„... filtry nie działają tylko na jednej mapie obiektów. Działają na całej objętości map obiektów, które zostały wygenerowane na określonej warstwie ... W rezultacie mapy obiektów muszą być zdolne do działania na objętościach, nie tylko obszary ”

... Rozumiem również, że poniższe obrazy wskazują, że filtr SAME SAM jest po prostu splatany we wszystkich trzech kanałach wejściowych (sprzeczne z tym, co pokazano na powyższej grafice CS231):

wprowadź opis zdjęcia tutaj

wprowadź opis zdjęcia tutaj

Ryan Chase
źródło
1
arxiv.org/pdf/1707.09725 rozdział 2
Martin Thoma

Odpowiedzi:

13

Czy w splotowej sieci neuronowej istnieje unikalny filtr dla każdego kanału wejściowego, czy te same nowe filtry są stosowane we wszystkich kanałach wejściowych?

Były. W rzeczywistości dla każdej kombinacji kanałów wejściowych / kanałów wyjściowych zdefiniowano osobne jądro.

Zazwyczaj w architekturze CNN, w jednym filtrze, jak opisano w number_of_filtersparametrze, jest jedno jądro 2D na kanał wejściowy. Istnieją input_channels * number_of_filterszestawy wag, z których każdy opisuje jądro splotu. Tak więc schematy przedstawiające jeden zestaw wag na kanał wejściowy dla każdego filtra są prawidłowe. Pierwszy diagram pokazuje również wyraźnie, że wyniki zastosowania tych jąder są łączone przez ich sumowanie i dodawanie odchylenia dla każdego kanału wyjściowego.

Można to również postrzegać jako użycie splotu 3D dla każdego kanału wyjściowego, który ma taką samą głębokość jak wejście. To właśnie pokazuje twój drugi diagram, a także to, co wiele bibliotek zrobi wewnętrznie. Matematycznie jest to ten sam wynik (pod warunkiem, że głębokości dokładnie pasują), chociaż typ warstwy jest zazwyczaj oznaczony jako „Conv2D” lub podobny. Podobnie, jeśli typ wejścia jest z natury 3D, taki jak woksele lub wideo, możesz użyć warstwy „Conv3D”, ale wewnętrznie można ją zaimplementować jako splot 4D.

Neil Slater
źródło
dzięki za wyjaśnienie. Wygląda na to, że każdy filtr faktycznie ma wiele input_channelswersji o różnych wagach . Czy masz „oficjalne” źródło, które potwierdza to zrozumienie?
Ryan Chase
@RyanChase: Tak, to prawda. Chciałbym tylko wskazać wam kurs Andrew Ng na CNN - zaczynając od tego, jak przetwarzany będzie kolorowy obraz: coursera.org/learn/convolutional-neural-networks/lecture/ctQZz/...
Neil Slater
Chciałbym zauważyć, że w tym źródle ( cs231n.github.io/convolutional-networks ) filtry (wagi lub kernesl) są objętościami (tj. Trójwymiarowymi) i mają ten sam trzeci wymiar, który ma jeden z danych wejściowych Tom. Ponadto, jak to (co najmniej) teraz podano w tym źródle, objętości zostały podzielone na 3 wymiar w celu lepszej wizualizacji zastosowania filtra do objętości wejściowej. Nie sądzę, że ogólnie „dla każdej kombinacji kanałów wejściowych / wyjściowych zdefiniowano osobne jądro”. jest poprawne.
nbro
Zauważ, że filtry (lub jądra) to wagi, których należy się nauczyć (tj. Nie są ustalone, ale w rzeczywistości są parametrami CNN). Może się zdarzyć, że są one (tzn. Plastry filtra) na końcu takie same w 3. wymiarze.
nbro
@nbro: Tak, możesz zaimplementować splot 2D na wielu odcinkach 2D jako pojedynczy splot 3D z głębokością jądra taką samą jak liczba kanałów. Matematycznie jest to identyczne z moim opisem. Można go również postrzegać jako skróconą, w pełni połączoną sieć przesyłania dalej z dzielonymi wagami (z których wiele jest zerowych). Ta odpowiedź koncentruje się na widoku filtrów 2D, ponieważ OP pyta o sposób ułożenia filtrów 2D. W rzeczywistości można je ułożyć w większe jądro 3D, ale nadal są one stosowane jako jądra 2D przy użyciu „sztuczki”, jaką jest spasowanie 3D.
Neil Slater
12

Poniższy obraz użyty w pytaniu bardzo dokładnie opisuje, co się dzieje. Pamiętaj, że każdy element filtra 3D (szara kostka) składa się z innej wartości ( 3x3x3=27wartości). Tak więc można połączyć trzy różne filtry 2D o rozmiarze, 3x3tworząc jeden filtr 3D o rozmiarze 3x3x3.

convnet2D

Fragment 3x3x3RGB z obrazu jest mnożony elementarnie przez filtr 3D (pokazany jako szary). W takim przypadku filtr ma 3x3x3=27ciężary. Gdy te wagi zostaną pomnożone przez element, a następnie zsumowane, daje to jedną wartość.


Czy istnieje oddzielny filtr dla każdego kanału wejściowego?

TAK , na obrazie jest tyle filtrów 2D, ile jest kanałów wejściowych. Jest to jednak pomocne, jeśli uważasz, że dla matryc wejściowych z więcej niż jednym kanałem istnieje tylko jeden filtr 3D (jak pokazano na powyższym obrazku).


Dlaczego więc nazywa się to splotem 2D (jeśli filtr to 3D, a matryca wejściowa to 3D)?

Jest to splot 2D, ponieważ kroki filtra znajdują się tylko wzdłuż wymiarów wysokości i szerokości ( NIE głębokości), a zatem moc wyjściowa wytwarzana przez to splot jest również matrycą 2D. Liczba kierunków ruchu filtra determinuje wymiary splotu.

Uwaga: jeśli zbudujesz swoje zrozumienie, wizualizując pojedynczy filtr 3D zamiast wielu filtrów 2D (po jednym dla każdej warstwy), będziesz mieć łatwy czas na zrozumienie zaawansowanych architektur CNN, takich jak Resnet, InceptionV3 itp.

Mohsin Bukhari
źródło
jest to dobre wytłumaczenie, ale dokładniej pytanie, które próbuję zrozumieć, dotyczy tego, czy filtry, które działają na każdym kanale wejściowym, są kopiami tych samych wag, czy też zupełnie innymi wagami. W rzeczywistości nie jest to pokazane na obrazie i w rzeczywistości dla mnie ten obraz sugeruje, że jest to ta sama waga zastosowana do każdego kanału (ponieważ ma ten sam kolor) ... Na odpowiedź Slatera brzmi jak każda filtr faktycznie ma wiele input_channelswersji o różnych wagach. Jeśli tak rozumiesz, czy istnieje „oficjalne” źródło, które to potwierdza?
Ryan Chase
Tak, to też jest moje zrozumienie. Dla mnie było to jasne, kiedy próbowałem wymyślić, że szary sześcian składa się z 27 różnych wartości masy. Oznacza to, że istnieją 3 różne filtry 2D zamiast tego samego filtra 2D zastosowanego do każdej warstwy wejściowej.
Mohsin Bukhari
Nie mogłem znaleźć żadnego oficjalnego źródła potwierdzającego to. Jednak kiedy próbowałem owinąć głowę wokół tej samej koncepcji, stworzyłem fałszywy filtr wejściowy i filtr wagowy w Tensorflow i obserwowałem wynik. Byłem z tego zadowolony. Jeśli znajdę jakieś oficjalne wyjaśnienie. Zmienię swoją odpowiedź powyżej.
Mohsin Bukhari
Jeśli podążasz ścieżką Tensorflow. Możesz wydrukować filtr wagowy po pokazaniu fikcyjnej warstwie CNN próbki wejściowej.
Mohsin Bukhari
@Moshsin Bukhari Na pewno postaram się zbadać filtry w TensorFlow. Czy zechciałbyś udostępnić swój kod dotyczący tego, jak zająłeś się badaniem zawartości filtrów? Czy jesteś w stanie wydrukować wartości filtru na przykład na każdym kroku w sieci?
Ryan Chase
3

Podążam za powyższymi odpowiedziami konkretnym przykładem w nadziei, że wyjaśnię, jak działa splot w odniesieniu do kanałów wejściowych i wyjściowych oraz wag:

Niech przykład będzie następujący (wrt na 1 warstwę splotową):

  • tensor wejściowy to 9x9x5, tj. 5 kanałów wejściowych, więc input_channels=5
  • rozmiar filtra / jądra wynosi 4x4, a krok 1
  • tensor wyjściowy to 6x6x56, tj. 56 kanałów wyjściowych, więc output_channels=56
  • rodzaj wypełnienia to „WAŻNY” (tzn. brak wypełnienia)

Zauważamy, że:

  • ponieważ wejście ma 5 kanałów, wymiar filtra staje się 4x4x5, tj. istnieje 5 oddzielnych, unikalnych filtrów 2D o rozmiarze 4x4 (tzn. każdy ma 16 wag); w celu splotu na wejściu o rozmiarze 9x9x5 filtr staje się 3D i musi mieć rozmiar 4x4x5
  • dlatego: dla każdego kanału wejściowego istnieje odrębny filtr 2D z 16 różnymi wagami. Innymi słowy, liczba filtrów 2D odpowiada liczbie kanałów wejściowych
  • ponieważ istnieje 56 kanałów wyjściowych, musi istnieć 56 trójwymiarowych filtrów W0, W1, ..., W55 o rozmiarze 4x4x5 (por. na grafice CS231 są 2 3-wymiarowe filtry W0, W1, aby uwzględnić 2 wyjściowe parametry kanały), gdzie trzeci wymiar rozmiaru 5 reprezentuje łącze do 5 kanałów wejściowych (por. na grafice CS231 każdy filtr 3D W0, W1 ma trzeci wymiar 3, który odpowiada 3 kanałom wejściowym)
  • dlatego: liczba filtrów 3D jest równa liczbie kanałów wyjściowych

Ta warstwa splotowa zawiera zatem:

56 3-wymiarowych filtrów o rozmiarze 4x4x5 (= 80 różnych ciężarów każdy) w celu uwzględnienia 56 kanałów wyjściowych, z których każdy ma wartość dla trzeciego wymiaru 5, aby pasować do 5 kanałów wejściowych. W sumie są

number_of_filters=input_channel*output_channels=5*56=280

Filtry 2D o rozmiarze 4x4 (tj. Łącznie 280 x 16 różnych wag).

Lukas Z.
źródło
0

Istnieją tylko ograniczenia w 2D. Czemu?

Wyobraź sobie w pełni połączoną warstwę.

Byłoby strasznie ogromne, każdy neuron byłby podłączony do może neuronów wejściowych 1000 x 1000 x 3. Ale wiemy, że przetwarzanie pobliskiego piksela ma sens, dlatego ograniczamy się do małego sąsiedztwa 2D, więc każdy neuron jest połączony tylko z 3x3 w pobliżu neuronów w 2D. Nic nie wiemy o kanałach, więc łączymy się ze wszystkimi kanałami.

Nadal byłoby zbyt wiele obciążników. Ale z powodu niezmienności tłumaczenia, filtr działający dobrze w jednym obszarze jest najprawdopodobniej przydatny w innym obszarze. Dlatego używamy tego samego zestawu wag w 2D. Ponownie, nie ma takiej niezmienności tłumaczenia między kanałami, więc nie ma takiego ograniczenia.

maaartinus
źródło
0

Patrz sekcja „Łączność lokalna” w http://cs231n.github.io/convolutional-networks/ i slajd 7-18.

Hiperparametr „pola recepcyjnego” filtra jest definiowany tylko przez wysokość i szerokość, ponieważ głębokość jest ustalana przez głębokość poprzedniej warstwy.

UWAGA: „Zasięg łączności wzdłuż osi głębokości jest zawsze równy GŁĘBOKOŚCI objętości wejściowej” lub GŁĘBOKOŚCI mapy aktywacji (w przypadku późniejszych warstw).

Intuicyjnie musi to wynikać z faktu, że dane kanałów obrazu są przeplatane, a nie płaskie. W ten sposób zastosowanie filtra można osiągnąć po prostu przez pomnożenie wektorów kolumnowych.

UWAGA: Convolutional Network uczy się wszystkich parametrów filtra (w tym wymiaru głębokości) i są one sumą „h w input_layer_depth + 1 (bias)”.

sunil4data
źródło
0

Jako odpowiedź zalecam rozdział 2.2.1 mojej pracy magisterskiej . Aby dodać do pozostałych odpowiedzi:

Keras jest Twoim przyjacielem, aby zrozumieć, co się dzieje:

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())

daje

_________________________________________________________________
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

Spróbuj sformułować swoje opcje. Co to oznaczałoby dla parametrów, gdyby coś innego miało miejsce?

2400=32(3)55)

Takie podejście pomaga również w przypadku innych typów warstw, nie tylko warstw splotowych.

Pamiętaj również, że możesz dowolnie wdrażać różne rozwiązania, które mogą mieć inną liczbę parametrów.

Martin Thoma
źródło
0

Żeby dwa szczegóły były absolutnie jasne:

N.N.3)×3)N.2)N. warstw 2D, w jaki sposób są one łączone?

N.2)N.3)×3)×

Najprostszym sposobem, aby się o tym przekonać, jest wyobrażenie sobie, co dzieje się w innych scenariuszach i przekonanie się, że obliczenia ulegają degeneracji - to znaczy, jeśli nie przeplatasz i nie zrekombinujesz wyników, wówczas różne dane wyjściowe w rzeczywistości nic nie zrobią - one miałby taki sam efekt jak pojedyncze wyjście z połączonymi wagami.

użytkownik2465201
źródło
0

Dla każdego, kto próbuje zrozumieć, w jaki sposób obliczane są zwoje, oto przydatny fragment kodu w 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))

Uruchomienie tego daje następujące dane wyjściowe:

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])

Zauważ, jak sumuje się każdy kanał splotu na wszystkich wyjściach poprzednich kanałów.

Simon Alford
źródło