You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mxnet.apache.org by jx...@apache.org on 2017/08/09 20:20:07 UTC

[incubator-mxnet] branch master updated: 1) Fixes for ImageIter (#7357)

This is an automated email from the ASF dual-hosted git repository.

jxie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-mxnet.git


The following commit(s) were added to refs/heads/master by this push:
     new f674bc4  1) Fixes for ImageIter (#7357)
f674bc4 is described below

commit f674bc40dbdb834919a37bd12af3003d7a427307
Author: Sergey Kolychev <se...@gmail.com>
AuthorDate: Wed Aug 9 13:20:04 2017 -0700

    1) Fixes for ImageIter (#7357)
    
    2) Convolutional RNN
    3) Improved Visualization
    4) PearsonCorrelation metric
    5) Fixed tests.
---
 perl-package/AI-MXNet/Changes                      |   3 +
 perl-package/AI-MXNet/MANIFEST                     |   1 -
 perl-package/AI-MXNet/META.json                    |   4 +-
 perl-package/AI-MXNet/META.yml                     |   4 +-
 perl-package/AI-MXNet/Makefile.PL                  |   4 +-
 perl-package/AI-MXNet/README                       |   2 +-
 perl-package/AI-MXNet/lib/AI/MXNet.pm              |   2 +-
 perl-package/AI-MXNet/lib/AI/MXNet/Base.pm         |   2 +-
 perl-package/AI-MXNet/lib/AI/MXNet/Image.pm        |   8 +-
 perl-package/AI-MXNet/lib/AI/MXNet/Metric.pm       |  50 ++++
 perl-package/AI-MXNet/lib/AI/MXNet/Module.pm       |   8 -
 perl-package/AI-MXNet/lib/AI/MXNet/RNN.pm          |   3 +
 perl-package/AI-MXNet/lib/AI/MXNet/RNN/Cell.pm     | 307 ++++++++++++++++++++-
 perl-package/AI-MXNet/lib/AI/MXNet/Symbol.pm       |   2 +-
 perl-package/AI-MXNet/lib/AI/MXNet/Types.pm        |   4 +-
 .../AI-MXNet/lib/AI/MXNet/Visualization.pm         |   8 +
 perl-package/AI-MXNet/t/test_model_parallel.t      |  74 +++++
 perl-package/AI-MXNet/t/test_rnn.t                 |  62 ++++-
 perl-package/AI-MXNetCAPI/Changes                  |   3 +
 perl-package/AI-MXNetCAPI/META.json                |   2 +-
 perl-package/AI-MXNetCAPI/META.yml                 |   2 +-
 perl-package/AI-MXNetCAPI/README                   |   2 +-
 perl-package/AI-MXNetCAPI/lib/AI/MXNetCAPI.pm      |   2 +-
 perl-package/AI-MXNetCAPI/mxnet.i                  |   7 +
 24 files changed, 537 insertions(+), 29 deletions(-)

diff --git a/perl-package/AI-MXNet/Changes b/perl-package/AI-MXNet/Changes
index 5d5c5a2..f8ecc75 100644
--- a/perl-package/AI-MXNet/Changes
+++ b/perl-package/AI-MXNet/Changes
@@ -1,5 +1,8 @@
 Revision history for Perl extension AI::MXNet
 
+1.0102 Sun Aug  6 16:55:08 PDT 2017
+        - bugfixes in Image.pm, updated tests, added PearsonCorrelation metric, added Convolutional RNN modules.
+
 1.0101  Sun Jul  2 17:16:01 PDT 2017
         - reworked CachedOp, two new optimizers, auto module reshape, using strings to index the kvstore.
 
diff --git a/perl-package/AI-MXNet/MANIFEST b/perl-package/AI-MXNet/MANIFEST
index 7a6d78b..48cb31d 100644
--- a/perl-package/AI-MXNet/MANIFEST
+++ b/perl-package/AI-MXNet/MANIFEST
@@ -10,7 +10,6 @@ examples/cudnn_lstm_bucketing.pl
 Makefile.PL
 Changes
 META.json
-t/test_autograd.t
 t/test_recordio.t
 t/test_random.t
 t/test_init.t
diff --git a/perl-package/AI-MXNet/META.json b/perl-package/AI-MXNet/META.json
index 5454592..692f1dd 100644
--- a/perl-package/AI-MXNet/META.json
+++ b/perl-package/AI-MXNet/META.json
@@ -30,7 +30,7 @@
       },
       "runtime" : {
          "requires" : {
-            "AI::MXNetCAPI" : "1.0101",
+            "AI::MXNetCAPI" : "1.0102",
             "AI::NNVMCAPI" : "1.01",
             "Function::Parameters" : "1.0705",
             "GraphViz" : "2.14",
@@ -43,5 +43,5 @@
       }
    },
    "release_status" : "stable",
-   "version" : "1.0101"
+   "version" : "1.0102"
 }
diff --git a/perl-package/AI-MXNet/META.yml b/perl-package/AI-MXNet/META.yml
index 8c09c96..5b92018 100644
--- a/perl-package/AI-MXNet/META.yml
+++ b/perl-package/AI-MXNet/META.yml
@@ -17,10 +17,10 @@ no_index:
     - t
     - inc
 requires:
-  AI::MXNetCAPI: '1.0101'
+  AI::MXNetCAPI: '1.0102'
   AI::NNVMCAPI: '1.01'
   Function::Parameters: '1.0705'
   GraphViz: '2.14'
   Mouse: v2.1.0
   PDL: '2.007'
-version: '1.0101'
+version: '1.0102'
diff --git a/perl-package/AI-MXNet/Makefile.PL b/perl-package/AI-MXNet/Makefile.PL
index 990176d..2c9bda8 100644
--- a/perl-package/AI-MXNet/Makefile.PL
+++ b/perl-package/AI-MXNet/Makefile.PL
@@ -19,7 +19,7 @@ my %WriteMakefileArgs = (
   "LICENSE" => "apache_2_0",
   "NAME" => "AI::MXNet",
   "PREREQ_PM" => {
-    "AI::MXNetCAPI" => "1.0101",
+    "AI::MXNetCAPI" => "1.0102",
     "AI::NNVMCAPI" => "1.01",
     "Function::Parameters" => "1.0705",
     "Mouse" => "v2.1.0",
@@ -35,7 +35,7 @@ my %WriteMakefileArgs = (
 
 
 my %FallbackPrereqs = (
-  "AI::MXNetCAPI" => "1.0101",
+  "AI::MXNetCAPI" => "1.0102",
   "AI::NNVMCAPI" => "1.01",
   "Function::Parameters" => "1.0705",
   "Mouse" => "v2.1.0",
diff --git a/perl-package/AI-MXNet/README b/perl-package/AI-MXNet/README
index f275d08..86b6cf1 100644
--- a/perl-package/AI-MXNet/README
+++ b/perl-package/AI-MXNet/README
@@ -1,5 +1,5 @@
 This archive contains the distribution AI-MXNet,
-version 1.0101:
+version 1.0102:
 
   Perl interface to MXNet machine learning library
 
diff --git a/perl-package/AI-MXNet/lib/AI/MXNet.pm b/perl-package/AI-MXNet/lib/AI/MXNet.pm
index 1d21253..40e84a6 100644
--- a/perl-package/AI-MXNet/lib/AI/MXNet.pm
+++ b/perl-package/AI-MXNet/lib/AI/MXNet.pm
@@ -46,7 +46,7 @@ use AI::MXNet::Image;
 use AI::MXNet::Contrib;
 use AI::MXNet::Contrib::AutoGrad;
 use AI::MXNet::CachedOp;
-our $VERSION = '1.0101';
+our $VERSION = '1.0102';
 
 sub import
 {
diff --git a/perl-package/AI-MXNet/lib/AI/MXNet/Base.pm b/perl-package/AI-MXNet/lib/AI/MXNet/Base.pm
index d5ff0dd..0c42fa9 100644
--- a/perl-package/AI-MXNet/lib/AI/MXNet/Base.pm
+++ b/perl-package/AI-MXNet/lib/AI/MXNet/Base.pm
@@ -20,7 +20,7 @@ use strict;
 use warnings;
 use PDL;
 use PDL::Types qw();
-use AI::MXNetCAPI 1.0101;
+use AI::MXNetCAPI 1.0102;
 use AI::NNVMCAPI 1.01;
 use AI::MXNet::Types;
 use Time::HiRes;
diff --git a/perl-package/AI-MXNet/lib/AI/MXNet/Image.pm b/perl-package/AI-MXNet/lib/AI/MXNet/Image.pm
index b996b02..18ef42a 100644
--- a/perl-package/AI-MXNet/lib/AI/MXNet/Image.pm
+++ b/perl-package/AI-MXNet/lib/AI/MXNet/Image.pm
@@ -764,7 +764,7 @@ sub BUILD
         {
             chomp($line);
             my @line = split(/\t/, $line);
-            my $label = AI::MXNet::NDArray->array([@line[1..@line-1]]);
+            my $label = AI::MXNet::NDArray->array([@line[1..@line-2]]);
             my $key   = $line[0];
             $imglist{$key} = [$label, $line[-1]];
             push @imgkeys, $key;
@@ -838,6 +838,10 @@ sub BUILD
     {
         $self->aug_list(AI::MXNet::Image->CreateAugmenter(data_shape => $self->data_shape, %{ $self->kwargs//{} }));
     }
+    else
+    {
+        $self->aug_list([]);
+    }
     $self->cur(0);
     $self->reset();
 }
@@ -877,7 +881,7 @@ method next_sample()
         }
         else
         {
-            my ($label, $fname) = $self->imglist->{$idx};
+            my ($label, $fname) = @{ $self->imglist->{$idx} };
             if(not defined $self->imgrec)
             {
                 open(F, $self->path_root . "/$fname") or confess("can't open $fname $!");
diff --git a/perl-package/AI-MXNet/lib/AI/MXNet/Metric.pm b/perl-package/AI-MXNet/lib/AI/MXNet/Metric.pm
index 6504481..c3a3183 100644
--- a/perl-package/AI-MXNet/lib/AI/MXNet/Metric.pm
+++ b/perl-package/AI-MXNet/lib/AI/MXNet/Metric.pm
@@ -510,6 +510,55 @@ method update(ArrayRef[AI::MXNet::NDArray] $labels, ArrayRef[AI::MXNet::NDArray]
     }, $labels, $preds);
 }
 
+package AI::MXNet::PearsonCorrelation;
+use Mouse;
+use AI::MXNet::Base;
+extends 'AI::MXNet::EvalMetric';
+has '+name'   => (default => 'pearson-correlation');
+
+=head1 NAME
+
+    AI::MXNet::PearsonCorrelation
+=cut
+
+=head1 DESCRIPTION
+
+    Computes Pearson correlation.
+
+    Parameters
+    ----------
+    name : str
+        Name of this metric instance for display.
+
+    Examples
+    --------
+    >>> $predicts = [mx->nd->array([[0.3, 0.7], [0, 1.], [0.4, 0.6]])]
+    >>> $labels   = [mx->nd->array([[1, 0], [0, 1], [0, 1]])]
+    >>> $pr = mx->metric->PearsonCorrelation()
+    >>> $pr->update($labels, $predicts)
+    >>> print pr->get()
+    ('pearson-correlation', '0.421637061887229')
+=cut
+
+method update(ArrayRef[AI::MXNet::NDArray] $labels, ArrayRef[AI::MXNet::NDArray] $preds)
+{
+    AI::MXNet::Metric::check_label_shapes($labels, $preds);
+    zip(sub {
+        my ($label, $pred) = @_;
+        AI::MXNet::Metric::check_label_shapes($label, $pred);
+        $label = $label->aspdl->flat;
+        $pred  = $pred->aspdl->flat;
+        my ($label_mean, $label_stdv) = ($label->stats)[0, 6];
+        my ($pred_mean, $pred_stdv) = ($pred->stats)[0, 6];
+        $self->sum_metric(
+            $self->sum_metric
+                +
+            ((($label-$label_mean)*($pred-$pred_mean))->sum/$label->nelem)/(($label_stdv*$pred_stdv)->at(0))
+        );
+        $self->num_inst($self->num_inst + 1);
+    }, $labels, $preds);
+}
+
 =head1 DESCRIPTION
 
     Custom evaluation metric that takes a sub ref.
@@ -574,6 +623,7 @@ my %metrics = qw/
     top_k_accuracy AI::MXNet::TopKAccuracy
     Perplexity     AI::MXNet::Perplexity
     perplexity     AI::MXNet::Perplexity
+    pearsonr       AI::MXNet::PearsonCorrelation
 /;
 
 method create(Metric|ArrayRef[Metric] $metric, %kwargs)
diff --git a/perl-package/AI-MXNet/lib/AI/MXNet/Module.pm b/perl-package/AI-MXNet/lib/AI/MXNet/Module.pm
index 967a511..3e4d938 100644
--- a/perl-package/AI-MXNet/lib/AI/MXNet/Module.pm
+++ b/perl-package/AI-MXNet/lib/AI/MXNet/Module.pm
@@ -796,14 +796,6 @@ method forward(
 )
 {
     assert($self->binded and $self->params_initialized);
-    # If starting to do the inference, force rebind the module.
-    if($self->label_shapes and not $data_batch->label)
-    {
-        confess(
-            "If you are trying to do inference, rebind module ".
-            "with 'force_rebind=True' and 'for_training=False'"
-        );
-    }
 
     my @curr_data_shapes = map { $_->shape } @{ $self->data_shapes };
     my @new_data_shapes  = map { $_->shape } @{ $data_batch->data };
diff --git a/perl-package/AI-MXNet/lib/AI/MXNet/RNN.pm b/perl-package/AI-MXNet/lib/AI/MXNet/RNN.pm
index 1ccab31..07e72a7 100644
--- a/perl-package/AI-MXNet/lib/AI/MXNet/RNN.pm
+++ b/perl-package/AI-MXNet/lib/AI/MXNet/RNN.pm
@@ -166,6 +166,9 @@ method SequentialRNNCell(@args)  { AI::MXNet::RNN::SequentialCell->new(@args) }
 method BidirectionalCell(@args)  { AI::MXNet::RNN::BidirectionalCell->new(@args) }
 method DropoutCell(@args)        { AI::MXNet::RNN::DropoutCell->new(@args) }
 method ZoneoutCell(@args)        { AI::MXNet::RNN::ZoneoutCell->new(@args) }
+method ConvRNNCell(@args)        { AI::MXNet::RNN::ConvCell->new(@args) }
+method ConvLSTMCell(@args)       { AI::MXNet::RNN::ConvLSTMCell->new(@args) }
+method ConvGRUCell(@args)        { AI::MXNet::RNN::ConvGRUCell->new(@args) }
 method ResidualCell(@args)       { AI::MXNet::RNN::ResidualCell->new(@args) }
 method encode_sentences(@args)   { AI::MXNet::RNN::IO->encode_sentences(@args) }
 method BucketSentenceIter(@args)
diff --git a/perl-package/AI-MXNet/lib/AI/MXNet/RNN/Cell.pm b/perl-package/AI-MXNet/lib/AI/MXNet/RNN/Cell.pm
index 0221a90..08c3094 100644
--- a/perl-package/AI-MXNet/lib/AI/MXNet/RNN/Cell.pm
+++ b/perl-package/AI-MXNet/lib/AI/MXNet/RNN/Cell.pm
@@ -766,7 +766,7 @@ has '_dropout'         => (is => 'ro', isa => 'Num',  init_arg => 'dropout',
 has '_get_next_state'  => (is => 'ro', isa => 'Bool', init_arg => 'get_next_state', default => 0);
 has '_bidirectional'   => (is => 'ro', isa => 'Bool', init_arg => 'bidirectional',  default => 0);
 has 'forget_bias'      => (is => 'ro', isa => 'Num',  default => 1);
-has 'initializer'      => (is => 'rw', isa => 'Maybe[AI::MXNet::Initializer]');
+has 'initializer'      => (is => 'rw', isa => 'Maybe[Initializer]');
 has '_mode'            => (
     is => 'ro',
     isa => enum([qw/rnn_relu rnn_tanh lstm gru/]),
@@ -1429,6 +1429,309 @@ method unroll(
     return($outputs, $states);
 }
 
+package AI::MXNet::RNN::ConvCell::Base;
+use Mouse;
+use AI::MXNet::Base;
+extends 'AI::MXNet::RNN::Cell::Base';
+
+=head1 NAME
+
+    AI::MXNet::RNN::Conv::Base
+=cut
+
+=head1 DESCRIPTION
+
+    Abstract base class for Convolutional RNN cells
+
+=cut
+
+has '_h2h_kernel'  => (is => 'ro', isa => 'Shape', init_arg => 'h2h_kernel');
+has '_h2h_dilate'  => (is => 'ro', isa => 'Shape', init_arg => 'h2h_dilate');
+has '_h2h_pad'     => (is => 'rw', isa => 'Shape', init_arg => undef);
+has '_i2h_kernel'  => (is => 'ro', isa => 'Shape', init_arg => 'i2h_kernel');
+has '_i2h_stride'  => (is => 'ro', isa => 'Shape', init_arg => 'i2h_stride');
+has '_i2h_dilate'  => (is => 'ro', isa => 'Shape', init_arg => 'i2h_dilate');
+has '_i2h_pad'     => (is => 'ro', isa => 'Shape', init_arg => 'i2h_pad');
+has '_num_hidden'  => (is => 'ro', isa => 'DimSize', init_arg => 'num_hidden');
+has '_input_shape' => (is => 'ro', isa => 'Shape', init_arg => 'input_shape');
+has '_conv_layout' => (is => 'ro', isa => 'Str', init_arg => 'conv_layout', default => 'NCHW');
+has '_activation'  => (is => 'ro', init_arg => 'activation');
+has '_state_shape' => (is => 'rw', init_arg => undef);
+has [qw/i2h_weight_initializer h2h_weight_initializer
+    i2h_bias_initializer h2h_bias_initializer/] => (is => 'rw', isa => 'Maybe[Initializer]');
+
+sub BUILD
+{
+    my $self = shift;
+    assert (
+        ($self->_h2h_kernel->[0] % 2 == 1 and $self->_h2h_kernel->[1] % 2 == 1),
+        "Only support odd numbers, got h2h_kernel= (@{[ $self->_h2h_kernel ]})"
+    );
+    $self->_h2h_pad([
+        int($self->_h2h_dilate->[0] * ($self->_h2h_kernel->[0] - 1) / 2),
+        int($self->_h2h_dilate->[1] * ($self->_h2h_kernel->[1] - 1) / 2)
+    ]);
+    # Infer state shape
+    my $data = AI::MXNet::Symbol->Variable('data');
+    my $state_shape = AI::MXNet::Symbol->Convolution(
+        data => $data,
+        num_filter => $self->_num_hidden,
+        kernel => $self->_i2h_kernel,
+        stride => $self->_i2h_stride,
+        pad => $self->_i2h_pad,
+        dilate => $self->_i2h_dilate,
+        layout => $self->_conv_layout
+    );
+    $state_shape = ($state_shape->infer_shape(data=>$self->_input_shape))[1]->[0];
+    $state_shape->[0] = 0;
+    $self->_state_shape($state_shape);
+}
+
+method state_info()
+{
+    return [
+                { shape => $self->_state_shape, __layout__ => $self->_conv_layout },
+                { shape => $self->_state_shape, __layout__ => $self->_conv_layout }
+    ];
+}
+
+method call($inputs, $states)
+{
+    confess("AI::MXNet::RNN::ConvCell::Base is abstract class for convolutional RNN");
+}
+
+package AI::MXNet::RNN::ConvCell;
+use Mouse;
+extends 'AI::MXNet::RNN::ConvCell::Base';
+
+=head1 NAME
+
+    AI::MXNet::RNN::ConvCell
+=cut
+
+=head1 DESCRIPTION
+
+    Convolutional RNN cells
+
+    Parameters
+    ----------
+    input_shape : array ref of int
+        Shape of input in single timestep.
+    num_hidden : int
+        Number of units in output symbol.
+    h2h_kernel : array ref of int, default (3, 3)
+        Kernel of Convolution operator in state-to-state transitions.
+    h2h_dilate : array ref of int, default (1, 1)
+        Dilation of Convolution operator in state-to-state transitions.
+    i2h_kernel : array ref of int, default (3, 3)
+        Kernel of Convolution operator in input-to-state transitions.
+    i2h_stride : array ref of int, default (1, 1)
+        Stride of Convolution operator in input-to-state transitions.
+    i2h_pad : array ref of int, default (1, 1)
+        Pad of Convolution operator in input-to-state transitions.
+    i2h_dilate : array ref of int, default (1, 1)
+        Dilation of Convolution operator in input-to-state transitions.
+    activation : str or Symbol,
+        default functools.partial(symbol.LeakyReLU, act_type='leaky', slope=0.2)
+        Type of activation function.
+    prefix : str, default 'ConvRNN_'
+        Prefix for name of layers (and name of weight if params is None).
+    params : RNNParams, default None
+        Container for weight sharing between cells. Created if None.
+    conv_layout : str, , default 'NCHW'
+        Layout of ConvolutionOp
+=cut
+
+has '+_h2h_kernel' => (default => sub { [3, 3] });
+has '+_h2h_dilate' => (default => sub { [1, 1] });
+has '+_i2h_kernel' => (default => sub { [3, 3] });
+has '+_i2h_stride' => (default => sub { [1, 1] });
+has '+_i2h_dilate' => (default => sub { [1, 1] });
+has '+_i2h_pad'    => (default => sub { [1, 1] });
+has '+_prefix'     => (default => 'ConvRNN_');
+has '+_activation' => (default => sub { sub { AI::MXNet::Symbol->LeakyReLU(@_, act_type => 'leaky', slope => 0.2) } });
+has '+i2h_bias_initializer' => (default => 'zeros');
+has '+h2h_bias_initializer' => (default => 'zeros');
+has 'forget_bias'  => (is => 'ro', isa => 'Num');
+has [qw/_iW _iB
+        _hW _hB/] => (is => 'rw', init_arg => undef);
+
+
+sub BUILD
+{
+    my $self = shift;
+    $self->_iW($self->_params->get('i2h_weight', init => $self->i2h_weight_initializer));
+    $self->_hW($self->_params->get('h2h_weight', init => $self->h2h_weight_initializer));
+    $self->_iB(
+        $self->params->get(
+            'i2h_bias',
+            (defined($self->forget_bias and not defined $self->i2h_bias_initializer)
+                ? (init => AI::MXNet::LSTMBias->new(forget_bias => $self->forget_bias))
+                : (init => $self->i2h_bias_initializer)
+            )
+        )
+    );
+    $self->_hB($self->_params->get('h2h_bias', init => $self->h2h_bias_initializer));
+}
+
+method _num_gates()
+{
+    scalar(@{ $self->_gate_names() });
+}
+
+method _gate_names()
+{
+    return ['']
+}
+
+method _conv_forward($inputs, $states, $name)
+{
+    my $i2h = AI::MXNet::Symbol->Convolution(
+        name       => "${name}i2h",
+        data       => $inputs,
+        num_filter => $self->_num_hidden*$self->_num_gates(),
+        kernel     => $self->_i2h_kernel,
+        stride     => $self->_i2h_stride,
+        pad        => $self->_i2h_pad,
+        dilate     => $self->_i2h_dilate,
+        weight     => $self->_iW,
+        bias       => $self->_iB
+    );
+    my $h2h = AI::MXNet::Symbol->Convolution(
+        name       => "${name}h2h",
+        data       => @{ $states }[0],
+        num_filter => $self->_num_hidden*$self->_num_gates(),
+        kernel     => $self->_h2h_kernel,
+        stride     => [1, 1],
+        pad        => $self->_h2h_pad,
+        dilate     => $self->_h2h_dilate,
+        weight     => $self->_hW,
+        bias       => $self->_hB
+    );
+    return ($i2h, $h2h);
+}
+
+method call(AI::MXNet::Symbol $inputs, AI::MXNet::Symbol|ArrayRef[AI::MXNet::Symbol] $states)
+{
+    $self->_counter($self->_counter + 1);
+    my $name = sprintf('%st%d_', $self->_prefix, $self->_counter);
+    my ($i2h, $h2h) = $self->_conv_forward($inputs, $states, $name);
+    my $output = $self->_get_activation($i2h + $h2h, $self->_activation, name => "${name}out");
+    return ($output, [$output]);
+}
+
+package AI::MXNet::RNN::ConvLSTMCell;
+use Mouse;
+extends 'AI::MXNet::RNN::ConvCell';
+has '+forget_bias' => (default => 1);
+has '+_prefix'     => (default => 'ConvLSTM_');
+
+=head1 NAME
+
+    AI::MXNet::RNN::ConvLSTMCell
+=cut
+
+=head1 DESCRIPTION
+
+    Convolutional LSTM network cell.
+
+    Reference:
+        Xingjian et al. NIPS2015
+=cut
+
+method _gate_names()
+{
+    return ['_i', '_f', '_c', '_o'];
+}
+
+method call(AI::MXNet::Symbol $inputs, AI::MXNet::Symbol|ArrayRef[AI::MXNet::Symbol] $states)
+{
+    $self->_counter($self->_counter + 1);
+    my $name = sprintf('%st%d_', $self->_prefix, $self->_counter);
+    my ($i2h, $h2h) = $self->_conv_forward($inputs, $states, $name);
+    my $gates = $i2h + $h2h;
+    my @slice_gates = @{ AI::MXNet::Symbol->SliceChannel(
+        $gates,
+        num_outputs => 4,
+        axis => index($self->_conv_layout, 'C'),
+        name => "${name}slice"
+    ) };
+    my $in_gate = AI::MXNet::Symbol->Activation(
+        $slice_gates[0],
+        act_type => "sigmoid",
+        name => "${name}i"
+    );
+    my $forget_gate = AI::MXNet::Symbol->Activation(
+        $slice_gates[1],
+        act_type => "sigmoid",
+        name => "${name}f"
+    );
+    my $in_transform = $self->_get_activation(
+        $slice_gates[2],
+        $self->_activation,
+        name => "${name}c"
+    );
+    my $out_gate = AI::MXNet::Symbol->Activation(
+        $slice_gates[3],
+        act_type => "sigmoid",
+        name => "${name}o"
+    );
+    my $next_c = AI::MXNet::Symbol->_plus(
+        $forget_gate * @{$states}[1],
+        $in_gate * $in_transform,
+        name => "${name}state"
+    );
+    my $next_h = AI::MXNet::Symbol->_mul(
+        $out_gate, $self->_get_activation($next_c, $self->_activation),
+        name => "${name}out"
+    );
+    return ($next_h, [$next_h, $next_c]);
+}
+
+package AI::MXNet::RNN::ConvGRUCell;
+use Mouse;
+extends 'AI::MXNet::RNN::ConvCell';
+has '+_prefix'     => (default => 'ConvGRU_');
+
+=head1 NAME
+
+    AI::MXNet::RNN::ConvGRUCell
+=cut
+
+=head1 DESCRIPTION
+
+    Convolutional GRU network cell.
+=cut
+
+method _gate_names()
+{
+    return ['_r', '_z', '_o'];
+}
+
+method call(AI::MXNet::Symbol $inputs, AI::MXNet::Symbol|ArrayRef[AI::MXNet::Symbol] $states)
+{
+    $self->_counter($self->_counter + 1);
+    my $name = sprintf('%st%d_', $self->_prefix, $self->_counter);
+    my ($i2h, $h2h) = $self->_conv_forward($inputs, $states, $name);
+    my ($i2h_r, $i2h_z, $h2h_r, $h2h_z);
+    ($i2h_r, $i2h_z, $i2h) = @{ AI::MXNet::Symbol->SliceChannel($i2h, num_outputs => 3, name => "${name}_i2h_slice") };
+    ($h2h_r, $h2h_z, $h2h) = @{ AI::MXNet::Symbol->SliceChannel($h2h, num_outputs => 3, name => "${name}_h2h_slice") };
+    my $reset_gate = AI::MXNet::Symbol->Activation(
+        $i2h_r + $h2h_r, act_type => "sigmoid",
+        name => "${name}_r_act"
+    );
+    my $update_gate = AI::MXNet::Symbol->Activation(
+        $i2h_z + $h2h_z, act_type => "sigmoid",
+        name => "${name}_z_act"
+    );
+    my $next_h_tmp = $self->_get_activation($i2h + $reset_gate * $h2h, $self->_activation, name => "${name}_h_act");
+    my $next_h = AI::MXNet::Symbol->_plus(
+        (1 - $update_gate) * $next_h_tmp, $update_gate * @{$states}[0],
+        name => "${name}out"
+    );
+    return ($next_h, [$next_h]);
+}
+
 package AI::MXNet::RNN::ModifierCell;
 use Mouse;
 use AI::MXNet::Base;
@@ -1593,7 +1896,7 @@ method call(AI::MXNet::Symbol $inputs, SymbolOrArrayOfSymbols $states)
             p => $p
         );
     };
-    my $prev_output = $self->prev_output || AI::MXNet::Symbol->zeros(shape => [0, 0]);
+    my $prev_output = $self->prev_output // AI::MXNet::Symbol->zeros(shape => [0, 0]);
     my $output = $p_outputs != 0
         ? AI::MXNet::Symbol->where(
             &{$mask}($p_outputs, $next_output),
diff --git a/perl-package/AI-MXNet/lib/AI/MXNet/Symbol.pm b/perl-package/AI-MXNet/lib/AI/MXNet/Symbol.pm
index a5298c7..eed6e93 100644
--- a/perl-package/AI-MXNet/lib/AI/MXNet/Symbol.pm
+++ b/perl-package/AI-MXNet/lib/AI/MXNet/Symbol.pm
@@ -1232,7 +1232,7 @@ method Variable(
     Maybe[Num]                    :$lr_mult=,
     Maybe[Num]                    :$wd_mult=,
     Maybe[Dtype]                  :$dtype=,
-    Maybe[AI::MXNet::Initializer] :$init=,
+    Maybe[Initializer]            :$init=,
     HashRef[Str]                  :$kwargs={},
     Maybe[Str]                    :$__layout__=
 )
diff --git a/perl-package/AI-MXNet/lib/AI/MXNet/Types.pm b/perl-package/AI-MXNet/lib/AI/MXNet/Types.pm
index e48ae3c..b4ec7e9 100644
--- a/perl-package/AI-MXNet/lib/AI/MXNet/Types.pm
+++ b/perl-package/AI-MXNet/lib/AI/MXNet/Types.pm
@@ -34,6 +34,7 @@ class_type 'AI::MXNet::Callback';
 class_type 'AI::MXNet::EvalMetric';
 class_type 'AI::MXNet::DataParallelExecutorGroup';
 class_type 'AI::MXNet::Optimizer';
+class_type 'AI::MXNet::Initializer';
 class_type 'AI::MXNet::InitDesc';
 class_type 'AI::MXNet::IRHeader';
 subtype "AcceptableInput" => as "Num|PDL|PDL::Matrix|AI::MXNet::NDArray|AI::MXNet::NDArray::Slice|ArrayRef";
@@ -55,6 +56,7 @@ subtype "NameShape"       => as "ArrayRef" => where {
 subtype "Callback"        => as "CodeRef|ArrayRef[Coderef]|AI::MXNet::Callback|ArrayRef[AI::MXNet::Callback]";
 subtype "EvalMetric"      => as "AI::MXNet::EvalMetric|Str|CodeRef";
 subtype "Optimizer"       => as "AI::MXNet::Optimizer|Str";
-subtype "Activation"      => as "AI::MXNet::Symbol|Str";
+subtype "Initializer"     => as "AI::MXNet::Initializer|Str";
+subtype "Activation"      => as "AI::MXNet::Symbol|Str|CodeRef";
 subtype "SymbolOrArrayOfSymbols" => as "AI::MXNet::Symbol|ArrayRef[AI::MXNet::Symbol]";
 subtype "NameShapeOrDataDesc" => as "NameShape|AI::MXNet::DataDesc";
diff --git a/perl-package/AI-MXNet/lib/AI/MXNet/Visualization.pm b/perl-package/AI-MXNet/lib/AI/MXNet/Visualization.pm
index 4cdc135..e28cd65 100644
--- a/perl-package/AI-MXNet/lib/AI/MXNet/Visualization.pm
+++ b/perl-package/AI-MXNet/lib/AI/MXNet/Visualization.pm
@@ -371,6 +371,7 @@ method plot_network(
         }
         $dot->graph->add_node($name, label => $label, %attr);
     };
+
     # add edges
     for my $node (@{ $nodes })
     {
@@ -395,6 +396,13 @@ method plot_network(
                     {
                         my $key = $input_name;
                         $key   .= '_output' if $input_node->{op} ne 'null';
+                        if($input_node->{op} ne 'null' and exists $input_node->{attr})
+                        {
+                            if(ref $input_node->{attr} eq 'HASH' and exists $input_node->{attr}{num_outputs})
+                            {
+                                $key .= ($input_node->{attr}{num_outputs} - 1);
+                            }
+                        }
                         my $end = @{ $shape_dict{$key} };
                         $attr{label} = join('x', @{ $shape_dict{$key} }[1..$end-1]);
                     }
diff --git a/perl-package/AI-MXNet/t/test_model_parallel.t b/perl-package/AI-MXNet/t/test_model_parallel.t
new file mode 100644
index 0000000..6a8aba7
--- /dev/null
+++ b/perl-package/AI-MXNet/t/test_model_parallel.t
@@ -0,0 +1,74 @@
+use strict;
+use warnings;
+use Test::More tests => 4;
+use AI::MXNet qw(mx);
+use AI::MXNet::TestUtils qw(reldiff);
+use AI::MXNet::Base;
+
+sub test_chain
+{
+    my $ctx1 = mx->cpu(0);
+    my $ctx2 = mx->cpu(1);
+    my $n = 2;
+    my $data1 = mx->sym->Variable('data1');
+    my $data2 = mx->sym->Variable('data2');
+    my $data3 = mx->sym->Variable('data2');
+    my $net;
+    {
+        local($mx::AttrScope) = mx->AttrScope(ctx_group=>'dev1');
+        $net = $data1 + $data2;
+        $net = $net * 3;
+    }
+    {
+        local($mx::AttrScope) = mx->AttrScope(ctx_group=>'dev2');
+        $net = $net + $data3;
+    }
+
+    my $arr = [];
+    my $arr_grad = [];
+    my $shape = [4, 5];
+    {
+        local($mx::Context) = $ctx1;
+        for (0..$n-1)
+        {
+            push @$arr, mx->nd->empty($shape);
+            push @$arr_grad, mx->nd->empty($shape);
+        }
+    }
+    {
+        local($mx::Context) = $ctx2;
+        push @$arr, mx->nd->empty($shape);
+        push @$arr_grad, mx->nd->empty($shape);
+    }
+
+    my $exec1 = $net->bind(
+        ctx          => $ctx1,
+        args         => $arr,
+        args_grad    => $arr_grad,
+        group2ctx    => { dev1 => $ctx1, dev2 => $ctx2 }
+    );
+    $arr->[0] .= 1;
+    $arr->[1] .= 2;
+    $arr->[2] .= 3;
+    my $arr2 = [map { $_->copyto($ctx1) } @$arr];
+    my $arr_grad2 = [map { $_->copyto($ctx1) } @$arr_grad];
+    my $exec2 = $net->bind(
+        ctx       => $ctx1,
+        args      => $arr2,
+        args_grad => $arr_grad2
+    );
+
+    $exec1->forward(1);
+    $exec2->forward(1);
+    ok(reldiff($exec1->outputs->[0]->aspdl, $exec2->outputs->[0]->aspdl) < 1e-6);
+    my $out_grad = mx->nd->empty($shape, ctx => $ctx1);
+    $out_grad .= 1;
+    $exec1->backward([$out_grad]);
+    $exec2->backward([$out_grad->copyto($ctx1)]);
+    zip(sub {
+        my ($a, $b) = @_;
+        ok(reldiff($a->aspdl, $b->aspdl) < 1e-6);
+    }, $arr_grad, $arr_grad2);
+}
+
+test_chain();
diff --git a/perl-package/AI-MXNet/t/test_rnn.t b/perl-package/AI-MXNet/t/test_rnn.t
index 77332b1..76242c0 100644
--- a/perl-package/AI-MXNet/t/test_rnn.t
+++ b/perl-package/AI-MXNet/t/test_rnn.t
@@ -3,7 +3,7 @@ use warnings;
 use AI::MXNet qw(mx);
 use AI::MXNet::TestUtils qw(same);
 use PDL;
-use Test::More tests => 45;
+use Test::More tests => 54;
 
 sub test_rnn
 {
@@ -201,6 +201,63 @@ sub test_zoneout
     is_deeply($outs, [[10, 100], [10, 100], [10, 100]]);
 }
 
+sub test_convrnn
+{
+    my $cell = mx->rnn->ConvRNNCell(input_shape => [1, 3, 16, 10], num_hidden=>10,
+                              h2h_kernel=>[3, 3], h2h_dilate=>[1, 1],
+                              i2h_kernel=>[3, 3], i2h_stride=>[1, 1],
+                              i2h_pad=>[1, 1], i2h_dilate=>[1, 1],
+                              prefix=>'rnn_');
+    my $inputs = [map { mx->sym->Variable("rnn_t${_}_data") } 0..2];
+    my ($outputs) = $cell->unroll(3, inputs => $inputs);
+    $outputs = mx->sym->Group($outputs);
+    is_deeply(
+        [sort keys %{ $cell->params->_params }],
+        ['rnn_h2h_bias', 'rnn_h2h_weight', 'rnn_i2h_bias', 'rnn_i2h_weight']
+    );
+    is_deeply($outputs->list_outputs(), ['rnn_t0_out_output', 'rnn_t1_out_output', 'rnn_t2_out_output']);
+    my (undef, $outs) = $outputs->infer_shape(rnn_t0_data=>[1, 3, 16, 10], rnn_t1_data=>[1, 3, 16, 10], rnn_t2_data=>[1, 3, 16, 10]);
+    is_deeply($outs, [[1, 10, 16, 10], [1, 10, 16, 10], [1, 10, 16, 10]]);
+}
+
+sub test_convlstm
+{
+    my $cell = mx->rnn->ConvLSTMCell(input_shape => [1, 3, 16, 10], num_hidden=>10,
+                              h2h_kernel=>[3, 3], h2h_dilate=>[1, 1],
+                              i2h_kernel=>[3, 3], i2h_stride=>[1, 1],
+                              i2h_pad=>[1, 1], i2h_dilate=>[1, 1],
+                              prefix=>'rnn_', forget_bias => 1);
+    my $inputs = [map { mx->sym->Variable("rnn_t${_}_data") } 0..2];
+    my ($outputs) = $cell->unroll(3, inputs => $inputs);
+    $outputs = mx->sym->Group($outputs);
+    is_deeply(
+        [sort keys %{ $cell->params->_params }],
+        ['rnn_h2h_bias', 'rnn_h2h_weight', 'rnn_i2h_bias', 'rnn_i2h_weight']
+    );
+    is_deeply($outputs->list_outputs(), ['rnn_t0_out_output', 'rnn_t1_out_output', 'rnn_t2_out_output']);
+    my (undef, $outs) = $outputs->infer_shape(rnn_t0_data=>[1, 3, 16, 10], rnn_t1_data=>[1, 3, 16, 10], rnn_t2_data=>[1, 3, 16, 10]);
+    is_deeply($outs, [[1, 10, 16, 10], [1, 10, 16, 10], [1, 10, 16, 10]]);
+}
+
+sub test_convgru
+{
+    my $cell = mx->rnn->ConvGRUCell(input_shape => [1, 3, 16, 10], num_hidden=>10,
+                              h2h_kernel=>[3, 3], h2h_dilate=>[1, 1],
+                              i2h_kernel=>[3, 3], i2h_stride=>[1, 1],
+                              i2h_pad=>[1, 1], i2h_dilate=>[1, 1],
+                              prefix=>'rnn_', forget_bias => 1);
+    my $inputs = [map { mx->sym->Variable("rnn_t${_}_data") } 0..2];
+    my ($outputs) = $cell->unroll(3, inputs => $inputs);
+    $outputs = mx->sym->Group($outputs);
+    is_deeply(
+        [sort keys %{ $cell->params->_params }],
+        ['rnn_h2h_bias', 'rnn_h2h_weight', 'rnn_i2h_bias', 'rnn_i2h_weight']
+    );
+    is_deeply($outputs->list_outputs(), ['rnn_t0_out_output', 'rnn_t1_out_output', 'rnn_t2_out_output']);
+    my (undef, $outs) = $outputs->infer_shape(rnn_t0_data=>[1, 3, 16, 10], rnn_t1_data=>[1, 3, 16, 10], rnn_t2_data=>[1, 3, 16, 10]);
+    is_deeply($outs, [[1, 10, 16, 10], [1, 10, 16, 10], [1, 10, 16, 10]]);
+}
+
 test_rnn();
 test_lstm();
 test_lstm_forget_bias();
@@ -211,3 +268,6 @@ test_stack();
 test_bidirectional();
 test_unfuse();
 test_zoneout();
+test_convrnn();
+test_convlstm();
+test_convgru();
diff --git a/perl-package/AI-MXNetCAPI/Changes b/perl-package/AI-MXNetCAPI/Changes
index 17595b4..1a6356c 100644
--- a/perl-package/AI-MXNetCAPI/Changes
+++ b/perl-package/AI-MXNetCAPI/Changes
@@ -1,5 +1,8 @@
 Revision history for Perl extension AI::MXNetCAPI
 
+1.0102 Sun Aug  6 16:55:08 PDT 2017
+        - updated autograd calls.
+
 1.0101  Sun Jul  2 17:16:01 PDT 2017
         - refactored CachedOp, using strings to index the kvstore.
 
diff --git a/perl-package/AI-MXNetCAPI/META.json b/perl-package/AI-MXNetCAPI/META.json
index a79b1e0..a6d65fd 100644
--- a/perl-package/AI-MXNetCAPI/META.json
+++ b/perl-package/AI-MXNetCAPI/META.json
@@ -37,5 +37,5 @@
       }
    },
    "release_status" : "stable",
-   "version" : "1.0101"
+   "version" : "1.0102"
 }
diff --git a/perl-package/AI-MXNetCAPI/META.yml b/perl-package/AI-MXNetCAPI/META.yml
index 84b7801..0e3bb53 100644
--- a/perl-package/AI-MXNetCAPI/META.yml
+++ b/perl-package/AI-MXNetCAPI/META.yml
@@ -19,4 +19,4 @@ no_index:
     - inc
 requires:
   Test::More: '0'
-version: '1.0101'
+version: '1.0102'
diff --git a/perl-package/AI-MXNetCAPI/README b/perl-package/AI-MXNetCAPI/README
index 07df0c3..5c53146 100644
--- a/perl-package/AI-MXNetCAPI/README
+++ b/perl-package/AI-MXNetCAPI/README
@@ -1,4 +1,4 @@
-AI-MXNetCAPI version 1.0101
+AI-MXNetCAPI version 1.0102
 =====================
 
 Swig interface to MXNet c api.
diff --git a/perl-package/AI-MXNetCAPI/lib/AI/MXNetCAPI.pm b/perl-package/AI-MXNetCAPI/lib/AI/MXNetCAPI.pm
index f092057..0a93d71 100644
--- a/perl-package/AI-MXNetCAPI/lib/AI/MXNetCAPI.pm
+++ b/perl-package/AI-MXNetCAPI/lib/AI/MXNetCAPI.pm
@@ -18,7 +18,7 @@
 package AI::MXNetCAPI;
 use base qw(DynaLoader);
 bootstrap AI::MXNetCAPI;
-our $VERSION = '1.0101';
+our $VERSION = '1.0102';
 1;
 __END__
 
diff --git a/perl-package/AI-MXNetCAPI/mxnet.i b/perl-package/AI-MXNetCAPI/mxnet.i
index bf00e68..fd1a471 100644
--- a/perl-package/AI-MXNetCAPI/mxnet.i
+++ b/perl-package/AI-MXNetCAPI/mxnet.i
@@ -459,6 +459,13 @@ int MXNDArrayGetContext(NDArrayHandle handle,
                                   int *out,
                                   int *out);
 /*!
+ * \brief return gradient buffer attached to this NDArray
+ * \param handle NDArray handle
+ * \return 0 when success, -1 when failure happens
+ */
+int MXNDArrayGetGrad(NDArrayHandle handle, NDArrayHandle *out);
+
+/*!
  * \brief detach and ndarray from computation graph by clearing entry_
  * \param handle NDArray handle
  * \return 0 when success, -1 when failure happens

-- 
To stop receiving notification emails like this one, please contact
['"commits@mxnet.apache.org" <co...@mxnet.apache.org>'].