Konvolutivne neuronske mreže¶

Preporučujem da pogledate sledeće snimke:
https://www.youtube.com/watch?v=KuXjwB4LzSA
https://www.youtube.com/watch?v=YRhxdVk_sIs

Delovi materijala su preuzeti iz skripte "Mašinsko učenje" profesora Mladena Nikolića

Zasnivaju se na sposobnosti mreža da konstruišu atribute, ali ne iz već datih atributa, već prvenstveno iz sirovog signala. Nazivaju se konvolutivnim zato što uče filtere, čijom konvolutivnom primenom detektuju određena svojstva signala. U obradi signala su dugo korišćeni filteri dizajnirani od strane inženjera i istraživača (npr. poput filtera za detekciju ivica na slikama). Značaj konvolutivnih mreža je upravo u tome što ne zahtevaju ljudski angažman u definisanju relevantnih svojstava signala, koji verovatno i ne bi proizveo najbolje rezultate, već u zavisnosti od problema koji je potrebno rešiti, same ustanovljavaju koja su svojstva bitna, kroz učenje adekvatnih filtera. Ipak, ovakva vrsta fleksibilnosti podrazumeva izazove pri optimizaciji, kao i veliku količinu podataka.
Ovaj tip mreža pronalazi široku primenu u obradi raznih vrsta signala, naročito slika, pa ćemo ih opisati na takvom primeru. Struktura konvolutivnih mreža obično podrazumeva smenjivanje dva tipa slojeva - konvolutivnog sloja (eng. convolutional layer) i sloja agregacije (eng. pooling layer), pri čemu je moguće i da se ista vrsta sloja ponovi više puta. Konvolutivni sloj sadrži više filtriranih slika koje su nastale procesom konvolucije polazne slike i tzv. filtera, kako bi se izdvojili upečatljivi oblici na slikama poput ivica objekata. Konvolucija podrazumeva pomeranje matrice - filtera duž polazne slike, pri čemu se u svakom pomeraju računa konvolucija odgovarajućeg bloka polazne matrice i filtera. Izlazi konvolutivnog sloja se transformišu nelinearnom aktivacionom funkcijom. Rezultat konvolucije je filtrirana slika manje dimenzije (matrica sa vrednostima prethodno izračunatih konvolucija). Podsetimo se definicije konvolucije:
Neka su $f$ i $g$ dve matrice dimenzija $m \times n$ i $p \times q$. Konvolucija ovih matrica, u oznaci $f\ast g$ je matrica definisana sa: $$(f \ast g)_{ij} = \sum_{k=0}^{p-1}\sum_{l=0}^{q-1}f_{i-k,j-l}g_{k,l} $$ U našem slučaju, $f$ je ulazna matrica podataka, a $g$ je filter.

slika1.png

Kao što vidimo, konvolucijom se smanjuje dimenzija podataka, tj. gube se atributi. Da bismo to izbegli, možemo proširiti matricu nulama. Bolji način je proširivanje matrice vrednostima koje se već nalaze na obodu. Takođe, prilikom računanja konvolucije, filter se duž slike ne mora pomerati za jedan piksel, već za neki korak.

Na filtriranim slikama u nižim slojevima mreže se prepoznaju jednostavniji oblici, dok se u višim slojevima mogu razaznati i neke složenije konture koje sa nalaze na polaznoj slici. slika2.png

U prethodnim razmatranjima nije diskutovano da li filter deluje samo nad tačno jednim kanalom prethodnog sloja ili nad više. U praksi se implementiraju višekanalne operacije konvolucije koje omogućavaju da filter deluje nad više kanala iz prethodnog sloja odjednom. Ovo je ilustrovano sledećom slikom. slika6.png

Sledeći sloj koji smo pomenuli je sloj agregacije. Sloj agregacije ukrupnjuje informacije koje dobija iz prethodnog sloja, obično tako što računa neku jednostavnu funkciju agregacije susednih jedinica prethodnog sloja, poput maksimuma ili proseka. Ukoliko agregira, na primer, $3 \times 3$ piksela, onda je broj izlaza ovog sloja 9 puta manji od broja izlaza prethodnog. Maksimum je najpopularniji izbor funkcije agregacije. Ukoliko $3 \times 3$ jedinice prethodnog sloja predstavljaju ulaz u jednu jedinicu sloja agregacije i ako ona računa njihov maksimum, dolazi do zanemarivanje informacije o tome gde je precizno neko svojstvo pronađeno, ali se ne gubi informacija da je pronađeno. Ovakva vrsta zanemarivanja informacije često ne šteti cilju koji treba postići. Na primer, ako su na slici pronađeni oko, uvo, usta i nos, informacija o tačnoj poziciji najverovatnije nije bitna za odlučivanje da li se na slici nalazi lice. Naime, u realnosti, šanse da slika koja sadrži navedene elemente ne predstavlja lice su izuzetno male. Ipak, ukoliko je potrebno napraviti mrežu koja igra igru u kojoj su pozicije objekata na ekranu bitne, nije poželjno koristiti agregaciju. Uloga agregacije je smanjenje broja računskih operacija u višim slojevima, a i smanjenje broja parametara (koji može biti ogroman) u potpuno povezanoj mreži koja sledi za slojevima konvolucije i agregacije. Sve to rezultuje smanjenjem računske zahtevnosti pri optimizaciji i smanjenjem fleksibilnosti modela.

slika3.png

Broj slojeva i dimenzije filtera su takođe hiperparametri.
Kao što smo rekli, na izlaze poslednjeg od tih slojeva se obično nadovezuje potpuno povezana mreža, koja uči nad atributima koje prethodni slojevi konstruišu. To je prikazano na sledećoj slici.
slika4.png

Kvaliteti i mane¶

Konvolutivne neuronske mreže su vrhunski model za obradu signala mašinskim učenjem – slika, zvuka i vremenskih serija. Imaju nekoliko glavnih kvaliteta koji su zaslužni za njihove dobre performanse.
Prvi su proređene interakcije, pod čime se podrazumeva da je svaka jedinica povezana samo sa malim brojem jedinica iz prethodnog sloja, umesto sa svim, kao što u slučaju potpuno povezanih mreža.
Drugi je deljenje parametara, koje se odnosi na to da sve jedinice jednog kanala imaju iste parametre – definisane filterom tog kanala. Kada parametri ne bi bili deljeni, kao kod potpuno povezane mreže, učeći različite parametre za različite delove slike, mreža bi učila da nekim delovima ulaza pridaje posebnu semantiku. To može zvučati dobro, ali u slučaju detekcije lica negde na slici, ne bi bilo dobro da se nauči da nos treba da bude baš na sredini slike, jer bi mogao biti i na nekom drugom mestu ako foto aparat nije bio centriran na lice. Deljenje parametara omogućava da se nauči filter koji traži nos bilo gde na slici.
Treći je specijalizovanost za topologiju signala. Naime i prethodno diskutovani modeli mašinskog učenja bi se mogli primenjivati na probleme vezane za zvuk, slike i slično, ali ako bi recimo trebalo da rade nad sirovom reprezentacijom slike u vidu piksela, raspored piksela bi bio potpuno proizvoljan, pošto ovi modeli ne uzimaju u obzir susednost piksela na slici, dok su konvolutivne mreže konstruisane imajući u vidu da to što su pikseli jedni u okolini drugih ima poseban značaj. Još jedan kvalitet, koji nije vezan za performanse je delimična interpretabilnost, zahvaljujući tome što se mogu vizualizovati ulazi na koje filteri daju najjači odgovor.
S druge strane, postoje i problemi. Naime, mreža je osetljiva na neke druge transformacije, poput rotacije i skaliranja (homotetije). Takođe, kako potpuno povezana mreža koja se nalazi na kraju niza slojeva konvolutivne mreže uvek mora imati fiksiran broj ulaza, tako i konvolutivni deo mora proizvesti izlaz tačno odredenih dimenzija, što zbog fiksiranog broja slojeva agregacije i fiksirane rezolucije agregiranja (npr. $3\times 3$ vrednosti se zamenjuju jednom), znači i da ulazi moraju biti istih dimenzija. Postoje određeni pristupi poboljšanja kojima se ublažuju prethodno pomenuti problemi. U slučaju potrebe za neosetljivošću na odredene transformacije, to se može donekle postići time što agregacija ne bi bila vršena nad jednim kanalom konvolucije, već nad više. U tom slučaju, različiti konvolutivni kanali mogu naučiti da traže različito transformisane uzorke od značaja i ukoliko ga jedan od kanala nađe, agregacija maksimumom prijavljuje da je obrazac nađen.
Drugi problem, vezan za veličine ulaza, može se rešiti na više načina. Neki su komplikovani i uključuju izgradnju novih modela i na njih se ne osvrćemo. Neki, poput skaliranja slike, su naivni i vode gubitku informacije. Dodatno, osetljivost na transformacije, uključujući skaliranje, je već konstatovana slabost konvolutivnih mreža. Poluzadovoljavajući pristup bi bio taj da rezolucija agregacije zavisi od dimenzija ulaza. Obično se ovaj tip agregacije radi samo na poslednjem nivou. Recimo, ako se za slike dimenzija $1000 \times 500$ na poslednjem nivou agregacije traži maksimum delova dimenzija $3 \times 3$, onda bi se za slike dimenzija $2000\times 1000$ tražio maksimum delova dimenzija $6 \times 6$. Na taj način, dimenzije ulaza u potpuno povezanu mrežu ostaju iste.
Pored diskutovanih problema koji su specifični za konvolutivne mreže, problemi diskutovani u slučaju potpuno povezanih mreža su takode prisutni. To što su ublaženi navedenim kvalitetima konvolutivnih mreža, vodi povećanju ambicija i izgradnji većih mreža, sa većim brojem parametara, pa isti problemi ponovo dolaze do izražaja.

Demonstriraćemo upotrebu konvolutivnih neuronskih mreža na bazi ručno pisanih brojeva MNIST. MNIST sadrži crno-bele slike dimenzije 28x28 piksela na kojima se nalazi neka od cifara od 0 do 9.

Sledeći kod je većinski preuzet od asistenata Anđelke Zečević i Milana Čugurovića sa časova vežbi iz Mašinskog učenja.

Prvo ćemo učitati sve neophodne biblioteke.

In [1]:
import numpy as np
from matplotlib import pyplot as plt
In [2]:
import tensorflow as tf
from tensorflow import keras
In [3]:
from tensorflow.keras.datasets import mnist
In [4]:
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Input, Dense, Dropout, Flatten, Conv2D, MaxPooling2D
from tensorflow.keras.losses import CategoricalCrossentropy
from tensorflow.keras.optimizers import Adam
from tensorflow.keras import utils
from tensorflow.keras import backend as K
In [5]:
np.random.seed(7)

Učitavanje podataka

In [6]:
(X_train, y_train), (X_test, y_test) = mnist.load_data()

Dimenzije slike ćemo sačuvati u okviru promenljivih img_rows i img_cols.

In [7]:
img_rows = 28
img_cols = 28

Prilikom obrade slika ulazi u mrežu treba da budu 4D tenzori. Uzimaju se u obzir širina i visina slike, broj kanala (jedan za monohromatske ili tri za RGB slike) i broj instanci. Stoga je učitane podatke potrebno transformisati na ovaj oblik.

Postoje dve konvencije zapisa slika. Channels last zapis je oblika (samples, height, width, color_depth), a chanell first oblika (samples, color_depth, height, width). Keras biblioteka (i TensorFlow) podrazumevano koriste Channel last zapis. Tako se, na primer, paketić veličine 128 koji sadrži crno-bele slike dimenzija 256x256 opisuje tenzorom dimenzija (128, 256, 256, 1), a recimo, paketić veličine 128 slika koje su u RGB modu tenzorom dimenzija (128, 256, 256, 3). Sledeća linija koda proverava koji zapis koristi Keras biblioteka.

In [8]:
K.image_data_format()
Out[8]:
'channels_last'
In [9]:
input_shape = (img_rows, img_cols, 1)
X_train = X_train.reshape(X_train.shape[0], img_rows, img_cols, 1)
X_test = X_test.reshape(X_test.shape[0], img_rows, img_cols, 1)

Slike ćemo pripremiti tako što ćemo ih normalizovati, tj. svesti vrednosti pojedinačnih piksela na vrednost iz intervala iz [0, 1]. Prethodno ćemo promeniti tip elemenata matrice sa uint8 na float32 kako bi mogli da sačuvamo realne vrednosti.

In [10]:
X_train = X_train.astype('float32')
X_train /= 255
X_test = X_test.astype('float32')
X_test /= 255

Vrednosti ciljne promenljive ćemo pripremiti tako što ćemo ih transformisati u vektore dužine 10 sa jedinicom na poziciji koja odgovara vrednosti cifre (takozvano one-hot kodiranje). Na primer, broj 3 će biti transformisan u vektor [0, 0, 0, 1, 0, 0, 0, 0, 0, 0] sa jedinicom na poziciji tri.

Za ovu transformaciju iskoristićemo funkciju to_categorical paketa keras.utils.

In [11]:
number_of_classes = 10

y_train = utils.to_categorical(y_train, number_of_classes)
y_test = utils.to_categorical(y_test, number_of_classes)

Naš model će se sastojati iz više konvolutivnih i agregacionih slojeva.

Konvolutivni slojevi se na nivou biblioteke kreiraju korišćenjem funkcije Conv2D. Prvo se navodi broj filtera (parametar filters, obično neimenovan), zatim veličine kernela (kernel_size parametar), veličina pomeraja (parametar strides) i uokvirenje (padding parametar).

Agregacioni slojevi (MaxPooling2D i AvgPooling2D) vrše redukciju slojeva svođenjem blokova zadatih večina na njihove maksimalne ili prosečne vrednosti. Veličina bloka zadaje se parametrom pool_size.

Prilikom treniranja koristićemo i Dropout tehniku regularizacije. Ova tehnika regularizacije podrazumeva isključivanje nasumično odabranih neurona, i na taj način se omogućava drugačiji protok podataka kroz mrežu i smanjuje zavisnost izlaza od ulaza. Da bi se primenila ova tehnika regularizacije, dovoljno je mreži dodati Dropout sloj. Njegov parametar je verovatnoća isključivanja pojedinačnih neurona.

Sloj ispravljanja, sloj Flatten, se korist za transformisanje matrica vrednosti u vektore i obično se koristi kao priprema za prelazak sa konvolutivnog dela na dalji gusti deo mreže.

In [12]:
model = Sequential([
    Input(shape=input_shape),
    Conv2D(filters=32, kernel_size=(3, 3), strides=(1, 1), padding='same',
           activation='relu'),
    Conv2D(filters=64, kernel_size=(3, 3), strides=(1, 1), padding='same',
           activation='relu'),
    MaxPooling2D(pool_size=(2, 2)),
    Dropout(0.25),
    Flatten(),
    Dense(128, activation='relu'),
    Dropout(0.5),
    Dense(number_of_classes, activation='softmax')
])

Prikaz modela i uvid u broj parametara koje mreža treba da nauči možemo da dobijemo pozivom funkcije summary.

In [13]:
model.summary()
Model: "sequential"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 conv2d (Conv2D)             (None, 28, 28, 32)        320       
                                                                 
 conv2d_1 (Conv2D)           (None, 28, 28, 64)        18496     
                                                                 
 max_pooling2d (MaxPooling2D  (None, 14, 14, 64)       0         
 )                                                               
                                                                 
 dropout (Dropout)           (None, 14, 14, 64)        0         
                                                                 
 flatten (Flatten)           (None, 12544)             0         
                                                                 
 dense (Dense)               (None, 128)               1605760   
                                                                 
 dropout_1 (Dropout)         (None, 128)               0         
                                                                 
 dense_1 (Dense)             (None, 10)                1290      
                                                                 
=================================================================
Total params: 1,625,866
Trainable params: 1,625,866
Non-trainable params: 0
_________________________________________________________________

Funkcija gubitka koju mreža minimizuje će biti kategorička unakrsna entropija. Za treniranje mreže ćemo koristiti optimizator Adam sa podešenim korakom učenja.

In [14]:
model.compile(loss=CategoricalCrossentropy(), optimizer=Adam(learning_rate=0.001),
              metrics=['accuracy'])

Mrežu ćemo trenirati koristeći paketiće veličine 128 u 12 epoha. Treniranje mreže će malo duže trajati.

In [15]:
batch_size = 128
epochs = 12
In [16]:
history = model.fit(X_train, y_train,
                    batch_size=batch_size,
                    epochs=epochs,
                    verbose=1,
                    validation_split=0.2)
Epoch 1/12
2023-03-26 18:54:15.225324: W tensorflow/core/platform/profile_utils/cpu_utils.cc:128] Failed to get CPU frequency: 0 Hz
375/375 [==============================] - 29s 77ms/step - loss: 0.2821 - accuracy: 0.9136 - val_loss: 0.0641 - val_accuracy: 0.9818
Epoch 2/12
375/375 [==============================] - 29s 78ms/step - loss: 0.0968 - accuracy: 0.9720 - val_loss: 0.0503 - val_accuracy: 0.9855
Epoch 3/12
375/375 [==============================] - 30s 79ms/step - loss: 0.0691 - accuracy: 0.9796 - val_loss: 0.0449 - val_accuracy: 0.9871
Epoch 4/12
375/375 [==============================] - 30s 81ms/step - loss: 0.0582 - accuracy: 0.9819 - val_loss: 0.0408 - val_accuracy: 0.9886
Epoch 5/12
375/375 [==============================] - 30s 81ms/step - loss: 0.0472 - accuracy: 0.9856 - val_loss: 0.0450 - val_accuracy: 0.9885
Epoch 6/12
375/375 [==============================] - 31s 81ms/step - loss: 0.0424 - accuracy: 0.9857 - val_loss: 0.0445 - val_accuracy: 0.9867
Epoch 7/12
375/375 [==============================] - 31s 83ms/step - loss: 0.0390 - accuracy: 0.9872 - val_loss: 0.0405 - val_accuracy: 0.9892
Epoch 8/12
375/375 [==============================] - 31s 81ms/step - loss: 0.0326 - accuracy: 0.9897 - val_loss: 0.0437 - val_accuracy: 0.9893
Epoch 9/12
375/375 [==============================] - 31s 82ms/step - loss: 0.0320 - accuracy: 0.9897 - val_loss: 0.0412 - val_accuracy: 0.9895
Epoch 10/12
375/375 [==============================] - 31s 84ms/step - loss: 0.0287 - accuracy: 0.9904 - val_loss: 0.0397 - val_accuracy: 0.9901
Epoch 11/12
375/375 [==============================] - 31s 83ms/step - loss: 0.0270 - accuracy: 0.9910 - val_loss: 0.0418 - val_accuracy: 0.9909
Epoch 12/12
375/375 [==============================] - 31s 83ms/step - loss: 0.0224 - accuracy: 0.9925 - val_loss: 0.0372 - val_accuracy: 0.9902

Možemo i nacrtati grafike funkcije gubitka i tačnosti u toku treniranja.

In [17]:
plt.figure(figsize=(10, 4))

plt.subplot(1, 2, 1)
plt.title('Loss')
plt.plot(np.arange(0, epochs), history.history['loss'], label='train')
plt.plot(np.arange(0, epochs), history.history['val_loss'], label='val')
plt.legend(loc='best')

plt.subplot(1, 2, 2)
plt.title('Accuracy')
plt.plot(np.arange(0, epochs), history.history['accuracy'], label='train')
plt.plot(np.arange(0, epochs), history.history['val_accuracy'], label='val')
plt.legend(loc='best')

plt.show()

Model ćemo oceniti na skupu za testiranje.

In [18]:
score = model.evaluate(X_test, y_test)
print('Test loss:', score[0])
print('Test accuracy:', score[1])
313/313 [==============================] - 2s 6ms/step - loss: 0.0309 - accuracy: 0.9911
Test loss: 0.030902909114956856
Test accuracy: 0.991100013256073

Zadatak za rad na času¶

Na bazi MNIST napraviti model pomoću potpuno povezane neuronske mreže. Uporediti dobijene rezultate sa rezultatima koje smo dobili koristeći konvolutivnu mrežu.