Projekt_AI-Automatyczny_saper/sieci-neuronowe.ipynb

437 lines
12 KiB
Plaintext
Raw Permalink Normal View History

2021-06-01 17:38:31 +02:00
{
"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
}