move functionality to new files / packages recurse and mlx
This commit is contained in:
parent
f08545db8e
commit
a70a1487eb
5 changed files with 151 additions and 158 deletions
|
@ -7,8 +7,9 @@
|
||||||
:homepage "https://www.cyberconcepts.org"
|
:homepage "https://www.cyberconcepts.org"
|
||||||
:description "Deconstruction as a method for implementing machine intelligence."
|
:description "Deconstruction as a method for implementing machine intelligence."
|
||||||
:depends-on (:alexandria
|
:depends-on (:alexandria
|
||||||
:scopes :scopes/test)
|
:scopes-core :scopes/test)
|
||||||
:components ((:file "decons")
|
:components ((:file "mlx" :depends-on ("recurse"))
|
||||||
|
(:file "recurse")
|
||||||
(:file "xplore"))
|
(:file "xplore"))
|
||||||
:long-description "decons: The base system of the decons project."
|
:long-description "decons: The base system of the decons project."
|
||||||
:in-order-to ((test-op (test-op "decons/test"))))
|
:in-order-to ((test-op (test-op "decons/test"))))
|
||||||
|
|
135
decons.lisp
135
decons.lisp
|
@ -1,135 +0,0 @@
|
||||||
;;;; decons
|
|
||||||
|
|
||||||
(defpackage :decons
|
|
||||||
(:use :common-lisp)
|
|
||||||
(:local-nicknames (:shape :scopes/shape)
|
|
||||||
(:util :scopes/util))
|
|
||||||
(:export #:rapply #:rreduce #:rreduce-1 #:radd #:rmul #:rsub #:rdiv #:rsqr
|
|
||||||
#:default-deviation #:l2-loss
|
|
||||||
*revisions* *alpha* #:gradient-descent
|
|
||||||
#:nabla-xp
|
|
||||||
#:line #:quad
|
|
||||||
#:*obj*))
|
|
||||||
|
|
||||||
(in-package :decons)
|
|
||||||
|
|
||||||
;;;; common (basic) stuff
|
|
||||||
|
|
||||||
(defun sqr (x) (* x x))
|
|
||||||
|
|
||||||
(defun sum (data) (reduce #'+ data))
|
|
||||||
|
|
||||||
;;;; rapply, rreduce - recursive application of operations
|
|
||||||
|
|
||||||
(defun rapply (op arg1 &optional arg2)
|
|
||||||
(if arg2
|
|
||||||
;(rcall (rcurry op arg1) arg2)
|
|
||||||
(rcall2 op arg1 arg2)
|
|
||||||
(rcall op arg1)))
|
|
||||||
|
|
||||||
(defgeneric rcall (op arg)
|
|
||||||
(:method (op arg) (funcall op arg))
|
|
||||||
(:method (op (arg list))
|
|
||||||
(mapcar (lambda (i) (rcall op i)) arg)))
|
|
||||||
|
|
||||||
(defgeneric rcall2 (op a1 a2)
|
|
||||||
(:method (op a1 a2) (funcall op a1 a2))
|
|
||||||
(:method (op (a1 list) a2)
|
|
||||||
(mapcar (lambda (i) (rcall2 op i a2)) a1))
|
|
||||||
(:method (op a1 (a2 list))
|
|
||||||
(mapcar (lambda (j) (rcall2 op a1 j)) a2))
|
|
||||||
(:method (op (a1 list) (a2 list))
|
|
||||||
(mapcar (lambda (i j) (rcall2 op i j)) a1 a2)))
|
|
||||||
|
|
||||||
(defgeneric rcurry (op arg)
|
|
||||||
(:method (op arg) (lambda (j) (funcall op arg j)))
|
|
||||||
(:method (op (arg list))
|
|
||||||
(lambda (j) (mapcar (lambda (i) (rapply op i j)) arg))))
|
|
||||||
|
|
||||||
(defun rreduce (op arg &key (initial-value 0))
|
|
||||||
(reduce op arg :initial-value initial-value
|
|
||||||
:key (lambda (v) (relement op v :initial-value initial-value))))
|
|
||||||
|
|
||||||
(defgeneric relement (op v &key initial-value)
|
|
||||||
(:method (op v &key (initial-value 0)) v)
|
|
||||||
(:method (op (v list) &key (initial-value 0))
|
|
||||||
(rreduce op v :initial-value initial-value)))
|
|
||||||
|
|
||||||
(defgeneric rreduce-1 (op arg &key initial-value)
|
|
||||||
(:method (op arg &key (initial-value 0)) arg)
|
|
||||||
(:method (op (arg list) &key (initial-value 0))
|
|
||||||
(if (some #'consp arg)
|
|
||||||
(mapcar (lambda (x) (rreduce-1 op x :initial-value initial-value)) arg)
|
|
||||||
(reduce op arg :initial-value initial-value))))
|
|
||||||
|
|
||||||
(defun radd (a b) (rapply #'+ a b))
|
|
||||||
(defun rmul (a b) (rapply #'* a b))
|
|
||||||
(defun rsub (a b) (rapply #'- a b))
|
|
||||||
(defun rdiv (a b) (rapply #'/ a b))
|
|
||||||
(defun rsqr (a) (rapply #'sqr a))
|
|
||||||
|
|
||||||
;;;; loss calculation, collect trial data (parameters, resulting loss)
|
|
||||||
|
|
||||||
(defun default-deviation (observed calculated &key (norm #'sqr))
|
|
||||||
(sum (mapcar (lambda (a b) (funcall norm (- a b)))
|
|
||||||
observed calculated)))
|
|
||||||
|
|
||||||
(defun l2-loss (target &key (deviation #'default-deviation))
|
|
||||||
(lambda (dataset) ; expectant function
|
|
||||||
(lambda (theta) ; objective function
|
|
||||||
(let* ((objective (funcall target (car dataset)))
|
|
||||||
(calculated (funcall objective theta)))
|
|
||||||
(funcall deviation (cadr dataset) calculated)))))
|
|
||||||
|
|
||||||
;;;; optimization by revision (= gradient descent)
|
|
||||||
|
|
||||||
(defparameter *revisions* 1000)
|
|
||||||
(defparameter *alpha* 0.01)
|
|
||||||
|
|
||||||
(defun gradient-descent (obj theta)
|
|
||||||
(flet ((try-theta (th)
|
|
||||||
(mapcar (lambda (p g) (- p (* *alpha* g)))
|
|
||||||
th (nabla-xp obj th))))
|
|
||||||
(dotimes (ix *revisions*)
|
|
||||||
(setf theta (try-theta theta)))
|
|
||||||
theta))
|
|
||||||
|
|
||||||
;;;; experimental differentiation
|
|
||||||
|
|
||||||
(defvar *diff-variation* 0.01)
|
|
||||||
|
|
||||||
(defun nabla-xp (fn args)
|
|
||||||
"Determine gradients by experiment: vary args and record changes."
|
|
||||||
(let ((base (funcall fn args))
|
|
||||||
(vargs (apply #'vector args))
|
|
||||||
(res nil))
|
|
||||||
(dotimes (ix (length vargs))
|
|
||||||
(push (diff fn vargs ix base) res))
|
|
||||||
(reverse res)))
|
|
||||||
|
|
||||||
(defun diff (fn vargs ix base)
|
|
||||||
(let* ((vdiff *diff-variation*)
|
|
||||||
(val (svref vargs ix))
|
|
||||||
(argsx (copy-seq vargs)) r+ r-)
|
|
||||||
(setf (svref argsx ix) (+ val vdiff))
|
|
||||||
(setf r+ (/ (- (funcall fn (map 'list #'identity argsx)) base) vdiff))
|
|
||||||
(setf (svref argsx ix) (- val vdiff))
|
|
||||||
(setf r- (/ (- base (funcall fn (map 'list #'identity argsx))) vdiff))
|
|
||||||
;(util:lgi base r+ r-)
|
|
||||||
(/ (+ r+ r-) 2)))
|
|
||||||
|
|
||||||
;;;; parameterized target functions
|
|
||||||
|
|
||||||
(defun line (x)
|
|
||||||
(lambda (theta)
|
|
||||||
(radd (rmul (first theta) x) (second theta))))
|
|
||||||
|
|
||||||
(defun quad (x)
|
|
||||||
(lambda (theta)
|
|
||||||
(radd (rmul (first theta) (rsqr x))
|
|
||||||
(radd (rmul (second theta) x)
|
|
||||||
(third theta)))))
|
|
||||||
|
|
||||||
;;;; working area
|
|
||||||
|
|
||||||
(defvar *obj* nil)
|
|
79
mlx.lisp
Normal file
79
mlx.lisp
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
;;;; decons/mlx - machine learning experiments
|
||||||
|
|
||||||
|
(defpackage :decons/mlx
|
||||||
|
(:use :common-lisp)
|
||||||
|
(:local-nicknames (:r :decons/recurse)
|
||||||
|
(:util :scopes/util))
|
||||||
|
(:export #:default-deviation #:l2-loss
|
||||||
|
*revisions* *alpha* #:gradient-descent
|
||||||
|
#:nabla-xp
|
||||||
|
#:line #:quad
|
||||||
|
#:*obj*))
|
||||||
|
|
||||||
|
(in-package :decons/mlx)
|
||||||
|
|
||||||
|
;;;; loss calculation
|
||||||
|
|
||||||
|
(defun default-deviation (observed calculated &key (norm (lambda (x) (* x x))))
|
||||||
|
(reduce #'+ (mapcar (lambda (a b) (funcall norm (- a b)))
|
||||||
|
observed calculated)))
|
||||||
|
|
||||||
|
(defun l2-loss (target &key (deviation #'default-deviation))
|
||||||
|
(lambda (dataset) ; expectant function
|
||||||
|
(lambda (theta) ; objective function
|
||||||
|
(let* ((objective (funcall target (car dataset)))
|
||||||
|
(calculated (funcall objective theta)))
|
||||||
|
(funcall deviation (cadr dataset) calculated)))))
|
||||||
|
|
||||||
|
;;;; optimization by revision (= gradient descent)
|
||||||
|
|
||||||
|
(defparameter *revisions* 1000)
|
||||||
|
(defparameter *alpha* 0.01)
|
||||||
|
|
||||||
|
(defun gradient-descent (obj theta)
|
||||||
|
(flet ((try-theta (th)
|
||||||
|
(mapcar (lambda (p g) (- p (* *alpha* g)))
|
||||||
|
th (nabla-xp obj th))))
|
||||||
|
(dotimes (ix *revisions*)
|
||||||
|
(setf theta (try-theta theta)))
|
||||||
|
theta))
|
||||||
|
|
||||||
|
;;;; experimental differentiation
|
||||||
|
|
||||||
|
(defvar *diff-variation* 0.01)
|
||||||
|
|
||||||
|
(defun nabla-xp (fn args)
|
||||||
|
"Determine gradients by experiment: vary args and record changing results."
|
||||||
|
(let ((base (funcall fn args))
|
||||||
|
(vargs (apply #'vector args))
|
||||||
|
(res nil))
|
||||||
|
(dotimes (ix (length vargs))
|
||||||
|
(push (diff fn vargs ix base) res))
|
||||||
|
(reverse res)))
|
||||||
|
|
||||||
|
(defun diff (fn vargs ix base)
|
||||||
|
(let* ((vdiff *diff-variation*)
|
||||||
|
(val (svref vargs ix))
|
||||||
|
(argsx (copy-seq vargs)) r+ r-)
|
||||||
|
(setf (svref argsx ix) (+ val vdiff))
|
||||||
|
(setf r+ (/ (- (funcall fn (map 'list #'identity argsx)) base) vdiff))
|
||||||
|
(setf (svref argsx ix) (- val vdiff))
|
||||||
|
(setf r- (/ (- base (funcall fn (map 'list #'identity argsx))) vdiff))
|
||||||
|
;(util:lgi base r+ r-)
|
||||||
|
(/ (+ r+ r-) 2)))
|
||||||
|
|
||||||
|
;;;; parameterized target functions
|
||||||
|
|
||||||
|
(defun line (x)
|
||||||
|
(lambda (theta)
|
||||||
|
(r:add (r:mul (first theta) x) (second theta))))
|
||||||
|
|
||||||
|
(defun quad (x)
|
||||||
|
(lambda (theta)
|
||||||
|
(r:add (r:mul (first theta) (r:sqr x))
|
||||||
|
(r:add (r:mul (second theta) x)
|
||||||
|
(third theta)))))
|
||||||
|
|
||||||
|
;;;; working area
|
||||||
|
|
||||||
|
(defvar *obj* nil)
|
46
recurse.lisp
Normal file
46
recurse.lisp
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
;;;; decons/recurse - recursive calculations
|
||||||
|
|
||||||
|
(defpackage :decons/recurse
|
||||||
|
(:use :common-lisp)
|
||||||
|
(:local-nicknames (:util :scopes/util))
|
||||||
|
(:export #:call #:call2 #:reduce-all #:reduce-1 #:add #:mul #:sub #:div #:sqr))
|
||||||
|
|
||||||
|
(in-package :decons/recurse)
|
||||||
|
|
||||||
|
;;;; recursive application of operations
|
||||||
|
|
||||||
|
(defgeneric call (op arg)
|
||||||
|
(:method (op arg) (funcall op arg))
|
||||||
|
(:method (op (arg list))
|
||||||
|
(mapcar (lambda (i) (call op i)) arg)))
|
||||||
|
|
||||||
|
(defgeneric call2 (op a1 a2)
|
||||||
|
(:method (op a1 a2) (funcall op a1 a2))
|
||||||
|
(:method (op (a1 list) a2)
|
||||||
|
(mapcar (lambda (i) (call2 op i a2)) a1))
|
||||||
|
(:method (op a1 (a2 list))
|
||||||
|
(mapcar (lambda (j) (call2 op a1 j)) a2))
|
||||||
|
(:method (op (a1 list) (a2 list))
|
||||||
|
(mapcar (lambda (i j) (call2 op i j)) a1 a2)))
|
||||||
|
|
||||||
|
(defun reduce-all (op arg &key (initial-value 0))
|
||||||
|
(reduce op arg :initial-value initial-value
|
||||||
|
:key (lambda (v) (element op v :initial-value initial-value))))
|
||||||
|
|
||||||
|
(defgeneric element (op v &key initial-value)
|
||||||
|
(:method (op v &key (initial-value 0)) v)
|
||||||
|
(:method (op (v list) &key (initial-value 0))
|
||||||
|
(reduce-all op v :initial-value initial-value)))
|
||||||
|
|
||||||
|
(defgeneric reduce-1 (op arg &key initial-value)
|
||||||
|
(:method (op arg &key (initial-value 0)) arg)
|
||||||
|
(:method (op (arg list) &key (initial-value 0))
|
||||||
|
(if (some #'consp arg)
|
||||||
|
(mapcar (lambda (x) (reduce-1 op x :initial-value initial-value)) arg)
|
||||||
|
(reduce op arg :initial-value initial-value))))
|
||||||
|
|
||||||
|
(defun add (a b) (call2 #'+ a b))
|
||||||
|
(defun mul (a b) (call2 #'* a b))
|
||||||
|
(defun sub (a b) (call2 #'- a b))
|
||||||
|
(defun div (a b) (call2 #'/ a b))
|
||||||
|
(defun sqr (a) (call (lambda (x) (* x x)) a))
|
|
@ -2,7 +2,9 @@
|
||||||
|
|
||||||
(defpackage :test-decons
|
(defpackage :test-decons
|
||||||
(:use :common-lisp)
|
(:use :common-lisp)
|
||||||
(:local-nicknames (:xplore :decons/xplore)
|
(:local-nicknames (:mlx :decons/mlx)
|
||||||
|
(:r :decons/recurse)
|
||||||
|
(:xplore :decons/xplore)
|
||||||
(:t :scopes/testing))
|
(:t :scopes/testing))
|
||||||
(:export #:run)
|
(:export #:run)
|
||||||
(:import-from :scopes/testing #:deftest #:== #:!=))
|
(:import-from :scopes/testing #:deftest #:== #:!=))
|
||||||
|
@ -13,7 +15,7 @@
|
||||||
(let ((t:*test-suite* (t:test-suite "decons")))
|
(let ((t:*test-suite* (t:test-suite "decons")))
|
||||||
(test-basic)
|
(test-basic)
|
||||||
(test-tensor)
|
(test-tensor)
|
||||||
(test-rapply)
|
(test-rcall)
|
||||||
(test-rreduce)
|
(test-rreduce)
|
||||||
(test-line)
|
(test-line)
|
||||||
(test-quad)
|
(test-quad)
|
||||||
|
@ -39,40 +41,40 @@
|
||||||
(== (xplore:scalar-p t1) nil)
|
(== (xplore:scalar-p t1) nil)
|
||||||
))
|
))
|
||||||
|
|
||||||
(deftest test-rapply ()
|
(deftest test-rcall ()
|
||||||
(== (decons:rapply #'1+ 7) 8)
|
(== (r:call #'1+ 7) 8)
|
||||||
(== (decons:rapply #'1+ '(2 3)) '(3 4))
|
(== (r:call #'1+ '(2 3)) '(3 4))
|
||||||
(== (decons:radd 2 3) 5)
|
(== (r:add 2 3) 5)
|
||||||
(== (decons:radd 3 '(4 5)) '(7 8))
|
(== (r:add 3 '(4 5)) '(7 8))
|
||||||
(== (decons:radd '(2 3) '(4 5)) '(6 8)) ; not '((6 7) (7 8))
|
(== (r:add '(2 3) '(4 5)) '(6 8)) ; not '((6 7) (7 8))
|
||||||
(== (decons:rsub '(6 7) '(4 5)) '(2 2)) ; not '((2 3) (1 2))
|
(== (r:sub '(6 7) '(4 5)) '(2 2)) ; not '((2 3) (1 2))
|
||||||
(== (decons:rsqr '(2 3 4)) '(4 9 16))
|
(== (r:sqr '(2 3 4)) '(4 9 16))
|
||||||
)
|
)
|
||||||
|
|
||||||
(deftest test-rreduce ()
|
(deftest test-rreduce ()
|
||||||
(== (decons:rreduce #'+ '(1 2 (3 4))) 10)
|
(== (r:reduce-all #'+ '(1 2 (3 4))) 10)
|
||||||
(== (decons:rreduce-1 #'+ '(1 2 (3 4))) '(1 2 7))
|
(== (r:reduce-1 #'+ '(1 2 (3 4))) '(1 2 7))
|
||||||
)
|
)
|
||||||
|
|
||||||
(defvar *ds1* '((2.0 1.0 4.0 3.0)
|
(defvar *ds1* '((2.0 1.0 4.0 3.0)
|
||||||
(1.8 1.2 4.2 3.3)))
|
(1.8 1.2 4.2 3.3)))
|
||||||
|
|
||||||
(deftest test-line ()
|
(deftest test-line ()
|
||||||
(let ((p1 (decons:line 0.0))
|
(let ((p1 (mlx:line 0.0))
|
||||||
(p2 (decons:line 1.0))
|
(p2 (mlx:line 1.0))
|
||||||
ps1 objective)
|
ps1 objective)
|
||||||
(== (funcall p1 '(0.5 2.0)) 2.0)
|
(== (funcall p1 '(0.5 2.0)) 2.0)
|
||||||
(== (funcall p2 '(0.5 2.0)) 2.5)
|
(== (funcall p2 '(0.5 2.0)) 2.5)
|
||||||
(setf ps1 (decons:line (car *ds1*)))
|
(setf ps1 (mlx:line (car *ds1*)))
|
||||||
(== (funcall ps1 '(0.5 2.0)) '(3.0 2.5 4.0 3.5))
|
(== (funcall ps1 '(0.5 2.0)) '(3.0 2.5 4.0 3.5))
|
||||||
(== (funcall ps1 '(1.0 0.0)) '(2.0 1.0 4.0 3.0))
|
(== (funcall ps1 '(1.0 0.0)) '(2.0 1.0 4.0 3.0))
|
||||||
(== (decons:default-deviation (cadr *ds1*) (funcall ps1 '(1.0 0.0)))
|
(== (mlx:default-deviation (cadr *ds1*) (funcall ps1 '(1.0 0.0)))
|
||||||
0.20999993) ;0.899999861)
|
0.20999993) ;0.899999861)
|
||||||
(setf objective (funcall (decons:l2-loss #'decons:line) *ds1*))
|
(setf objective (funcall (mlx:l2-loss #'mlx:line) *ds1*))
|
||||||
(setf decons:*obj* objective) ; for interactive experiments
|
(setf mlx:*obj* objective) ; for interactive experiments
|
||||||
(== (funcall objective '(0.0 0.0)) 33.21)
|
(== (funcall objective '(0.0 0.0)) 33.21)
|
||||||
(== (decons:nabla-xp objective '(0.0 0.0)) '(-62.999725 -21.0001))
|
(== (mlx:nabla-xp objective '(0.0 0.0)) '(-62.999725 -21.0001))
|
||||||
(== (decons:gradient-descent objective '(0.0 0.0)) '(1.0499986 3.6358833e-6))
|
(== (mlx:gradient-descent objective '(0.0 0.0)) '(1.0499986 3.6358833e-6))
|
||||||
))
|
))
|
||||||
|
|
||||||
(defvar *ds2* '((-1.0 0.0 1.0 2.0 3.0)
|
(defvar *ds2* '((-1.0 0.0 1.0 2.0 3.0)
|
||||||
|
@ -80,6 +82,6 @@
|
||||||
|
|
||||||
(deftest test-quad ()
|
(deftest test-quad ()
|
||||||
(let (ps2 objective)
|
(let (ps2 objective)
|
||||||
(setf ps2 (decons:quad (car *ds2*)))
|
(setf ps2 (mlx:quad (car *ds2*)))
|
||||||
(== (funcall ps2 '(1.0 0.0 0.0)) '(1.0 0.0 1.0 4.0 9.0))
|
(== (funcall ps2 '(1.0 0.0 0.0)) '(1.0 0.0 1.0 4.0 9.0))
|
||||||
))
|
))
|
||||||
|
|
Loading…
Add table
Reference in a new issue