cl-waffe
Package :cl-waffe
⚠️This package is under development and APIs can be changed without notice.
This package has:
- WaffeTensor
- Basic Tensor Operators
- Utils for define extensions (e.g.: defmodel)
- 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.
- 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.
- Trigonometric Functions
- Math Functions
- Activations
- Handling Multidimensional Tensors
!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
!fill fill a tensor with specified value
- Random
- Sampling Probability Distributions
- The usage of Model And Node
- The usage of Trainers
- The usage of Optimizers
- The usage of 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:
- :backward must return list, where that length corresponds with the length of input's argument, otherwise an error occurs when backward.
- 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:
- calling models
- calling criterions
- calling backward
- calling optimizer
- calling zero-grad
- 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)
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)
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-dataset-length
(dataset)
optimizer
Tutorials heredefoptimizer
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