Aby zrozumieć VAE, najpierw zaczniemy od łatwej sieci i będziemy dodawać części krok po kroku.

Wspólnym sposobem opisu sieci neuronowej jest przybliżenie pewnej funkcji, którą chcemy modelować. Jednakże, będą one nawet postrzegane jako struktura wiedzy, która zawiera informacje.

http://kvfrans.com/content/images/2016/08/dat.jpg

Powiedzmy, że mieliśmy sieć składającą się z kilku warstw dekonwolucyjnych. Ustawiliśmy wejście, aby zawsze było wektorem tych warstw. Następnie przeszkolimy sieć do skalowania średniego błędu kwadratowego między sobą a jednym obrazem docelowym. Dane” dla tego obrazu są teraz zawarte w parametrach sieci.

Teraz spróbujmy tego na wielu obrazach. Zamiast wektora jednego z nich, użyjemy jedno-gorącego wektora dla wejścia. [1, 0, 0, 0] może oznaczać obraz kota, a [0, 1, 0, 0] może oznaczać psa. To działa, ale będziemy przechowywać tylko do 4 obrazów. Zastosowanie dłuższego wektora oznacza dodanie dodatkowych i więcej parametrów, dzięki czemu sieć może zapamiętywać różne obrazy.

Aby to naprawić, używamy wektora rzeczywistych liczb, a nie wektora jednokrotnego. Rozważymy to jako kod dla obrazu, od którego pochodzą pojęcia kodowania/dekodowania. Na przykład [3.3, 4.5, 2.1, 9.8] może reprezentować obrazek kota, natomiast [3.4, 2.1, 6.7, 4.2] może reprezentować psa. Ten pierwszy wektor jest rozumiany jako nasze ukryte zmienne.

http://kvfrans.com/content/images/2016/08/autoenc.jpg

Wybieranie utajonych zmiennych losowo, jak to zrobiłem powyżej, jest ewidentnie paskudnym pomysłem. W autoenkoderze dodajemy inny komponent, który bierze w obrębie oryginalnych obrazów i koduje je dla nas wektorem. Warstwy dekonwolucyjne następnie “dekodują” wektory z powrotem do pierwszych obrazów.

W końcu osiągnęliśmy etap, w którym nasz model ma pewne praktyczne zastosowanie. Będziemy szkolić naszą sieć na dowolnej liczbie obrazów. Jeśli zapiszemy zakodowany wektor obrazu, zrekonstruujemy go później, przekazując go do części dekodera. To co mamy, to standardowy autokoder.

Staramy się jednak stworzyć tu model generatywny, a nie tylko rozmyty układ, który “zapamięta” obrazy. Nie będziemy jeszcze niczego generować, ponieważ nie potrafimy zrobić ukrytych wektorów poza zakodowaniem ich z obrazów.

Jest tu proste rozwiązanie. Dodajemy ograniczenie na sieć kodującą, które zmusza ją do uzyskania ukrytych wektorów, które z grubsza podążają za jednostkowym rozkładem normalnym . to właśnie to ograniczenie oddziela zmienny autokoder od typowego.

Generowanie nowych obrazów jest teraz proste: wszystko, co chcielibyśmy spróbować, to spróbować ukrytego wektora z jednostki gaussian i przekazać go do dekodera.

W praktyce, istnieje kompromis między tym, jak dokładne są nasze sieci często i sposób zamknięcia jego utajone zmienne mogą dopasować się do jednostki rozkładu normalnego .

Pozwalamy, aby sieć sama o tym decydowała. Dla naszego terminu straty, podsumowujemy dwie oddzielne straty: stratę generatywną, która może być średnim błędem kwadratowym, który mierzy jak dokładnie sieć zrekonstruowała obrazy, oraz ukrytą stratę, która jest rozbieżnością KL, która mierzy jak ściśle ukryte zmienne pasują do jednostki gaussian.

generation_loss = średnia(kwadrat(generated_image – real_image))

latent_loss = KL-Divergence(latent_variable, unit_gaussian)

loss = generation_loss + latent_loss

http://kvfrans.com/content/images/2016/08/vae.jpg

Aby zoptymalizować dywergencję KL, chcielibyśmy użyć prostej sztuczki reparametryzacji: zamiast kodera generującego wektor rzeczywistych wartości, wygeneruje on wektor środków i wektor zwykłych odchyleń.

To pozwala nam obliczyć dywergencję KL w następujący sposób:

# z_mean i z_stddev są dwoma wektorami generowanymi przez sieć koderów

latent_loss = 0,5 * tf.reduce_sum(tf.square(z_mean) + tf.square(z_stddev) – tf.log(tf.square(z_stddev)) – 1,1)

Kiedy obliczamy straty dla sieci dekoderów, pobieramy tylko próbki z odchyleń jakościowych i dodajemy średnią, i używamy tego jako naszego ukrytego wektora:

sample = tf.random_normal([batchsize,n_z],0,1,dtype=tf.float32)

sampled_z = z_mean + (z_stddev * próbki)

Ograniczenie to nie tylko pozwala nam na uzyskiwanie losowych utajonych zmiennych, ale także poprawia uogólnienie naszej sieci.

Aby to zwizualizować, będziemy rozważać ukryte zmienne jako transfer wiedzy .

Powiedzmy, że masz kilka par rzeczywistych liczb pomiędzy [0, 10], wzdłuż boku reputację . na przykład , 5,43 oznacza jabłko, a 5,44 oznacza banana. Kiedy ktoś da ci kwotę 5,43, uznasz, że nie ma potrzeby mówić o jabłku. zasadniczo będziemy kodować nieskończoną ilość informacji w ten sposób, ponieważ nie ma ograniczenia co do tego, jaki procent różnych liczb rzeczywistych będziemy mieli pomiędzy [0, 10].

Co by było, gdyby za każdym razem, gdy ktoś próbował podać ci jakąś liczbę, dodawano odgłos gazy o 1? Teraz, gdy otrzymasz kwotę 5,43, pierwsza liczba może być wszędzie wokół [4,4 ~ 6,4], więc druga osoba mogła równie dobrze mieć na myśli banana (5,44).

Im większa różnica w dodawanych szumach, tym mniej informacji przekażemy za pomocą tej jednej zmiennej.

Teraz zastosujemy tę samą logikę do utajonej zmiennej przekazywanej pomiędzy enkoderem a dekoderem. Im efektywniej zakodujemy pierwszy obraz, tym wyżej podniesiemy odchylenie jakości na naszym gaussiu, aż do jego osiągnięcia.

To ograniczenie zmusza koder do bardzo wydajnego działania, tworząc bogate w informacje zmienne ukryte. Poprawia to uogólnienie, więc utajone zmienne, które albo wygenerowaliśmy losowo, albo otrzymaliśmy z kodowania nieszkolących się obrazów, będą dawały ładniejszy wynik podczas dekodowania.

Jak dobrze to działa?

http://kvfrans.com/content/images/2016/08/mnist.jpg

Przeprowadziłem kilka testów, aby upewnić się, jak dobrze autokoder wariantowy działałby na zbiorze danych pisma ręcznego MNIST.