Mala neuronska mreža i scikit-learn

Modul 3 · Lekcija 5 od 7 · Veštačka inteligencija

Do sada smo sve pisali sami. Prave neuronske mreže imaju hiljade težina i složeno učenje, pa ih ne pišemo od nule – koristimo biblioteku scikit-learn. U ovoj lekciji mreža uči da prepoznaje rukom pisane cifre i postiže tačnost od skoro 98 %.

Podaci: rukom pisane cifre

Biblioteka scikit-learn ima ugrađen skup od 1797 malih slika rukom pisanih cifara. Svaka slika ima 8 × 8 = 64 piksela, a svaki piksel je broj od 0 (belo) do 16 (crno).

# Pogled na podatke: rukom pisane cifre (8 × 8 piksela) iz biblioteke scikit-learn
import matplotlib.pyplot as plt
from sklearn.datasets import load_digits

cifre = load_digits()
print("Broj slika:", len(cifre.images))
print("Veličina jedne slike:", cifre.images[0].shape)      # (8, 8)
print("Prva slika kao brojevi (0 = belo, 16 = najtamnije):")
print(cifre.images[0])

# prikaz prvih 10 slika sa oznakama
for i in range(10):
    plt.subplot(2, 5, i + 1)
    plt.imshow(cifre.images[i], cmap="gray_r")
    plt.title(f"oznaka: {cifre.target[i]}")
    plt.axis("off")
plt.show()

Ispis programa:

Broj slika: 1797
Veličina jedne slike: (8, 8)
Prva slika kao brojevi (0 = belo, 16 = najtamnije):
[[ 0.  0.  5. 13.  9.  1.  0.  0.]
 [ 0.  0. 13. 15. 10. 15.  5.  0.]
 [ 0.  3. 15.  2.  0. 11.  8.  0.]
 [ 0.  4. 12.  0.  0.  8.  8.  0.]
 [ 0.  5.  8.  0.  0.  9.  8.  0.]
 [ 0.  4. 11.  0.  1. 12.  7.  0.]
 [ 0.  2. 14.  5. 10. 12.  0.  0.]
 [ 0.  0.  6. 13. 10.  0.  0.  0.]]
Prvih deset slika rukom pisanih cifara sa oznakama
Prvih 10 slika iz skupa: računar „vidi“ samo 64 broja

Program: mreža uči cifre

# Neuronska mreža prepoznaje rukom pisane cifre (scikit-learn, MLPClassifier)
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
from sklearn.neural_network import MLPClassifier
from sklearn.metrics import accuracy_score, confusion_matrix

cifre = load_digits()
X = cifre.data / 16.0        # svaka slika je red od 64 broja; delimo sa 16 da budu od 0 do 1
y = cifre.target             # tačne oznake: 0..9

# 80 % za učenje, 20 % za proveru
X_ucenje, X_provera, y_ucenje, y_provera = train_test_split(X, y, test_size=0.2, random_state=0)
print("Za učenje:", len(X_ucenje), "slika, za proveru:", len(X_provera))

# mreža sa jednim skrivenim slojem od 64 neurona
mreza = MLPClassifier(hidden_layer_sizes=(64,), max_iter=500, random_state=0)
mreza.fit(X_ucenje, y_ucenje)                # TRENING

predvidjeno = mreza.predict(X_provera)       # PROVERA
print(f"Tačnost na proveri: {accuracy_score(y_provera, predvidjeno) * 100:.1f} %")

print("Tabela grešaka (red = tačna cifra, kolona = šta je mreža rekla):")
print(confusion_matrix(y_provera, predvidjeno))

# pet slika na kojima je mreža pogrešila
greske = [i for i in range(len(y_provera)) if predvidjeno[i] != y_provera[i]]
for i in greske[:5]:
    print(f"slika {i}: tačno {y_provera[i]}, mreža kaže {predvidjeno[i]}")

Ispis programa:

Za učenje: 1437 slika, za proveru: 360
Tačnost na proveri: 97.8 %
Tabela grešaka (red = tačna cifra, kolona = šta je mreža rekla):
[[27  0  0  0  0  0  0  0  0  0]
 [ 0 34  0  0  0  0  0  0  1  0]
 [ 0  0 36  0  0  0  0  0  0  0]
 [ 0  0  0 29  0  0  0  0  0  0]
 [ 0  0  0  0 29  0  0  1  0  0]
 [ 0  0  0  0  0 39  0  0  0  1]
 [ 0  1  0  0  0  0 43  0  0  0]
 [ 0  0  0  0  0  0  0 39  0  0]
 [ 0  1  1  0  0  0  0  0 37  0]
 [ 0  0  0  0  0  1  0  1  0 39]]
slika 118: tačno 6, mreža kaže 1
slika 124: tačno 8, mreža kaže 1
slika 130: tačno 9, mreža kaže 5
slika 143: tačno 9, mreža kaže 7
slika 181: tačno 5, mreža kaže 9
  • cifre.data je tabela: svaka slika je jedan red od 64 broja – to su osobine. cifre.target su oznake (0–9).
  • train_test_split deli podatke na 80 % za učenje i 20 % za proveru – kao u lekciji Kako mašina uči.
  • MLPClassifier(hidden_layer_sizes=(64,)) je mreža sa jednim skrivenim slojem od 64 neurona; ulaznih neurona ima 64 (pikseli), izlaznih 10 (cifre).
  • fit = trening, predict = pogađanje.
  • U tabeli grešaka brojevi na dijagonali su tačni odgovori. Na primer, u drugom redu vidimo da je jedna jedinica proglašena za osmicu.

Poređenje: k-NN i mreža

# Poređenje: k-NN i neuronska mreža na istim podacima
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.neural_network import MLPClassifier

cifre = load_digits()
X, y = cifre.data / 16.0, cifre.target
X_u, X_p, y_u, y_p = train_test_split(X, y, test_size=0.2, random_state=0)

modeli = {
    "k-NN (k = 3)": KNeighborsClassifier(n_neighbors=3),
    "mreža, 16 neurona": MLPClassifier(hidden_layer_sizes=(16,), max_iter=800, random_state=0),
    "mreža, 64 neurona": MLPClassifier(hidden_layer_sizes=(64,), max_iter=500, random_state=0),
}
for ime, model in modeli.items():
    model.fit(X_u, y_u)
    print(f"{ime:20} tačnost: {model.score(X_p, y_p) * 100:.1f} %")

Ispis programa:

k-NN (k = 3)         tačnost: 98.3 %
mreža, 16 neurona    tačnost: 97.2 %
mreža, 64 neurona    tačnost: 97.8 %

Zanimljivo: na ovim malim slikama jednostavni k-NN je podjednako dobar kao mreža (čak malo bolji). Neuronske mreže pokazuju prednost na velikim slikama i složenim zadacima – tamo gde k-NN postaje prespor i nepouzdan. Pouka: uvek uporedi sa jednostavnim modelom pre nego što posegneš za složenim.

Za čas (nastavnicima): trajanje 45 minuta; pribor: računari sa scikit-learn.
Učenici pokreću program, pa u grupama menjaju broj neurona i broj slika za učenje (zadaci 1 i 3) i upisuju tačnost u zajedničku tabelu na tabli.

Česte zablude

  • „98 % znači da mreža skoro nikad ne greši“ – na ovim slikama; na ciframa napisanim drugačije (npr. tvojim rukopisom) može da greši mnogo češće – videćeš u lekciji 18.
  • „Više neurona je uvek bolje“ – posle neke granice tačnost raste malo, a učenje traje duže; previše neurona može da dovede i do preprilagođavanja.

Zadaci za vežbu

Pokušaj prvo sam, pa tek onda otvori rešenje.

  1. Isprobaj mreže sa 4, 8, 32 i 128 neurona u skrivenom sloju. Kako se menja tačnost?
  2. Prikaži slike na kojima mreža greši, sa tačnom oznakom i odgovorom mreže.
  3. Koliko je mreža tačna ako uči na samo 50, 100 ili 500 slika?
Rešenje 1: broj neurona
# Zadatak: kako broj neurona u skrivenom sloju utiče na tačnost
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
from sklearn.neural_network import MLPClassifier

cifre = load_digits()
X_u, X_p, y_u, y_p = train_test_split(cifre.data / 16.0, cifre.target, test_size=0.2, random_state=0)

for n in [4, 8, 32, 128]:
    mreza = MLPClassifier(hidden_layer_sizes=(n,), max_iter=1000, random_state=0)
    mreza.fit(X_u, y_u)
    print(f"{n:4} neurona: tačnost {mreza.score(X_p, y_p) * 100:.1f} %")

Ispis programa:

   4 neurona: tačnost 92.8 %
   8 neurona: tačnost 94.4 %
  32 neurona: tačnost 97.8 %
 128 neurona: tačnost 98.6 %

Sa 4 neurona tačnost je oko 93 %, sa 32 i više oko 98 %. Ako se pojavi upozorenje ConvergenceWarning, mreža nije završila učenje za zadati broj krugova (max_iter) – rezultat i dalje važi, ali možeš da povećaš max_iter.

Rešenje 2: slike grešaka
# Zadatak: prikaži slike na kojima mreža greši
import matplotlib.pyplot as plt
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
from sklearn.neural_network import MLPClassifier

cifre = load_digits()
X_u, X_p, y_u, y_p = train_test_split(cifre.data / 16.0, cifre.target, test_size=0.2, random_state=0)
mreza = MLPClassifier(hidden_layer_sizes=(64,), max_iter=500, random_state=0).fit(X_u, y_u)
predvidjeno = mreza.predict(X_p)

greske = [i for i in range(len(y_p)) if predvidjeno[i] != y_p[i]]
for k, i in enumerate(greske[:6]):
    plt.subplot(1, 6, k + 1)
    plt.imshow(X_p[i].reshape(8, 8), cmap="gray_r")      # 64 broja nazad u sliku 8 × 8
    plt.title(f"{y_p[i]} → {predvidjeno[i]}")
    plt.axis("off")
plt.suptitle("tačno → šta je mreža rekla")
plt.show()
Šest slika na kojima je mreža pogrešila
Neke greške su razumljive – i čoveku bi bilo teško
Rešenje 3: malo podataka
# Zadatak: koliko je mreža dobra ako uči na samo 100 slika?
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
from sklearn.neural_network import MLPClassifier

cifre = load_digits()
X_u, X_p, y_u, y_p = train_test_split(cifre.data / 16.0, cifre.target, test_size=0.2, random_state=0)

for broj in [50, 100, 500, len(X_u)]:
    mreza = MLPClassifier(hidden_layer_sizes=(64,), max_iter=1000, random_state=0)
    mreza.fit(X_u[:broj], y_u[:broj])
    print(f"uči na {broj:4} slika: tačnost {mreza.score(X_p, y_p) * 100:.1f} %")

Ispis programa:

uči na   50 slika: tačnost 80.8 %
uči na  100 slika: tačnost 90.6 %
uči na  500 slika: tačnost 96.7 %
uči na 1437 slika: tačnost 97.8 %

Što manje primera, to manja tačnost – podaci su zaista „hrana“ za veštačku inteligenciju.

Proveri znanje

Otvori kviz

Kviz: neuronska mreža i scikit-learn

1. Koliko ulaza ima mreža za slike cifara 8 × 8?




2. Šta radi fit?




3. Šta pokazuju brojevi na dijagonali tabele grešaka?




4. Šta se desi kada mreža uči na samo 50 slika?