% !TeX encoding = UTF-8
% !TeX spellcheck = pl_PL
\chapter{Podstawy teoretyczne sieci neuronowych}\hypertarget{chap:2}{}

Celem niniejszej pracy jest przeprowadzenie eksperymentu na danych zebranych w ramach omawianego na późniejszych stronach projektu powstałego z ramienia inicjatywy digitalizacyjnej Chronicling America, t.j. Newspaper Navigator. Z racji tego istnieje naturalna potrzeba wytłumaczenia pojęcia, jakimi są głębokie sieci neuronowe ze szczególnym naciskiem na sieci przetwarzające obraz, ponieważ technologia ta jest kluczowym elementem całego projektu Newspaper Navigator. 

\section{Podstawowe informacje o sieciach neuronowych}
Najprościej mówiąc i nie wdając się jeszcze zanadto w szczegóły sieć neuronowa jest to stos połączonych ze sobą warstw, na które składają się neurony, czyli podstawowe jednostki obliczeniowe w sieciach. Wyróżniamy warstwę wejściową, ukryte warstwy stanowiące wnętrze sieci, a także warstwę wyjściową. Z jednej strony wprowadza się dane, a z drugiej strony otrzymuje się ich przetworzoną postać. Każda warstwa wykonuje serię operacji matematycznych na otrzymanych danych, a także posiada zestaw parametrów (wag) podlegających modyfikacji w procesie optymalizacji. Przedstawiony na rycinie \ref{DNN} typ sieci neuronowych to najbardziej popularny typ, nazywany w pełni połączoną siecią (\emph{z ang. Fully Connected Network}). W sieci w pełni połączonej każda jednostka wyjściowa jest obliczana jako suma ważona wszystkich wejść. Termin ,,w pełni połączona'' pochodzi właśnie od tego zachowania: każde wyjście jest połączone z każdym wejściem \cite{osinga2018deep}. Neurony zawarte w warstwie wejściowej (\emph{z ang. Input Layer}) wprowadzają do modelu informacje zewnętrzne, nie wykonują się tutaj żadne operacje matematyczne, jedynie wprowadza się dane. Liczba neuronów w warstwie wejściowej zależy od danych uczących, od ich wymiaru. Tradycyjnie dla warstwy wejściowej wygląda to tak jak na rysunku \ref{neurony_licz}.
\begin{equation}
Liczba\: neuronow = Liczba\: cech\: w\: danych\: treningowych + 1
\label{neurony_licz}
\end{equation}
Wyrażenie '+ 1' w powyższym wzorze odnosi się do wyrazu wolnego lub inaczej obciążenia (\emph{z ang. bias}), który intuicyjnie pełni podobne zadanie przy sieciach neuronowych jak ma to miejsce w klasycznej regresji liniowej. Wyraz wolny to po prostu stała wartość. Obciążenie jest wykorzystywane do zrównoważenia wyniku, służy do przesunięcia wyniku funkcji aktywacji (o której więcej w dalszej części pracy) w stronę dodatnią lub ujemną. Najważniejsze operacje odbywają się za to w warstwach ukrytych (\emph{z ang. hidden layers}), zgromadzone tam neurony przetwarzają informacje zebrane w warstwie wejściowej, a następnie przekazują je do warstwy wyjściowej. Warstw ukrytych w sieciach może być zero lub kilka, nie jest to w żaden sposób regulowane, a dotyczy od danego przypadku oraz wizji twórcy konkretnej sieci. Im bardziej skomplikowany przypadek ma dana sieć rozwiązać, tym zazwyczaj z większej liczby ukrytych warstw musi się ona składać \cite{Malik2021Dec}.

\begin{figure}[h!]
\centering
\includegraphics[width=0.85\textwidth]{images/nn2.png}
\caption{Wizualizacja podstawowej architektury sieci neuronowej}
\label{DNN}
\ref{neuronymoje}
\end{figure}

Neurony w każda warstwie połączone są krawędziami (reprezentowane na rysunku \ref{neuronymoje} jako linie łączące poszczególne neurony ze sobą), a każda z krawędzi posiada wagę. Pojedynczy neuron zatem jest sumą ważoną wartości z poprzedniej warstwy a także dodaje się do nich wyraz wolny, w przypadku pierwszej warstwy ukrytej tymi wartościami jest jeden wiersz wartości wszystkich cech zbioru danych. Wagi podlegają optymalizacji w trakcie procesu uczenia sieci. Ich wartość jest o tyle kluczowa, że niektóre cechy w danych będą miały większe lub mniejsze znaczenie dla przyszłej predykcji, to właśnie jest regulowana przez wagi. Początkowo wagi mają ustawiane wartości w postaci małych liczb losowych \cite{Jain2021Dec}. Wzór \ref{wyrazwol} określa zachodzącą operacje wewnątrz neuronów.
\begin{equation}
Y = \sum (wagi * dane\: wejsciowe) + wyraz\: wolny
\label{wyrazwol}
\end{equation}
Zanim wyliczona suma zostanie przekazana do kolejny ukrytej warstwy lub warstwy wyjściowej, nakłada się na nią pewną operacje nieliniową, poprzez tak zwaną funkcje aktywacji (\emph{z ang. activation function}). Funkcja aktywacji dokonuje transformacji powstałej sumy ważonej w dane wyjściowe warstwy, dzieje się to w celu ułatwienia sieci uczenia się złożonych (nieliniowych) wzorców w danych. Funkcje aktywacji zazwyczaj są różniczkowalne, co oznacza, że dla danej wartości wejściowej można obliczyć pochodną pierwszego rzędu. Jest to konieczne, z racji tego, że sieci neuronowe są zwykle uczone z wykorzystaniem algorytmu wstecznej propagacji, który wymaga wyliczenia pochodnej na błędzie predykcji w celu późniejszej optymalizacji wag sieci \cite{BibEntry2021Jan_active}. Ponadto wartość neuronu powinna być kontrolowana i zawierać się w pewnym przedziale. Jeżeli ta wartość nie jest ograniczona do pewnego limitu, wówczas może przybierać bardzo duże wartości, co w przypadku bardzo głębokich sieci neuronowych, które mają miliony parametrów może prowadzić do poważnych problemów obliczeniowych. Ogólna postać funkcji aktywacji prezentuje wzór \ref{activo}.
\begin{equation}
Y = f( \sum (wagi * dane\: wejsciowe) + wyraz\: wolny)
\label{activo}
\end{equation}
Najczęściej stosowane funkcje aktywacji przedstawia tabela \ref{tab_activ}.
\begin{table}[h!]
\centering
 \begin{tabular}{c c c c}
   \toprule
   Nazwa & Funkcja & Zakres & Wykres \\
   \toprule
   Sigmoid & $\sigma(x) = \dfrac{1}{1+e^{-x}}$ & (0,1) &      \adjustimage{height=1.5cm, valign=m}{images/sigmoid.png} \\
   \midrule
   TanH    & $tanh(x) = \dfrac{2}{1+e^{-2x}} - 1$ & <-1,1> & \adjustimage{height=1.5cm, valign=m}{images/tanh.png} \\
   \midrule
   ReLU    & $f(x) = max(0, x) $ & $\begin{cases} 0 & x \leq 0 \\ x & x > 0 \end{cases} $ & \adjustimage{height=1.5cm, valign=m}{images/relu.png} \\
   \bottomrule
 \end{tabular}
 \caption{Najczęściej stosowane funkcje aktywacji}
 \label{tab_activ}
 \end{table}
 \clearpage

Po nałożeniu funkcji aktywacji na ważoną sumę cały proces powtarza się dla kolejnych warstw. Ostatnim krokiem jest przekazanie wartości do warstwy wyjściowej sieci, gdzie liczony jest finalny błąd predykcji. Cały taki proces nazywamy propagacją w przód (\emph{z ang. forward propagation}) \cite{Jain2021Dec_activatio}. Powyżej opisany proces prezentuje rysunek \ref{layers}. Przedstawione zostały wartości wag i neuronów dla każdej warstwy sieci prezentując algorytm propagacji w przód. W tym przypadku dla uproszczenia sieć neuronowa składa się z trzech warstw: jednej wejściowej, jednej ukrytej i jednej wyjściowej. Warstwy wejściowa i ukryta zawierają trzy neurony, a warstwa wyjściowa zaś tylko jeden.
\begin{figure}[h!]
\centering
\includegraphics[width=0.7\textwidth]{images/for_prop.png}
\caption{Wizualizacja algorytmu propagacji w przód \cite{BibEntry2020Aug_nn_guide}}
\label{layers}
\end{figure}

Przykładowe wyliczenia sumy ważonej dla neuronu 'h1':
\begin{equation}
w1*x1 + w2*x2 + w3*x3 = 1*0,9 + 0*0,8 + 1*0,1 = 1
\end{equation}
Następnie na wyliczoną sumę ważoną nakłada się nieliniowość (w tym wypadku sigmoidę):
\begin{equation}
h1 = \sigma(1) = \dfrac{1}{1+e^{-1}} \approx 0,73
\end{equation}

Identycznie postępujemy dla kolejnych neuronów warstwy ukrytej, a następnie dla warstwy wyjściowej. W tym przypadku błąd predykcji, czyli różnica między wartością prawdziwą a wartością oszacowaną, jest równy +0,77. Zakładając, że dana sieć zwraca wartości z przedziału od 0 do 1 oraz że 0 jest wartością prawdziwą, nietrudno dojść do wniosku, że wartość neuronu wyjściowego jest daleka od wartości prawdziwej. Wynika to z faktu, że wagi początkowe są wartościami losowymi. Aby przybliżyć wartość predykcji do wartości prawdziwej, następuje proces uczenia się sieci, tzw. algorytm propagacji wstecznej (\emph{z ang backward propagation}), czyli krótko mówiąc optymalizacji wag sieci \cite{BibEntry2020Aug_nn_guide}. 
\newline

Zanim jednak przejdziemy do algorytmu propagacji wstecznej stosownym wydaje się być wytłumaczenie pojęcia funkcji kosztu lub inaczej straty (\emph{z ang. cost/loss function}). W obrębie zagadnień związanych z uczeniem maszynowym pod tym pojęciem określa się funkcje, które badają różnice pomiędzy wartościami oczekiwanym na wyjściu sieci neuronowej a wartościami jakie zostały zwrócone (predykcjami). Funkcja ta określa, jak bardzo sieć myli się podczas predykcji wartości, poprzez wyliczenie błędu, jakimi obciążone są jej wyniki. Pojęcie funkcji kosztu różni się w zależności od przypadku, tj. regresji, klasyfikacji binarnej czy klasyfikacji wieloklasowej. W przypadku regresji wyliczany jest dystans między poszczególnymi wartościami rzeczywistymi od krzywej predykcji \cite{BibEntry2021Mar_loss_cost}. Celem modelu podczas etapu treningu jest minimalizacja funkcji kosztu, czyli minimalizacji błędów, jakie są przez niego popełnianie. Najczęściej używane funkcje kosztu przy regresji to:
\begin{equation}
MSE = \dfrac{1}{n}\sum(y-y')^2
\end{equation}
MSE, czyli błąd średniokwadratowy (\emph{z ang. mean squared error}) oblicza średnią kwadratowych różnic między wartościami rzeczywistymi a wartościami przewidywanymi. Cechą charakterystyczną tej funkcji jest to, że model jest bardziej ,,karcony'' za duże błędy.
\begin{equation}
MAE = \dfrac{1}{n}\sum|y-y'|
\end{equation}
MAE, czyli średni błąd bezwzględny (\emph{z ang. mean absolute error}) oblicza średnią sumy różnic bezwzględnych między wartościami rzeczywistymi a wartościami przewidywanymi. Charakteryzuje się większą odpornością na wartości odstające (\emph{z ang. outliers}) występujące w rozkładzie zmiennej objaśnianej.
\begin{equation}
MSLE = \dfrac{1}{n}\sum(\log(y+1)-\log(y'+1))^2
\end{equation}
MSLE, czyli błąd średniokwadratowy zlogarytmizowany (\emph{z ang. mean squared logarithmic error}) obliczany jest tak samo jak błąd średniokwadratowy, z tą różnicą, że używany jest logarytm naturalny wartości przewidywanej i rzeczywistej zmiennej objaśnianej. Stosowany jest najczęściej w sytuacjach, kiedy nie chcemy aby model był tak mocno karany w przypadku dużych wartości błędu, jak dzieje się to w przypadku MSE. Oprócz tego często używa się również wersji błędu średniokwadratowego oraz błędu średniokwadratowego zlogarytmizowanego zawierających pierwiastek, są to kolejno RMSE oraz RMSLE. Interpretacja ich jest o tyle prostsza, że operujemy w tych samych jednostkach, a nie jak w przypadku standardowych wersji kwadratach tych jednostek. MAPE to z kolei odpowiednik MAE w którym liczony jest średni błąd absolutny wyrażony procentowo. Te metryki jednak częściej stosowane są przy prezentacji wyników, aniżeli bezpośrednio jako funkcje straty w modelach między innymi dzięki łatwej do zrozumienia, dla osób mniej biegłych technicznie, interpretacji \cite{BibEntry2021Mar_loss_details}. 
\newline

W kontekście klasyfikacji funkcja straty mierzy, jak często model źle klasyfikuje przynależność obserwacji do poszczególnych etykiet. W przypadku klasyfikacji binarnych generalnie problem polega na przewidywaniu wartości 0 lub 1 dla pierwszej lub drugiej klasy. Jest to realizowane jako przewidywanie prawdopodobieństwa, aby zdecydować, czy dany element należy do klasy pierwszej czy drugiej. Najczęściej stosowaną miarą w przypadku problemów klasyfikacji jest BCE.
\begin{equation}
BCE = -\dfrac{1}{n}\sum(y\log(p) + (1 - y)\log(1 - p))
\end{equation}
BCE, czyli binarna entropia krzyżowa (\emph{z ang. binary cross-entropy}) jest powszechnie stosowaną funkcją straty w problemie klasyfikacji binarnej (istnieją tylko dwie klasy, 0 lub 1). Mierzy on wydajność modelu klasyfikacji, którego wyjście jest wartością prawdopodobieństwa pomiędzy 0 a 1. Funcja straty wzrasta tym bardziej, im przewidywane prawdopodobieństwo różni się od rzeczywistej etykiety. Teoretycznie doskonały model ma binarną stratę cross-entropii równą 0 \cite{Kumar2021Dec_class_loss}. Z kolei klasyfikacja wieloklasowa to te problemy modelowania predykcyjnego, w których przykłady są przypisane do jednej z więcej niż dwóch klas. Problem jest często implementowany jako przewidywanie prawdopodobieństwa przynależności przykładu do każdej znanej klasy. W przypadku tego rodzaju problemów najpopularniejszą funkcją straty jest również entropia krzyżowa. Tym razem obliczana jest binarna entropia krzyżowa dla każdej klasy osobno, a następnie sumuje się ją dla pełnej straty \cite{Martinek2022Mar_muulti_class_loss}. 
\newline

Skoro pojęcie funkcji straty zostało już omówione możliwe jest przejście do algorytmu propagacji wstecznej, który odpowiada za tak zwany proces uczenia się w przypadku sieci neuronowych. Jak wskazuje już sama nazwa, podąża się tym razem w odwrotnym kierunki aniżeli podczas algorytmu propagacji w przód (od warstwy wejściowej, przez warstwy ukryte, aż do warstwy wyjściowej). Celem w tym przypadku jest minimalizacja funkcji kosztu poprzez dostosowanie wag sieci. Wagi aktualizowane są w ten sposób, aby ich zmiany powodowały, że rzeczywiste wyjście sieci jest bliższe wyjściu docelowemu, minimalizując w ten sposób błąd dla każdego neuronu wyjściowego i sieci jako całości. Zmiany wag zachodzą sekwencyjnie od ostatniej do pierwszej warstwy. Poziom dostosowania jest określany przez gradienty funkcji kosztu względem wag. Gradient funkcji jest wektorem pochodnych cząstkowych tej funkcji. Pochodna funkcji mierzy wrażliwość na zmianę wartości wyjściowej w odniesieniu do zmiany jej wartości wejściowej. Innymi słowy, pochodna mówi nam, w jakim kierunku zmierza funkcja. Gradient zaś pokazuje, jak bardzo musi zmienić się jej parametr (w kierunku dodatnim lub ujemnym), aby zminimalizować daną funkcje. Rozważmy następujący wycinek sieci przedstawiający jeden neuron wraz z połączeniami. Wycinek ten przedstawia rycina \ref{neuuro}.
\begin{figure}[h!]
\centering
\includegraphics[width=0.5\textwidth]{images/nn_part_for_backprop.png}
\caption{Wizualizacja wycinka sieci neuronowej \cite{BibEntry2020Aug_nn_guide}}
\label{neuuro}
\end{figure}

Z algorytmu propagacji w przód wiadome jest, że:
\begin{equation}
\sigma(z) = \dfrac{1}{1+e^{-z}}
\end{equation}
\begin{equation}
z = \sum w*x= w_1*x_1 + w_2*x_2 + w_3*x_3
\end{equation}
Zakładając, że chcemy zbadać jaka zmiana w1 spowoduje minimalizacje funkcji kosztu, oznaczmy ją jako E, musimy zastosować regułę łańcuchową (\emph{z ang. chain rule}) służącą do obliczania pochodnych funkcji złożonych. Opisuje to wzór \ref{chain}.
\begin{equation}
\frac{\partial E}{\partial w_1} = \frac{\partial E}{\partial \sigma(z)} * \frac{\partial \sigma(z)}{\partial z} * \frac{\partial z}{\partial w_1}
\label{chain}
\end{equation}
Zaktualizowana wartość wagi $w_1$ prezentuje przykład \ref{w1}.
\begin{equation}
w_1' = w_1 - (\frac{\partial E}{\partial w_1} * lr)
\label{w1}
\end{equation}
Powyższy proces nosi nazwę metody gradientu prostego (\emph{z ang. gradient descent}). Pod symbolem 'lr' kryje się parametr oznaczający długość kroku (\emph{z ang. learning rate}), jaki zostanie wykonany w stronę minimum funkcji podczas jednego przejścia procesu optymalizacji wag. Parametr ten jest ustawiany z góry podczas deklarowania parametrów sieci. Używany jest on w celu uniknięcia sytuacji, w której algorytm pominie minimum funkcji z powodu wykonanej zbyt dużej modyfikacji wagi względem jej pierwotnego stanu. Ponieważ proces propagacji wstecznej jest wykonywany wielokrotnie, tak samo jak propagacji w przód, a nie jednorazowo przez cały zbiór uczący (1 epoka = 1 przejście sieci w przód i w tył), wagi mogą być uaktualniane o mniejsze wartości tym samym zabezpieczając przed pominięciem minimum funkcji. Oczywiście krok ten nie może być również zbyt mały, ponieważ proces optymalizacji będzie wówczas trwał zbyt długo, a przy tym pochłaniał zbyt dużo mocy obliczeniowej. Dobór tego parametru jest zazwyczaj serią prób i błędów, zanim dojdzie się do jego odpowiedniej wartości. Wpływ długości kroku na znajdowanie minimum funkcji obrazuje rycina \ref{minim}.
\begin{figure}[h!]
\centering
\includegraphics[width=1\textwidth]{images/lr.png}
\caption{Wizualizacja procesu minimalizacji funkcji kosztu w zależności od długości kroku \cite{JeremyJordan2020Aug_lr}}
\label{minim}
\end{figure}

Cały proces opisany powyżej wykonywany jest dla każdej wagi, jaka znajduje się w sieci, idąc po kolei warstwa po warstwie, a zaczynając od warstwy wyjściowej sieci \cite{BibEntry2020Aug_nn_guide}. Podejście do procesu optymalizacji wag przy dzisiejszych architekturach sieci neuronowych zależne jest od wybranego optymalizatora. Do najpopularniejszych aktualnie optymalizatorów należą optymalizatory adaptacyjne, które dostosowują długość kroku automatycznie w trakcie procesu uczenia się sieci, takie jak: ADAM \cite{Kingma2014Dec}, RMSprop \cite{rmsprop_unpublished_Hinton}, czy AdaDelta \cite{Zeiler2012Dec}. Po wykonaniu się ostatniej epoki, aktualne wartości wag stają się finalnymi wagami w sieci i będą wykorzystywane przy predykcji na zbiorze testowym.
\clearpage

\section{Splotowe sieci neuronowe przetwarzające obraz}
\bigbreak

W przypadku danych wejściowych, którymi są obrazy, do czynienia mamy z ich reprezentacją macierzową. Jej elementy to poszczególne występujące na zdjęciu piksele. Jeżeli rozważylibyśmy przepuszczenie takich danych przez, wcześniej omówione, klasyczne w pełni połączone sieci wówczas z dużą dozą prawdopodobieństwa bylibyśmy świadkami ich problemów ze zbyt dużą złożonością obliczeniową zadania. Jest to spowodowane, tym że obrazy z natury są bardzo duże, ich rozdzielczość określa wielkość reprezentującej jej macierzy. W tym momencie warto dodać, że na tym liczba elementów wchodzących w skład jednego wejścia do modelu się nie kończy, bowiem każdy piksel może przybierać jeden z 3 kolorów (wedle modelu przestrzeni barw RGB). Wówczas pojedyncze wejście modelu dla obrazu o popularnej rozdzielczości 256x256 pikseli w rzeczywistości reprezentuje liczba 256×256×3. Zakładając model o jednej warstwie ukrytej z 1000 neuronów, warstwa ta dysponować będzie prawie 200 milionami parametrów, podlegającymi procesom optymalizacji podczas przechodzenia algorytmu propagacji wstecznej. Z racji tego, że modele obrazów wymagają dość dużej liczby warstw, aby dobrze radzić sobie z klasyfikacją, to wówczas używając sieci w pełni połączonych, otrzymalibyśmy miliardy parametrów. Przy tak dużej liczbie parametrów uniknięcie problemu nadmiernego dostosowania modelu jest prawie niemożliwe. Korzystając z architektury splotowych sieci neuronowych (\emph{z ang. convolutional neural networks - CNN}), potocznie nazywanych konwolucyjnymi, otrzymujemy znacznie mniejszą liczbę parametrów, skuteczniejsze modele, a także dużo krótszy czas ich trenowania \cite{osinga2018deep}. CNN to wyspecjalizowane sieci do danych o topologii przypominającej siatkę, dlatego właśnie świetnie spisują się w przypadku obrazów. Stosuje się je również do szeregów czasowych, gdzie za takową siatkę odpowiada interwał czasowy. Sama architektura tych sieci znacznie różni się od tej przedstawionej w pierwszym podrozdziale choć istota pozostaje wciąż taka sama, co przedstawia rycina \ref{convner}. 
\clearpage
\begin{figure}[h!]
\centering
\includegraphics[width=1\textwidth]{images/cnn.png}
\caption{Architektura splotowych sieci neuronowych \cite{Sharma2020May_cnn_png}}
\label{convner}
\end{figure}

Istotą splotowych sieci neuronowych jest operacja matematyczna zwaną splotem lub inaczej konwolucją (\emph{z ang. convolution}). Splot pozwala wyostrzyć pewne elementy obrazu poprzez użycie tak zwanego jądra lub inaczej filtra (\emph{z ang. kernel} lub \emph{filter}). 
\begin{figure}[h!]
\centering
\includegraphics[width=0.5\textwidth]{images/kernels.png}
\caption{Przykładowe filtry i ich wpływ na obraz \cite{ContributorstoWikimediaprojects2022May}}
\end{figure}
\clearpage
Pozwala to lepiej klasyfikować obraz, gdyż jego cechy są dla sieci, można by powiedzieć, lepiej widoczne. Filtry to mniejsze macierze o losowych parametrach, których to wartości koryguje algorytm propagacji wstecznej, dlatego intuicyjnie można utożsamiać je z wagami jakie występowały w przypadku w pełni połączonych sieci. Filtrów zazwyczaj nakłada się wiele, dla przykładu przy zadaniu rozpoznawania twarzy jeden filtr może uwypuklać oczy, drugi uszy, kolejny linie włosów i tak dalej. Oprócz tego takie podejście redukuje również szum, czyli niepotrzebne elementy obrazu przestają mieć znaczenie. Przykładowe filtry i ich wpływ na obraz przedstawione zostały w tabeli na obrazku powyżej. Wracając jednak do istoty konwolucji, dokonuje ona modyfikacji części obrazu. Filtr przechodzi krok po kroku po obrazie i przemnaża wartości zwracając wynik (zarówno wielkość filtra, jak i krok to parametry modyfikowalne podczas implementacji sieci). Wizualizacja tego procesu przedstawiona została na rysunku \ref{fe}. 
\begin{figure}[h!]
\centering
\includegraphics[width=1\textwidth]{images/kernel_cnn_vis.png}
\caption{Wizualizacja procesu zachodzącego w warstwie konwolucyjnej \cite{Galante2022Mar}}
\label{fe}
\end{figure}

Wobec tego splot tworzy nam nowy, mniejszy ,,obraz'' zawierający już wyekstrahowane cechy obrazu pierwotnego, który posłuży jako wejście kolejnej warstwy sieci. Taki obraz nazywany jest mapą cech (\emph{z ang. feature map}). Wyekstrahowane cechy w postaci mapy są o tyle znaczące, że dzięki temu ich lokalizacja na obrazie przestaje mieć znaczenie, a liczy się jedynie fakt ich występowania. Przy późniejszej predykcji zmiana położenia danej cechy nie będzie zatem znacząca w kontekście wyniku. Oczywiście jak każda architektura sieci neuronowych może być ona wielowarstwowa, wówczas wszystko wygląda identycznie, a proces powtarzany jest kilkukrotnie powodując wykrywanie subcech poszczególnych cech. 
\newline

Oprócz wspomnianych wcześniej dwóch hiperparametrów, tj. kroku (\emph{z ang. stride}) oraz wielkości (\emph{z ang. size}), dysponujemy także wypełnieniem (\emph{z ang. padding}). Wypełnienie odpowiada za poszerzenie granic obrazu, dzięki czemu możliwa jest ekstrakcja większej ilości cech, a co za tym idzie większa ilość informacji jest przetwarzana wewnątrz sieci. Takie działanie odbywa się kosztem wydajności. Następnym krokiem jest nałożenie na otrzymaną mapę cech nieliniowości, poprzez zastosowanie funkcji aktywacji, podobnie jak miało to miejsce w przypadku w pełni połączonych sieci. W przypadku CNN praktycznie zawsze jest to funkcja ReLU. Nieliniowość jest przyczyną tak dobrej skuteczności sieci neuronowych, ponieważ pozwala znajdować skomplikowane zależności w danych, których nie sposób zapisać jako kombinacje liniową. Funkcja aktywacji w tym wypadku pozwala zadecydować, czy dany neuron będzie miał znaczący udział w późniejszej predykcji, czy też nie. W przypadku konwolucyjnych sieci neuronowych przetwarzane wartości piksele interpretować możemy jako neurony, zaś parametry macierzy filtrów jako wagi \cite{albawi2017understanding}. 
\newline

Zgodnie z rysunkiem \ref{convner} kolejnym elementem architektury splotowych sieci neuronowych jest krok o nazwie łączenie (\emph{z ang. pooling}). Konwolucyjna warstwa, której wyjście stanowi wejście dla warstwy poolingu, powoduje redundancje danych. Część tych samych cech została wyekstrahowana w kolejnych przesunięciach filtra. Na potrzeby wizualizacji tego tematu, jeżeli użyty zostanie filtr wykrywający krawędzie poziome na obrazie i znajdzie on silną krawędź w określonym miejscu, to wówczas z dużą dozą prawdopodobieństwa ta sama cecha znajdzie się również w miejscu przesunięciu filtra o przykładowo 1 piksel w lewo, czy prawo. Zatem po wykonaniu splotu wypadałoby nieco zmniejszyć wymiarowość przetwarzanych danych. Zmniejszając rozmiar obrazu, zmniejszamy liczbę parametrów do wytrenowania, czym skracamy zarówno czas działania sieci, jak i poziom jej obliczeniowego skomplikowania. Za właśnie ten proces odpowiada warstwa poolingu. Co warte podkreślenia pomimo redukcji wymiaru nie tracimy wówczas praktycznie żadnych informacji. Dzieje się tak, ponieważ nie interesuje nas wartość każdego piksela po konwolucji, a jedynie te wartości, które dają silny sygnał sieci. W praktyce proces ten wygląda w ten sposób, że kilka sąsiednich pikseli przykładowo o rozmiarze 2x2 mapowana jest na 1 piksel (rozmiar podobnie jak w przypadku filtrów jest hiperparametrem deklarowanym podczas implementacji sieci). Zadeklarowane okno, również jak w przypadku filtrów, przechodzi wzdłuż i wszerz po całym obrazie. Dwa najpopularniejsze i praktycznie jedyne stosowane rodzaje łączenia to:
\begin{enumerate}
	\item \textbf{pooling} -- mapowaną wartością jest maksymalna wartość wynikająca z sąsiednich wartości pikseli,
	\item \textbf{average pooling} -- mapowaną wartością jest średnia wartość wynikająca z sąsiednich wartości pikseli.
\end{enumerate}
Dzięki zastosowania warstwy poolingu sieci łatwiej jest identyfikować te cechy obrazu, których sygnał jest najsilniejszy \cite{Yamashita2018Aug}. Proces ten wizualizuje rysunek \ref{poole}. 
\begin{figure}[h!]
\centering
\includegraphics[width=0.6\textwidth]{images/pool.png}
\caption{Wizualizacja procesu zachodzącego w warstwie poolingu \cite{Yani2019May}}
\label{poole}
\end{figure}

Ostatnią warstwą CNN jest, znana już z opisywanej w poprzednim podrozdziale klasycznej architektury sieci neuronowych, warstwa w pełni połączona (\emph{z ang. fully connected layer}). Warstwa ta odpowiedzialna jest za klasyfikacje. Używając funkcji aktywacji softmax (lub wcześniej scharakteryzowana funkcja sigmoidalna), opisanej wzorem \ref{wzorel}, otrzymaną predykcją będzie prawdopodobieństwo przynależności obrazu do danej klasy.
\begin{eqnarray}
\sigma(y_{i}) = \left(\frac{e^{y_{i}}}{ \sum\limits_{j} e^{y_{j}}}\right)\: 
j = 1,...,n
\label{wzorel}
\end{eqnarray}
Użycie tej funkcji w celu utworzenia prawdopodobieństw opisuje zaś przykład \ref{matrixmatrix}.
\begin{eqnarray}
y \rightarrow 
\begin{bmatrix}{2}\\{1}\\{0,1}\end{bmatrix} 
\longrightarrow
\left[\frac{e^{y_{i}}}{ \sum\limits_{j} e^{y_{j}}}\right]
\longrightarrow
\begin{bmatrix}{0,7}\\{0,2}\\{0,1}\end{bmatrix} = 1
\label{matrixmatrix}
\end{eqnarray}
\begin{equation}
0,7 + 0,2 + 0,1 = 1
\end{equation}
Tak samo jak w klasycznej architekturze sieci w tej warstwie liczony jest błąd predykcji, który korygowany będzie poprzez algorytm propagacji wstecznej, czyli optymalizacji wag, a w tym przypadku parametrów macierzy filtrów. W CNN możemy zadeklarować więcej niż jedną warstwę w pełni połączoną, jest to podyktowane już charakterystyką danego zbioru treningowego. To samo dotyczy zresztą pozostałych warstw, co zostało już poruszone wcześniej w tym podrozdziale, a także zademonstrowane jest na rysunku 2.5 \cite{BibEntry2022Jun_cnn_output}. 