MGL Manual
Table of Contents
- [1 Introduction][f7aa]
- [1.1 Overview][9192]
- [1.2 Links][00ee]
- [1.3 Dependencies][e7ea]
- [1.4 Code Organization][443c]
- [1.5 Glossary][4a8e]
- [2 Common Stuff][e198]
- [3 Datasets][109e]
- [3.1 Samplers][7bc3]
- [3.1.1 Function Sampler][be8d]
- [3.1 Samplers][7bc3]
- [4 Resampling][a39b]
- [4.1 Shuffling][8611]
- [4.2 Partitions][f790]
- [4.3 Cross-validation][f17b]
- [4.4 Bagging][b647]
- [4.5 CV Bagging][3f9f]
- [4.6 Miscellaneous Operations][59c2]
- [5 Core][f257]
- [5.1 Persistence][29a1]
- [5.2 Batch Processing][ff82]
- [5.3 Executors][4476]
- [5.3.1 Parameterized Executor Cache][ada2]
- [6 Monitoring][e668]
- [6.1 Monitors][c701]
- [6.2 Measurers][cd3b]
- [6.3 Counters][be95]
- [6.3.1 Attributes][6da5]
- [6.3.2 Counter classes][7ee3]
- [7 Classification][60e3]
- [7.1 Classification Monitors][c573]
- [7.2 Classification Measurers][0ba7]
- [7.3 Classification Counters][6598]
- [7.3.1 Confusion Matrices][07c7]
- [8 Features][c8db]
- [8.1 Feature Selection][1b5e]
- [8.2 Feature Encoding][24aa]
- [9 Gradient Based Optimization][c74a]
- [9.1 Iterative Optimizer][779d]
- [9.2 Cost Function][e746]
- [9.3 Gradient Descent][10e7]
- [9.3.1 Batch Based Optimizers][2c39]
- [9.3.2 Segmented GD Optimizer][989a]
- [9.3.3 Per-weight Optimization][a884]
- [9.3.4 Utilities][c40e]
- [9.4 Conjugate Gradient][83e6]
- [9.5 Extension API][6a6f]
- [9.5.1 Implementing Optimizers][5748]
- [9.5.2 Implementing Gradient Sources][c58b]
- [9.5.3 Implementing Gradient Sinks][a210]
- [10 Differentiable Functions][2981]
- [11 Backpropagation Neural Networks][8788]
- [11.1 Backprop Overview][56b2]
- [11.2 Clump API][7a28]
- [11.3
BPNs][d1e0]- [11.3.1 Training][0d82]
- [11.3.2 Monitoring][4f0e]
- [11.3.3 Feed-Forward Nets][1355]
- [11.3.4 Recurrent Neural Nets][871e]
- [11.4 Lumps][9641]
- [11.4.1 Lump Base Class][3045]
- [11.4.2 Inputs][207b]
- [11.4.3 Weight Lump][6872]
- [11.4.4 Activations][9105]
- [11.4.5 Activation Functions][5d86]
- [11.4.6 Losses][93a7]
- [11.4.7 Stochasticity][aa2e]
- [11.4.8 Arithmetic][2fe9]
- [11.4.9 Operations for
RNNs][51f7]
- [11.5 Utilities][91f3]
- [12 Boltzmann Machines][332e]
- [13 Gaussian Processes][60b3]
- [14 Natural Language Processing][0d6a]
- [14.1 Bag of Words][0784]
- [15 Logging][3f42]
[in package MGL]
-
[system] "mgl"
- Version: 0.1.0
- Description:
MGLis a machine learning library for backpropagation neural networks, boltzmann machines, gaussian processes and more. - Licence: MIT, see COPYING.
- Author: Gábor Melis
- Mailto: mega@retes.hu
- Homepage:
- Bug tracker:
- Source control: GIT
- Depends on: alexandria, array-operations, cl-reexport, closer-mop, lla, mgl-gnuplot, mgl-mat, mgl-pax, named-readtables, num-utils, pythonic-string-reader, swank(?)
1 Introduction
1.1 Overview
MGL is a Common Lisp machine learning library by Gábor Melis with some parts originally contributed by Ravenpack International. It mainly concentrates on various forms of neural networks (boltzmann machines, feed-forward and recurrent backprop nets). Most of MGL is built on top of MGL-MAT so it has BLAS and CUDA support.
In general, the focus is on power and performance not on ease of use. Perhaps one day there will be a cookie cutter interface with restricted functionality if a reasonable compromise is found between power and utility.
1.2 Links
The official repository is , and this document in available in various formats on for the latest version.
1.3 Dependencies
MGL used to rely on LLA to interface to BLAS and LAPACK. That's mostly history by now, but configuration of foreign libraries is still done via LLA. See the README in LLA on how to set things up. Note that these days OpenBLAS is easier to set up and just as fast as ATLAS.
CL-CUDA and
MGL-MAT are the two main
dependencies and also the ones not yet in quicklisp, so just drop
them into quicklisp/local-projects/. If there is no suitable GPU
on the system or the CUDA SDK is not installed, MGL will simply
fall back on using BLAS and Lisp code. Wrapping code in
MGL-MAT:WITH-CUDA* is basically all that's needed to run on the GPU,
and with MGL-MAT:CUDA-AVAILABLE-P one can check whether the GPU is
really being used.
1.4 Code Organization
MGL consists of several packages dedicated to different tasks.
For example, package MGL-RESAMPLE is about
[Resampling][a39b] and MGL-GD is about [Gradient Descent][10e7]
and so on. On one hand, having many packages makes it easier to
cleanly separate API and implementation and also to explore into a
specific task. At other times, they can be a hassle, so the MGL
package itself reexports every external symbol found in all the
other packages that make up MGL and MGL-MAT (see
MAT Manual) on which it heavily relies.
One exception to this rule is the bundled, but independent MGL-GNUPLOT library.
The built in tests can be run with:
(ASDF:OOS 'ASDF:TEST-OP '#:MGL)
Note, that most of the tests are rather stochastic and can fail once in a while.
1.5 Glossary
Ultimately machine learning is about creating models of some domain. The observations in the modelled domain are called instances (also known as examples or samples). Sets of instances are called datasets. Datasets are used when fitting a model or when making predictions. Sometimes the word predictions is too specific, and the results obtained from applying a model to some instances are simply called results.
2 Common Stuff
[in package MGL-COMMON]
-
[generic-function] NAME OBJECT
-
[function] NAME= X Y
Return
Tif X and Y are [EQL][38a2] or if they are structured components whose elements are [EQUAL][3fb5]. Strings and bit-vectors areEQUALif they are the same length and have identical components. Other arrays must be [EQ][5a82] to beEQUAL. -
[generic-function] SIZE OBJECT
-
[generic-function] NODES OBJECT
Returns a
MGL-MAT:MATobject representing the state or result ofOBJECT. The first dimension of the returned matrix is equal to the number of stripes. -
[generic-function] DEFAULT-VALUE OBJECT
-
[generic-function] GROUP-SIZE OBJECT
-
[generic-function] BATCH-SIZE OBJECT
-
[generic-function] WEIGHTS OBJECT
-
[generic-function] SCALE OBJECT
3 Datasets
[in package MGL-DATASET]
An instance can often be any kind of object of the user's choice.
It is typically represented by a set of numbers which is called a
feature vector or by a structure holding the feature vector, the
label, etc. A dataset is a [SEQUENCE][ae23] of such instances or a
[Samplers][7bc3] object that produces instances.
-
[function] MAP-DATASET FN DATASET
Call
FNwith each instance inDATASET. This is basically equivalent to iterating over the elements of a sequence or a sampler (see [Samplers][7bc3]). -
[function] MAP-DATASETS FN DATASETS &KEY IMPUTE
Call
FNwith a list of instances, one from each dataset inDATASETS. Return nothing. IfIMPUTEis specified then iterate until the largest dataset is consumed imputingIMPUTEfor missing values. IfIMPUTEis not specified then iterate until the smallest dataset runs out.(map-datasets #'prin1 '((0 1 2) (:a :b))) .. (0 :A)(1 :B) (map-datasets #'prin1 '((0 1 2) (:a :b)) :impute nil) .. (0 :A)(1 :B)(2 NIL)It is of course allowed to mix sequences with samplers:
(map-datasets #'prin1 (list '(0 1 2) (make-sequence-sampler '(:a :b) :max-n-samples 2))) .. (0 :A)(1 :B)
3.1 Samplers
Some algorithms do not need random access to the entire dataset and
can work with a stream observations. Samplers are simple generators
providing two functions: [SAMPLE][f956] and [FINISHEDP][401f].
-
[generic-function] SAMPLE SAMPLER
If
SAMPLERhas not run out of data (see [FINISHEDP][401f])SAMPLEreturns an object that represents a sample from the world to be experienced or, in other words, simply something the can be used as input for training or prediction. It is not allowed to callSAMPLEifSAMPLERisFINISHEDP. -
[generic-function] FINISHEDP SAMPLER
See if
SAMPLERhas run out of examples. -
[function] LIST-SAMPLES SAMPLER MAX-SIZE
Return a list of samples of length at most
MAX-SIZEor less ifSAMPLERruns out. -
[function] MAKE-SEQUENCE-SAMPLER SEQ &KEY MAX-N-SAMPLES
Create a sampler that returns elements of
SEQin their original order. IfMAX-N-SAMPLESis non-nil, then at mostMAX-N-SAMPLESare sampled. -
[function] MAKE-RANDOM-SAMPLER SEQ &KEY MAX-N-SAMPLES (REORDER #'MGL-RESAMPLE:SHUFFLE)
Create a sampler that returns elements of
SEQin random order. IfMAX-N-SAMPLESis non-nil, then at mostMAX-N-SAMPLESare sampled. The first pass over a shuffled copy ofSEQ, and this copy is reshuffled whenever the sampler reaches the end of it. Shuffling is performed by calling theREORDERfunction. -
[variable] *INFINITELY-EMPTY-DATASET* *#*
This is the default dataset for [
MGL-OPT:MINIMIZE][46a4]. It's an infinite stream ofNILs.
3.1.1 Function Sampler
-
[class] FUNCTION-SAMPLER
A sampler with a function in its [
GENERATOR][08ac] that produces a stream of samples which may or may not be finite depending on [MAX-N-SAMPLES][1cab]. [FINISHEDP][401f] returnsTiffMAX-N-SAMPLESis non-nil, and it's not greater than the number of samples generated ([N-SAMPLES][bdf9]).(list-samples (make-instance 'function-sampler :generator (lambda () (random 10)) :max-n-samples 5) 10) => (3 5 2 3 3) -
[reader] GENERATOR [FUNCTION-SAMPLER][715c] (:GENERATOR)
A generator function of no arguments that returns the next sample.
-
[accessor] MAX-N-SAMPLES [FUNCTION-SAMPLER][715c] (:MAX-N-SAMPLES = NIL)
-
[reader] NAME [FUNCTION-SAMPLER][715c] (:NAME = NIL)
An arbitrary object naming the sampler. Only used for printing the sampler object.
-
[reader] N-SAMPLES [FUNCTION-SAMPLER][715c] (:N-SAMPLES = 0)
4 Resampling
[in package MGL-RESAMPLE]
The focus of this package is on resampling methods such as cross-validation and bagging which can be used for model evaluation, model selection, and also as a simple form of ensembling. Data partitioning and sampling functions are also provided because they tend to be used together with resampling.
4.1 Shuffling
-
[function] SHUFFLE SEQ
Copy of
SEQand shuffle it using Fisher-Yates algorithm. -
[function] SHUFFLE! SEQ
Shuffle
SEQusing Fisher-Yates algorithm.
4.2 Partitions
The following functions partition a dataset (currently only
[SEQUENCE][ae23]s are supported) into a number of partitions. For each
element in the original dataset there is exactly one partition that
contains it.
-
[function] FRACTURE FRACTIONS SEQ &KEY WEIGHT
Partition
SEQinto a number of subsequences.FRACTIONSis either a positive integer or a list of non-negative real numbers.WEIGHTisNILor a function that returns a non-negative real number when called with an element fromSEQ. IfFRACTIONSis a positive integer then return a list of that many subsequences with equal sum of weights bar rounding errors, else partitionSEQinto subsequences, where the sum of weights of subsequence I is proportional to element I ofFRACTIONS. IfWEIGHTisNIL, then it's element is assumed to have the same weight.To split into 5 sequences:
(fracture 5 '(0 1 2 3 4 5 6 7 8 9)) => ((0 1) (2 3) (4 5) (6 7) (8 9))To split into two sequences whose lengths are proportional to 2 and 3:
(fracture '(2 3) '(0 1 2 3 4 5 6 7 8 9)) => ((0 1 2 3) (4 5 6 7 8 9)) -
[function] STRATIFY SEQ &KEY (KEY #'IDENTITY) (TEST #'EQL)
Return the list of strata of
SEQ.SEQis a sequence of elements for which the functionKEYreturns the class they belong to. Such classes are opaque objects compared for equality withTEST. A stratum is a sequence of elements with the same (underTEST)KEY.(stratify '(0 1 2 3 4 5 6 7 8 9) :key #'evenp) => ((0 2 4 6 8) (1 3 5 7 9)) -
[function] FRACTURE-STRATIFIED FRACTIONS SEQ &KEY (KEY #'IDENTITY) (TEST #'EQL) WEIGHT
Similar to [
FRACTURE][6f82], but also makes sure that keys are evenly distributed among the partitions (see [STRATIFY][ba91]). It can be useful for classification tasks to partition the data set while keeping the distribution of classes the same.Note that the sets returned are not in random order. In fact, they are sorted internally by
KEY.For example, to make two splits with approximately the same number of even and odd numbers:
(fracture-stratified 2 '(0 1 2 3 4 5 6 7 8 9) :key #'evenp) => ((0 2 1 3) (4 6 8 5 7 9))
4.3 Cross-validation
-
[function] CROSS-VALIDATE DATA FN &KEY (N-FOLDS 5) (FOLDS (ALEXANDRIA:IOTA N-FOLDS)) (SPLIT-FN #'SPLIT-FOLD/MOD) PASS-FOLD
Map
FNover theFOLDSofDATAsplit withSPLIT-FNand collect the results in a list. The simplest demonstration is:(cross-validate '(0 1 2 3 4) (lambda (test training) (list test training)) :n-folds 5) => (((0) (1 2 3 4)) ((1) (0 2 3 4)) ((2) (0 1 3 4)) ((3) (0 1 2 4)) ((4) (0 1 2 3)))Of course, in practice one would typically train a model and return the trained model and/or its score on
TEST. Also, sometimes one may want to do only some of the folds and remember which ones they were:(cross-validate '(0 1 2 3 4) (lambda (fold test training) (list :fold fold test training)) :folds '(2 3) :pass-fold t) => ((:fold 2 (2) (0 1 3 4)) (:fold 3 (3) (0 1 2 4)))Finally, the way the data is split can be customized. By default [
SPLIT-FOLD/MOD][5ded] is called with the argumentsDATA, the fold (from amongFOLDS) andN-FOLDS.SPLIT-FOLD/MODreturns two values which are then passed on toFN. One can use [SPLIT-FOLD/CONT][5293] or [SPLIT-STRATIFIED][8cb8] or any other function that works with these arguments. The only real constraint is thatFNhas to take as many arguments (plus the fold argument ifPASS-FOLD) asSPLIT-FNreturns. -
[function] SPLIT-FOLD/MOD SEQ FOLD N-FOLDS
Partition
SEQinto two sequences: one with elements ofSEQwith indices whose remainder isFOLDwhen divided withN-FOLDS, and a second one with the rest. The second one is the larger set. The order of elements remains stable. This function is suitable as theSPLIT-FNargument of [CROSS-VALIDATE][9524]. -
[function] SPLIT-FOLD/CONT SEQ FOLD N-FOLDS
Imagine dividing
SEQintoN-FOLDSsubsequences of the same size (bar rounding). Return the subsequence of indexFOLDas the first value and the all the other subsequences concatenated into one as the second value. The order of elements remains stable. This function is suitable as theSPLIT-FNargument of [CROSS-VALIDATE][9524]. -
[function] SPLIT-STRATIFIED SEQ FOLD N-FOLDS &KEY (KEY #'IDENTITY) (TEST #'EQL) WEIGHT
Split
SEQintoN-FOLDSpartitions (as in [FRACTURE-STRATIFIED][627a]). Return the partition of indexFOLDas the first value, and the concatenation of the rest as the second value. This function is suitable as theSPLIT-FNargument of [CROSS-VALIDATE][9524] (mostly likely as a closure withKEY,TEST,WEIGHTbound).
4.4 Bagging
-
[function] BAG SEQ FN &KEY (RATIO 1) N WEIGHT (REPLACEMENT T) KEY (TEST #'EQL) (RANDOM-STATE *RANDOM-STATE*)
Sample from
SEQwith [SAMPLE-FROM][86fd] (passingRATIO,WEIGHT,REPLACEMENT), or [SAMPLE-STRATIFIED][aee6] ifKEYis notNIL. CallFNwith the sample. IfNisNILthen keep repeating this untilFNperforms a non-local exit. ElseNmust be a non-negative integer,Niterations will be performed, the primary values returned byFNcollected into a list and returned. SeeSAMPLE-FROMandSAMPLE-STRATIFIEDfor examples. -
[function] SAMPLE-FROM RATIO SEQ &KEY WEIGHT REPLACEMENT (RANDOM-STATE *RANDOM-STATE*)
Return a sequence constructed by sampling with or without
REPLACEMENTfromSEQ. The sum of weights in the result sequence will approximately be the sum of weights ofSEQtimesRATIO. IfWEIGHTisNILthen elements are assumed to have equal weights, elseWEIGHTshould return a non-negative real number when called with an element ofSEQ.To randomly select half of the elements:
(sample-from 1/2 '(0 1 2 3 4 5)) => (5 3 2)To randomly select some elements such that the sum of their weights constitute about half of the sum of weights across the whole sequence:
(sample-from 1/2 '(0 1 2 3 4 5 6 7 8 9) :weight #'identity) => ;; sums to 28 that's near 45/2 (9 4 1 6 8)To sample with replacement (that is, allowing the element to be sampled multiple times):
(sample-from 1 '(0 1 2 3 4 5) :replacement t) => (1 1 5 1 4 4) -
[function] SAMPLE-STRATIFIED RATIO SEQ &KEY WEIGHT REPLACEMENT (KEY #'IDENTITY) (TEST #'EQL) (RANDOM-STATE *RANDOM-STATE*)
Like [
SAMPLE-FROM][86fd] but makes sure that the weighted proportion of classes in the result is approximately the same as the proportion inSEQ. See [STRATIFY][ba91] for the description ofKEYandTEST.
4.5 CV Bagging
-
[function] BAG-CV DATA FN &KEY N (N-FOLDS 5) (FOLDS (ALEXANDRIA:IOTA N-FOLDS)) (SPLIT-FN #'SPLIT-FOLD/MOD) PASS-FOLD (RANDOM-STATE *RANDOM-STATE*)
Perform cross-validation on different shuffles of
DATANtimes and collect the results. Since [CROSS-VALIDATE][9524] collects the return values ofFN, the return value of this function is a list of lists ofFNresults. IfNisNIL, don't collect anything just keep doing repeated CVs untilFNperforms a non-local exit.The following example simply collects the test and training sets for 2-fold CV repeated 3 times with shuffled data:
;;; This is non-deterministic. (bag-cv '(0 1 2 3 4) #'list :n 3 :n-folds 2) => ((((2 3 4) (1 0)) ((1 0) (2 3 4))) (((2 1 0) (4 3)) ((4 3) (2 1 0))) (((1 0 3) (2 4)) ((2 4) (1 0 3))))CV bagging is useful when a single CV is not producing stable results. As an ensemble method, CV bagging has the advantage over bagging that each example will occur the same number of times and after the first CV is complete there is a complete but less reliable estimate for each example which gets refined by further CVs.
4.6 Miscellaneous Operations
-
[function] SPREAD-STRATA SEQ &KEY (KEY #'IDENTITY) (TEST #'EQL)
Return a sequence that's a reordering of
SEQsuch that elements belonging to different strata (underKEYandTEST, see [STRATIFY][ba91]) are distributed evenly. The order of elements belonging to the same stratum is unchanged.For example, to make sure that even and odd numbers are distributed evenly:
(spread-strata '(0 2 4 6 8 1 3 5 7 9) :key #'evenp) => (0 1 2 3 4 5 6 7 8 9)Same thing with unbalanced classes:
(spread-strata (vector 0 2 3 5 6 1 4) :key (lambda (x) (if (member x '(1 4)) t nil))) => #(0 1 2 3 4 5 6) -
[function] ZIP-EVENLY SEQS &KEY RESULT-TYPE
Make a single sequence out of the sequences in
SEQSso that in the returned sequence indices of elements belonging to the same source sequence are spread evenly across the whole range. The result is a list isRESULT-TYPEis [LIST][9271], it's a vector ifRESULT-TYPEis [VECTOR][64b3]. IfRESULT-TYPEisNIL, then it's determined by the type of the first sequence inSEQS.(zip-evenly '((0 2 4) (1 3))) => (0 1 2 3 4)
5 Core
[in package MGL-CORE]
5.1 Persistence
-
[function] LOAD-STATE FILENAME OBJECT
Load weights of
OBJECTfromFILENAME. ReturnOBJECT. -
[function] SAVE-STATE FILENAME OBJECT &KEY (IF-EXISTS :ERROR) (ENSURE T)
Save weights of
OBJECTtoFILENAME. IfENSURE, then [ENSURE-DIRECTORIES-EXIST][876d] is called onFILENAME.IF-EXISTSis passed on to [OPEN][6547]. ReturnOBJECT. -
[function] READ-STATE OBJECT STREAM
Read the weights of
OBJECTfrom the bivalentSTREAMwhere weights mean the learnt parameters. There is currently no sanity checking of data which will most certainly change in the future together with the serialization format. ReturnOBJECT. -
[function] WRITE-STATE OBJECT STREAM
Write weight of
OBJECTto the bivalentSTREAM. ReturnOBJECT. -
[generic-function] READ-STATE* OBJECT STREAM CONTEXT
This is the extension point for [
READ-STATE][8148]. It is guaranteed that primaryREAD-STATE*methods will be called only once for eachOBJECT(under [EQ][5a82]).CONTEXTis an opaque object and must be passed on to any recursiveREAD-STATE*calls. -
[generic-function] WRITE-STATE* OBJECT STREAM CONTEXT
This is the extension point for [
WRITE-STATE][95fe]. It is guaranteed that primaryWRITE-STATE*methods will be called only once for eachOBJECT(under [EQ][5a82]).CONTEXTis an opaque object and must be passed on to any recursiveWRITE-STATE*calls.
5.2 Batch Processing
Processing instances one by one during training or prediction can be slow. The models that support batch processing for greater efficiency are said to be striped.
Typically, during or after creating a model, one sets [MAX-N-STRIPES][16c4]
on it a positive integer. When a batch of instances is to be fed to
the model it is first broken into subbatches of length that's at
most MAX-N-STRIPES. For each subbatch, [SET-INPUT][0c9e] (FIXDOC) is called
and a before method takes care of setting [N-STRIPES][8dd7] to the actual
number of instances in the subbatch. When MAX-N-STRIPES is set
internal data structures may be resized which is an expensive
operation. Setting N-STRIPES is a comparatively cheap operation,
often implemented as matrix reshaping.
Note that for models made of different parts (for example,
[MGL-BP:BPN][5187] consists of [MGL-BP:LUMP][c1ac]s) , setting these
values affects the constituent parts, but one should never change
the number stripes of the parts directly because that would lead to
an internal inconsistency in the model.
-
[generic-function] MAX-N-STRIPES OBJECT
The number of stripes with which the
OBJECTis capable of dealing simultaneously. -
[generic-function] SET-MAX-N-STRIPES MAX-N-STRIPES OBJECT
Allocate the necessary stuff to allow for
MAX-N-STRIPESnumber of stripes to be worked with simultaneously inOBJECT. This is called whenMAX-N-STRIPESis [SETF][a138]'ed. -
[generic-function] N-STRIPES OBJECT
The number of stripes currently present in
OBJECT. This is at most [MAX-N-STRIPES][16c4]. -
[generic-function] SET-N-STRIPES N-STRIPES OBJECT
Set the number of stripes (out of [
MAX-N-STRIPES][16c4]) that are in use inOBJECT. This is called whenN-STRIPESis [SETF][a138]'ed. -
[macro] WITH-STRIPES SPECS &BODY BODY
Bind start and optionally end indices belonging to stripes in striped objects.
(WITH-STRIPES ((STRIPE1 OBJECT1 START1 END1) (STRIPE2 OBJECT2 START2) ...) ...)This is how one's supposed to find the index range corresponding to the Nth input in an input lump of a bpn:
(with-stripes ((n input-lump start end)) (loop for i upfrom start below end do (setf (mref (nodes input-lump) i) 0d0)))Note how the input lump is striped, but the matrix into which we are indexing ([
NODES][cc1c]) is not known toWITH-STRIPES. In fact, for lumps the same stripe indices work withNODESand [MGL-BP:DERIVATIVES][a81b]. -
[generic-function] STRIPE-START STRIPE OBJECT
Return the start index of
STRIPEin some array or matrix ofOBJECT. -
[generic-function] STRIPE-END STRIPE OBJECT
Return the end index (exclusive) of
STRIPEin some array or matrix ofOBJECT. -
[generic-function] SET-INPUT INSTANCES MODEL
Set
INSTANCESas inputs inMODEL.INSTANCESis always a [SEQUENCE][ae23] of instances even for models not capable of batch operation. It sets [N-STRIPES][8dd7] to ([LENGTH][2f78]INSTANCES) in a:BEFOREmethod. -
[function] MAP-BATCHES-FOR-MODEL FN DATASET MODEL
Call
FNwith batches of instances fromDATASETsuitable forMODEL. The number of instances in a batch is [MAX-N-STRIPES][16c4] ofMODELor less if there are no more instances left. -
[macro] DO-BATCHES-FOR-MODEL (BATCH (DATASET MODEL)) &BODY BODY
Convenience macro over [
MAP-BATCHES-FOR-MODEL][5fdc].
5.3 Executors
-
[generic-function] MAP-OVER-EXECUTORS FN INSTANCES PROTOTYPE-EXECUTOR
Divide
INSTANCESbetween executors that perform the same function asPROTOTYPE-EXECUTORand callFNwith the instances and the executor for which the instances are.Some objects conflate function and call: the forward pass of a [
MGL-BP:BPN][5187] computes output from inputs so it is like a function but it also doubles as a function call in the sense that the bpn (function) object changes state during the computation of the output. Hence not even the forward pass of a bpn is thread safe. There is also the restriction that all inputs must be of the same size.For example, if we have a function that builds bpn a for an input of a certain size, then we can create a factory that creates bpns for a particular call. The factory probably wants to keep the weights the same though. In [Parameterized Executor Cache][ada2], [
MAKE-EXECUTOR-WITH-PARAMETERS][331b] is this factory.Parallelization of execution is another possibility
MAP-OVER-EXECUTORSallows, but there is no prebuilt solution for it, yet.The default implementation simply calls
FNwithINSTANCESandPROTOTYPE-EXECUTOR. -
[macro] DO-EXECUTORS (INSTANCES OBJECT) &BODY BODY
Convenience macro on top of [
MAP-OVER-EXECUTORS][b01b].
5.3.1 Parameterized Executor Cache
-
[class] PARAMETERIZED-EXECUTOR-CACHE-MIXIN
Mix this into a model, implement [
INSTANCE-TO-EXECUTOR-PARAMETERS][0078] and [MAKE-EXECUTOR-WITH-PARAMETERS][331b] and [DO-EXECUTORS][f98e] will be to able build executors suitable for different instances. The canonical example is using a BPN to compute the means and convariances of a gaussian process. Since each instance is made of a variable number of observations, the size of the input is not constant, thus we have a bpn (an executor) for each input dimension (the parameters). -
[generic-function] MAKE-EXECUTOR-WITH-PARAMETERS PARAMETERS CACHE
Create a new executor for
PARAMETERS.CACHEis a [PARAMETERIZED-EXECUTOR-CACHE-MIXIN][d3b2]. In the BPN gaussian process example,PARAMETERSwould be a list of input dimensions. -
[generic-function] INSTANCE-TO-EXECUTOR-PARAMETERS INSTANCE CACHE
Return the parameters for an executor able to handle
INSTANCE. Called by [MAP-OVER-EXECUTORS][b01b] onCACHE(that's a [PARAMETERIZED-EXECUTOR-CACHE-MIXIN][d3b2]). The returned parameters are keys in an [EQUAL][3fb5] parameters->executor hash table.
6 Monitoring
[in package MGL-CORE]
When training or applying a model, one often wants to track various statistics. For example, in the case of training a neural network with cross-entropy loss, these statistics could be the average cross-entropy loss itself, classification accuracy, or even the entire confusion matrix and sparsity levels in hidden layers. Also, there is the question of what to do with the measured values (log and forget, add to some counter or a list).
So there may be several phases of operation when we want to keep an eye on. Let's call these events. There can also be many fairly independent things to do in response to an event. Let's call these monitors. Some monitors are a composition of two operations: one that extracts some measurements and another that aggregates those measurements. Let's call these two measurers and counters, respectively.
For example, consider training a backpropagation neural network. We
want to look at the state of of network just after the backward
pass. [MGL-BP:BP-LEARNER][00a0] has a [MONITORS][6202] event hook corresponding to the moment after
backpropagating the gradients. Suppose we are interested in how the
training cost evolves:
(push (make-instance 'monitor
:measurer (lambda (instances bpn)
(declare (ignore instances))
(mgl-bp:cost bpn))
:counter (make-instance 'basic-counter))
(monitors learner))
During training, this monitor will track the cost of training
examples behind the scenes. If we want to print and reset this
monitor periodically we can put another monitor on
[MGL-OPT:ITERATIVE-OPTIMIZER][8da0]'s [MGL-OPT:ON-N-INSTANCES-CHANGED][4f0b]
accessor:
(push (lambda (optimizer gradient-source n-instances)
(declare (ignore optimizer))
(when (zerop (mod n-instances 1000))
(format t "n-instances: ~S~%" n-instances)
(dolist (monitor (monitors gradient-source))
(when (counter monitor)
(format t "~A~%" (counter monitor))
(reset-counter (counter monitor)))))
(mgl-opt:on-n-instances-changed optimizer))
Note that the monitor we push can be anything as long as
[APPLY-MONITOR][bbdf] is implemented on it with the appropriate signature.
Also note that the [ZEROP][ec8b] + [MOD][e363] logic is fragile, so you will likely
want to use [MGL-OPT:MONITOR-OPTIMIZATION-PERIODICALLY][4528] instead of
doing the above.
So that's the general idea. Concrete events are documented where they are signalled. Often there are task specific utilities that create a reasonable set of default monitors (see [Classification Monitors][c573]).
-
[function] APPLY-MONITORS MONITORS &REST ARGUMENTS
Call [
APPLY-MONITOR][bbdf] on each monitor inMONITORSandARGUMENTS. This is how an event is fired. -
[generic-function] APPLY-MONITOR MONITOR &REST ARGUMENTS
Apply
MONITORtoARGUMENTS. This sound fairly generic, because it is.MONITORcan be anything, even a simple function or symbol, in which case this is just [CL:APPLY][d811]. See [Monitors][c701] for more. -
[generic-function] COUNTER MONITOR
Return an object representing the state of
MONITORorNIL, if it doesn't have any (say because it's a simple logging function). Most monitors have counters into which they accumulate results until they are printed and reset. See [Counters][be95] for more. -
[function] MONITOR-MODEL-RESULTS FN DATASET MODEL MONITORS
Call
FNwith batches of instances fromDATASETuntil it runs out (as in [DO-BATCHES-FOR-MODEL][faaa]).FNis supposed to applyMODELto the batch and return some kind of result (for neural networks, the result is the model state itself). ApplyMONITORSto each batch and the result returned byFNfor that batch. Finally, return the list of counters ofMONITORS.The purpose of this function is to collect various results and statistics (such as error measures) efficiently by applying the model only once, leaving extraction of quantities of interest from the model's results to
MONITORS.See the model specific versions of this functions such as [
MGL-BP:MONITOR-BPN-RESULTS][0933]. -
[generic-function] MONITORS OBJECT
Return monitors associated with
OBJECT. See various methods such as [MONITORS][6202] for more documentation.
6.1 Monitors
-
[class] MONITOR
A monitor that has another monitor called [
MEASURER][eb05] embedded in it. When this monitor is applied, it applies the measurer and passes the returned values to [ADD-TO-COUNTER][62de] called on its [COUNTER][a077] slot. One may further specialize [APPLY-MONITOR][bbdf] to change that.This class is useful when the same event monitor is applied repeatedly over a period and its results must be aggregated such as when training statistics are being tracked or when predictions are begin made. Note that the monitor must be compatible with the event it handles. That is, the embedded
MEASURERmust be prepared to take the arguments that are documented to come with the event. -
[reader] MEASURER [MONITOR][7068] (:MEASURER)
This must be a monitor itself which only means that [
APPLY-MONITOR][bbdf] is defined on it (but see [Monitoring][e668]). The returned values are aggregated by [COUNTER][5752]. See [Measurers][cd3b] for a library of measurers. -
[reader] COUNTER [MONITOR][7068] (:COUNTER)
The
COUNTERof a monitor carries out the aggregation of results returned by [MEASURER][eb05]. The See [Counters][be95] for a library of counters.
6.2 Measurers
[MEASURER][eb05] is a part of [MONITOR][7068] objects, an embedded monitor that
computes a specific quantity (e.g. classification accuracy) from the
arguments of event it is applied to (e.g. the model results).
Measurers are often implemented by combining some kind of model
specific extractor with a generic measurer function.
All generic measurer functions return their results as multiple
values matching the arguments of [ADD-TO-COUNTER][62de] for a counter of a
certain type (see [Counters][be95]) so as to make them easily used in a
MONITOR:
(multiple-value-call #'add-to-counter
)
The counter class compatible with the measurer this way is noted for each function.
For a list of measurer functions see [Classification Measurers][0ba7].
6.3 Counters
-
[generic-function] ADD-TO-COUNTER COUNTER &REST ARGS
Add
ARGStoCOUNTERin some way. See specialized methods for type specific documentation. The kind of arguments to be supported is the what the measurer functions (see [Measurers][cd3b]) intended to be paired with the counter return as multiple values. -
[generic-function] COUNTER-VALUES COUNTER
Return any number of values representing the state of
COUNTER. See specialized methods for type specific documentation. -
[generic-function] COUNTER-RAW-VALUES COUNTER
Return any number of values representing the state of
COUNTERin such a way that passing the returned values as arguments [ADD-TO-COUNTER][62de] on a fresh instance of the same type recreates the original state. -
[generic-function] RESET-COUNTER COUNTER
Restore state of
COUNTERto what it was just after creation.
6.3.1 Attributes
-
[class] ATTRIBUTED
This is a utility class that all counters subclass. The [
ATTRIBUTES][cc37] plist can hold basically anything. Currently the attributes are only used when printing and they can be specified by the user. The monitor maker functions such as those in [Classification Monitors][c573] also add attributes of their own to the counters they create.With the
:PREPEND-ATTRIBUTESinitarg when can easily add new attributes without clobbering the those in the:INITFORM, (:TYPE"rmse") in this case.(princ (make-instance 'rmse-counter :prepend-attributes '(:event "pred." :dataset "test"))) ;; pred. test rmse: 0.000e+0 (0) => # -
[accessor] ATTRIBUTES [ATTRIBUTED][9715] (:ATTRIBUTES = NIL)
A plist of attribute keys and values.
-
[method] NAME (ATTRIBUTED ATTRIBUTED)
Return a string assembled from the values of the [
ATTRIBUTES][cc37] ofATTRIBUTED. If there are multiple entries with the same key, then they are printed near together.Values may be padded according to an enclosing [
WITH-PADDED-ATTRIBUTE-PRINTING][2e8b]. -
[macro] WITH-PADDED-ATTRIBUTE-PRINTING (ATTRIBUTEDS) &BODY BODY
Note the width of values for each attribute key which is the number of characters in the value's [
PRINC-TO-STRING][a541]'ed representation. InBODY, if attributes with they same key are printed they are forced to be at least this wide. This allows for nice, table-like output:(let ((attributeds (list (make-instance 'basic-counter :attributes '(:a 1 :b 23 :c 456)) (make-instance 'basic-counter :attributes '(:a 123 :b 45 :c 6))))) (with-padded-attribute-printing (attributeds) (map nil (lambda (attributed) (format t "~A~%" attributed)) attributeds))) ;; 1 23 456: 0.000e+0 (0) ;; 123 45 6 : 0.000e+0 (0) -
[function] LOG-PADDED ATTRIBUTEDS
Log (see [
LOG-MSG][f85e])ATTRIBUTEDSnon-escaped (as in [PRINC][676d] or ~A) with the output being as table-like as possible.
6.3.2 Counter classes
In addition to the really basic ones here, also see [Classification Counters][6598].
-
[class] BASIC-COUNTER [ATTRIBUTED][9715]
A simple counter whose [
ADD-TO-COUNTER][62de] takes two additional parameters: an increment to the internal sums of called the [NUMERATOR][8af5] and [DENOMINATOR][5cd8]. [COUNTER-VALUES][20e8] returns two values:-
NUMERATORdivided byDENOMINATOR(or 0 ifDENOMINATORis 0) and -
DENOMINATOR
Here is an example the compute the mean of 5 things received in two batches:
(let ((counter (make-instance 'basic-counter))) (add-to-counter counter 6.5 3) (add-to-counter counter 3.5 2) counter) => # -
-
[class] RMSE-COUNTER [BASIC-COUNTER][5979]
A [
BASIC-COUNTER][5979] with whose nominator accumulates the square of some statistics. It has the attribute:TYPE"rmse". [COUNTER-VALUES][20e8] returns the square root of whatBASIC-COUNTER'sCOUNTER-VALUESwould return.(let ((counter (make-instance 'rmse-counter))) (add-to-counter counter (+ (* 3 3) (* 4 4)) 2) counter) => # -
[class] CONCAT-COUNTER [ATTRIBUTED][9715]
A counter that simply concatenates sequences.
(let ((counter (make-instance 'concat-counter))) (add-to-counter counter '(1 2 3) #(4 5)) (add-to-counter counter '(6 7)) (counter-values counter)) => (1 2 3 4 5 6 7) -
[reader] CONCATENATION-TYPE [CONCAT-COUNTER][0f83] (:CONCATENATION-TYPE = 'LIST)
A type designator suitable as the RESULT-TYPE argument to [
CONCATENATE][2ecb].
7 Classification
[in package MGL-CORE]
To be able to measure classification related quantities, we need to define what the label of an instance is. Customization is possible by implementing a method for a specific type of instance, but these functions only ever appear as defaults that can be overridden.
-
[generic-function] LABEL-INDEX INSTANCE
Return the label of
INSTANCEas a non-negative integer. -
[generic-function] LABEL-INDEX-DISTRIBUTION INSTANCE
Return a one dimensional array of probabilities representing the distribution of labels. The probability of the label with [
LABEL-INDEX][cc80]Iis element at indexIof the returned arrray.
The following two functions are basically the same as the previous two, but in batch mode: they return a sequence of label indices or distributions. These are called on results produced by models. Implement these for a model and the monitor maker functions below will automatically work. See FIXDOC: for bpn and boltzmann.
-
[generic-function] LABEL-INDICES RESULTS
Return a sequence of label indices for
RESULTSproduced by some model for a batch of instances. This is akin to [LABEL-INDEX][cc80]. -
[generic-function] LABEL-INDEX-DISTRIBUTIONS RESULT
Return a sequence of label index distributions for
RESULTSproduced by some model for a batch of instances. This is akin to [LABEL-INDEX-DISTRIBUTION][caec].
7.1 Classification Monitors
The following functions return a list monitors. The monitors are
for events of signature (INSTANCES MODEL) such as those produced by
[MONITOR-MODEL-RESULTS][e50c] and its various model specific variations.
They are model-agnostic functions, extensible to new classifier
types.
-
[function] MAKE-CLASSIFICATION-ACCURACY-MONITORS MODEL &KEY OPERATION-MODE ATTRIBUTES (LABEL-INDEX-FN #'LABEL-INDEX)
Return a list of [
MONITOR][7068] objects associated with [CLASSIFICATION-ACCURACY-COUNTER][430d]s.LABEL-INDEX-FNis a function like [LABEL-INDEX][cc80]. See that function for more.Implemented in terms of [
MAKE-CLASSIFICATION-ACCURACY-MONITORS*][2aa3]. -
[function] MAKE-CROSS-ENTROPY-MONITORS MODEL &KEY OPERATION-MODE ATTRIBUTES (LABEL-INDEX-DISTRIBUTION-FN #'LABEL-INDEX-DISTRIBUTION)
Return a list of [
MONITOR][7068] objects associated with [CROSS-ENTROPY-COUNTER][b186]s.LABEL-INDEX-DISTRIBUTION-FNis a function like [LABEL-INDEX-DISTRIBUTION][caec]. See that function for more.Implemented in terms of [
MAKE-CROSS-ENTROPY-MONITORS*][e46f]. -
[function] MAKE-LABEL-MONITORS MODEL &KEY OPERATION-MODE ATTRIBUTES (LABEL-INDEX-FN #'LABEL-INDEX) (LABEL-INDEX-DISTRIBUTION-FN #'LABEL-INDEX-DISTRIBUTION)
Return classification accuracy and cross-entropy monitors. See [
MAKE-CLASSIFICATION-ACCURACY-MONITORS][911c] and [MAKE-CROSS-ENTROPY-MONITORS][6004] for a description of paramters.
The monitor makers above can be extended to support new classifier types via the following generic functions.
-
[generic-function] MAKE-CLASSIFICATION-ACCURACY-MONITORS* MODEL OPERATION-MODE LABEL-INDEX-FN ATTRIBUTES
Identical to [
MAKE-CLASSIFICATION-ACCURACY-MONITORS][911c] bar the keywords arguments. Specialize this to add to support for new model types. The default implementation also allows for some extensibility: if [LABEL-INDICES][31ed] is defined onMODEL, then it will be used to extract label indices from model results. -
[generic-function] MAKE-CROSS-ENTROPY-MONITORS* MODEL OPERATION-MODE LABEL-INDEX-DISTRIBUTION-FN ATTRIBUTES
Identical to [
MAKE-CROSS-ENTROPY-MONITORS][6004] bar the keywords arguments. Specialize this to add to support for new model types. The default implementation also allows for some extensibility: if [LABEL-INDEX-DISTRIBUTIONS][9385] is defined onMODEL, then it will be used to extract label distributions from model results.
7.2 Classification Measurers
The functions here compare some known good solution (also known as ground truth or target) to a prediction or approximation and return some measure of their [dis]similarity. They are model independent, hence one has to extract the ground truths and predictions first. Rarely used directly, they are mostly hidden behind [Classification Monitors][c573].
-
[function] MEASURE-CLASSIFICATION-ACCURACY TRUTHS PREDICTIONS &KEY (TEST #'EQL) TRUTH-KEY PREDICTION-KEY WEIGHT
Return the number of correct classifications and as the second value the number of instances (equal to length of
TRUTHSin the non-weighted case).TRUTHS(keyed byTRUTH-KEY) is a sequence of opaque class labels compared withTESTto another sequence of classes labels inPREDICTIONS(keyed byPREDICTION-KEY). IfWEIGHTis non-nil, then it is a function that returns the weight of an element ofTRUTHS. Weighted cases add their weight to both counts (returned as the first and second values) instead of 1 as in the non-weighted case.Note how the returned values are suitable for [
MULTIPLE-VALUE-CALL][e4dd] with #'[ADD-TO-COUNTER][62de] and a [CLASSIFICATION-ACCURACY-COUNTER][430d]. -
[function] MEASURE-CROSS-ENTROPY TRUTHS PREDICTIONS &KEY TRUTH-KEY PREDICTION-KEY (MIN-PREDICTION-PR 1.0d-15)
Return the sum of the cross-entropy between pairs of elements with the same index of
TRUTHSandPREDICTIONS.TRUTH-KEYis a function that's when applied to an element ofTRUTHSreturns a sequence representing some kind of discrete target distribution (P in the definition below).TRUTH-KEYmay beNILwhich is equivalent to the [IDENTITY][8ae0] function.PREDICTION-KEYis the same kind of key forPREDICTIONS, but the sequence it returns represents a distribution that approximates (Q below) the true one.Cross-entropy of the true and approximating distributions is defined as:
cross-entropy(p,q) = - sum_i p(i) * log(q(i))of which this function returns the sum over the pairs of elements of
TRUTHSandPREDICTIONSkeyed byTRUTH-KEYandPREDICTION-KEY.Due to the logarithm, if q(i) is close to zero, we run into numerical problems. To prevent this, all q(i) that are less than
MIN-PREDICTION-PRare treated as if they wereMIN-PREDICTION-PR.The second value returned is the sum of p(i) over all
TRUTHSand allI. This is normally equal to(LENGTH TRUTHS), since elements ofTRUTHSrepresent a probability distribution, but this is not enforced which allows relative importance of elements to be controlled.The third value returned is a plist that maps each index occurring in the distribution sequences to a list of two elements:
sum_j p_j(i) * log(q_j(i))and
sum_j p_j(i)where
Jindexes intoTRUTHSandPREDICTIONS.(measure-cross-entropy '((0 1 0)) '((0.1 0.7 0.2))) => 0.35667497 1 (2 (0.0 0) 1 (0.35667497 1) 0 (0.0 0))Note how the returned values are suitable for [
MULTIPLE-VALUE-CALL][e4dd] with #'[ADD-TO-COUNTER][62de] and a [CROSS-ENTROPY-COUNTER][b186]. -
[function] MEASURE-ROC-AUC PREDICTIONS PRED &KEY (KEY #'IDENTITY) WEIGHT
Return the area under the ROC curve for
PREDICTIONSrepresenting predictions for a binary classification problem.PREDis a predicate function for deciding whether a prediction belongs to the so called positive class.KEYreturns a number for each element which is the predictor's idea of how much that element is likely to belong to the class, although it's not necessarily a probability.If
WEIGHTisNIL, then all elements ofPREDICTIONScount as 1 towards the unnormalized sum within AUC. ElseWEIGHTmust be a function likeKEY, but it should return the importance (a positive real number) of elements. If the weight of an prediction is 2 then it's as if there were another identical copy of that prediction inPREDICTIONS.The algorithm is based on algorithm 2 in the paper 'An introduction to ROC analysis' by Tom Fawcett.
ROC AUC is equal to the probability of a randomly chosen positive having higher
KEY(score) than a randomly chosen negative element. With equal scores in mind, a more precise version is: AUC is the expectation of the above probability over all possible sequences sorted by scores. -
[function] MEASURE-CONFUSION TRUTHS PREDICTIONS &KEY (TEST #'EQL) TRUTH-KEY PREDICTION-KEY WEIGHT
Create a [
CONFUSION-MATRIX][60d2] fromTRUTHSandPREDICTIONS.TRUTHS(keyed byTRUTH-KEY) is a sequence of class labels compared withTESTto another sequence of class labels inPREDICTIONS(keyed byPREDICTION-KEY). IfWEIGHTis non-nil, then it is a function that returns the weight of an element ofTRUTHS. Weighted cases add their weight to both counts (returned as the first and second values).Note how the returned confusion matrix can be added to another with [
ADD-TO-COUNTER][62de].
7.3 Classification Counters
-
[class] CLASSIFICATION-ACCURACY-COUNTER [BASIC-COUNTER][5979]
A [
BASIC-COUNTER][5979] with "acc." as its:TYPEattribute and a [PRINT-OBJECT][3f2e] method that prints percentages. -
[class] CROSS-ENTROPY-COUNTER [BASIC-COUNTER][5979]
A [
BASIC-COUNTER][5979] with "xent" as its:TYPEattribute.
7.3.1 Confusion Matrices
-
[class] CONFUSION-MATRIX
A confusion matrix keeps count of classification results. The correct class is called
target' and the output of the classifier is calledprediction'. -
[function] MAKE-CONFUSION-MATRIX &KEY (TEST #'EQL)
Classes are compared with
TEST. -
[generic-function] SORT-CONFUSION-CLASSES MATRIX CLASSES
Return a list of
CLASSESsorted for presentation purposes. -
[generic-function] CONFUSION-CLASS-NAME MATRIX CLASS
Name of
CLASSfor presentation purposes. -
[generic-function] CONFUSION-COUNT MATRIX TARGET PREDICTION
-
[generic-function] MAP-CONFUSION-MATRIX FN MATRIX
Call
FNwithTARGET,PREDICTION, [COUNT][3155] paramaters for each cell in the confusion matrix. Cells with a zero count may be ommitted. -
[generic-function] CONFUSION-MATRIX-CLASSES MATRIX
A list of all classes. The default is to collect classes from the counts. This can be overridden if, for instance, some classes are not present in the results.
-
[function] CONFUSION-MATRIX-ACCURACY MATRIX &KEY FILTER
Return the overall accuracy of the results in
MATRIX. It's computed as the number of correctly classified cases (hits) divided by the name of cases. Return the number of hits and the number of cases as the second and third value. IfFILTERfunction is given, then call it with the target and the prediction of the cell. Disregard cell for whichFILTERreturnsNIL.Precision and recall can be easily computed by giving the right filter, although those are provided in separate convenience functions.
-
[function] CONFUSION-MATRIX-PRECISION MATRIX PREDICTION
Return the accuracy over the cases when the classifier said
PREDICTION. -
[function] CONFUSION-MATRIX-RECALL MATRIX TARGET
Return the accuracy over the cases when the correct class is
TARGET. -
[function] ADD-CONFUSION-MATRIX MATRIX RESULT-MATRIX
Add
MATRIXintoRESULT-MATRIX.
8 Features
[in package MGL-CORE]
8.1 Feature Selection
The following scoring functions all return an [EQUAL][3fb5] hash table
that maps features to scores.
-
[function] COUNT-FEATURES DOCUMENTS MAPPER &KEY (KEY #'IDENTITY)
Return scored features as an [
EQUAL][3fb5] hash table whose keys are features ofDOCUMENTSand values are counts of occurrences of features.MAPPERtakes a function and a document and calls function with features of the document.(sort (alexandria:hash-table-alist (count-features '(("hello" "world") ("this" "is" "our" "world")) (lambda (fn document) (map nil fn document)))) #'string< :key #'car) => (("hello" . 1) ("is" . 1) ("our" . 1) ("this" . 1) ("world" . 2)) -
[function] FEATURE-LLRS DOCUMENTS MAPPER CLASS-FN &KEY (CLASSES (ALL-DOCUMENT-CLASSES DOCUMENTS CLASS-FN))
Return scored features as an [
EQUAL][3fb5] hash table whose keys are features ofDOCUMENTSand values are their log likelihood ratios.MAPPERtakes a function and a document and calls function with features of the document.(sort (alexandria:hash-table-alist (feature-llrs '((:a "hello" "world") (:b "this" "is" "our" "world")) (lambda (fn document) (map nil fn (rest document))) #'first)) #'string< :key #'car) => (("hello" . 2.6032386) ("is" . 2.6032386) ("our" . 2.6032386) ("this" . 2.6032386) ("world" . 4.8428774e-8)) -
[function] FEATURE-DISAMBIGUITIES DOCUMENTS MAPPER CLASS-FN &KEY (CLASSES (ALL-DOCUMENT-CLASSES DOCUMENTS CLASS-FN))
Return scored features as an [
EQUAL][3fb5] hash table whose keys are features ofDOCUMENTSand values are their disambiguities.MAPPERtakes a function and a document and calls function with features of the document.From the paper 'Using Ambiguity Measure Feature Selection Algorithm for Support Vector Machine Classifier'.
8.2 Feature Encoding
Features can rarely be fed directly to algorithms as is, they need
to be transformed in some way. Suppose we have a simple language
model that takes a single word as input and predicts the next word.
However, both input and output is to be encoded as float vectors of
length 1000. What we do is find the top 1000 words by some
measure (see [Feature Selection][1b5e]) and associate these words with
the integers in [0..999] (this is [ENCODE][fedd]ing). By using for
example one-hot encoding, we
translate a word into a float vector when passing in the input. When
the model outputs the probability distribution of the next word, we
find the index of the max and find the word associated with it (this
is [DECODE][1339]ing)
-
[generic-function] ENCODE ENCODER DECODED
Encode
DECODEDwithENCODER. This interface is generic enough to be almost meaningless. See [ENCODER/DECODER][1beb] for a simple, [MGL-NLP:BAG-OF-WORDS-ENCODER][cbb4] for a slightly more involved example.If
ENCODERis a function designator, then it's simply [FUNCALL][03c7]ed withDECODED. -
[generic-function] DECODE DECODER ENCODED
Decode
ENCODEDwithENCODER. For anDECODER/ENCODERpair,(DECODE DECODER (ENCODE ENCODER OBJECT))must be equal in some sense toOBJECT.If
DECODERis a function designator, then it's simply [FUNCALL][03c7]ed withENCODED. -
[class] ENCODER/DECODER
Implements O(1) [
ENCODE][fedd] and [DECODE][1339] by having an internal decoded-to-encoded and an encoded-to-decoded [EQUAL][3fb5] hash table.ENCODER/DECODERobjects can be saved and loaded (see [Persistence][29a1]) as long as the elements in the hash tables have read/write consitency.(let ((indexer (make-indexer (alexandria:alist-hash-table '(("I" . 3) ("me" . 2) ("mine" . 1))) 2))) (values (encode indexer "I") (encode indexer "me") (encode indexer "mine") (decode indexer 0) (decode indexer 1) (decode indexer 2))) => 0 => 1 => NIL => "I" => "me" => NIL -
[function] MAKE-INDEXER SCORED-FEATURES N &KEY (START 0) (CLASS 'ENCODER/DECODER)
Take the top
Nfeatures fromSCORED-FEATURES(see [Feature Selection][1b5e]), assign indices to them starting fromSTART. Return an [ENCODER/DECODER][1beb] (or anotherCLASS) that converts between objects and indices.
Also see [Bag of Words][0784].
9 Gradient Based Optimization
[in package MGL-OPT]
We have a real valued, differentiable function F and the task is to find the parameters that minimize its value. Optimization starts from a single point in the parameter space of F, and this single point is updated iteratively based on the gradient and value of F at or around the current point.
Note that while the stated problem is that of global optimization, for non-convex functions, most algorithms will tend to converge to a local optimum.
Currently, there are two optimization algorithms: [Gradient Descent][10e7] (with several variants) and [Conjugate Gradient][83e6] both of which are first order methods (they do not need second order gradients) but more can be added with the [Extension API][6a6f].
-
[function] MINIMIZE OPTIMIZER GRADIENT-SOURCE &KEY (WEIGHTS (LIST-SEGMENTS GRADIENT-SOURCE)) (DATASET *INFINITELY-EMPTY-DATASET*)
Minimize the value of the real valued function represented by
GRADIENT-SOURCEby updating some of its parameters inWEIGHTS(aMATor a sequence ofMATs). ReturnWEIGHTS.DATASET(see [Datasets][109e]) is a set of unoptimized parameters of the same function. For example,WEIGHTSmay be the weights of a neural network whileDATASETis the training set consisting of inputs suitable for [SET-INPUT][0c9e]. The defaultDATASET, ([*INFINITELY-EMPTY-DATASET*][ad8f]) is suitable for when all parameters are optimized, so there is nothing left to come from the environment.Optimization terminates if
DATASETis a sampler and it runs out or when some other condition met (see [TERMINATION][9006], for example). IfDATASETis a [SEQUENCE][ae23], then it is reused over and over again.Examples for various optimizers are provided in [Gradient Descent][10e7] and [Conjugate Gradient][83e6].
9.1 Iterative Optimizer
-
[class] ITERATIVE-OPTIMIZER
An abstract base class of [Gradient Descent][10e7] and [Conjugate Gradient][83e6] based optimizers that iterate over instances until a termination condition is met.
-
[reader] N-INSTANCES [ITERATIVE-OPTIMIZER][8da0] (:N-INSTANCES = 0)
The number of instances this optimizer has seen so far. Incremented automatically during optimization.
-
[accessor] TERMINATION [ITERATIVE-OPTIMIZER][8da0] (:TERMINATION = NIL)
If a number, it's the number of instances to train on in the sense of [
N-INSTANCES][4c73]. IfN-INSTANCESis equal or greater than this value optimization stops. IfTERMINATIONisNIL, then optimization will continue. If it isT, then optimization will stop. If it is a function of no arguments, then its return value is processed as if it was returned byTERMINATION. -
[accessor] ON-OPTIMIZATION-STARTED [ITERATIVE-OPTIMIZER][8da0] (:ON-OPTIMIZATION-STARTED = NIL)
An event hook with parameters
(OPTIMIZER GRADIENT-SOURCE N-INSTANCES). Called after initializations are performed (INITIALIZE-OPTIMIZER*, INITIALIZE-GRADIENT-SOURCE*) but before optimization is started. -
[accessor] ON-OPTIMIZATION-FINISHED [ITERATIVE-OPTIMIZER][8da0] (:ON-OPTIMIZATION-FINISHED = NIL)
An event hook with parameters
(OPTIMIZER GRADIENT-SOURCE N-INSTANCES). Called when optimization has finished. -
[accessor] ON-N-INSTANCES-CHANGED [ITERATIVE-OPTIMIZER][8da0] (:ON-N-INSTANCES-CHANGED = NIL)
An event hook with parameters
(OPTIMIZER GRADIENT-SOURCE N-INSTANCES). Called when optimization of a batch of instances is done and [N-INSTANCES][4c73] is incremented.
Now let's discuss a few handy utilities.
-
[function] MONITOR-OPTIMIZATION-PERIODICALLY OPTIMIZER PERIODIC-FNS
For each periodic function in the list of
PERIODIC-FNS, add a monitor toOPTIMIZER's [ON-OPTIMIZATION-STARTED][ebd4], [ON-OPTIMIZATION-FINISHED][0072] and [ON-N-INSTANCES-CHANGED][4f0b] hooks. The monitors are simple functions that just call each periodic function with the event parameters (OPTIMIZERGRADIENT-SOURCE[N-INSTANCES][4c73]). ReturnOPTIMIZER.To log and reset the monitors of the gradient source after every 1000 instances seen by
OPTIMIZER:(monitor-optimization-periodically optimizer '((:fn log-my-test-error :period 2000) (:fn reset-optimization-monitors :period 1000 :last-eval 0)))Note how we don't pass it's allowed to just pass the initargs for a
PERIODIC-FNinstead ofPERIODIC-FNitself. The:LAST-EVAL0 bit prevents [RESET-OPTIMIZATION-MONITORS][ca09] from being called at the start of the optimization when the monitors are empty anyway. -
[generic-function] RESET-OPTIMIZATION-MONITORS OPTIMIZER GRADIENT-SOURCE
Report the state of [
MONITORS][8f37] ofOPTIMIZERandGRADIENT-SOURCEand reset their counters. See [MONITOR-OPTIMIZATION-PERIODICALLY][4528] for an example of how this is used. -
[method] RESET-OPTIMIZATION-MONITORS (OPTIMIZER ITERATIVE-OPTIMIZER) GRADIENT-SOURCE
Log the counters of the monitors of
OPTIMIZERandGRADIENT-SOURCEand reset them. -
[generic-function] REPORT-OPTIMIZATION-PARAMETERS OPTIMIZER GRADIENT-SOURCE
A utility that's often called at the start of optimization (from [
ON-OPTIMIZATION-STARTED][ebd4]). The default implementation logs the description ofGRADIENT-SOURCE(as in [DESCRIBE][6651]) andOPTIMIZERand calls [LOG-MAT-ROOM][ea7d].
9.2 Cost Function
The function being minimized is often called the cost or the loss function.
-
[generic-function] COST MODEL
Return the value of the cost function being minimized. Calling this only makes sense in the context of an ongoing optimization (see [
MINIMIZE][46a4]). The cost is that of a batch of instances. -
[function] MAKE-COST-MONITORS MODEL &KEY OPERATION-MODE ATTRIBUTES
Return a list of [
MONITOR][7068] objects, each associated with one [BASIC-COUNTER][5979] with attribute:TYPE"cost". Implemented in terms of [MAKE-COST-MONITORS*][3815]. -
[generic-function] MAKE-COST-MONITORS* MODEL OPERATION-MODE ATTRIBUTES
Identical to [
MAKE-COST-MONITORS][46c2] bar the keywords arguments. Specialize this to add to support for new model types.
9.3 Gradient Descent
[in package MGL-GD]
Gradient descent is a first-order optimization algorithm. Relying completely on first derivatives, it does not even evaluate the function to be minimized. Let's see how to minimize a numerical lisp function with respect to some of its parameters.
(cl:defpackage :mgl-example-sgd
(:use #:common-lisp #:mgl))
(in-package :mgl-example-sgd)
;;; Create an object representing the sine function.
(defparameter *diff-fn-1*
(make-instance 'mgl-diffun:diffun
:fn #'sin
;; We are going to optimize its only parameter.
:weight-indices '(0)))
;;; Minimize SIN. Note that there is no dataset involved because all
;;; parameters are being optimized.
(minimize (make-instance 'sgd-optimizer :termination 1000)
*diff-fn-1*
:weights (make-mat 1))
;;; => A MAT with a single value of about -pi/2.
;;; Create a differentiable function for f(x,y)=(x-y)^2. X is a
;;; parameter whose values come from the DATASET argument passed to
;;; MINIMIZE. Y is a parameter to be optimized (a 'weight').
(defparameter *diff-fn-2*
(make-instance 'mgl-diffun:diffun
:fn (lambda (x y)
(expt (- x y) 2))
:parameter-indices '(0)
:weight-indices '(1)))
;;; Find the Y that minimizes the distance from the instances
;;; generated by the sampler.
(minimize (make-instance 'sgd-optimizer :batch-size 10)
*diff-fn-2*
:weights (make-mat 1)
:dataset (make-instance 'function-sampler
:generator (lambda ()
(list (+ 10
(gaussian-random-1))))
:max-n-samples 1000))
;;; => A MAT with a single value of about 10, the expected value of
;;; the instances in the dataset.
;;; The dataset can be a SEQUENCE in which case we'd better set
;;; TERMINATION else optimization would never finish.
(minimize (make-instance 'sgd-optimizer :termination 1000)
*diff-fn-2*
:weights (make-mat 1)
:dataset '((0) (1) (2) (3) (4) (5)))
;;; => A MAT with a single value of about 2.5.
We are going to see a number of accessors for optimizer paramaters.
In general, it's allowed to [SETF][a138] real slot accessors (as opposed to
readers and writers) at any time during optimization and so is
defining a method on an optimizer subclass that computes the value
in any way. For example, to decay the learning rate on a per
mini-batch basis:
(defmethod learning-rate ((optimizer my-sgd-optimizer))
(* (slot-value optimizer 'learning-rate)
(expt 0.998
(/ (n-instances optimizer) 60000))))
9.3.1 Batch Based Optimizers
First let's see everything common to all batch based optimizers,
then discuss [SGD Optimizer][25fd], [Adam Optimizer][bd13] and
[Normalized Batch Optimizer][0c91]. All batch based optimizers
are [ITERATIVE-OPTIMIZER][8da0]s, so see
[Iterative Optimizer][779d] too.
-
[class] BATCH-GD-OPTIMIZER [ITERATIVE-OPTIMIZER][8da0]
Another abstract base class for gradient based optimizers tath updates all weights simultaneously after chewing through [
BATCH-SIZE][fa6d] inputs. See subclasses [SGD-OPTIMIZER][2a2f], [ADAM-OPTIMIZER][e0e6] and [NORMALIZED-BATCH-GD-OPTIMIZER][f6ae].[
PER-WEIGHT-BATCH-GD-OPTIMIZER][5a43] may be a better choice when some weights can go unused for instance due to missing input values. -
[accessor] BATCH-SIZE GD-OPTIMIZER (:BATCH-SIZE = 1)
After having gone through
BATCH-SIZEnumber of inputs, weights are updated. WithBATCH-SIZE1, one gets Stochastics Gradient Descent. WithBATCH-SIZEequal to the number of instances in the dataset, one gets standard, 'batch' gradient descent. WithBATCH-SIZEbetween these two extremes, one gets the most practical 'mini-batch' compromise. -
[accessor] LEARNING-RATE GD-OPTIMIZER (:LEARNING-RATE = 0.1)
This is the step size along the gradient. Decrease it if optimization diverges, increase it if it doesn't make progress.
-
[accessor] MOMENTUM GD-OPTIMIZER (:MOMENTUM = 0)
A value in the [0, 1) interval.
MOMENTUMtimes the previous weight change is added to the gradient. 0 means no momentum. -
[reader] MOMENTUM-TYPE GD-OPTIMIZER (:MOMENTUM-TYPE = :NORMAL)
One of
:NORMAL,:NESTEROVor:NONE. For pure optimization Nesterov's momentum may be better, but it may also increases chances of overfitting. Using:NONEis equivalent to 0 momentum, but it also uses less memory. Note that with:NONE, [MOMENTUM][af05] is ignored even it it is non-zero. -
[accessor] WEIGHT-DECAY GD-OPTIMIZER (:WEIGHT-DECAY = 0)
An L2 penalty. It discourages large weights, much like a zero mean gaussian prior.
WEIGHT-DECAY* WEIGHT is added to the gradient to penalize large weights. It's as if the function whose minimum is sought hadWEIGHT-DECAY*sum_i{0.5 * WEIGHT_i^2} added to it. -
[accessor] WEIGHT-PENALTY GD-OPTIMIZER (:WEIGHT-PENALTY = 0)
An L1 penalty. It encourages sparsity.
SIGN(WEIGHT) *WEIGHT-PENALTYis added to the gradient pushing the weight towards negative infinity. It's as if the function whose minima is sought hadWEIGHT-PENALTY*sum_i{abs(WEIGHT_i)} added to it. Putting it on feature biases consitutes a sparsity constraint on the features. -
[reader] USE-SEGMENT-DERIVATIVES-P GD-OPTIMIZER (:USE-SEGMENT-DERIVATIVES-P = NIL)
Save memory if both the gradient source (the model being optimized) and the optimizer support this feature. It works like this: the accumulator into which the gradient source is asked to place the derivatives of a segment will be [
SEGMENT-DERIVATIVES][9a5b] of the segment. This allows the optimizer not to allocate an accumulator matrix into which the derivatives are summed. -
[accessor] AFTER-UPDATE-HOOK GD-OPTIMIZER (:AFTER-UPDATE-HOOK = NIL)
A list of functions with no arguments called after each weight update.
-
[accessor] BEFORE-UPDATE-HOOK [BATCH-GD-OPTIMIZER][d94e] (:BEFORE-UPDATE-HOOK = NIL)
A list of functions of no parameters. Each function is called just before a weight update takes place (after accumulated gradients have been divided the length of the batch). Convenient to hang some additional gradient accumulating code on.
SGD Optimizer
-
[class] SGD-OPTIMIZER [BATCH-GD-OPTIMIZER][d94e]
With [
BATCH-SIZE][fa6d] 1 this is Stochastic Gradient Descent. With higher batch sizes, one gets mini-batch and Batch Gradient Descent.Assuming that
ACCUMULATORhas the sum of gradients for a mini-batch, the weight update looks like this:$$ \Delta_w^{t+1} = momentum * \Delta_w^t + \frac{accumulator}{batchsize} + l_2 w + l_1 sign(w) $$
$$ w^{t+1} = w^{t} - learningrate * \Delta_w, $$
which is the same as the more traditional formulation:
$$ \Delta_w^{t+1} = momentum * \Delta_w^{t} + learningrate * \left(\frac{\frac{df}{dw}}{batchsize} + l_2 w + l_1 sign(w)\right) $$
$$ w^{t+1} = w^{t} - \Delta_w, $$
but the former works better when batch size, momentum or learning rate change during the course of optimization. The above is with normal momentum, Nesterov's momentum (see [
MOMENTUM-TYPE][5611]) momentum is also available.See [Batch Based Optimizers][2c39] for the description of the various options common to all batch based optimizers.
Adam Optimizer
-
[class] ADAM-OPTIMIZER [BATCH-GD-OPTIMIZER][d94e]
Adam is a first-order stochasistic gradient descent optimizer. It maintains an internal estimation for the mean and raw variance of each derivative as exponential moving averages. The step it takes is basically
M/(sqrt(V)+E)whereMis the estimated mean,Vis the estimated variance, andEis a small adjustment factor to prevent the gradient from blowing up. See version 5 of the paper for more.Note that using momentum is not supported with Adam. In fact, an error is signalled if it's not
:NONE.See [Batch Based Optimizers][2c39] for the description of the various options common to all batch based optimizers.
-
[accessor] LEARNING-RATE [ADAM-OPTIMIZER][e0e6] (= 2.0e-4)
Same thing as [
LEARNING-RATE][09ed] but with the default suggested by the Adam paper. -
[accessor] MEAN-DECAY [ADAM-OPTIMIZER][e0e6] (:MEAN-DECAY = 0.9)
A number between 0 and 1 that determines how fast the estimated mean of derivatives is updated. 0 basically gives you RMSPROP (if [
VARIANCE-DECAY][0900] is not too large) or AdaGrad (ifVARIANCE-DECAYis close to 1 and the learning rate is annealed. This is $\beta_1$ in the paper. -
[accessor] MEAN-DECAY-DECAY [ADAM-OPTIMIZER][e0e6] (:MEAN-DECAY-DECAY = (- 1 1.0d-7))
A value that should be close to 1. [
MEAN-DECAY][011d] is multiplied by this value after each update. This is $\lambda$ in the paper. -
[accessor] VARIANCE-DECAY [ADAM-OPTIMIZER][e0e6] (:VARIANCE-DECAY = 0.999)
A number between 0 and 1 that determines how fast the estimated variance of derivatives is updated. This is $\beta_2$ in the paper.
-
[accessor] VARIANCE-ADJUSTMENT [ADAM-OPTIMIZER][e0e6] (:VARIANCE-ADJUSTMENT = 1.0d-7)
Within the bowels of adam, the estimated mean is divided by the square root of the estimated variance (per weight) which can lead to numerical problems if the denominator is near zero. To avoid this,
VARIANCE-ADJUSTMENT, which should be a small positive number, is added to the denominator. This isepsilonin the paper.
Normalized Batch Optimizer
-
[class] NORMALIZED-BATCH-GD-OPTIMIZER [BATCH-GD-OPTIMIZER][d94e]
Like [
BATCH-GD-OPTIMIZER][d94e] but keeps count of how many times each weight was used in the batch and divides the accumulated gradient by this count instead of dividing byN-INSTANCES-IN-BATCH. This only makes a difference if there are missing values in the learner that's being trained. The main feature that distuinguishes this class from [PER-WEIGHT-BATCH-GD-OPTIMIZER][5a43] is that batches end at same time for all weights. -
[accessor] N-WEIGHT-USES-IN-BATCH [NORMALIZED-BATCH-GD-OPTIMIZER][f6ae]
Number of uses of the weight in its current batch.
9.3.2 Segmented GD Optimizer
-
[class] SEGMENTED-GD-OPTIMIZER [ITERATIVE-OPTIMIZER][8da0]
An optimizer that delegates training of segments to other optimizers. Useful to delegate training of different segments to different optimizers (capable of working with segmentables) or simply to not train all segments.
-
[reader] SEGMENTER [SEGMENTED-GD-OPTIMIZER][3ce0] (:SEGMENTER)
When this optimizer is initialized it loops over the segment of the learner with [
MAP-SEGMENTS][2312].SEGMENTERis a function that is called with each segment and returns an optimizer orNIL. Several segments may be mapped to the same optimizer. After the segment->optimizer mappings are collected, each optimizer is initialized by INITIALIZE-OPTIMIZER with the list of segments mapped to it. -
[reader] SEGMENTS [SEGMENTED-GD-OPTIMIZER][3ce0]
[SEGMENTED-GD-OPTIMIZER][3ce0] inherits from [ITERATIVE-OPTIMIZER][8da0], so see
[Iterative Optimizer][779d] too.
9.3.3 Per-weight Optimization
-
[class] PER-WEIGHT-BATCH-GD-OPTIMIZER [ITERATIVE-OPTIMIZER][8da0]
This is much like [Batch Based Optimizers][2c39] but it is more clever about when to update weights. Basically every weight has its own batch independent from the batches of others. This has desirable properties. One can for example put two neural networks together without adding any connections between them and the learning will produce results equivalent to the separated case. Also, adding inputs with only missing values does not change anything.
Due to its very non-batch nature, there is no CUDA implementation of this optimizer.
-
[accessor] N-WEIGHT-USES-IN-BATCH [PER-WEIGHT-BATCH-GD-OPTIMIZER][5a43]
Number of uses of the weight in its current batch.
9.3.4 Utilities
-
[function] CLIP-L2-NORM MATS L2-UPPER-BOUND &KEY CALLBACK
Scale
MATSso that their $L_2$ norm does not exceedL2-UPPER-BOUND.Compute the norm of of
MATSas if they were a single vector. If the norm is greater thanL2-UPPER-BOUND, then scale each matrix destructively by the norm divided byL2-UPPER-BOUNDand if non-NILcall the functionCALLBACKwith the scaling factor. -
[function] ARRANGE-FOR-CLIPPING-GRADIENTS BATCH-GD-OPTIMIZER L2-UPPER-BOUND &KEY CALLBACK
Make it so that the norm of the batch normalized gradients accumulated by
BATCH-GD-OPTIMIZERis clipped toL2-UPPER-BOUNDbefore every update. See [CLIP-L2-NORM][af6b].
9.4 Conjugate Gradient
[in package MGL-CG]
Conjugate gradient is a first-order optimization algorithm. It's more advanced than gradient descent as it does line searches which unfortunately also makes it unsuitable for non-deterministic functions. Let's see how to minimize a numerical lisp function with respect to some of its parameters.
;;; Create an object representing the sine function.
(defparameter *diff-fn-1*
(make-instance 'mgl-diffun:diffun
:fn #'sin
;; We are going to optimize its only parameter.
:weight-indices '(0)))
;;; Minimize SIN. Note that there is no dataset involved because all
;;; parameters are being optimized.
(minimize (make-instance 'cg-optimizer
:batch-size 1
:termination 1)
*diff-fn-1*
:weights (make-mat 1))
;;; => A MAT with a single value of about -pi/2.
;;; Create a differentiable function for f(x,y)=(x-y)^2. X is a
;;; parameter whose values come from the DATASET argument passed to
;;; MINIMIZE. Y is a parameter to be optimized (a 'weight').
(defparameter *diff-fn-2*
(make-instance 'mgl-diffun:diffun
:fn (lambda (x y)
(expt (- x y) 2))
:parameter-indices '(0)
:weight-indices '(1)))
;;; Find the Y that minimizes the distance from the instances
;;; generated by the sampler.
(minimize (make-instance 'cg-optimizer :batch-size 10)
*diff-fn-2*
:weights (make-mat 1)
:dataset (make-instance 'function-sampler
:generator (lambda ()
(list (+ 10
(gaussian-random-1))))
:max-n-samples 1000))
;;; => A MAT with a single value of about 10, the expected value of
;;; the instances in the dataset.
;;; The dataset can be a SEQUENCE in which case we'd better set
;;; TERMINATION else optimization would never finish. Note how a
;;; single epoch suffices.
(minimize (make-instance 'cg-optimizer :termination 6)
*diff-fn-2*
:weights (make-mat 1)
:dataset '((0) (1) (2) (3) (4) (5)))
;;; => A MAT with a single value of about 2.5.
-
[function] CG FN W &KEY (MAX-N-LINE-SEARCHES *DEFAULT-MAX-N-LINE-SEARCHES*) (MAX-N-EVALUATIONS-PER-LINE-SEARCH *DEFAULT-MAX-N-EVALUATIONS-PER-LINE-SEARCH*) (MAX-N-EVALUATIONS *DEFAULT-MAX-N-EVALUATIONS*) (SIG *DEFAULT-SIG*) (RHO *DEFAULT-RHO*) (INT *DEFAULT-INT*) (EXT *DEFAULT-EXT*) (RATIO *DEFAULT-RATIO*) SPARE-VECTORS
[
CG-OPTIMIZER][ee97] passes each batch of data to this function with its [CG-ARGS][9749] passed on.Minimize a differentiable multivariate function with conjugate gradient. The Polak-Ribiere flavour of conjugate gradients is used to compute search directions, and a line search using quadratic and cubic polynomial approximations and the Wolfe-Powell stopping criteria is used together with the slope ratio method for guessing initial step sizes. Additionally a bunch of checks are made to make sure that exploration is taking place and that extrapolation will not be unboundedly large.
FNis a function of two parameters: [WEIGHTS][ab3c] andDERIVATIVES.WEIGHTSis aMATof the same size asWthat is where the search start from.DERIVATIVESis also aMATof that size and it is whereFNshall place the partial derivatives.FNreturns the value of the function that is being minimized.CGperforms a number of line searches and invokesFNat each step. A line search invokesFNat mostMAX-N-EVALUATIONS-PER-LINE-SEARCHnumber of times and can succeed in improving the minimum by the sufficient margin or it can fail. Note, the even a failed line search may improve further and hence change the weights it's just that the improvement was deemed too small.CGstops when either:-
two line searches fail in a row
-
MAX-N-LINE-SEARCHESis reached -
MAX-N-EVALUATIONSis reached
CGreturns aMATthat contains the best weights, the minimum, the number of line searches performed, the number of succesful line searches and the number of evaluations.When using
MAX-N-EVALUATIONSremember that there is an extra evaluation ofFNbefore the first line search.SPARE-VECTORSis a list of preallocatedMATs of the same size asW. Passing 6 of them covers the current need of the algorithm and it will not cons up vectors of sizeWat all.NOTE: If the function terminates within a few iterations, it could be an indication that the function values and derivatives are not consistent (ie, there may be a bug in the implementation ofFNfunction).SIGandRHOare the constants controlling the Wolfe-Powell conditions.SIGis the maximum allowed absolute ratio between previous and new slopes (derivatives in the search direction), thus settingSIGto low (positive) values forces higher precision in the line-searches.RHOis the minimum allowed fraction of the expected (from the slope at the initial point in the linesearch). Constants must satisfy 0 <RHO<SIG< 1. Tuning ofSIG(depending on the nature of the function to be optimized) may speed up the minimization; it is probably not worth playing much withRHO. -
-
[variable] *DEFAULT-INT* 0.1
Don't reevaluate within
INTof the limit of the current bracket. -
[variable] *DEFAULT-EXT* 3
Extrapolate maximum
EXTtimes the current step-size. -
[variable] *DEFAULT-SIG* 0.1
SIGandRHOare the constants controlling the Wolfe-Powell conditions.SIGis the maximum allowed absolute ratio between previous and new slopes (derivatives in the search direction), thus settingSIGto low (positive) values forces higher precision in the line-searches. -
[variable] *DEFAULT-RHO* 0.05
RHOis the minimum allowed fraction of the expected (from the slope at the initial point in the linesearch). Constants must satisfy 0 <RHO<SIG< 1. -
[variable] *DEFAULT-RATIO* 10
Maximum allowed slope ratio.
-
[variable] *DEFAULT-MAX-N-LINE-SEARCHES* NIL
-
[variable] *DEFAULT-MAX-N-EVALUATIONS-PER-LINE-SEARCH* 20
-
[variable] *DEFAULT-MAX-N-EVALUATIONS* NIL
-
[class] CG-OPTIMIZER [ITERATIVE-OPTIMIZER][8da0]
Updates all weights simultaneously after chewing through [
BATCH-SIZE][fa6d] inputs. -
[accessor] BATCH-SIZE [CG-OPTIMIZER][ee97] (:BATCH-SIZE)
After having gone through
BATCH-SIZEnumber of instances, weights are updated. Normally, [CG][4ffb] operates on all available data, but it may be useful to introduce some noise into the optimization to reduce overfitting by using smaller batch sizes. IfBATCH-SIZEis not set, it is initialized to the size of the dataset at the start of optimization. -
[accessor] CG-ARGS [CG-OPTIMIZER][ee97] (:CG-ARGS = 'NIL)
-
[accessor] ON-CG-BATCH-DONE [CG-OPTIMIZER][ee97] (:ON-CG-BATCH-DONE = NIL)
An event hook called when processing a conjugate gradient batch is done. The handlers on the hook are called with 8 arguments:
(optimizer gradient-source instances best-w best-f n-line-searches n-succesful-line-searches n-evaluations)The latter 5 of which are the return values of the [
CG][4ffb] function. -
[generic-function] LOG-CG-BATCH-DONE OPTIMIZER GRADIENT-SOURCE INSTANCES BEST-W BEST-F N-LINE-SEARCHES N-SUCCESFUL-LINE-SEARCHES N-EVALUATIONS
This is a function can be added to [
ON-CG-BATCH-DONE][d10a]. The default implementation simply logs the event arguments. -
[reader] SEGMENT-FILTER [CG-OPTIMIZER][ee97] (:SEGMENT-FILTER = (CONSTANTLY T))
A predicate function on segments that filters out uninteresting segments. Called from [
INITIALIZE-OPTIMIZER*][7c2f].
9.5 Extension API
9.5.1 Implementing Optimizers
The following generic functions must be specialized for new optimizer types.
-
[generic-function] MINIMIZE* OPTIMIZER GRADIENT-SOURCE WEIGHTS DATASET
Called by [
MINIMIZE][46a4] after [INITIALIZE-OPTIMIZER*][7c2f] and [INITIALIZE-GRADIENT-SOURCE*][dd95], this generic function is the main extension point for writing optimizers. -
[generic-function] INITIALIZE-OPTIMIZER* OPTIMIZER GRADIENT-SOURCE WEIGHTS DATASET
Called automatically before training starts, this function sets up
OPTIMIZERto be suitable for optimizingGRADIENT-SOURCE. It typically creates appropriately sized accumulators for the gradients. -
[generic-function] SEGMENTS OPTIMIZER
Several weight matrices known as segments can be optimized by a single optimizer. This function returns them as a list.
The rest are just useful for utilities for implementing optimizers.
-
[function] TERMINATE-OPTIMIZATION-P N-INSTANCES TERMINATION
Utility function for subclasses of [
ITERATIVE-OPTIMIZER][8da0]. It returns whether optimization is to be terminated based onN-INSTANCESandTERMINATIONthat are values of the respective accessors ofITERATIVE-OPTIMIZER. -
[function] SET-N-INSTANCES OPTIMIZER GRADIENT-SOURCE N-INSTANCES
Set [
N-INSTANCES][4c73] ofOPTIMIZERand fire [ON-N-INSTANCES-CHANGED][4f0b]. [ITERATIVE-OPTIMIZER][8da0] subclasses must call this to increment [N-INSTANCES][4c73]. -
[class] SEGMENT-SET
This is a utility class for optimizers that have a list of [
SEGMENTS][f00d] and (the weights being optimized) is able to copy back and forth between those segments and a singleMAT(the accumulator). -
[reader] SEGMENTS [SEGMENT-SET][418a] (:SEGMENTS)
A list of weight matrices.
-
[reader] SIZE [SEGMENT-SET][418a]
The sum of the sizes of the weight matrices of [
SEGMENTS][f00d]. -
[macro] DO-SEGMENT-SET (SEGMENT &OPTIONAL START) SEGMENT-SET &BODY BODY
Iterate over [
SEGMENTS][f00d] inSEGMENT-SET. IfSTARTis specified, the it is bound to the start index ofSEGMENTwithinSEGMENT-SET. The start index is the sum of the sizes of previous segments. -
[function] SEGMENT-SET<-MAT SEGMENT-SET MAT
Copy the values of
MATto the weight matrices ofSEGMENT-SETas if they were concatenated into a singleMAT. -
[function] SEGMENT-SET->MAT SEGMENT-SET MAT
Copy the values of
SEGMENT-SETtoMATas if they were concatenated into a singleMAT.
9.5.2 Implementing Gradient Sources
Weights can be stored in a multitude of ways. Optimizers need to
update weights, so it is assumed that weights are stored in any
number of MAT objects called segments.
The generic functions in this section must all be specialized for new gradient sources except where noted.
-
[generic-function] MAP-SEGMENTS FN GRADIENT-SOURCE
Apply
FNto each segment ofGRADIENT-SOURCE. -
[generic-function] MAP-SEGMENT-RUNS FN SEGMENT
Call
FNwith start and end of intervals of consecutive indices that are not missing inSEGMENT. Called by optimizers that support partial updates. The default implementation assumes that all weights are present. This only needs to be specialized if one plans to use an optimizer that knows how to deal unused/missing weights such as [MGL-GD:NORMALIZED-BATCH-GD-OPTIMIZER][f6ae] andOPTIMIZER[MGL-GD:PER-WEIGHT-BATCH-GD-OPTIMIZER][5a43]. -
[generic-function] SEGMENT-WEIGHTS SEGMENT
Return the weight matrix of
SEGMENT. A segment doesn't need to be aMATobject itself. For example, it may be aMGL-BM:CHUNKof aMGL-BM:BMor a [MGL-BP:LUMP][c1ac] of a [MGL-BP:BPN][5187] whose [NODES][cc1c] slot holds the weights. -
[method] SEGMENT-WEIGHTS (MAT MAT)
When the segment is really a
MAT, then just return it. -
[generic-function] SEGMENT-DERIVATIVES SEGMENT
Return the derivatives matrix of
SEGMENT. A segment doesn't need to be aMATobject itself. For example, it may be aMGL-BM:CHUNKof aMGL-BM:BMor a [MGL-BP:LUMP][c1ac] of a [MGL-BP:BPN][5187] whose DERIVATIVES slot holds the gradient. -
[function] LIST-SEGMENTS GRADIENT-SOURCE
A utility function that returns the list of segments from [
MAP-SEGMENTS][2312] onGRADIENT-SOURCE. -
[generic-function] INITIALIZE-GRADIENT-SOURCE* OPTIMIZER GRADIENT-SOURCE WEIGHTS DATASET
Called automatically before [
MINIMIZE*][ae3d] is called, this function may be specialized ifGRADIENT-SOURCEneeds some kind of setup. -
[method] INITIALIZE-GRADIENT-SOURCE* OPTIMIZER GRADIENT-SOURCE WEIGHTS DATASET
The default method does nothing.
-
[generic-function] ACCUMULATE-GRADIENTS* GRADIENT-SOURCE SINK BATCH MULTIPLIER VALUEP
Add
MULTIPLIERtimes the sum of first-order gradients to accumulators ofSINK(normally accessed with [DO-GRADIENT-SINK][20ca]) and ifVALUEP, return the sum of values of the function being optimized for aBATCHof instances.GRADIENT-SOURCEis the object representing the function being optimized,SINKis gradient sink.Note the number of instances in
BATCHmay be larger than whatGRADIENT-SOURCEprocess in one go (in the sense of say, [MAX-N-STRIPES][16c4]), so [DO-BATCHES-FOR-MODEL][faaa] or something like (GROUPBATCHMAX-N-STRIPES) can be handy.
9.5.3 Implementing Gradient Sinks
Optimizers call [ACCUMULATE-GRADIENTS*][4bf1] on gradient sources. One
parameter of ACCUMULATE-GRADIENTS* is the SINK. A gradient sink
knows what accumulator matrix (if any) belongs to a segment. Sinks
are defined entirely by [MAP-GRADIENT-SINK][aabd].
-
[generic-function] MAP-GRADIENT-SINK FN SINK
Call
FNof lambda list (SEGMENTACCUMULATOR) on each segment and their corresponding accumulatorMATinSINK. -
[macro] DO-GRADIENT-SINK ((SEGMENT ACCUMULATOR) SINK) &BODY BODY
A convenience macro on top of [
MAP-GRADIENT-SINK][aabd].
10 Differentiable Functions
[in package MGL-DIFFUN]
-
[class] DIFFUN
DIFFUNdresses a lisp function (in its [FN][f491] slot) as a gradient source (see [Implementing Gradient Sources][c58b]), which allows it to be used in [MINIMIZE][46a4]. See the examples in [Gradient Descent][10e7] and [Conjugate Gradient][83e6]. -
[reader] FN [DIFFUN][1a61] (:FN)
A real valued lisp function. It may have any number of parameters.
-
[reader] PARAMETER-INDICES [DIFFUN][1a61] (:PARAMETER-INDICES = NIL)
The list of indices of parameters that we don't optimize. Values for these will come from the DATASET argument of [
MINIMIZE][46a4]. -
[reader] WEIGHT-INDICES [DIFFUN][1a61] (:WEIGHT-INDICES = NIL)
The list of indices of parameters to be optimized, the values of which will come from the
WEIGHTSargument of [MINIMIZE][46a4].
11 Backpropagation Neural Networks
[in package MGL-BP]
11.1 Backprop Overview
Backpropagation Neural Networks are just functions with lots of
parameters called weights and a layered structure when presented
as a computational
graph. The
network is trained to [MINIMIZE][46a4] some kind of loss function whose
value the network computes.
In this implementation, a [BPN][5187] is assembled from several
[LUMP][c1ac]s (roughly corresponding to layers). Both feed-forward and
recurrent neural nets are supported ([FNN][9de4] and [RNN][b0f3], respectively).
BPNs can contain not only LUMPs but other BPNs, too. As we
see, networks are composite objects and the abstract base class for
composite and simple parts is called [CLUMP][a4fe].
-
[class] CLUMP
A
CLUMPis a [LUMP][c1ac] or a [BPN][5187]. It represents a differentiable function. Arguments of clumps are given during instantiation. Some arguments are clumps themselves so they get permenantly wired together like this:(->v*m (->input :size 10 :name 'input) (->weight :dimensions '(10 20) :name 'weight) :name 'activation)The above creates three clumps: the vector-matrix multiplication clumps called
ACTIVATIONwhich has a reference to its operands:INPUTandWEIGHT. Note that the example just defines a function, no actual computation has taken place, yet.This wiring of
CLUMPs is how one builds feed-forward nets ([FNN][9de4]) or recurrent neural networks ([RNN][b0f3]) that areCLUMPs themselves so one can build nets in a hiearchical style if desired. Non-compositeCLUMPs are calledLUMP(note the loss ofCthat stands for composite). The variousLUMPsubtypes correspond to different layer types ([->SIGMOID][83f9], [->DROPOUT][441b], [->RELU][9d3a], [->TANH][5309], etc).
At this point, you may want to jump ahead to get a feel for how
things work by reading the [FNN Tutorial][6b38].
11.2 Clump API
These are mostly for extension purposes. About the only thing
needed from here for normal operation is [NODES][cc1c] when clamping inputs
or extracting predictions.
-
[generic-function] STRIPEDP CLUMP
For efficiency, forward and backprop phases do their stuff in batch mode: passing a number of instances through the network in batches. Thus clumps must be able to store values of and gradients for each of these instances. However, some clumps produce the same result for each instance in a batch. These clumps are the weights, the parameters of the network.
STRIPEDPreturns true iffCLUMPdoes not represent weights (i.e. it's not a [->WEIGHT][b76f]).For striped clumps, their [
NODES][cc1c] and [DERIVATIVES][a81b] areMATobjects with a leading dimension (number of rows in the 2d case) equal to the number of instances in the batch. Non-striped clumps have no restriction on their shape apart from what their usage dictates. -
[generic-function] NODES OBJECT
Returns a
MGL-MAT:MATobject representing the state or result ofOBJECT. The first dimension of the returned matrix is equal to the number of stripes.
[CLUMP][a4fe]s' [NODES][cc1c] holds the result computed by the most recent
[FORWARD][c1ae]. For [->INPUT][f54e] lumps, this is where input values shall be
placed (see [SET-INPUT][0c9e]). Currently, the matrix is always two
dimensional but this restriction may go away in the future.
-
[generic-function] DERIVATIVES CLUMP
Return the
MATobject representing the partial derivatives of the functionCLUMPcomputes. The returned partial derivatives were accumulated by previous [BACKWARD][5bd4] calls.This matrix is shaped like the matrix returned by [
NODES][cc1c]. -
[generic-function] FORWARD CLUMP
Compute the values of the function represented by
CLUMPfor all stripes and place the results into [NODES][cc1c] ofCLUMP. -
[generic-function] BACKWARD CLUMP
Compute the partial derivatives of the function represented by
CLUMPand add them to [DERIVATIVES][a81b] of the corresponding argument clumps. TheDERIVATIVESofCLUMPcontains the sum of partial derivatives of all clumps by the corresponding output. This function is intended to be called after a [FORWARD][c1ae] pass.Take the [
->SIGMOID][83f9] clump for example when the network is being applied to a batch of two instancesx1andx2.x1andx2are set in the [->INPUT][f54e] lump X. The sigmoid computes1/(1+exp(-x))whereXis its only argument clump.f(x) = 1/(1+exp(-x))When
BACKWARDis called on the sigmoid lump, itsDERIVATIVESis a 2x1MATobject that contains the partial derivatives of the loss function:dL(x1)/df dL(x2)/dfNow the
BACKWARDmethod of the sigmoid needs to adddL(x1)/dx1anddL(x2)/dx2toDERIVATIVESofX. Now,dL(x1)/dx1 = dL(x1)/df * df(x1)/dx1and the first term is what we have inDERIVATIVESof the sigmoid so it only needs to calculate the second term.
In addition to the above, clumps also have to support [SIZE][019f],
[N-STRIPES][8dd7], [MAX-N-STRIPES][16c4] (and the [SETF][a138] methods of the latter two)
which can be accomplished just by inheriting from [BPN][5187], [FNN][9de4], [RNN][b0f3], or
a [LUMP][c1ac].
11.3 BPNs
-
[class] BPN [CLUMP][a4fe]
Abstract base class for [
FNN][9de4] and [RNN][b0f3]. -
[reader] N-STRIPES [BPN][5187] (:N-STRIPES = 1)
The current number of instances the network has. This is automatically set to the number of instances passed to [
SET-INPUT][0c9e], so it rarely has to be manipulated directly although it can be set. When setN-STRIPESof all [CLUMPS][f7c1] get set to the same value. -
[reader] MAX-N-STRIPES [BPN][5187] (:MAX-N-STRIPES = NIL)
The maximum number of instances the network can operate on in parallel. Within [
BUILD-FNN][606c] or [BUILD-RNN][764b], it defaults toMAX-N-STRIPESof that parent network, else it defaults to 1. When setMAX-N-STRIPESof all [CLUMPS][f7c1] get set to the same value. -
[reader] CLUMPS [BPN][5187] (:CLUMPS = (MAKE-ARRAY 0 :ELEMENT-TYPE 'CLUMP :ADJUSTABLE T :FILL-POINTER T))
A topological sorted adjustable array with a fill pointer that holds the clumps that make up the network. Clumps are added to it by [
ADD-CLUMP][82d8] or, more often, automatically when within a [BUILD-FNN][606c] or [BUILD-RNN][764b]. Rarely needed, [FIND-CLUMP][175f] takes care of most uses. -
[function] FIND-CLUMP NAME BPN &KEY (ERRORP T)
Find the clump with
NAMEamong [CLUMPS][f7c1] ofBPN. As always, names are compared with [EQUAL][3fb5]. If not found, then returnNILor signal and error depending onERRORP. -
[function] ADD-CLUMP CLUMP BPN
Add
CLUMPtoBPN. [MAX-N-STRIPES][16c4] ofCLUMPgets