Opisywany perceptron nie posiada trwałej pamięci i składa się z dwóch niezależnych od siebie neuronów, z których każdy symuluje jedną bramkę. Bramki posiadają po dwa wejścia (A i B) i na każde z nich, można wprowadzić po jednej dowolnej wartości, ze zbioru liczb całkowitych {0,1}. Korzystając z wariacji z powtórzeniami, otrzymujemy 4 różne warunki wejściowe.
Dlaczego nie XOR ? Nie ma możliwości opisania jednym neuronem bramki XOR, ponieważ pojedynczy neuron nie potrafi odróżniać zbiorów nieseparowalnych liniowo.
Neuron perceptronu, jako bramka logiczna, musi posiadać dwa wejścia. Sygnał na każde z wejść, jest wyrażony wartością (s=xw+b) gdzie:
s - sygnał,
x - liczba całkowita z przedziału {0,1},
w - waga wejścia, jak ważne jest to co wchodzi na dane wejście, należy pamiętać, że w > 0,
b - odchylenie (bias), jest to wartość odpowiadająca za nieliniowe przekształcenie wejść w wyjście
W opisywanym perceptronie wartość x = {0,1}, waga poszczególnych wejść = 1, a odchylenie = 0.
Ostatnią wartością jaką musimy określić jest poziom aktywacji neuronu, który w rzeczywistości, jest sumatorem wartości sygnałów wejściowych. I tak dla:
- bramki AND: poziom aktywacji >= 2,
- bramki OR: poziom aktywacji >= 1,
- bramki XOR: 2 > poziom aktywacji >= 1
Poziom aktywacji to wartość graniczna, po której neuron zaczyna zwracać dane na wyjście.
Dlaczego Asembler ? Złożone sieci neuronowe będą wymagały szybkich urządzeń, a pisanie w języku niskiego poziomu zmniejsza ilość kodu wynikowego oraz daje możliwość optymalizowania procedur już na poziomie wykorzystania rejestrów - o czym przekonamy się w prezentowanym przykładzie.
OPTION DOTNAME option casemap:none .DATA weight_A db 1 ; waga dla operacji AND i OR ustalona dla wejścia A = 1 weight_B db 1 ; waga dla operacji AND i OR ustalona dla wejścia B = 1 bias_A db 0 ; odchylenie dla operacji AND i OR ustalone dla wejścia A = 0 bias_B db 0 ; odchylenie dla operacji AND i OR ustalone dla wejścia B = 0 .CODE start: main PROC xor rax, rax ; zerujemu rejestry: RAX, RCX, R9, R10 xor rcx, rcx xor r9, r9 xor r10, r10 mov rbx, 0101000101000000h ; dane wejściowe - w opisywanym przypadku mamy 4 możliwości ; dla wejść AB - 11, 01, 10, 00. Aby zaoszczędzić pamięć ; posłużyłem się rejestrem 64 bitowym, ponieważ wszystkie ; próbki (przy założeniu, że każda z nich zajmuje 16 ; bitów, a każde wejście 8) właśnie tyle miejsca wymagają. ; każda próbka składa się z 2 bajtów, po jednym dla wejść A i B @run_neuron: xor r8, r8 ; wejście A - oba neurony obsługujemy jedną parą wejść AB mov cl, BYTE PTR [weight_A] ; waga dla wejścia A neuronów AND i OR mov al, bl ; pobieramy pierwszy bajt z próbki dla wejścia A, imul cl ; mnożenie wagi z bajtem wejściowym add al, BYTE PTR [bias_A] ; dodajemy odchylenie add r8, rax ; wynik zapisujemy w R8 - rejestrze wynikowym dla neuronów shr rbx, 08h ; przechodzimy do następnego bajtu z próbki mov cl, BYTE PTR [weight_B] ; waga dla wejścia B neuronów AND i OR mov al, bl ; pobieramy drugi bajt z próbki dla wejścia B, imul cl ; mnożenie wagi z bajtem wejściowym add al, BYTE PTR [bias_B] ; dodajemy odchylenie add r8, rax ; wynik dodajemy do wyniku z wejścia A w R8 shr rbx, 08h ; przechodzimy do następnej próbki @check_and: cmp r8, 2 ; w R8 trzymamy wynik, aby porównać go z poziomem aktywacji ; neuronu. Dla bramki AND poziom ten wynosi 2 jge @and_active ; jeśli w R8 jest wartość >= 2 to aktywujemy neuron AND @check_or: cmp r8, 1 ; w R8 trzymamy wynik, aby porównać go z poziomem aktywacji ; neuronu. Dla bramki OR poziom ten wynosi 1 jge @or_active ; jeśli w R8 jest wartość >= 1 to aktywujemy neuron OR jmp @next ; po zakończeniu analizy sprawdzamy czy jest kolejna próbka @and_active: inc r9 ; aktywacja neuronu AND w naszym przypadku to zwiększenie R9 o 1 ; w ten sposób policzymy ile razy aktywował się neuron, a dla ; operacji AND powinien zrobić to tylko jeden raz - tabelka jmp @check_or ; tutaj przechodzimy do sprawdzenia czy aktywować neuron OR ; dla takich samych parametrów wejściowych @or_active: inc r10 ; aktywacja neuronu OR w naszym przypadku to zwiększenie R10 o 1 ; w ten sposób policzymy ile razy aktywował się neuron, a dla ; operacji OR powinien zrobić to trzy razy - tabelka @next: cmp rbx, 0 ; przesuwanie RBX o 8 bitów powoduje przechodzenie do kolejnej ; wartości wejść AB i po 4 próbkach rejestr się wyzeruje. Dzieje ; się tak dlatego, że dane wejściowe zostały zapisane w ; kolejności 11, 01, 10, 00 - inny zapis spowodowałby błędne ; działanie programu - tzn. nie wszystkie próbki zostały by ; sprawdzone jnz @run_neuron ; jeśli RBX > 0 to przechodzimy do następnej próbki ret main ENDP END
W prezentowanym przykładzie jest sporo niepotrzebnego kodu, bo waga = 1, a odchylenie = 0. Operacje odpowiedzialne za ich obsługę można śmiało usunąć. Etykiety również można poukładać w taki sposób. aby zoptymalizować działanie programu. Kod został napisany tak, aby pokazać przebieg procedury. Ostatecznie śledząc program powinniśmy otrzymać, przed wyjściem z niego, wartości R9 = 1, a R10 = 3. Analizując kod zobaczymy, które próbki aktywowały neurony AND i OR. Wszystko wyszło zgodnie z tabelką działania bramek logicznych bez użycia instrukcji and i or w kodzie.
Brak komentarzy:
Prześlij komentarz