{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "Sztuczne sieci neuronowe\n", "========================\n", "\n", "Perceptron\n", "----------\n", "\n", "### Schemat uczenia perceptronu\n", "\n", "Dopóki nie zostanie spełniony warunek stopu (np. wykonana zostanie pewna ustalona z góry liczba rund) dla każdego\n", "przykładu $(x, y)$ wykonaj:\n", "\n", "$$w = w + a (y - f(w \\cdot x))x$$\n", "\n", "### Objaśnienia do algorytmu\n", "$x$ -- wektor wejściowy\n", "\n", "$y$ -- oczekiwane wyjście dla wektora $x$\n", "\n", "$w$ -- wektor wag\n", "\n", "$a$ -- stała ucząca\n", "\n", "$f$ -- funkcja aktywacji\n", "\n", "Sieci wielowarstwowe\n", "--------------------\n", "\n", "### Algorytm propagacji wstecznej\n", "\n", "Dopóki nie zostanie spełniony warunek stopu dla każdego przykładu $(x,y)$ wykonaj:\n", "\n", " 1. dla każdego wierzchołka $j$ w warstwie wejściowej $a[j] = x[j]$\n", " 2. dla każdej warstwy $l$ od $2$ do $liczba\\_warstw$\n", " 1. dla każdego wierzchołka $i$ w warstwie $l$\n", " 1. dla każdej krawędzi z $j$ do $i$\n", "\n", " $sum[i] += w[j,i]*a[j]$\n", "\n", " 2. $a[i] = g(sum[i])$\n", "\n", " 3. dla każdego wierzchołka $i$ warstwy wyjściowej\n", "\n", " $d[i] = g'(sum[i]) * (y[i] - a[i])$\n", "\n", " 4. dla każdej warstwy $l$ od $liczba\\_warstw-1$ do $1$\n", " 1. dla każdego wierzchołka $j$ w warstwie $l$\n", " 1. $sum = 0$\n", " 2. dla każdej krawędzi z $j$ do $i$\n", "\n", " $sum += w[j,i] * d[i]$\n", "\n", " 3. $d[j] = g'(sum[j])*sum$\n", "\n", " 4. dla każdego wierzchołka $i$ w warstwie $l+1$\n", "\n", " $w[j,i] += a*a[j]*d[i]$\n", "\n", "### Objaśnienia do algorytmu\n", "\n", "$w[i, j]$ – macierz wag\n", "\n", "$a[i]$ – wartości funkcji aktywacji (poza warstwą wejściową, gdzie a[i]=x[i])\n", "\n", "$d[i]$ – „delta” (propagowany błąd)\n", "\n", "$a$ – stała ucząca\n", "\n", "$g$ – funkcja aktywacji (np. $\\frac{1}{1+e^{-x}}$)\n", "\n", "$g'$ – pochodna $g$" ] }, { "cell_type": "markdown", "metadata": { "lines_to_next_cell": 0 }, "source": [ "Przykład\n", "--------\n", "\n", "Zastosujemy algorytm propagacji wstecznej do wytrenowania sieci neuronowej\n", "której zadaniem będzie rozpoznawanie odręcznie zapisanych cyfr.\n", "Do uczenia i testowania naszej sieci wykorzystamy bazę danych\n", "[MNIST](http://yann.lecun.com/exdb/mnist), która zawiera zdjęcia (w skali szarości) odręcznie\n", "zapisanych cyfr. Zbiory uczący i testowy pobrane z w/w strony i zapisane w bieżącym katalogu\n", "można wczytać do Pythona korzystając z biblioteki [python-mnist](https://pypi.org/project/python-mnist/)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from mnist import MNIST\n", "\n", "dataset = MNIST('./', gz=True, return_type='numpy')\n", "train_images, train_labels = dataset.load_training()\n", "test_images, test_labels = dataset.load_testing()" ] }, { "cell_type": "markdown", "metadata": { "lines_to_next_cell": 0 }, "source": [ "Zdjęcia cyfr można wyświetlić korzystając z następującej funkcji" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "from matplotlib.pyplot import imshow\n", "\n", "def plotdigit(image):\n", " img = np.reshape(image, (-1, 28))\n", " imshow(img, cmap='Greys', vmin=0, vmax=255)" ] }, { "cell_type": "markdown", "metadata": { "lines_to_next_cell": 0 }, "source": [ "Wyświetlmy pierwsze cztery cyfry ze zbioru uczącego razem z ich etykietami." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plotdigit(train_images[0])\n", "train_labels[0]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plotdigit(train_images[1])\n", "train_labels[1]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plotdigit(train_images[2])\n", "train_labels[2]" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "lines_to_next_cell": 2 }, "outputs": [], "source": [ "plotdigit(train_images[3])\n", "train_labels[3]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Zanim przystąpimy do uczenia sieci znormalizujemy dane wejściowe" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "train_images = train_images / 255\n", "test_images = test_images / 255" ] }, { "cell_type": "markdown", "metadata": { "lines_to_next_cell": 0 }, "source": [ "W procesie uczenia skorzystamy z biblioteki [PyTorch](https://pytorch.org/)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import torch\n", "from torch import nn\n", "from torch import optim\n", "\n", "train_images = [torch.tensor(image, dtype=torch.float32) for image in train_images]\n", "train_labels = [torch.tensor(label, dtype=torch.long) for label in train_labels]\n", "test_images = [torch.tensor(image, dtype=torch.float32) for image in test_images]\n", "test_labels = [torch.tensor(label, dtype=torch.long) for label in test_labels]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Na początek zbudujmy sieć złożoną z pojedynczej warstwy neuronów." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "input_dim = 28*28\n", "output_dim = 10\n", "\n", "model = nn.Sequential(\n", " nn.Linear(input_dim, output_dim),\n", " nn.LogSoftmax()\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Sieć wytrenujemy korzystając z następującej funkcji" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def train(model, n_iter):\n", " criterion = nn.NLLLoss()\n", " optimizer = optim.SGD(model.parameters(), lr=0.001)\n", "\n", " for epoch in range(n_iter):\n", " for image, label in zip(train_images, train_labels):\n", " optimizer.zero_grad()\n", "\n", " output = model(image)\n", " loss = criterion(output.unsqueeze(0), label.unsqueeze(0))\n", " loss.backward()\n", " optimizer.step()\n", "\n", " print(f'epoch: {epoch:03}')\n", "\n", "train(model, 100)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Sprawdźmy jaka jest skuteczność wytrenowanej sieci na zbiorze uczącym" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def accuracy(expected, predicted):\n", " return len([_ for e, p in zip(expected, predicted) if e == p])/len(expected)\n", "\n", "predicted = [model(image).argmax() for image in train_images]\n", "accuracy(train_labels, predicted)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Wynik uzyskany na zbiorze uczącym jest zwykle zbyt optymistyczny.\n", "Sprawdźmy jak nasza sieć zachowuje się na zbiorze danych, który nie był wykorzystywany w procesie\n", "uczenia." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "predicted = [model(image).argmax() for image in test_images]\n", "accuracy(test_labels, predicted)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Wprowadźmy w naszej sieci kolejną warstwę neuronów, tworząc w ten sposób perceptron wielowarstwowy\n", "(ang. MLP -- Multilayer Perceptron)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "hidden_dim = 10\n", "\n", "model = nn.Sequential(\n", " nn.Linear(input_dim, hidden_dim),\n", " nn.ReLU(),\n", " nn.Linear(hidden_dim, output_dim),\n", " nn.LogSoftmax()\n", ")\n", "\n", "train(model, 100)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Proces uczenia zdefiniowanej powyższej sieci trwa około godziny na cztero-rdzeniowym\n", "procesorze (2.5 GHz). Jeżeli dysponują Państwo kartą graficzną kompatybilną\n", "z biblioteką [CUDA](http://www.nvidia.com/object/cuda_home_new.html), to\n", "warto przeprowadzić uczenie z wykorzystaniem procesora karty graficznej\n", "(parametr `device=mx.gpu()` w wywołaniu powyżej).\n", "W przypadku mojego komputera czas uczenia uległ skróceniu do ok. 70 sekund.\n", "\n", "Sprawdźmy skuteczność perceptronu wielowarstwowego na zbiorach uczącym i testowym" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "predicted = [model(image).argmax() for image in train_images]\n", "accuracy(train_labels, predicted)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "predicted = [model(image).argmax() for image in test_images]\n", "accuracy(test_labels, predicted)" ] }, { "cell_type": "markdown", "metadata": { "lines_to_next_cell": 0 }, "source": [ "Całkiem nieźle, ale sporo jeszcze można poprawić (por. wyniki ze strony [MNIST](http://yann.lecun.com/exdb/mnist/)).\n", "\n", "Sprawdźmy jeszcze, które cyfry są najczęściej mylone ze sobą" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from sklearn.metrics import confusion_matrix\n", "from tabulate import tabulate\n", "\n", "tabulate(confusion_matrix(test_labels, predicted), tablefmt='html', showindex=True, headers=[''] + list(range(10)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Zadanie\n", "\n", " 1. Przeanalizować jak wielkość zbioru uczącego wpływa na skuteczność klasyfikatora na zbiorze\n", " testowym. Wykorzystać odpowiednio $10 \\%$, $25 \\%$, $50 \\%$ i $100 \\%$ przykładów ze zbioru uczącego.\n", "\n", " 2. Sprawdzić jak na skuteczność klasyfikatora wpływa wielkość warstwy ukrytej\n", " (parametr `hidden_dim`).\n", "\n", " 3. Sprawdzić jak na skuteczność klasyfikatora oraz czas trwania procesu uczenia\n", " wpłynie wprowadzenie dodatkowych warstw ukrytych.\n", "\n", " 4. Przeanalizować wpływ liczby iteracji (parametr `n_iter`) na skuteczność sieci na zbiorze uczącym i testowym.\n", "\n", " 5. Sprawdzić jak na skuteczność perceptronu wielowarstwowego wpłynie pominięcie funkcji aktywacji\n", " (`ReLU`) w definicji sieci.\n", "\n", " 6. Zmodyfikować procedurę uczenia w taki sposób, żeby zamiast pojedynczych przykładów akceptowała ona tzw. wsady (ang. batch). Zob np. https://pytorch.org/tutorials/beginner/blitz/cifar10_tutorial.html.\n", "\n", " 7. (Dla posiadaczy kart graficznych NVIDIA) zmienić procedurę uczenia w taki sposób, żeby\n", " obliczenia przebiegały na procesorze karty graficznej." ] } ], "metadata": { "jupytext": { "cell_metadata_filter": "-all", "main_language": "python", "notebook_metadata_filter": "-all" } }, "nbformat": 4, "nbformat_minor": 4 }