Autoenkoder w TensorFlow z przykładem

Co to jest autoenkoder w głębokim uczeniu się?

An Autokoder to narzędzie do efektywnej nauki kodowania danych w sposób bez nadzoru. Jest to rodzaj sztucznej sieci neuronowej, która pomaga nauczyć się reprezentacji zbiorów danych w celu redukcji wymiarowości poprzez uczenie sieci neuronowej ignorowania szumu sygnału. Jest to doskonałe narzędzie do odtwarzania danych wejściowych.

Krótko mówiąc, maszyna wykonuje, powiedzmy, obraz i może wyprodukować obraz ściśle powiązany. Dane wejściowe w tego rodzaju sieci neuronowej nie są oznakowane, co oznacza, że ​​sieć może się uczyć bez nadzoru. Mówiąc dokładniej, dane wejściowe są kodowane przez sieć w celu skupienia się tylko na najbardziej krytycznej funkcji. Jest to jeden z powodów, dla których autoenkoder jest popularny ze względu na redukcję wymiarowości. Poza tym do produkcji można używać autoenkoderów generatywne modele uczenia się. Na przykład sieć neuronową można trenować za pomocą zestawu twarzy, a następnie tworzyć nowe twarze.

Jak działa autoenkoder TensorFlow?

Celem autoenkodera jest uzyskanie przybliżenia danych wejściowych poprzez skupienie się tylko na istotnych cechach. Możesz pomyśleć, dlaczego nie po prostu nauczyć się kopiować i wklejać dane wejściowe, aby uzyskać wynik. W rzeczywistości autoenkoder to zestaw ograniczeń, które zmuszają sieć do uczenia się nowych sposobów reprezentowania danych, innych niż zwykłe kopiowanie danych wyjściowych.

Typowy autoenkoder ma wejście, reprezentację wewnętrzną i wyjście (przybliżenie sygnału wejściowego). Uczenie się odbywa się w warstwach dołączonych do reprezentacji wewnętrznej. W rzeczywistości istnieją dwa główne bloki warstw, które wyglądają jak tradycyjna sieć neuronowa. Niewielka różnica polega na tym, że warstwa zawierająca dane wyjściowe musi być równa wejściu. Na poniższym obrazku oryginalne wejście trafia do pierwszego bloku zwanego koder. Ta wewnętrzna reprezentacja kompresuje (zmniejsza) rozmiar danych wejściowych. W drugim bloku następuje rekonstrukcja wejścia. To jest faza dekodowania.

Działanie autoenkodera
Działanie autoenkodera

Działanie autoenkodera

Model zaktualizuje wagi poprzez minimalizację funkcji straty. Model jest karany, jeśli wynik rekonstrukcji różni się od danych wejściowych.

Konkretnie wyobraźmy sobie obraz o wymiarach 50×50 (tj. 250 pikseli) i sieć neuronową z tylko jedną ukrytą warstwą złożoną ze stu neuronów. Uczenie odbywa się na mapie cech, która jest dwa razy mniejsza niż dane wejściowe. Oznacza to, że sieć musi znaleźć sposób na zrekonstruowanie 250 pikseli przy użyciu jedynie wektora neuronów równego 100.

Przykład skumulowanego autoenkodera

W tym samouczku Autoencoder nauczysz się, jak używać autoencodera skumulowanego. Architektura jest podobna do tradycyjnej sieci neuronowej. Dane wejściowe trafiają do ukrytej warstwy, aby je skompresować lub zmniejszyć ich rozmiar, a następnie docierają do warstw rekonstrukcji. Celem jest wytworzenie obrazu wyjściowego tak bliskiego oryginałowi. Model musi nauczyć się sposobu wykonania swojego zadania przy zachowaniu zestawu ograniczeń, czyli przy niższym wymiarze.

Obecnie autoenkodery w głęboki Learning są używane głównie do odszumiania obrazu. Wyobraź sobie obraz z zadrapaniami; człowiek nadal jest w stanie rozpoznać treść. Pomysł odszumiania autoenkodera polega na dodaniu szumu do obrazu, aby zmusić sieć do poznania wzorca kryjącego się za danymi.

Inną użyteczną rodziną głębokiego uczenia się Autoenkodera jest autoenkoder wariacyjny. Ten typ sieci może generować nowe obrazy. Wyobraź sobie, że trenujesz sieć za pomocą obrazu mężczyzny; taka sieć może stworzyć nowe twarze.

Jak zbudować autoenkoder za pomocą TensorFlow

W tym samouczku dowiesz się, jak zbudować skumulowany autokoder w celu rekonstrukcji obrazu.

Użyjesz Zbiór danych CIFAR-10 który zawiera 60000 kolorowych obrazów 32×32. Zbiór danych Autoenkodera jest już podzielony na 50000 10000 obrazów do trenowania i XNUMX XNUMX do testowania. Istnieje aż dziesięć klas:

  • Samolot
  • Samochód
  • Ptak
  • Kot
  • Jeleń
  • Pies
  • żaba
  • Koń
  • Statek
  • Ciężarówka

Musisz pobrać obrazy z tego adresu URL https://www.cs.toronto.edu/~kriz/cifar.html i rozpakować je. Folder for-10-batches-py zawiera pięć partii danych po 10000 obrazów każda w losowej kolejności.

Zanim zbudujesz i wytrenujesz model, musisz zastosować przetwarzanie danych. Będziesz postępować w następujący sposób:

  1. Importuj dane
  2. Konwertuj dane do formatu czarno-białego
  3. Dołącz wszystkie partie
  4. Skonstruuj zbiór danych szkoleniowych
  5. Skonstruuj wizualizator obrazu

Wstępne przetwarzanie obrazu

Krok 1) Zaimportuj dane

Według oficjalnej strony internetowej, możesz przesłać dane za pomocą następującego kodu. Kod Autoencodera załaduje dane do słownika za pomocą dane i etykieta. Należy pamiętać, że kod jest funkcją.

import numpy as np
import tensorflow as tf
import pickle
def unpickle(file):
    import pickle
    with open(file, 'rb') as fo:
        dict = pickle.load(fo, encoding='latin1')
    return dict

Krok 2) Konwertuj dane na format czarno-biały

Dla uproszczenia skonwertujesz dane do skali szarości. Oznacza to, że tylko jeden wymiar w porównaniu z trzema dla obrazu kolorów. Większość sieci neuronowych działa tylko z wejściem jednego wymiaru.

def grayscale(im):
    return im.reshape(im.shape[0], 3, 32, 32).mean(1).reshape(im.shape[0], -1)

Krok 3) Dołącz wszystkie partie

Teraz, gdy obie funkcje są już utworzone i załadowany jest zbiór danych, możesz napisać pętlę, która dołączy dane do pamięci. Jeśli sprawdzisz dokładnie, rozpakowany plik z danymi będzie nosił nazwę data_batch_ z liczbą od 1 do 5. Możesz przeglądać pliki w pętli i dołączać je do danych.

Po wykonaniu tego kroku dane dotyczące kolorów zostaną przekonwertowane na format skali szarości. Jak widać, kształt danych wynosi 50000 i 1024. Piksele 32*32 są teraz spłaszczone do roku 2014.

# Load the data into memory
data, labels = [], []
## Loop over the b
for i in range(1, 6):
    filename = './cifar-10-batches-py/data_batch_' + str(i)
    open_data = unpickle(filename)
    if len(data) > 0:
        data = np.vstack((data, open_data['data']))
        labels = np.hstack((labels, open_data['labels']))
    else:
        data = open_data['data']
        labels = open_data['labels']

data = grayscale(data)
x = np.matrix(data)
y = np.array(labels)
print(x.shape)
(50000, 1024)

Uwaga: Zmień „./cifar-10-batches-py/data_batch_” na rzeczywistą lokalizację pliku. Na przykład dla Windows maszynie, ścieżka może mieć postać filename = 'E:\cifar-10-batches-py\data_batch_' + str(i)

Krok 4) Skonstruuj zbiór danych uczących

Aby trening był szybszy i łatwiejszy, będziesz trenował modela wyłącznie na obrazach koni. Konie stanowią siódmą klasę w danych na etykiecie. Jak wspomniano w dokumentacji zbioru danych CIFAR-10, każda klasa zawiera 5000 obrazów. Możesz wydrukować kształt danych, aby potwierdzić, że istnieje 5.000 obrazów z 1024 kolumnami, jak pokazano poniżej TensorFlow Przykładowy krok autoenkodera.

horse_i = np.where(y == 7)[0]
horse_x = x[horse_i]
print(np.shape(horse_x)) 
(5000, 1024)

Krok 5) Skonstruuj wizualizator obrazu

Na koniec konstruujesz funkcję, która wykreśla obrazy. Ta funkcja będzie potrzebna do wydrukowania zrekonstruowanego obrazu z autoenkodera.

Łatwym sposobem drukowania obrazów jest użycie obiektu imshow z biblioteki matplotlib. Pamiętaj, że musisz przekonwertować kształt danych z 1024 na 32*32 (tj. format obrazu).

# To plot pretty figures
%matplotlib inline
import matplotlib
import matplotlib.pyplot as plt
def plot_image(image, shape=[32, 32], cmap = "Greys_r"):
    plt.imshow(image.reshape(shape), cmap=cmap,interpolation="nearest")
    plt.axis("off")   

Funkcja przyjmuje 3 argumenty:

  • Obraz: wejście
  • Shape: lista, wymiar obrazu
  • Mapa:wybierz mapę kolorów. Domyślnie szary

Możesz spróbować wykreślić pierwszy obraz w zestawie danych. Powinieneś zobaczyć mężczyznę na koniu.

plot_image(horse_x[1], shape=[32, 32], cmap = "Greys_r")

Skonstruuj wizualizator obrazu

Ustaw estymator zbioru danych

W porządku, teraz, gdy zbiór danych jest gotowy do użycia, możesz zacząć korzystać z Tensorflow. Przed zbudowaniem modelu użyjmy estymatora zestawu danych Tensorflow do zasilania sieci.

Zbudujesz zbiór danych za pomocą estymatora TensorFlow. Aby odświeżyć umysł, musisz użyć:

  • from_tensor_slices
  • powtarzać
  • partia

Pełny kod do zbudowania zbioru danych to:

dataset = tf.data.Dataset.from_tensor_slices(x).repeat().batch(batch_size)

Należy pamiętać, że x jest symbolem zastępczym o następującym kształcie:

  • [Brak, n_wejść]: Ustaw na Brak, ponieważ liczba obrazów przesyłanych do sieci jest równa wielkości partii.

Aby uzyskać szczegółowe informacje, zapoznaj się z samouczkiem regresja liniowa.

Następnie musisz utworzyć iterator. Bez tego wiersza kodu żadne dane nie zostaną przesłane przez potok.

iter = dataset.make_initializable_iterator() # create the iteratorfeatures = iter.get_next()

Teraz, gdy rurociąg jest już gotowy, możesz sprawdzić, czy pierwszy obraz jest taki sam jak poprzednio (tj. mężczyzna na koniu).

Ustawiłeś rozmiar partii na 1, ponieważ chcesz zasilić zestaw danych tylko jednym obrazem. Możesz zobaczyć wymiary danych za pomocą print(sess.run(features).shape). Jest on równy (1, 1024). 1 oznacza, że ​​tylko jeden obraz z 1024 jest zasilony. Jeśli rozmiar partii jest ustawiony na dwa, to przez potok przejdą dwa obrazy. (Nie zmieniaj rozmiaru partii. W przeciwnym razie zostanie zgłoszony błąd. Tylko jeden obraz na raz może trafić do funkcji plot_image().

## Parameters
n_inputs = 32 * 32
BATCH_SIZE = 1
batch_size = tf.placeholder(tf.int64)

# using a placeholder
x = tf.placeholder(tf.float32, shape=[None,n_inputs])
## Dataset
dataset = tf.data.Dataset.from_tensor_slices(x).repeat().batch(batch_size)
iter = dataset.make_initializable_iterator() # create the iterator
features = iter.get_next()

## Print the image
with tf.Session() as sess:
    # feed the placeholder with data
    sess.run(iter.initializer, feed_dict={x: horse_x,
                                         batch_size: BATCH_SIZE}) 
    print(sess.run(features).shape) 
    plot_image(sess.run(features), shape=[32, 32], cmap = "Greys_r")
(1, 1024)

Ustaw estymator zbioru danych

Zbuduj sieć

Nadszedł czas na budowę sieci. Będziesz trenować skumulowany autoenkoder, czyli sieć z wieloma ukrytymi warstwami.

Twoja sieć będzie miała jedną warstwę wejściową zawierającą 1024 punkty, tj. kształt obrazu 32×32.

Blok kodera będzie miał jedną górną warstwę ukrytą zawierającą 300 neuronów i warstwę centralną zawierającą 150 neuronów. Blok dekodera jest symetryczny względem kodera. Sieć można zwizualizować na poniższym obrazku. Pamiętaj, że możesz zmienić wartości warstw ukrytych i środkowych.

Zbuduj sieć
Budowa sieci dla Autoenkodera

Budowanie autoenkodera jest bardzo podobne do każdego innego modelu głębokiego uczenia się.

Model zbudujesz, wykonując następujące kroki:

  1. Zdefiniuj parametry
  2. Zdefiniuj warstwy
  3. Zdefiniuj architekturę
  4. Zdefiniuj optymalizację
  5. Uruchom model
  6. Oceń model

W poprzedniej sekcji nauczyłeś się, jak utworzyć potok zasilający model, więc nie ma potrzeby ponownego tworzenia zbioru danych. Skonstruujesz autoenkoder z czterema warstwami. Używasz inicjalizacji Xaviera. Jest to technika polegająca na ustaleniu początkowych wag równych wariancji zarówno danych wejściowych, jak i wyjściowych. Na koniec użyj funkcji aktywacji elu. Regularyzujesz funkcję straty za pomocą regularyzatora L2.

Krok 1) Zdefiniuj parametry

Pierwszy krok polega na zdefiniowaniu liczby neuronów w każdej warstwie, szybkości uczenia się i hiperparametru regularyzatora.

Przedtem importujesz funkcję częściowo. Jest to lepsza metoda definiowania parametrów gęstych warstw. Poniższy kod definiuje wartości architektury autoenkodera. Jak wspomniano wcześniej, autoenkoder ma dwie warstwy, z 300 neuronami w pierwszej warstwie i 150 w drugiej warstwie. Ich wartości są przechowywane w n_hidden_1 i n_hidden_2.

Musisz zdefiniować szybkość uczenia się i hiperparametr L2. Wartości są przechowywane w learning_rate i l2_reg

from functools import partial

## Encoder
n_hidden_1 = 300
n_hidden_2 = 150  # codings

## Decoder
n_hidden_3 = n_hidden_1
n_outputs = n_inputs

learning_rate = 0.01
l2_reg = 0.0001

Technika inicjalizacji Xaviera jest wywoływana z obiektem xavier_initializer z estymatora contrib. W tym samym estymatorze możesz dodać regularyzator za pomocą l2_regularizer

## Define the Xavier initialization
xav_init =  tf.contrib.layers.xavier_initializer()
## Define the L2 regularizer
l2_regularizer = tf.contrib.layers.l2_regularizer(l2_reg)

Krok 2) Zdefiniuj warstwy

Ustawiono wszystkie parametry gęstych warstw; możesz spakować wszystko do zmiennej gęstej_warstwy, używając obiektu częściowego. gęsty_warstwa, który wykorzystuje aktywację ELU, inicjalizację Xaviera i regularyzację L2.

## Create the dense layer
dense_layer = partial(tf.layers.dense,
                         activation=tf.nn.elu,
                         kernel_initializer=xav_init,
                         kernel_regularizer=l2_regularizer)

Krok 3) Zdefiniuj architekturę

Jeśli spojrzysz na zdjęcie architektury, zauważysz, że sieć układa trzy warstwy z warstwą wyjściową. W poniższym kodzie łączysz odpowiednie warstwy. Na przykład pierwsza warstwa oblicza iloczyn skalarny między cechami macierzy wejściowych i macierzami zawierającymi 300 wag. Po obliczeniu iloczynu skalarnego dane wyjściowe trafiają do funkcji aktywacji Elu. Dane wyjściowe stają się danymi wejściowymi następnej warstwy, dlatego używasz ich do obliczenia hidden_2 itd. Mnożenie macierzy jest takie samo dla każdej warstwy, ponieważ używasz tej samej funkcji aktywacji. Zauważ, że ostatnia warstwa, wyjściowa, nie stosuje funkcji aktywacji. Ma to sens, ponieważ jest to zrekonstruowane dane wejściowe.

## Make the mat mul
hidden_1 = dense_layer(features, n_hidden_1)
hidden_2 = dense_layer(hidden_1, n_hidden_2)
hidden_3 = dense_layer(hidden_2, n_hidden_3)
outputs = dense_layer(hidden_3, n_outputs, activation=None)

Krok 4) Zdefiniuj optymalizację

Ostatnim krokiem jest skonstruowanie optymalizatora. Używasz błędu średniokwadratowego jako funkcji straty. Jeśli pamiętasz samouczek dotyczący regresji liniowej, wiesz, że MSE jest obliczane na podstawie różnicy między przewidywanym wynikiem a rzeczywistą etykietą. W tym przypadku etykieta jest cechą, ponieważ model próbuje zrekonstruować dane wejściowe. Dlatego potrzebna jest średnia sumy różnicy kwadratów między przewidywanymi wynikami i danymi wejściowymi. Za pomocą TensorFlow możesz zakodować funkcję straty w następujący sposób:

loss = tf.reduce_mean(tf.square(outputs - features))

Następnie należy zoptymalizować funkcję straty. Do obliczania gradientów używasz optymalizatora Adama. Funkcja celu to minimalizacja straty.

## Optimize
loss = tf.reduce_mean(tf.square(outputs - features))
optimizer = tf.train.AdamOptimizer(learning_rate)
train  = optimizer.minimize(loss)

Jeszcze jedno ustawienie przed trenowaniem modelu. Chcesz użyć partii o rozmiarze 150, czyli zasilić potok 150 obrazami w każdej iteracji. Należy ręcznie obliczyć liczbę iteracji. Jest to banalne:

Jeśli chcesz przekazać 150 obrazów za każdym razem i wiesz, że w zestawie danych jest 5000 obrazów, liczba iteracji jest równa . W Pythonie możesz uruchomić następujące kody i upewnić się, że wynik wynosi 33:

BATCH_SIZE = 150
### Number of batches :  length dataset / batch size
n_batches = horse_x.shape[0] // BATCH_SIZE
print(n_batches)
33

Krok 5) Uruchom model

Na koniec należy wytrenować model. Trenujesz model za pomocą 100 epok. Oznacza to, że model zobaczy 100 razy więcej obrazów przy zoptymalizowanych wagach.

Znasz już kody do uczenia modelu w Tensorflow. Niewielka różnica polega na przesłaniu danych przed uruchomieniem szkolenia. W ten sposób model trenuje szybciej.

Interesuje Cię wydrukowanie straty po dziesięciu epokach, aby sprawdzić, czy model się czegoś uczy (tj. strata maleje). Szkolenie trwa od 2 do 5 minut, w zależności od sprzętu maszyny.

## Set params
n_epochs = 100

## Call Saver to save the model and re-use it later during evaluation
saver = tf.train.Saver()

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    # initialise iterator with train data
    sess.run(iter.initializer, feed_dict={x: horse_x,
                                          batch_size: BATCH_SIZE})
    print('Training...')
    print(sess.run(features).shape) 
    for epoch in range(n_epochs):       
        for iteration in range(n_batches):
            sess.run(train)
        if epoch % 10 == 0:
            loss_train = loss.eval()   # not shown
            print("\r{}".format(epoch), "Train MSE:", loss_train) 
        #saver.save(sess, "./my_model_all_layers.ckpt") 
    save_path = saver.save(sess, "./model.ckpt")    
    print("Model saved in path: %s" % save_path)  
Training...
(150, 1024)
0 Train MSE: 2934.455
10 Train MSE: 1672.676
20 Train MSE: 1514.709
30 Train MSE: 1404.3118
40 Train MSE: 1425.058
50 Train MSE: 1479.0631
60 Train MSE: 1609.5259
70 Train MSE: 1482.3223
80 Train MSE: 1445.7035
90 Train MSE: 1453.8597
Model saved in path: ./model.ckpt

Krok 6) Oceń model

Teraz, gdy masz już wyszkolony model, czas go ocenić. Musisz zaimportować zestaw testowy z pliku /cifar-10-batches-py/.

test_data = unpickle('./cifar-10-batches-py/test_batch')
test_x = grayscale(test_data['data'])
#test_labels = np.array(test_data['labels'])

UWAGA: Dla Windows maszynie, kod staje się test_data = unpickle(r”E:\cifar-10-batches-py\test_batch”)

Możesz spróbować wydrukować obrazki 13, które przedstawiają konia

plot_image(test_x[13], shape=[32, 32], cmap = "Greys_r")

Oceń model

Aby ocenić model, użyjesz wartości pikseli tego obrazu i sprawdzisz, czy koder może zrekonstruować ten sam obraz po zmniejszeniu o 1024 piksele. Pamiętaj, że definiujesz funkcję do oceny modelu na różnych obrazach. Model powinien lepiej działać tylko na koniach.

Funkcja przyjmuje dwa argumenty:

  • df: Importuj dane testowe
  • numer_obrazu: wskaż, który obraz ma zostać zaimportowany

Funkcja jest podzielona na trzy części:

  1. Zmień kształt obrazu do prawidłowego wymiaru, tj. 1, 1024
  2. Nakarm model niewidzialnym obrazem, zakoduj/dekoduj obraz
  3. Wydrukuj obraz rzeczywisty i zrekonstruowany
def reconstruct_image(df, image_number = 1):
    ## Part 1: Reshape the image to the correct dimension i.e 1, 1024
    x_test = df[image_number]
    x_test_1 = x_test.reshape((1, 32*32))
    
    ## Part 2: Feed the model with the unseen image, encode/decode the image
    with tf.Session() as sess:     
        sess.run(tf.global_variables_initializer()) 
        sess.run(iter.initializer, feed_dict={x: x_test_1,
                                      batch_size: 1})
    ## Part 3:  Print the real and reconstructed image
      # Restore variables from disk.
        saver.restore(sess, "./model.ckpt")  
        print("Model restored.")
      # Reconstruct image
        outputs_val = outputs.eval()
        print(outputs_val.shape)
        fig = plt.figure()
      # Plot real
        ax1 = fig.add_subplot(121)
        plot_image(x_test_1, shape=[32, 32], cmap = "Greys_r")
      # Plot estimated
        ax2 = fig.add_subplot(122)
        plot_image(outputs_val, shape=[32, 32], cmap = "Greys_r")
        plt.tight_layout()
        fig = plt.gcf()

Teraz, gdy funkcja oceny jest zdefiniowana, możesz przyjrzeć się zrekonstruowanemu obrazowi numer trzynaście

reconstruct_image(df =test_x, image_number = 13)
INFO:tensorflow:Restoring parameters from ./model.ckpt
Model restored.
(1, 1024)

Oceń model

Podsumowanie

  • Podstawowym celem autoenkodera jest kompresja danych wejściowych, a następnie dekompresja ich do postaci wyjściowej, która wygląda bardzo podobnie do oryginalnych danych.
  • Architektura autoenkodera jest symetryczna i posiada warstwę bazową zwaną warstwą centralną.
  • Możesz utworzyć autoenkoder za pomocą:
  • Częściowa: aby utworzyć gęste warstwy przy typowym ustawieniu:

      	tf.layers.dense,                         
      	activation=tf.nn.elu,                         
      	kernel_initializer=xav_init,                         
      	kernel_regularizer=l2_regularizer

    gęsta_warstwa(): aby wykonać mnożenie macierzy

  • Funkcję straty i optymalizację można zdefiniować za pomocą:
  • loss = tf.reduce_mean(tf.square(outputs - features))
    optimizer = tf.train.AdamOptimizer(learning_rate)
    train  = optimizer.minimize(loss)
    
  • Ostatnie uruchomienie sesji w celu wytrenowania modelu.