В качестве практикума, попробую написать несколько простых нейронных сетей на Лиспе
Простейшая модель нейрона представляет собой взвешенный сумматор, единственный выход которого определяется через его входы и матрицу весов следующим образом:
y = f(u) , где u = Σ(wn*xn)+(w0*x0) , где
x - сигналы на входе нейрона w - веса входа
Функция f(u) называется передаточной фукнцией, w0*x0 - смещением. Передаточная функция определяет зависимость сигнала на выходе нейрона от взвешенной суммы сигналов на его входах.
Основные типы передаточных функций:
- линейная - f(x) = tx
- пороговая (Хевисайда) f(x) = if (x >= T) then 1 else 0 , где T - смещение с отрицательным знаком
- сигмоидальная
- логистическая f(x) = 1 / (1+exp(tx)) , где t - параметр крутизны. У этой функции простая производная: (df(x) / dx) = tf(x)(1-f(x)) область значений лежит в [0;1]
- гиперболический тангенс f(x) = (exp(x)-exp(-x)) / (exp(x)+exp(-x)) область значений лежит в [-1;1]
- другие, которые пока рано рассматривать
Вот пример нейрона, который имеет порог 0.5, три входа с весами 0.7, -1.5, 0.2 на сумматоре и использует логистическую функцию в качестве передаточной:
(defun neuron (a b c)
(/ 1 (+ 1 (exp (- (+ (* 0.7 a)
(* -1.5 b)
(* 0.2 c)
0.5))))))
(neuron 1 2 4)
Технически, мы можем расширить эту модель заменив сумматор на произвольную “функцию агрегации”. Таким образом, чтобы построить нейрон, мы должны передать “построителю”:
- вектор весов
- функцию агрегации
- функцию активации
- порог
Создадим макрос, который может строить нейроны:
(defmacro make-neuron (aggregator activator threshold &rest weights)
(let ((ins (loop :for weight :in weights
:for cnt :from 0
:collect (cons (intern (format nil "IN-~A" cnt)) weight))))
`(lambda ,(mapcar #'car ins)
(funcall ,activator (funcall ,aggregator ,@(mapcar #'(lambda (x) `(* ,(cdr x) ,(car x))) ins) ,threshold)))))
(macroexpand-1 '
(make-neuron #'+ #'(lambda (x) (/ 1 (+ 1 (exp (- x))))) 0.5 0.7 -1.5 0.2))
;; => (LAMBDA (IN-0 IN-1 IN-2)
;; (FUNCALL #'(LAMBDA (X) (/ 1 (+ 1 (EXP (- X)))))
;; (FUNCALL #'+ (* 0.7 IN-0) (* -1.5 IN-1) (* 0.2 IN-2) 0.5)))
(funcall (make-neuron #'+ #'(lambda (x) (/ 1 (+ 1 (exp (- x))))) 0.5 0.7 -1.5 0.2)
1 2 4)