Jak wdrażany jest Przestrzenny rezygnacja z 2D?

14

Odnosi się to do artykułu Efektywna lokalizacja obiektu za pomocą sieci konwergentnych i z tego, co rozumiem, rezygnacja jest realizowana w 2D.

Po odczytaniu kodu z Keras o tym, jak zaimplementowano Upadek przestrzenny 2D, w zasadzie implementowana jest losowa binarna maska ​​kształtu [batch_size, 1, 1, num_channels]. Co jednak dokładnie robi to przestrzenne opuszczanie 2D dla wejściowego bloku splotu kształtu [rozmiar_wsady, wysokość, szerokość, kanały_wierszy]?

Moje obecne przypuszczenie jest takie, że dla każdego piksela, jeśli którakolwiek z warstw / kanałów piksela ma wartość ujemną, całe kanały tego jednego piksela zostaną domyślnie ustawione na zero. Czy to jest poprawne?

Jeśli jednak moje przypuszczenie jest słuszne, to w jaki sposób użycie binarnej maski kształtu [rozmiar partii, wysokość, szerokość, liczba kanałów], które są dokładnie w wymiarze oryginalnego bloku wejściowego, daje zwykłe pominięcie elementu (jest to zgodne z oryginalna implementacja tensorflow, która ustawia kształt maski binarnej jako kształt danych wejściowych)? Ponieważ oznaczałoby to, że jeśli dowolny piksel w bloku konwekcyjnym ma wartość ujemną, wówczas cały blok konwekcyjny zostanie domyślnie ustawiony na 0. Jest to myląca część, której nie do końca rozumiem.

infomin101
źródło

Odpowiedzi:

14

Ta odpowiedź jest nieco spóźniona, ale sam musiałem się tym zająć i pomyślałem, że to może pomóc.

Patrząc na dokument, wydaje się, że w Spatial Dropout losowo ustawiamy całe mapy obiektów (znane również jako kanały) na 0, a nie poszczególne „piksele”.

To, co mówią, ma sens, że zwykłe usuwanie nie działałoby tak dobrze na obrazach, ponieważ sąsiednie piksele są silnie skorelowane. Więc jeśli ukryjesz piksele losowo, nadal mogę mieć dobry pomysł, po prostu patrząc na sąsiednie piksele. Usunięcie całych map obiektów może być lepiej dostosowane do pierwotnego zamiaru rezygnacji.

Oto funkcja, która implementuje go w Tensorflow, na podstawie tf.nn.dropout. Jedyną prawdziwą zmianą w porównaniu z tf.nn.dropout jest to, że kształt naszej maski upuszczania to BatchSize * 1 * 1 * NumFeatureMaps, w przeciwieństwie do BatchSize * Szerokość * Wysokość * NumFeatureMaps

def spatial_dropout(x, keep_prob, seed=1234):
    # x is a convnet activation with shape BxWxHxF where F is the 
    # number of feature maps for that layer
    # keep_prob is the proportion of feature maps we want to keep

    # get the batch size and number of feature maps
    num_feature_maps = [tf.shape(x)[0], tf.shape(x)[3]]

    # get some uniform noise between keep_prob and 1 + keep_prob
    random_tensor = keep_prob
    random_tensor += tf.random_uniform(num_feature_maps,
                                       seed=seed,
                                       dtype=x.dtype)

    # if we take the floor of this, we get a binary matrix where
    # (1-keep_prob)% of the values are 0 and the rest are 1
    binary_tensor = tf.floor(random_tensor)

    # Reshape to multiply our feature maps by this tensor correctly
    binary_tensor = tf.reshape(binary_tensor, 
                               [-1, 1, 1, tf.shape(x)[3]])
    # Zero out feature maps where appropriate; scale up to compensate
    ret = tf.div(x, keep_prob) * binary_tensor
    return ret

Mam nadzieję, że to pomaga!

nlml
źródło
3

Moje obecne przypuszczenie jest takie, że dla każdego piksela, jeśli którakolwiek z warstw / kanałów piksela ma wartość ujemną, całe kanały tego jednego piksela zostaną domyślnie ustawione na zero. Czy to jest poprawne?

Nie jestem do końca pewien, co masz na myśli, ale rezygnacja występuje niezależnie od innych wartości niż losowo narysowane dla maski rezygnacji. Na to, że na upuszczenie nie mają wpływu wartości pikseli , wagi filtrów ani wartości mapy obiektów. Jeśli użyjesz maski rozmiaru [batch_size, 1, 1, num_channels], otrzymasz maskę binarną o tym rozmiarze podczas rezygnacji. Zera w tej masce binarnej występują z prawdopodobieństwem rate(przynajmniej w implementacji Keras, pierwszy argument do Dropoutwarstwy). Ta maska ​​jest następnie mnożona przez mapy obiektów, więc dowolny rozmiar maski ma rozmiar 1 - ten wymiar maski jest nadawany w celu dopasowania do kształtu mapy obiektów.
Wyobraź sobie prostszą sytuację - załóżmy, że masz mapy obiektów o rozmiarze [height, num_channels](na razie zignorujmy rozmiar partii), a wartości map obiektów to:

print(feature_maps)

[[2 1 4]
 [1 3 2]
 [5 2 6]
 [2 2 1]]

print(feature_maps.shape)

(4, 3)

Następnie wyobraź sobie binarną maskę porzucania [1, num_channels], taką jak ta:

print(dropout_mask)

[[0 1 0]]

print(dropout_mask.shape)

(1, 3)

Teraz zwróć uwagę na to, co się stanie, gdy pomnożysz feature_mapsi dropout_mask:

print(feature_maps * dropout_mask)

[[0 1 0]
 [0 3 0]
 [0 2 0]
 [0 2 0]]

Nadawano wartości w dropout_maskcelu dopasowania wysokości każdej mapy obiektów, a następnie przeprowadzono mnożenie element po elemencie. W rezultacie wszystkie mapy obiektów zostały wyzerowane - i dokładnie to robi zrzut przestrzenny.

mmagnuski
źródło