MXNet Perl Symbolic API

Topics:

How to Compose Symbols

The symbolic API provides a way to configure computation graphs. You can configure the graphs either at the level of neural network layer operations or as fine-grained operations.

The following example configures a two-layer neural network.

pdl> use AI::MXNet qw(mx)
pdl> $data = mx->symbol->Variable("data")
pdl> $fc1  = mx->symbol->FullyConnected(data => $data, name => "fc1", num_hidden => 128)
pdl> $act1 = mx->symbol->Activation(data => $fc1, name => "relu1", act_type => "relu")
pdl> $fc2 =  mx->symbol->FullyConnected(data => $act1, name => "fc2", num_hidden => 64)
pdl> $net =  mx->symbol->SoftmaxOutput(data => $fc2, name => "out")

The basic arithmetic operators (plus, minus, div, multiplication) are overloaded for element-wise operations of symbols.

The following example creates a computation graph that adds two inputs together.

pdl> use AI::MXNet qw(mx)
pdl> $a =  mx->symbol->Variable("a")
pdl> $b =  mx->symbol->Variable("b")
pdl> $c = $a + $b

Symbol Attributes

You can add an attribute to a symbol by providing an attribute hash when you create a symbol.

$data =  mx->symbol->Variable("data", attr => { mood => "angry" })
$op   =  mx->symbol->Convolution(data => $data, kernel => [1, 1], num_filter => 1, attr => { mood => "so so" })

For proper communication with the C++ backend, both the key and values of the attribute dictionary should be strings. To retrieve the attributes, use ->attr($key):

    $data->attr("mood")

To attach attributes, you can use AI::MXNet::AttrScope. AI::MXNet::AttrScopeAttrScope automatically adds the specified attributes to all of the symbols created within that scope. The user can also inherit this object to change naming behavior. For example:

use AI::MXNet qw(mx);
use Test::More tests => 3;
my ($data, $gdata);
{
    local($mx::AttrScope) = mx->AttrScope(group=>4, data=>'great');
    $data = mx->sym->Variable("data", attr => { dtype => "data", group => "1" });
    $gdata = mx->sym->Variable("data2");
}
ok($gdata->attr("group") == 4);
ok($data->attr("group") == 1);

my $exceedScopeData = mx->sym->Variable("data3");
ok((not defined $exceedScopeData->attr("group")), "No group attr in global attr scope");

Serialization

There are two ways to save and load the symbols. You can use the mx->symbol->save and mxnet->symbol->load functions to serialize the AI::MXNet::Symbol objects. The advantage of using save and load functions is that it is language agnostic and cloud friendly. The symbol is saved in JSON format. You can also get a JSON string directly using $symbol->tojson.

The following example shows how to save a symbol to an S3 bucket, load it back, and compare two symbols using a JSON string.

pdl> use AI::MXNet qw(mx)
pdl> $a = mx->sym->Variable("a")
pdl> $b = mx->sym->Variable("b")
pdl> $c = $a + $b
pdl> $c->save("s3://my-bucket/symbol-c.json")
pdl> $c2 = $c->load("s3://my-bucket/symbol-c.json")
pdl> ok($c->tojson eq $c2->tojson)
ok 1

Executing Symbols

After you have assembled a set of symbols into a computation graph, the MXNet engine can evaluate them. If you are training a neural network, this is typically handled by the high-level AI::MXNet::Module package and the [fit()] function.

For neural networks used in “feed-forward”, “prediction”, or “inference” mode (all terms for the same thing: running a trained network), the input arguments are the input data, and the weights of the neural network that were learned during training.

To manually execute a set of symbols, you need to create an [AI::MXNet::Executor] object, which is typically constructed by calling the [simple_bind()] method on a AI::MXNet::Symbol.

Multiple Outputs

To group the symbols together, use the AI::MXNet::Symbol->Group function.

pdl> use AI::MXNet qw(mx)
pdl> use Data::Dumper
pdl> $data  = mx->sym->Variable("data")
pdl> $fc1   = mx->sym->FullyConnected($data, name => "fc1", num_hidden => 128)
pdl> $act1  = mx->sym->Activation($fc1, name => "relu1", act_type => "relu")
pdl> $fc2   = mx->sym->FullyConnected($act1, name => "fc2", num_hidden => 64)
pdl> $net   = mx->sym->SoftmaxOutput($fc2, name => "softmax")
pdl> $group = mx->sym->Group([$fc1, $net])
pdl> print Dumper($group->list_outputs())
$VAR1 = [
    'fc1_output',
    'softmax_output'
];

After you get the Group, you can bind on group instead. The resulting executor will have two outputs, one for fc1_output and one for softmax_output.