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