# The `opt` module This module provides methods for optimizing functions which take multiple parameters and produce a single output loss (i.e. negative objective) value. It implements several stochastic gradient optimization methods which can use either automatically-computed or user-provided gradients. In addition, the module exposes convenience utilities to make common neural net training tasks easier. ### Training neural nets `opt.nnTrain` is a convenience function for training a single neural net from data: ```javascript var ad = require('adnn/ad'); var nn = require('adnn/nn'); var opt = require('adnn/opt'); // Train a classifier // e.g. 20 features -> 5 classes // data is an array of {input: Tensor([20]), output: Number}, // where the output number is a class label var classifier = nn.sequence([ nn.linear(20, 10), nn.tanh, nn.linear(10, 5), nn.softmax ]); var data = ...; // Load training data here opt.nnTrain(classifier, data, opt.classificationLoss, { batchSize: 10, iterations: 1000, method: opt.adagrad(), verbose: true // prints iteration count }); // Train a regressor // e.g. linear function R^20 -> R^5 // data is an array of {input: Tensor([20]), output: Tensor([5])} var regressor = nn.linear(20, 5); var data = ...; // Load training data here opt.nnTrain(regressor, data, opt.regressionLoss, { batchSize: 10, iterations: 1000, method: opt.adagrad() }); // Can use your own loss functions instead of 'classificationLoss' or 'regressionLoss' // argument 0: the output of the neural net // argument 1: the 'output' field from a training data point // Returns an AD node containing the (scalar) loss value function customLoss(nnOutput, trainingDataOutput) { ... } // There are several optimization methods available var optMethods = [ // stepSizeDecay: multiplicative decay factor on stepSize after each update opt.sgd({stepSize: 0.1, stepSizeDecay: 0.99}), opt.adagrad({stepSize: 0.1}), opt.rmsprop({stepSize: 0.1, decayRate: 0.9}), // decayRate1: decay rate for first moment estimate // decayRate2: decay rate for second moment estimate opt.adam({stepSize: 0.1, decayRate1: 0.9, decayRate2: 0.99}) ]; ``` ### Training AD functions `opt.adTrain` provides a nearly identical interface to `opt.nnTrain`, but for training general AD functions that are not encapsulated in a neural net. In this case, in addition to its output, the function must also return its parameters so that the optimizer knows what free parameters to optimize. `opt.nnTrain` is actually implemented in terms of `opt.adTrain`. ```javascript var ad = require('adnn/ad'); var opt = require('adnn/opt'); // Learn the parameters of a simple linear function // (i.e. 'params' dot 'input') var params = ad.params([10]); function dot(input) { var output = ad.tensor.sumreduce(ad.scalar.mul(input, params)); return { output: output, parameters: params }; } var data = ...; // Load training data here opt.adTrain(dot, data, opt.regressionLoss, { batchSize: 1, iterations: 1000, method: opt.sgd({stepSize: 0.1}) }); // In the above example, 'parameters' was a single lifted Tensor // In general, 'parameters' can be an arbitrary structure of the following type: // ParamStruct = Tensor | array(ParamStruct) | object(ParamStruct}) // So the following are all valid parameter structures: var parameters1 = ad.params([20]); var parameters2 = [ ad.params([5]), ad.params([10]) ]; var parameters3 = { p1: ad.params([5]), p2: ad.params([10]); var parameters4 = { p1: [ ad.params([5]), ad.params([10]) ], p2: [ ad.params([6]), ad.params([14]) ] }; ``` ### Optimizing AD functions If you want to optimize an objective that isn't based on training data, then you want `opt.adOptimize`. The function to be optimized must return a loss value (a scalar AD node) as well as its free parameters: ```javascript var ad = require('adnn/ad'); var opt = require('adnn/opt'); var tensor = require('adnn/tensor'); // Find the minimum of some arbitrary function var params = ad.params([10]); function foo() { var x = ad.lift(new tensor([10]).fromFlatArray([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])); var diff = ad.tensor.sub(params, x); var sqdiff = ad.tensor.mul(diff, diff); var output = ad.tensor.sumreduce(sqdiff); return { loss: output, parameters: params }; } opt.adOptimize(foo, { iterations: 1000, method: opt.sgd({stepSize: 0.1}) }); ``` ### Optimizing with user-provided gradients It is also possible to use `opt`'s optimization facilities without using AD (i.e. by calculating gradients yourself). `opt.optimize` does this: it expects a function that returns both its free parameters as well as the gradient of the loss with respect to those parameters. All of the other optimization/training methods in `opt` are implemented in terms of this method. ```javascript var Tensor = require('adnn/tensor'); var opt = require('adnn/opt'); // Find the minimum of some arbitrary function var params = new Tensor([10]).fillRandom(); function foo(input) { var output = input.add(params).sqrt().exp().sumreduce(); var gradients = ...; // Compute gradients here (I'm too lazy... :P) return { gradients: gradients, parameters: params }; } opt.optimize(foo, { iterations: 1000, method: opt.sgd({stepSize: 0.1}) }); ```