---
title: "Wprowadzenie do konwolucyjnych sieci neuronowych"
output: 
  html_notebook: 
    theme: cerulean
    highlight: textmate
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(warning = FALSE, message = FALSE)
```

Za chwilę zagłębimy się w zagadnienia teoretyczne związane z konwolucyjnymi sieciami neuronowymi. Dlaczego sieci te tak dobrze sprawdzają się podczas przetwarzania obrazu? Zacznijmy od przyjrzenia się prostemu przykładowi sieci konwolucyjnej. Sieć ta służy do klasyfikacji zbioru cyfr MNIST. Problemem tym zajmowaliśmy się już wcześniej w rozdziale 2. — zastosowana wówczas sieć o gęstych połączeniach uzyskała dokładność testową na poziomie 97,8%. Mimo że sieć konwolucyjna, którą przedstawię za chwilę, będzie bardzo prosta, to uzyska o wiele lepszy wynik od modelu z rozdziału 2.

Poniższy kod przedstawia wygląd podstawowej sieci konwolucyjnej. Jest ona stosem warstw layer_conv_2d i layer_max_pooling_2d. Za chwilę opiszę szczegółowo działanie tych warstw.

Sieć neuronowa przyjmuje tensor o kształcie określanym przez wysokość obrazu, jego szerokość i kanały obrazu — nie wliczamy tutaj wymiaru próbki. W związku z tym sieć ma przetwarzać obiekty wejściowe o rozmiarze (28, 28, 1) — taki jest właśnie format obrazów wchodzących w skład zbioru MNIST. Dlatego do pierwszej warstwy sieci przekazujemy argument input_shape=(28, 28, 1).


```{r}
library(keras)

model <- keras_model_sequential() %>% 
  layer_conv_2d(filters = 32, kernel_size = c(3, 3), activation = "relu",
                input_shape = c(28, 28, 1)) %>% 
  layer_max_pooling_2d(pool_size = c(2, 2)) %>% 
  layer_conv_2d(filters = 64, kernel_size = c(3, 3), activation = "relu") %>% 
  layer_max_pooling_2d(pool_size = c(2, 2)) %>% 
  layer_conv_2d(filters = 64, kernel_size = c(3, 3), activation = "relu")
```

Wyświetlmy aktualną architekturę sieci:

```{r}
summary(model)
```

Na wyjściu każdej warstwy layer_conv_2d i layer_max_pooling_2d pojawia się trójwymiarowy tensor o kształcie (wysokość, szerokość, kanały). Wraz z zagłębianiem się przez Ciebie w sieć wysokość i szerokość mają tendencję do przyjmowania mniejszych wartości. Liczbę kanałów określa pierwszy argument przekazany do warstw layer_conv_2d (możemy mieć np. 32 lub 64 kanały).

Kolejnym krokiem jest przekazanie ostatniego tensora wyjściowego o kształcie (3, 3, 64) do klasyfikatora w postaci sieci gęstej (ten typ sieci opisywałem wcześniej) — stosu warstw dense. Klasyfikatory te przetwarzają jednowymiarowe wektory, a nasze dane mają postać trójwymiarowego tensora. W związku z tym musimy spłaszczyć nasze dane wyjściowe, a następnie dodać kilka górnych warstw dense:

```{r}
model <- model %>% 
  layer_flatten() %>% 
  layer_dense(units = 64, activation = "relu") %>% 
  layer_dense(units = 10, activation = "softmax")
```

Klasyfikacja ma na celu dokonanie przyporządkowania do jednego z dziesięciu zbiorów, a więc będziemy korzystać z ostatniej warstwy generującej 10 wartości wyjściowych i użyjemy funkcji aktywacji softmax. Oto aktualna charakterystyka architektury sieci:

```{r}
summary(model)
```

Jak widać, tensory przed skierowaniem do dwóch warstw Dense miały początkowo kształt (3, 3, 64) i zostały spłaszczone do wektorów o kształcie (576).

Spróbujmy wytrenować sieć konwolucyjną na zbiorze MNIST. Możemy w tym celu ponownie użyć dużej części kodu przedstawionego wcześniej w rozdziale 2.

```{r, echo=TRUE, results='hide'}
mnist <- dataset_mnist()
c(c(train_images, train_labels), c(test_images, test_labels)) %<-% mnist

train_images <- array_reshape(train_images, c(60000, 28, 28, 1))
train_images <- train_images / 255

test_images <- array_reshape(test_images, c(10000, 28, 28, 1))
test_images <- test_images / 255

train_labels <- to_categorical(train_labels)
test_labels <- to_categorical(test_labels)

model %>% compile(
  optimizer = "rmsprop",
  loss = "categorical_crossentropy",
  metrics = c("accuracy")
)
              
model %>% fit(
  train_images, train_labels, 
  epochs = 5, batch_size=64
)
```

Sprawdźmy działanie modelu na testowym zbiorze danych:

```{r, echo=TRUE, results='hide'}
results <- model %>% evaluate(test_images, test_labels)
```

```{r}
results
```

Gęsta sieć zastosowana w celu rozwiązania tego problemu w rozdziale 2. charakteryzowała się testową dokładnością na poziomie 97,8%, a podstawowa sieć konwolucyjna uzyskała wynik 99,3%. Względna wartość błędu została zmniejszona o 68%. Nieźle! 
