Projekt_AI-Automatyczny_saper/sieci-neuronowe.ipynb
2021-06-01 17:38:31 +02:00

437 lines
12 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
"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
}