Napisz program klasyfikujący obrazki z bazy MNIST używając modelu Softmax Regression.

Model powinien trenować na 60k obrazkach z pliku 'train-images-idx3-ubyte.gz', przy czym pierwsze 5k i ostatnie 5k obrazków z tego pliku powinno służyć do walidacji bieżącego modelu. Dopiero ostatecznie wybrany model jednokrotnie sprawdzamy na 10k obrazkach z pliku 't10k-images-idx3-ubyte.gz'. Procent błędnych odpowiedzi na tym ostatnim teście jest benchmarkiem naszego modelu.

Model zakłada, że funkcja z obrazków w 10-elemetowy wektor floatów (interpretowany jako prawdopodobieństwa, że obrazek przedstawia cyfrę 0,...,9) jest postaci
softmax(img*W+b), gdzie
* img jest obrazkiem, a więc tablicą 784 floatów (z przedziału [0,1]),
* W jest macierzą przejścia o wymiarach 784 x 10,
* softmax to funkcja przekształcająca wektor 10 floatów, w wektor 10 nieujemnych floatów sumujących się do 1.

Model powinien zawierać nastepujace stałe (podane wartości są propozycją inicjalizacji):
* init_scale = 0.05
* learning_rate = 0.01
* batch_size = 128
* num_of_epochs = 25
* training_set_size = 50k

Inicjalizacja:
* tablicę W wypełniamy małymi losowymi wartościami dookoła 0
(init_scale * 2 * (np.random.random(W.shape)) - 0.5)
* b możemy wypełnić zerami

Nauka modelu:
* w idealnym swiecie model powinien przyjąć wartości W i b minimalizujące funkcję kosztu zadaną przez:
softmax(IMG * W + b) * (LABELS), gdzie IMG to wszystkie 50k obrazków treningowych, a LABELS to ich etykiety
* niestety taka minimalizacja jest nie-do-zrobienia...
* zamiast tego wartosci tablicy W i wektora b ustalamy używajac metody stochastic gradient descent próbującej minimalizować wartość podanej wyżej funkcji (patrz tutaj; obrazek na samym dole porównuje różne współczesne podejścia), czyli ...
* definiujemy funkcję kosztu: f(IMG_BATCH, W, b, LABELS_BATCH):
-sum( log(softmax(IMG_BATCH * W + b)) * LABELS_BATCH ) / batch_size
** Uwagi do funkcji kosztu:
* (1) zamiast minimalizować softmax(IMG * W + b) * (LABELS) będziemy minimalizować przeciwność logarytmu tego wyrażenia, czyli
-log(softmax(IMG * W + b))
(to decyzja z uwagi na stabilność numeryczną obliczeń)
* (2) IMG_BATCH to tablica (batch_size x 784) pewnych batch_size obrazków (nazywana batchem), LABELS_BATCH to tablica o wymiarch (batch_size x 10) etykiet tych obrazków
* (3) koszt pojedynczego obrazka interpretujemy jako skalę niepewności modelu co do poprawnej odpowiedzi; zauważmy, że jeśli wartość predykcj dla prawdziwej odpowiedzi zmierza do zera to koszt obrazka zmierza do nieskończoności; funkcja kosztu sumuje te niepewności dla wszystkich obrazków z batcha
* liczymy pochodną funkcji f, g=grad(f) po każdej zmiennej w W i b
(liczymy pochodną symbolicznie używając funkcji grad z autograd)
* permutujemy obrazki ze zbioru treningowego i bierzemy kolejne batche obrazków i etykiet wzdłuż tej permutacji, i aktualizujemy:
W -= g(IMG_BATCH, W, b, LABELS_BATCH) * learning_rate
b -= g(IMG_BATCH, W, b, LABELS_BATCH) * learning_rate
* przejście w powyższy sposób wzdłuż ustalonej permutacji, przez cąły zbiór treningowy nazywamy epoką. Liczba epok, które wykonujemy to num_of_epochs.
* po każdej epoce drukujemy wynik naszego modelu na zbiorze 10k obrazków przeznaczonych do walidacji
* jeśli jesteśmy zadowoleni z wyniku; testujemy w sposób ostateczny model na zbiorze testowym

** w pierwszych liniach kodu zamieszczamy oświadczenie o samodzielności wykonania zadania
** model powinien mieć co najmniej 90% skuteczności na zbiorze przeznaczonym do walidacji i na zbiorze testowym
** wraz z modelem proszę przygotować kilka statystyk własnego pomysłu ... dla przykładu: instancje cyfr dla których model jest najpewniejszy swej odpowiedzi; obrazki na których model się myli (wraz z wykresem predykcji), itp.