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"
|
||||
:description "Deconstruction as a method for implementing machine intelligence."
|
||||
:depends-on (:alexandria
|
||||
:scopes :scopes/test)
|
||||
:components ((:file "decons")
|
||||
:scopes-core :scopes/test)
|
||||
:components ((:file "mlx" :depends-on ("recurse"))
|
||||
(:file "recurse")
|
||||
(:file "xplore"))
|
||||
:long-description "decons: The base system of the decons project."
|
||||
: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
|
||||
(:use :common-lisp)
|
||||
(:local-nicknames (:xplore :decons/xplore)
|
||||
(:local-nicknames (:mlx :decons/mlx)
|
||||
(:r :decons/recurse)
|
||||
(:xplore :decons/xplore)
|
||||
(:t :scopes/testing))
|
||||
(:export #:run)
|
||||
(:import-from :scopes/testing #:deftest #:== #:!=))
|
||||
|
@ -13,7 +15,7 @@
|
|||
(let ((t:*test-suite* (t:test-suite "decons")))
|
||||
(test-basic)
|
||||
(test-tensor)
|
||||
(test-rapply)
|
||||
(test-rcall)
|
||||
(test-rreduce)
|
||||
(test-line)
|
||||
(test-quad)
|
||||
|
@ -39,40 +41,40 @@
|
|||
(== (xplore:scalar-p t1) nil)
|
||||
))
|
||||
|
||||
(deftest test-rapply ()
|
||||
(== (decons:rapply #'1+ 7) 8)
|
||||
(== (decons:rapply #'1+ '(2 3)) '(3 4))
|
||||
(== (decons:radd 2 3) 5)
|
||||
(== (decons:radd 3 '(4 5)) '(7 8))
|
||||
(== (decons:radd '(2 3) '(4 5)) '(6 8)) ; not '((6 7) (7 8))
|
||||
(== (decons:rsub '(6 7) '(4 5)) '(2 2)) ; not '((2 3) (1 2))
|
||||
(== (decons:rsqr '(2 3 4)) '(4 9 16))
|
||||
(deftest test-rcall ()
|
||||
(== (r:call #'1+ 7) 8)
|
||||
(== (r:call #'1+ '(2 3)) '(3 4))
|
||||
(== (r:add 2 3) 5)
|
||||
(== (r:add 3 '(4 5)) '(7 8))
|
||||
(== (r:add '(2 3) '(4 5)) '(6 8)) ; not '((6 7) (7 8))
|
||||
(== (r:sub '(6 7) '(4 5)) '(2 2)) ; not '((2 3) (1 2))
|
||||
(== (r:sqr '(2 3 4)) '(4 9 16))
|
||||
)
|
||||
|
||||
(deftest test-rreduce ()
|
||||
(== (decons:rreduce #'+ '(1 2 (3 4))) 10)
|
||||
(== (decons:rreduce-1 #'+ '(1 2 (3 4))) '(1 2 7))
|
||||
(== (r:reduce-all #'+ '(1 2 (3 4))) 10)
|
||||
(== (r:reduce-1 #'+ '(1 2 (3 4))) '(1 2 7))
|
||||
)
|
||||
|
||||
(defvar *ds1* '((2.0 1.0 4.0 3.0)
|
||||
(1.8 1.2 4.2 3.3)))
|
||||
|
||||
(deftest test-line ()
|
||||
(let ((p1 (decons:line 0.0))
|
||||
(p2 (decons:line 1.0))
|
||||
(let ((p1 (mlx:line 0.0))
|
||||
(p2 (mlx:line 1.0))
|
||||
ps1 objective)
|
||||
(== (funcall p1 '(0.5 2.0)) 2.0)
|
||||
(== (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 '(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)
|
||||
(setf objective (funcall (decons:l2-loss #'decons:line) *ds1*))
|
||||
(setf decons:*obj* objective) ; for interactive experiments
|
||||
(setf objective (funcall (mlx:l2-loss #'mlx:line) *ds1*))
|
||||
(setf mlx:*obj* objective) ; for interactive experiments
|
||||
(== (funcall objective '(0.0 0.0)) 33.21)
|
||||
(== (decons: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:nabla-xp objective '(0.0 0.0)) '(-62.999725 -21.0001))
|
||||
(== (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)
|
||||
|
@ -80,6 +82,6 @@
|
|||
|
||||
(deftest test-quad ()
|
||||
(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))
|
||||
))
|
||||
|
|
Loading…
Add table
Reference in a new issue