cl-waffe

cl-waffe

Package :cl-waffe

⚠️This package is under development and APIs can be changed without notice.

This package has:

  1. WaffeTensor
  2. Basic Tensor Operators
  3. Utils for define extensions (e.g.: defmodel)
  4. Utils for training and validating (e.g.: deftrainer)

Sections

Four Arithmetic Operations

!add Applying two tensors +

!sub Applying two tensors -

!mul Applying two tensors *

!div Applying two tensors /

Sum up and obtain a average

!sum Sum up the given tensor in specified dims, and if needed, repeat it.

!mean Find the average of a specified dimension.

Multiplying matrices

!dot Returns the dot product of two tensors which are 1D.

!matmul Multiplying matrices x and y. The returned value depends on the dimension of x and y.

!einsum

Shaping

!squeeze Returns a new tensor with a dimension of size one removed at the specified position.

!unsqueeze Returns a new tensor with a dimension of size one inserted at the specified position.

!transpose transpose a tensor for !matmul

!transpose1 transpose a tensor but doens't produce an lazy-evaluated tensor.

!reshape

!repeats

!flatten

!ravel

!shape

!dims

!size

Trigonometric Functions

!sin

!cos

!tan

!asin

!acos

!atan

!sinh

!cosh

!tanh

!asinh

!acosh

!atanh

Math Functions

!abs

!log

!exp

!pow

!sqrt

Activations

!tanh

!sigmoid

!relu

!gelu

!leakey-relu

!swish

!softmax

Handling Multidimensional Tensors

!aref

!dotensors

!set-batch

!where

!index

!filter

!split Note: backward is not yet implemented.

!concatenate the fastest one.

!stack a little slower than concatenate

!vstack the same as numpy's one.

!hstack the same as numpy's one.

Initialize A Tensor With Specified Elements

!zeros

!ones

!fill fill a tensor with specified value

!zeros-like

!ones-like

!full-like

!arange

!init-with

Random

!random

!random-with

!uniform-random

Sampling Probability Distributions

!normal

!randn

!beta

!gamma

!chisquare

!bernoulli

The usage of Model And Node

Model And Node

Define Model And Use it

Define Node And Use it

The usage of Trainers

Trainer

Define Trainer

The usage of Optimizers

Optimizer

Define Optimizer

The usage of Datasets

Datasets

Define Datasets

Describe Docstrign in the template
with-usage

Model And Node

Todo: tutorial

defmodel

defmodel(name args &key (parameters nil) forward (optimize nil) (document An model, defined by cl-waffe))

This macro defines a cl-waffe model as name.

At the same time, a constructor name is defined and you can initialize your model like:

(cl-waffe.nn:LinearLayer 100 20) ; => [Model: Linearlayer]

name
Your model and constructor name
args
The arguments of a constructor
parameters

The parameters your model has.

Every time you initialize the model, the parameters are initialized.

Note that defmodel behaves like class.

The arguments are the same as defstruct

Format Example: ((param-name param-initial-value &key (type your-type)))

optimize
when t, your forward slot is defined with (declare (optimize (speed 3)(space 0)(debug 0))). It helps faster training after you ensured debugged.
forward

Define here the forward propagation of your model.

When backward, Automatic differentiation applies.

call(model &rest args)

Calls the forward steps which defined in: defnode, defmodel, defoptimizer.

All forward steps must be called through this function, otherwise the returned tensor doesn't have: computation nodes, thread-datum which supports performance.

Building computation nodes is ignored when *no-grad* is t.

model
Your initialized model/node/optimizer objects
args
Arguments :forward needs

Example:

(defnode Add nil
  :optimize t
  :parameters nil
  :forward  ((x y)
	     (sysconst (+ (data x)(data y))))
  :backward ((dy)(list dy dy)))

(call (Add)(const 1.0)(const 1.0))
;=>Const(2.0)

Output: Waffetensor of list which comprised of waffetensor.

backward(tensor)

Compute back propagation by traversing the Tensor's computation node.

The parameters of the model defined by (tensor) or to which (Parameter tensor) is applied, store the gradient in grad slot.

Note that: tensor must be the shape of `(1) or single value. Otherwise an error occurs.

In the process calculating backward, new backwards won't be created. (*no-grad* automatically becomes t)

Input
WaffeTensor
Output
NIL
with-calling-layers(input &rest layers)

This macro allows to sequentially call layers.

the argument input must be a tensor.

Refering each layers from (self) macro, destructively modifying x with the returned value.

Note: This macro supposes models to be returned a single tensor, not a list.

(defmodel MLP (activation)
   :parameters ((layer1   (denselayer (* 28 28) 512 T activation))
   	        (layer2   (denselayer 512 256 T activation))
	        (layer3   (linearlayer 256 10 T)))
   :forward ((x)
	     (with-calling-layers x
	       (layer1 x)
 	       (layer2 x)
               (layer3 x))))

For the different arguments.

(with-calling-layers x
     (layer1 x 1 1)
     (layer2 1 x 2)
     (layer3 x y))

Output: An last value of layers.

defnode

defnode(name args &key parameters forward backward optimize (regard-as-node t) (document An node, defined by cl-waffe.))

Defining computation nodes.

defnode is useful when you want to define the derivative yourself.Note that parameter tensors in :parameter won't updated by optimizers.

If you want to update params, define additional models.

regard-as-node
When the slot :regard-as-node is nil, an optimizer in cl-waffe.caches regards this as model (i.e. argument could be destructed.) Default is t.

Note that:

  1. :backward must return list, where that length corresponds with the length of input's argument, otherwise an error occurs when backward.
  2. In forward and backward, computation node isn't needed to be continuous.However, the last values of :forward and :backward step, must posses :thread-data, which can be obtained by (waffetensor-thread-data tensor)

Example:

(defnode AddTensor nil
  :optimize t
  :parameters nil
  :forward  ((x y)
	     (with-searching-calc-node :add x y))
  :backward ((dy)(list dy dy)))

(call (AddTensor) tensor1 tensor2)
call(model &rest args)

Calls the forward steps which defined in: defnode, defmodel, defoptimizer.

All forward steps must be called through this function, otherwise the returned tensor doesn't have: computation nodes, thread-datum which supports performance.

Building computation nodes is ignored when *no-grad* is t.

model
Your initialized model/node/optimizer objects
args
Arguments :forward needs

Example:

(defnode Add nil
  :optimize t
  :parameters nil
  :forward  ((x y)
	     (sysconst (+ (data x)(data y))))
  :backward ((dy)(list dy dy)))

(call (Add)(const 1.0)(const 1.0))
;=>Const(2.0)

Output: Waffetensor of list which comprised of waffetensor.

backward(tensor)

Compute back propagation by traversing the Tensor's computation node.

The parameters of the model defined by (tensor) or to which (Parameter tensor) is applied, store the gradient in grad slot.

Note that: tensor must be the shape of `(1) or single value. Otherwise an error occurs.

In the process calculating backward, new backwards won't be created. (*no-grad* automatically becomes t)

Input
WaffeTensor
Output
NIL

warranty(tensor)

Notice waffe's optimizer that do not delete tensor given until warranty called in the calc node.

When you encountered error in the forward step that the tensor that attempted to read has already cached ~, try this like:

(warranty your-tensor)
(print your-tensor)
with-kernel-case(target var &key (mgl nil) (mgl-cuda nil) (copy t) &aux (out (gensym)))

Reading the target's device, this macro invokes property codes described in :mgl, :mgl-cuda etc...

Dynamically defining and caching cpu and cuda kernel.

Every time reaches this macro, cl-waffe caches the target (i.e. the target is allowed to be destructed).

This macro won't create computation nodes.

The available slot is in *kernels*

When :mgl-cuda is nil, automatically calls :mgl

This macro returns the last value of called slots.

The last value of :mgl, :mgl-cuda and so on, must be type of list (cons), or mgl-mat:mat, waffetensorcontenttype.

Note: the target's thread-data must be already created. (i.e. By the time tensors reach this macro, at least once they needed to be pathed through Trainer or Model.) So, use this macro when you defining :forward and :backward in defnode macro because in defnode, backprop is disabled and computation nodes isn't always required.

Inputs

target
an target tensor.
var
where an copied tensor of target will be assigned.
:mgl
mgl-mat, when using cpu.
:mgl-mat
mgl-mat, when using cuda.

Return: An tensor (where tensor is made by sysconst)

Example:

(with-kernel-case x o
     :mgl (progn
            (axpy! 1.0 a o)) ; axpy! = !add
     :mgl-cuda nil) => #Const(((0.0 1.0 ~ 2.0 3.0)        
                 ...
      (0.0 4.0 ~ 5.0 6.0)) :mgl t :shape (10 10))

 ; This is useful when defining :backward
 (with-kernel-case x o
     :mgl (progn
            (list 1 1)))
call-and-dispatch-kernel(kernel-function output overwrite &rest args)

Invoke kernel and run kernel-function. return new sysconst It's the most general way for users to access cl-waffe's kernel.

If output is specified, write a result to output destructively.

If overwrite is t, side effect will occurs. (i.e.: args can be destructed)

Trainer

Todo: tutorials

deftrainer

deftrainer(name args &key model optimizer optimizer-args step-model predict (document An trainer structure defined by cl-waffe.))

Defining trainer, which is made in order to call train function.

The slots you defined can be invoked by using (step-model model &rest args), (predict model &rest args). See below.

model
An model defined by (defmodel) which you want to train.
optimizer
An optimizer defined by (defoptimizer)
optimizer-args
An arguments for optimizer
step-model
For each batch step, :step-model is called in (train) function. Describe here forward step, backward, zero-grad, update for training.
predict
an code for predicting

These macro below are defined by macrolet and you can use them in :step-model, :predict

(self name)
access trainer's parameters.
(model)
access trainer's model, defined by :model keyword.
(zero-grad)
Find model's all parameters and constants, and initialize their grads. (i.e. call optimizer's backward)
(update)
Find model's all parameters, and call optimizer and change parameter's data. (i.e. call optimizer's forward)

This trainer macro is defined in order to integrate following works:

  1. calling models
  2. calling criterions
  3. calling backward
  4. calling optimizer
  5. calling zero-grad
  6. defining predict

Example:

(deftrainer MLPTrainer (activation lr)
  :model          (MLP activation)
  :optimizer      cl-waffe.optimizers:Adam ; Note: :optimizer requires a single variable.
  :optimizer-args (:lr lr) ; these arguments directly expanded to optimizer's args.
  :step-model ((x y)
	       (zero-grad) ; call zero-grad
	       (let ((out (cl-waffe.nn:softmax-cross-entropy (call (model) x) y))) ; get criterion
		 (backward out) ; backward
		 (update) ; call optimizer
		 out)) ; return loss
 :predict ((x)(call (model) x))) ;for predict

(setq trainer (MLPTrainer :relu 1e-4)) ; init your trainer

; Train:   (step-model trainer model-input-x model-input-y)
; Predict: (predict trainer model-input-x)

step-model(trainer &rest args)

An function for calling trainer object defined by deftrainer By using this function, trainer's step-model will be invoked.

Input: Trainer, Args

predict(trainer &rest args)
An function for calling trainer's predict slot

train(trainer dataset &key (valid-dataset nil) (valid-each 100) (enable-animation t) (epoch 1) (batch-size 1) (max-iterate nil) (verbose t) (stream t) (progress-bar-freq 1) (save-model-path nil) (width 45) (random nil) (height 10) (print-each 10))

Trainining given trainer. If any, valid valid-dataset

trainer
Trainer you defined by deftrainer
dataset
Dataset you defined by defdataset
valid-dataset
If valid-dataset=your dataset, use this to valid. If nil, ignored
enable-animation
Ignored
epoch
Iterate training by epoch, default=1
batch-size
Do batch training. default=1
verbose
if t, put log to stream

This function is temporary and other arguments are ignored.

And this function has a lot of todo.

valid(trainer dataset batch-size)
Valid trainer

Datasets

Todo: tutorials here

defdataset

defdataset(name args &key parameters next length (document An dataset structure defined by cl-waffe.))

Defining dataset. (This is kinda pytorch's dataloader)

The slots you defined can be invoked by using (get-dataset dataset index)(get-length dataset).

parameters
parameters datasets have.
next
when function (get-dataset dataset index) is called, this slot invokes. Return waffetensor for the next batch in response to your task.
length
In this form, the function must return the total length of your datasets where the value is fixnum. (Not a batch, and not a current index.)
(defdataset Mnistdata (train valid batch-size)
  :parameters ((train train)(valid valid)(batch-size batch-size))
  :next    ((index)
	    (list (!set-batch (self train) index (self batch-size))
		  (!set-batch (self valid) index (self batch-size))))
  :length (()(car (!shape (self train)))))

cl-waffe excepts index to be 1, 2, 3, ... (dataset-maxlen)

So, please manage batch-sizes in args and :next slots.

get-dataset(dataset index)
Get datum of the index from dataset. Input: dataset ... dataset defined by defdataset. index ... fixnum
get-dataset-length(dataset)
Get total size of your dataset.

optimizer

Tutorials here

defoptimizer

defoptimizer(name args &key parameters update optimize (document An optimizer, defined by cl-waffe.))

Defining optimizers. Internally, This is paraphase of defmodel, which slot names are just different.

Note: by calling :backward slot, optimizers work as (zero-grad).

Name
The optimizer's structure and constructor will be defined based on name
Args
Initializer of the optimizer. The first value of initializer is the hash-table that collected model's parameter where the key is fixnum from 0 to n. You have to store it.
parameters
An parameters that it has.
update
when training and (update) is called, this slot is called and you optimizer your parameters.
optimize
when t, the :update slot is defined with (optimize (speed 3)(space 0)(debug 0)) Default: nil
document
docstring for optimizers. You can use string or (with-usage) macro

Example:

;defoptimizer's args must start with params (symbol-name doesn't matter) which receives hash-table whose key is 1..n

(defoptimizer SGD (params &key (lr 1e-3))
  :optimize t
  :parameters ((params params :type hash-table)
               (lr lr :type single-float))
  :update (()
       (dotimes (i (hash-table-count (self params)))
         ; W(n+1) = W(n) - n * grad
         (!modify (gethash i (self params))) :+=
               (!mul (self lr)(grad (gethash i (self params)))))))

Documentation Template

with-usage(object-name &key (overview Nothing) (args describe like &rest this) (forward Nothing) (step-args describe like &rest this) (backward Nothing) (update Nothing) (step-model Nothing) (predict Nothing) (next Nothing) (length Nothing) (note ))

In :document slot, (for `defmodel, defnode, defoptimizer deftrainer defdataset`) this macro will be useful.

Keyword: step-args, arguments for :forward or :step-model, :next.

cl-waffe automatically generate docstrings.

Todo:Write Docs

build-docstring(usage object-type)

Build docstring based on usage and object-type

Todo: Write Doc