import math import random from copy import copy class NeuronovaSit: def __init__(self, NI, NH, NO): # Pocet uzlu ve vrstvach. +1 pridava jeste jeden neuron pro bias. # Pozn pro nepythonisty: 'self' se pouziva pro pristup k promennym a metodam objektu (resp. instance). self.ni = NI + 1 self.nh = NH + 1 self.no = NO # Hodnota vystupujici z neuronu self.ai, self.ah, self.ao = [], [], [] self.ai = [1.0] * self.ni self.ah = [1.0] * self.nh self.ao = [1.0] * self.no # Vahy self.wi = self.vytvor_matici(self.ni, self.nh) self.wo = self.vytvor_matici(self.nh, self.no) # Nastaveni vah na nahodna cisla self.nahodna_matice(self.wi, -0.5, 0.5) self.nahodna_matice(self.wo, -0.5, 0.5) def vypocti_vystup(self, vstupy): if len(vstupy) != self.ni - 1: print('Spatny pocet vstupu') for i in range(self.ni - 1): self.ai[i] = vstupy[i] for j in range(self.nh - 1): sum = 0.0 for i in range(self.ni): sum += (self.ai[i] * self.wi[i][j]) self.ah[j] = self.aktivacni_funkce(sum) for k in range(self.no): sum = 0.0 for j in range(self.nh): sum += (self.ah[j] * self.wo[j][k]) self.ao[k] = self.aktivacni_funkce(sum) return self.ao def zpetna_propagace(self, vystupy, alfa): # vypocet delty vystupni vrstvy odelta = [0.0] * self.no for k in range(self.no): chyba = vystupy[k] - self.ao[k] odelta[k] = chyba * self.derivace_aktivacni_funkce(self.ao[k]) # update vah for j in range(self.nh): for k in range(self.no): self.wo[j][k] += alfa * odelta[k] * self.ah[j] # vypocet delty skryte vrstvy hdelta = [0.0] * self.nh for j in range(self.nh): chyba = 0.0 for k in range(self.no): chyba += odelta[k] * self.wo[j][k] hdelta[j] = chyba * self.derivace_aktivacni_funkce(self.ah[j]) # update vah for i in range(self.ni): for j in range(self.nh): self.wi[i][j] += alfa * hdelta[j] * self.ai[i] # vypocti chybu E = 0.0 for k in range(len(vystupy)): E = 0.5 * (vystupy[k] - self.ao[k]) ** 2 return E def vypis_vahy(self): print('Vstupni vahy:') for i in range(self.ni): print(self.wi[i]) print() print('Vystupni vahy:') for j in range(self.nh): print(self.wo[j]) print() def testuj(self, data): for p in data: inputs = p[0] print('Vstup:', p[0], '-->', self.vypocti_vystup(inputs), '\tSpravny vystup', p[1]) def trenuj(self, data, test=None, max_iteraci=1000, cilova_chyba=10e-6, alfa=0.5): if test is None: test = data for i in range(max_iteraci): # Train for p in data: vstup = p[0] spravny_vystup = p[1] self.vypocti_vystup(vstup) self.zpetna_propagace(spravny_vystup, alfa) # Test chyba = 0 for (vstup, spravny_vystup) in test: for k in range(len(spravny_vystup)): vystup = self.vypocti_vystup(vstup) chyba += 0.5 * (spravny_vystup[k] - vystup[k]) ** 2 chyba /= len(data) if chyba < cilova_chyba: print('E = ', chyba) return True if i % 50 == 0: # Kazdych 50 iteraci vypis chybu print('E = ', chyba) return False def aktivacni_funkce(self, x): return math.tanh(x) def derivace_aktivacni_funkce(self, y): # Derivace sigmoidy return 1 - y ** 2 def vytvor_matici(self, I, J, hodnota=0.0): m = [] for i in range(I): m.append([hodnota] * J) return m def nahodna_matice(self, matice, a, b): for i in range(len(matice)): for j in range(len(matice[0])): matice[i][j] = random.uniform(a, b) def druh_na_cislo(druh): druhy = ["setosa", "versicolor", "virginica"] return druhy.index(druh) def normalizuj_vstupy(data): minima = copy(data[0][0]) # Abychom pri prirazeni do pole minim neupravovali i data[0][0], vytvorime kopii. maxima = copy(data[0][0]) sloupcu = len(data[0][0]) for (vstup, vystup) in data: for i in range(sloupcu): if vstup[i] < minima[i]: minima[i] = vstup[i] if vstup[i] > maxima[i]: maxima[i] = vstup[i] for (vstup, vystup) in data: for i in range(sloupcu): vstup[i] = (vstup[i]-minima[i])/(maxima[i]-minima[i]) # Rozprostre hodnoty sloupce do rozsahu 0 az 1. def najdi_index_maxima(pole): max = pole[0] idx = 0 for i in range(len(pole)): if pole[i] > max: max = pole[i] idx = i return idx def main(): data = [] for radka in open("iris.csv").readlines()[1:]: pole = radka.strip("\n").split(",") # Odstran z konce radku znak noveho radku a rozsekej do pole podle carek. vstup = [float(x) for x in pole[:4]] # Preved prvni 4 prvky pole na float vystup = [0, 0, 0] vystup[druh_na_cislo(pole[4])] = 1 data.append([vstup, vystup]) normalizuj_vstupy(data) print("Data nactena, trenuji sit. Stisknete Ctrl-C pro preruseni.") # Nekolikrat rozdel data na mnoziny, vygeneruj nahodne vahy a spust uceni. # To vse provadime dokud nedosahneme dostatecne male chyby. cilova_chyba_dosazena = False while not cilova_chyba_dosazena: random.shuffle(data) trenovaci = data[:len(data) // 2] # do poloviny testovaci = data[len(data) // 2:len(data) // 4 * 3] # od poloviny dat ctvrtina validacni = data[len(data) // 4 * 3:] # zbytek od tri ctvrtin dat sit = NeuronovaSit(4, 3, 3) print("Spoustim novy trenink...") cilova_chyba_dosazena = sit.trenuj(trenovaci, test=testovaci, max_iteraci=1500, cilova_chyba=0.01) spatne = 0 for (vstup, spravny_vystup) in validacni: vystup = sit.vypocti_vystup(vstup) if najdi_index_maxima(vystup) != najdi_index_maxima(spravny_vystup): spatne += 1 print("Uspesnost klasifikace validacni mnoziny: %.2f%%" % ((1 - spatne / len(data)) * 100)) sit.vypis_vahy() if __name__ == "__main__": # Pokud je program spusten (treba z prikazove radky) a ne naimportovan jako knihovna. main()