From bab57100736b677a2d257178a891d864bf23e4c2 Mon Sep 17 00:00:00 2001 From: Sergey Kolychev Date: Mon, 2 Jul 2018 22:37:22 -0700 Subject: [PATCH 1/7] MXNET-650 1) Added Gluon Contrib modules 2) Added Gluon Model Zoo 3) Added Gluon image classification and style transfer examples 4) Miscellaneous bugfixes and improvements. --- perl-package/AI-MXNet-Gluon-Contrib/Changes | 5 + perl-package/AI-MXNet-Gluon-Contrib/MANIFEST | 9 + perl-package/AI-MXNet-Gluon-Contrib/META.json | 42 + perl-package/AI-MXNet-Gluon-Contrib/META.yml | 21 + .../AI-MXNet-Gluon-Contrib/Makefile.PL | 63 ++ perl-package/AI-MXNet-Gluon-Contrib/README | 7 + .../lib/AI/MXNet/Gluon/Contrib.pm | 24 + .../AI/MXNet/Gluon/Contrib/NN/BasicLayers.pm | 201 +++++ .../t/AI-MXNet-Gluon-Contrib.t | 21 + perl-package/AI-MXNet-Gluon-ModelZoo/Changes | 5 + perl-package/AI-MXNet-Gluon-ModelZoo/MANIFEST | 19 + .../AI-MXNet-Gluon-ModelZoo/META.json | 43 + perl-package/AI-MXNet-Gluon-ModelZoo/META.yml | 22 + .../AI-MXNet-Gluon-ModelZoo/Makefile.PL | 65 ++ perl-package/AI-MXNet-Gluon-ModelZoo/README | 7 + .../examples/image_classification.pl | 63 ++ .../lib/AI/MXNet/Gluon/ModelZoo.pm | 128 +++ .../lib/AI/MXNet/Gluon/ModelZoo/ModelStore.pm | 164 ++++ .../lib/AI/MXNet/Gluon/ModelZoo/Vision.pm | 48 + .../AI/MXNet/Gluon/ModelZoo/Vision/AlexNet.pm | 115 +++ .../MXNet/Gluon/ModelZoo/Vision/DenseNet.pm | 277 ++++++ .../MXNet/Gluon/ModelZoo/Vision/Inception.pm | 287 ++++++ .../MXNet/Gluon/ModelZoo/Vision/MobileNet.pm | 518 +++++++++++ .../AI/MXNet/Gluon/ModelZoo/Vision/ResNet.pm | 828 ++++++++++++++++++ .../MXNet/Gluon/ModelZoo/Vision/SqueezeNet.pm | 212 +++++ .../lib/AI/MXNet/Gluon/ModelZoo/Vision/VGG.pm | 321 +++++++ .../t/AI-MXNet-Gluon-ModelZoo.t | 21 + .../t/test_gluon_model_zoo.t | 51 ++ perl-package/AI-MXNet/Changes | 5 + perl-package/AI-MXNet/MANIFEST | 7 +- perl-package/AI-MXNet/META.json | 2 +- perl-package/AI-MXNet/META.yml | 2 +- perl-package/AI-MXNet/Makefile.PL | 10 +- perl-package/AI-MXNet/README | 2 +- .../examples/gluon/style_transfer/README.md | 31 + .../examples/gluon/style_transfer/get_data.sh | 31 + .../examples/gluon/style_transfer/net.pl | 325 +++++++ .../gluon/style_transfer/style_transfer.pl | 58 ++ .../examples/gluon/style_transfer/utils.pl | 73 ++ perl-package/AI-MXNet/lib/AI/MXNet.pm | 2 +- perl-package/AI-MXNet/lib/AI/MXNet/Base.pm | 8 +- .../AI-MXNet/lib/AI/MXNet/Gluon/Block.pm | 119 ++- .../AI-MXNet/lib/AI/MXNet/Gluon/Utils.pm | 2 +- perl-package/AI-MXNet/lib/AI/MXNet/Image.pm | 6 +- .../AI-MXNet/lib/AI/MXNet/Visualization.pm | 18 +- perl-package/test.sh | 10 + 46 files changed, 4272 insertions(+), 26 deletions(-) create mode 100644 perl-package/AI-MXNet-Gluon-Contrib/Changes create mode 100644 perl-package/AI-MXNet-Gluon-Contrib/MANIFEST create mode 100644 perl-package/AI-MXNet-Gluon-Contrib/META.json create mode 100644 perl-package/AI-MXNet-Gluon-Contrib/META.yml create mode 100644 perl-package/AI-MXNet-Gluon-Contrib/Makefile.PL create mode 100644 perl-package/AI-MXNet-Gluon-Contrib/README create mode 100644 perl-package/AI-MXNet-Gluon-Contrib/lib/AI/MXNet/Gluon/Contrib.pm create mode 100644 perl-package/AI-MXNet-Gluon-Contrib/lib/AI/MXNet/Gluon/Contrib/NN/BasicLayers.pm create mode 100644 perl-package/AI-MXNet-Gluon-Contrib/t/AI-MXNet-Gluon-Contrib.t create mode 100644 perl-package/AI-MXNet-Gluon-ModelZoo/Changes create mode 100644 perl-package/AI-MXNet-Gluon-ModelZoo/MANIFEST create mode 100644 perl-package/AI-MXNet-Gluon-ModelZoo/META.json create mode 100644 perl-package/AI-MXNet-Gluon-ModelZoo/META.yml create mode 100644 perl-package/AI-MXNet-Gluon-ModelZoo/Makefile.PL create mode 100644 perl-package/AI-MXNet-Gluon-ModelZoo/README create mode 100755 perl-package/AI-MXNet-Gluon-ModelZoo/examples/image_classification.pl create mode 100644 perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo.pm create mode 100644 perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo/ModelStore.pm create mode 100644 perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo/Vision.pm create mode 100644 perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo/Vision/AlexNet.pm create mode 100644 perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo/Vision/DenseNet.pm create mode 100644 perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo/Vision/Inception.pm create mode 100644 perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo/Vision/MobileNet.pm create mode 100644 perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo/Vision/ResNet.pm create mode 100644 perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo/Vision/SqueezeNet.pm create mode 100644 perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo/Vision/VGG.pm create mode 100644 perl-package/AI-MXNet-Gluon-ModelZoo/t/AI-MXNet-Gluon-ModelZoo.t create mode 100644 perl-package/AI-MXNet-Gluon-ModelZoo/t/test_gluon_model_zoo.t create mode 100644 perl-package/AI-MXNet/examples/gluon/style_transfer/README.md create mode 100755 perl-package/AI-MXNet/examples/gluon/style_transfer/get_data.sh create mode 100644 perl-package/AI-MXNet/examples/gluon/style_transfer/net.pl create mode 100755 perl-package/AI-MXNet/examples/gluon/style_transfer/style_transfer.pl create mode 100644 perl-package/AI-MXNet/examples/gluon/style_transfer/utils.pl diff --git a/perl-package/AI-MXNet-Gluon-Contrib/Changes b/perl-package/AI-MXNet-Gluon-Contrib/Changes new file mode 100644 index 000000000000..7b3b27a3722c --- /dev/null +++ b/perl-package/AI-MXNet-Gluon-Contrib/Changes @@ -0,0 +1,5 @@ +Revision history for Perl extension AI::MXNet::Gluon::Contrib + +1.3 Tue Jul 10 21:19:13 PDT 2018 + - Initial release + diff --git a/perl-package/AI-MXNet-Gluon-Contrib/MANIFEST b/perl-package/AI-MXNet-Gluon-Contrib/MANIFEST new file mode 100644 index 000000000000..8b3e383068b0 --- /dev/null +++ b/perl-package/AI-MXNet-Gluon-Contrib/MANIFEST @@ -0,0 +1,9 @@ +Changes +lib/AI/MXNet/Gluon/Contrib.pm +lib/AI/MXNet/Gluon/Contrib/NN/BasicLayers.pm +Makefile.PL +MANIFEST +META.json +META.yml +README +t/AI-MXNet-Gluon-Contrib.t diff --git a/perl-package/AI-MXNet-Gluon-Contrib/META.json b/perl-package/AI-MXNet-Gluon-Contrib/META.json new file mode 100644 index 000000000000..52c32309879d --- /dev/null +++ b/perl-package/AI-MXNet-Gluon-Contrib/META.json @@ -0,0 +1,42 @@ +{ + "abstract" : "Perl interface to MXNet Gluon Contrib", + "author" : [ + "Sergey Kolychev " + ], + "dynamic_config" : 0, + "generated_by" : "ExtUtils::MakeMaker version 7.24, CPAN::Meta::Converter version 2.143240", + "license" : [ + "apache_2_0" + ], + "meta-spec" : { + "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", + "version" : "2" + }, + "name" : "AI-MXNet-Gluon-Contrib", + "no_index" : { + "directory" : [ + "t", + "inc" + ] + }, + "prereqs" : { + "build" : { + "requires" : {} + }, + "configure" : { + "requires" : { + "ExtUtils::MakeMaker" : "6.30" + } + }, + "runtime" : { + "requires" : { + "AI::MXNet" : "1.31", + } + }, + "test" : { + "requires" : {} + } + }, + "release_status" : "stable", + "version" : "1.3" +} diff --git a/perl-package/AI-MXNet-Gluon-Contrib/META.yml b/perl-package/AI-MXNet-Gluon-Contrib/META.yml new file mode 100644 index 000000000000..b059f0f51bfd --- /dev/null +++ b/perl-package/AI-MXNet-Gluon-Contrib/META.yml @@ -0,0 +1,21 @@ +--- +abstract: 'Perl interface to MXNet Gluon Contrib' +author: + - 'Sergey Kolychev ' +build_requires: {} +configure_requires: + ExtUtils::MakeMaker: '6.30' +dynamic_config: 0 +generated_by: 'ExtUtils::MakeMaker version 7.24, CPAN::Meta::Converter version 2.143240' +license: apache +meta-spec: + url: http://module-build.sourceforge.net/META-spec-v1.4.html + version: '1.4' +name: AI-MXNet-Gluon-Contrib +no_index: + directory: + - t + - inc +requires: + AI::MXNet: '1.31' +version: '1.3' diff --git a/perl-package/AI-MXNet-Gluon-Contrib/Makefile.PL b/perl-package/AI-MXNet-Gluon-Contrib/Makefile.PL new file mode 100644 index 000000000000..b27d59b3b558 --- /dev/null +++ b/perl-package/AI-MXNet-Gluon-Contrib/Makefile.PL @@ -0,0 +1,63 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +use strict; +use warnings; + +use 5.014000; + +use ExtUtils::MakeMaker 6.30; + + + +my %WriteMakefileArgs = ( + "ABSTRACT" => "Perl interface to MXNet Gluon Contrib", + "AUTHOR" => "Sergey Kolychev ", + "BUILD_REQUIRES" => {}, + "CONFIGURE_REQUIRES" => { + "ExtUtils::MakeMaker" => "6.30" + }, + "DISTNAME" => "AI-MXNet-Gluon-Contrib", + "EXE_FILES" => [], + "LICENSE" => "apache_2_0", + "NAME" => "AI::MXNet::Gluon::Contrib", + "PREREQ_PM" => { + "AI::MXNet" => "1.31", + }, + "TEST_REQUIRES" => {}, + "VERSION" => "1.3", + "test" => { + "TESTS" => "t/*.t" + } +); + + +my %FallbackPrereqs = ( + "AI::MXNet" => "1.31" +); + + +unless ( eval { ExtUtils::MakeMaker->VERSION(6.63_03) } ) { + delete $WriteMakefileArgs{TEST_REQUIRES}; + delete $WriteMakefileArgs{BUILD_REQUIRES}; + $WriteMakefileArgs{PREREQ_PM} = \%FallbackPrereqs; +} + +delete $WriteMakefileArgs{CONFIGURE_REQUIRES} + unless eval { ExtUtils::MakeMaker->VERSION(6.52) }; + +WriteMakefile(%WriteMakefileArgs); diff --git a/perl-package/AI-MXNet-Gluon-Contrib/README b/perl-package/AI-MXNet-Gluon-Contrib/README new file mode 100644 index 000000000000..1481c3e66c1a --- /dev/null +++ b/perl-package/AI-MXNet-Gluon-Contrib/README @@ -0,0 +1,7 @@ +This archive contains the distribution AI-MXNet-Gluon-Contrib, +version 1.3: + + Perl interface to MXNet Gluon Contib modules, a collection of supplemental Gluon blocks. + +This library is licensed under Apache 2.0 license https://www.apache.org/licenses/LICENSE-2.0 + diff --git a/perl-package/AI-MXNet-Gluon-Contrib/lib/AI/MXNet/Gluon/Contrib.pm b/perl-package/AI-MXNet-Gluon-Contrib/lib/AI/MXNet/Gluon/Contrib.pm new file mode 100644 index 000000000000..f88fb8a7b595 --- /dev/null +++ b/perl-package/AI-MXNet-Gluon-Contrib/lib/AI/MXNet/Gluon/Contrib.pm @@ -0,0 +1,24 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +use strict; +use warnings; +use AI::MXNet; +use AI::MXNet::Gluon::Contrib::NN::BasicLayers; +our $VERSION = '1.3'; + +1; \ No newline at end of file diff --git a/perl-package/AI-MXNet-Gluon-Contrib/lib/AI/MXNet/Gluon/Contrib/NN/BasicLayers.pm b/perl-package/AI-MXNet-Gluon-Contrib/lib/AI/MXNet/Gluon/Contrib/NN/BasicLayers.pm new file mode 100644 index 000000000000..455284e30483 --- /dev/null +++ b/perl-package/AI-MXNet-Gluon-Contrib/lib/AI/MXNet/Gluon/Contrib/NN/BasicLayers.pm @@ -0,0 +1,201 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +use strict; +use warnings; +use AI::MXNet::Function::Parameters; +package AI::MXNet::Gluon::NN::Concurrent; +use AI::MXNet::Gluon::Mouse; +extends 'AI::MXNet::Gluon::NN::Sequential'; + +=head1 NAME + + AI::MXNet::Gluon::NN::Concurrent - Lays Blocks concurrently. +=cut + +=head1 DESCRIPTION + + Lays Blocks concurrently. + + This block feeds its input to all children blocks, and + produces the output by concatenating all the children blocks' outputs + on the specified axis. + + Example: + + $net = nn->Concurrent(); + # use net's name_scope to give children blocks appropriate names. + $net->name_scope(sub { + $net->add(nn->Dense(10, activation=>'relu')); + $net->add(nn->Dense(20)); + $net->add(nn->Identity()); + }); + + Parameters + ---------- + axis : int, default -1 + The axis on which to concatenate the outputs. +=cut +has 'axis' => (is => 'rw', isa => 'Int', default => -1); +method python_constructor_arguments() { ['axis'] } + +method forward(GluonInput $x) +{ + return AI::MXNet::NDArray->concat((map { $_->($x) } $self->_children->values), dim=>$self->axis); +} + +__PACKAGE__->register('AI::MXNet::Gluon::NN'); + +package AI::MXNet::Gluon::NN::HybridConcurrent; +use AI::MXNet::Gluon::Mouse; +extends 'AI::MXNet::Gluon::NN::HybridSequential'; + +=head1 NAME + + AI::MXNet::Gluon::NN::HybridConcurrent - Lays HubridBlocks concurrently. +=cut + +=head1 DESCRIPTION + + Lays HybridBlocks concurrently. + + This block feeds its input to all children blocks, and + produces the output by concatenating all the children blocks' outputs + on the specified axis. + + Example: + + $net = nn->HybridConcurrent(); + # use net's name_scope to give children blocks appropriate names. + $net->name_scope(sub { + $net->add(nn->Dense(10, activation=>'relu')); + $net->add(nn->Dense(20)); + $net->add(nn->Identity()); + }); + + Parameters + ---------- + axis : int, default -1 + The axis on which to concatenate the outputs. +=cut +has 'axis' => (is => 'rw', isa => 'Int', default => -1); +method python_constructor_arguments() { ['axis'] } + +method hybrid_forward(GluonClass $F, GluonInput $x) +{ + return $F->concat((map { $_->($x) } $self->_children->values), dim=>$self->axis); +} + +__PACKAGE__->register('AI::MXNet::Gluon::NN'); + +package AI::MXNet::Gluon::NN::Identity; +use AI::MXNet::Gluon::Mouse; +extends 'AI::MXNet::Gluon::HybridBlock'; + +=head1 NAME + + AI::MXNet::Gluon::NN::Identity - Block that passes through the input directly. +=cut + +=head1 DESCRIPTION + + Block that passes through the input directly. + + This block can be used in conjunction with HybridConcurrent + block for residual connection. + + Example: + + $net = nn->HybridConcurrent(); + # use net's name_scope to give child Blocks appropriate names. + $net->name_scope(sub { + $net->add(nn->Dense(10, activation=>'relu')); + $net->add(nn->Dense(20)); + $net->add(nn->Identity()); + }); +=cut + +method hybrid_forward(GluonClass $F, GluonInput $x) +{ + return $x; +} + +__PACKAGE__->register('AI::MXNet::Gluon::NN'); + +package AI::MXNet::Gluon::NN::SparseEmbedding; +use AI::MXNet::Gluon::Mouse; +extends 'AI::MXNet::Gluon::Block'; + +=head1 NAME + + AI::MXNet::Gluon::NN::SparseEmbedding - Turns non-negative integers (indexes/tokens) into dense vectors. +=cut + +=head1 DESCRIPTION + + Turns non-negative integers (indexes/tokens) into dense vectors + of fixed size. eg. [4, 20] -> [[0.25, 0.1], [0.6, -0.2]] + + This SparseBlock is designed for distributed training with extremely large + input dimension. Both weight and gradient w.r.t. weight are AI::MXNet::NDArray::RowSparse. + + Parameters + ---------- + input_dim : int + Size of the vocabulary, i.e. maximum integer index + 1. + output_dim : int + Dimension of the dense embedding. + dtype : Dtype, default 'float32' + Data type of output embeddings. + weight_initializer : Initializer + Initializer for the embeddings matrix. +=cut + +has 'input_dim' => (is => 'ro', isa => 'Int', required => 1); +has 'output_dim' => (is => 'ro', isa => 'Int', required => 1); +has 'dtype' => (is => 'ro', isa => 'Dtype', default => 'float32'); +has 'weight_initializer' => (is => 'ro', isa => 'Maybe[Initializer]'); +method python_constructor_arguments() { [qw/input_dim output_dim dtype weight_initializer/] } + +sub BUILD +{ + my $self = shift; + $self->_kwargs({ + input_dim => $self->input_dim, + output_dim => $self->output_dim, + dtype => $self->dtype, + sparse_grad => 1 + }); + $self->weight($self->params->get('weight', shape=>[$self->input_dim, $self->output_dim], + init=>$self->weight_initializer, dtype=>$self->dtype, + grad_stype=>'row_sparse', stype=>'row_sparse')); +} + +method forward(GluonInput $x) +{ + my $weight = $self->weight->row_sparse_data($x); + return AI::MXNet::NDArray->Embedding($x, $weight, { name=>'fwd', %{ $self->_kwargs } }); +} + +use overload '""' => sub { + my $self = shift; + $self->_class_name.'('.$self->input_dim.' -> '.$self->input_dim.', '.$self->dtype.')'; +}; + +__PACKAGE__->register('AI::MXNet::Gluon::NN'); + +1; diff --git a/perl-package/AI-MXNet-Gluon-Contrib/t/AI-MXNet-Gluon-Contrib.t b/perl-package/AI-MXNet-Gluon-Contrib/t/AI-MXNet-Gluon-Contrib.t new file mode 100644 index 000000000000..4af95036fe0c --- /dev/null +++ b/perl-package/AI-MXNet-Gluon-Contrib/t/AI-MXNet-Gluon-Contrib.t @@ -0,0 +1,21 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +use strict; +use warnings; +use Test::More tests => 1; +BEGIN { use_ok('AI::MXNet::Gluon::Contrib') }; diff --git a/perl-package/AI-MXNet-Gluon-ModelZoo/Changes b/perl-package/AI-MXNet-Gluon-ModelZoo/Changes new file mode 100644 index 000000000000..c233f92458db --- /dev/null +++ b/perl-package/AI-MXNet-Gluon-ModelZoo/Changes @@ -0,0 +1,5 @@ +Revision history for Perl extension AI::MXNet::Gluon::ModelZoo + +1.3 Tue Jul 10 21:19:13 PDT 2018 + - Initial release + diff --git a/perl-package/AI-MXNet-Gluon-ModelZoo/MANIFEST b/perl-package/AI-MXNet-Gluon-ModelZoo/MANIFEST new file mode 100644 index 000000000000..d01d25e85b1c --- /dev/null +++ b/perl-package/AI-MXNet-Gluon-ModelZoo/MANIFEST @@ -0,0 +1,19 @@ +Changes +examples/image_classification.pl +lib/AI/MXNet/Gluon/ModelZoo.pm +lib/AI/MXNet/Gluon/ModelZoo/ModelStore.pm +lib/AI/MXNet/Gluon/ModelZoo/Vision.pm +lib/AI/MXNet/Gluon/ModelZoo/Vision/AlexNet.pm +lib/AI/MXNet/Gluon/ModelZoo/Vision/DenseNet.pm +lib/AI/MXNet/Gluon/ModelZoo/Vision/Inception.pm +lib/AI/MXNet/Gluon/ModelZoo/Vision/MobileNet.pm +lib/AI/MXNet/Gluon/ModelZoo/Vision/ResNet.pm +lib/AI/MXNet/Gluon/ModelZoo/Vision/SqueezeNet.pm +lib/AI/MXNet/Gluon/ModelZoo/Vision/VGG.pm +Makefile.PL +MANIFEST +META.json +META.yml +README +t/AI-MXNet-Gluon-ModelZoo.t +t/test_gluon_model_zoo.t diff --git a/perl-package/AI-MXNet-Gluon-ModelZoo/META.json b/perl-package/AI-MXNet-Gluon-ModelZoo/META.json new file mode 100644 index 000000000000..c0e1ad3af8ae --- /dev/null +++ b/perl-package/AI-MXNet-Gluon-ModelZoo/META.json @@ -0,0 +1,43 @@ +{ + "abstract" : "Perl interface to MXNet Gluon ModelZoo", + "author" : [ + "Sergey Kolychev " + ], + "dynamic_config" : 0, + "generated_by" : "ExtUtils::MakeMaker version 7.24, CPAN::Meta::Converter version 2.143240", + "license" : [ + "apache_2_0" + ], + "meta-spec" : { + "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", + "version" : "2" + }, + "name" : "AI-MXNet-Gluon-ModelZoo", + "no_index" : { + "directory" : [ + "t", + "inc" + ] + }, + "prereqs" : { + "build" : { + "requires" : {} + }, + "configure" : { + "requires" : { + "ExtUtils::MakeMaker" : "6.30" + } + }, + "runtime" : { + "requires" : { + "AI::MXNet" : "1.31", + "AI::MXNet::Gluon::Contrib" : "1.3" + } + }, + "test" : { + "requires" : {} + } + }, + "release_status" : "stable", + "version" : "1.3" +} diff --git a/perl-package/AI-MXNet-Gluon-ModelZoo/META.yml b/perl-package/AI-MXNet-Gluon-ModelZoo/META.yml new file mode 100644 index 000000000000..2493af60bbec --- /dev/null +++ b/perl-package/AI-MXNet-Gluon-ModelZoo/META.yml @@ -0,0 +1,22 @@ +--- +abstract: 'Perl interface to MXNet Gluon ModelZoo' +author: + - 'Sergey Kolychev ' +build_requires: {} +configure_requires: + ExtUtils::MakeMaker: '6.30' +dynamic_config: 0 +generated_by: 'ExtUtils::MakeMaker version 7.24, CPAN::Meta::Converter version 2.143240' +license: apache +meta-spec: + url: http://module-build.sourceforge.net/META-spec-v1.4.html + version: '1.4' +name: AI-MXNet-Gluon-ModelZoo +no_index: + directory: + - t + - inc +requires: + AI::MXNet: '1.31' + AI::MXNet::Gluon::Contrib: '1.3' +version: '1.3' diff --git a/perl-package/AI-MXNet-Gluon-ModelZoo/Makefile.PL b/perl-package/AI-MXNet-Gluon-ModelZoo/Makefile.PL new file mode 100644 index 000000000000..8427aef3dbc4 --- /dev/null +++ b/perl-package/AI-MXNet-Gluon-ModelZoo/Makefile.PL @@ -0,0 +1,65 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +use strict; +use warnings; + +use 5.014000; + +use ExtUtils::MakeMaker 6.30; + + + +my %WriteMakefileArgs = ( + "ABSTRACT" => "Perl interface to MXNet Gluon ModelZoo", + "AUTHOR" => "Sergey Kolychev ", + "BUILD_REQUIRES" => {}, + "CONFIGURE_REQUIRES" => { + "ExtUtils::MakeMaker" => "6.30" + }, + "DISTNAME" => "AI-MXNet-Gluon-ModelZoo", + "EXE_FILES" => [], + "LICENSE" => "apache_2_0", + "NAME" => "AI::MXNet::Gluon::ModelZoo", + "PREREQ_PM" => { + "AI::MXNet" => "1.31", + "AI::MXNet::Gluon::Contrib" => "1.3" + }, + "TEST_REQUIRES" => {}, + "VERSION" => "1.3", + "test" => { + "TESTS" => "t/*.t" + } +); + + +my %FallbackPrereqs = ( + "AI::MXNet" => "1.31", + "AI::MXNet::Gluon::Contrib" => "1.3" +); + + +unless ( eval { ExtUtils::MakeMaker->VERSION(6.63_03) } ) { + delete $WriteMakefileArgs{TEST_REQUIRES}; + delete $WriteMakefileArgs{BUILD_REQUIRES}; + $WriteMakefileArgs{PREREQ_PM} = \%FallbackPrereqs; +} + +delete $WriteMakefileArgs{CONFIGURE_REQUIRES} + unless eval { ExtUtils::MakeMaker->VERSION(6.52) }; + +WriteMakefile(%WriteMakefileArgs); diff --git a/perl-package/AI-MXNet-Gluon-ModelZoo/README b/perl-package/AI-MXNet-Gluon-ModelZoo/README new file mode 100644 index 000000000000..d6d697292dbe --- /dev/null +++ b/perl-package/AI-MXNet-Gluon-ModelZoo/README @@ -0,0 +1,7 @@ +This archive contains the distribution AI-MXNet-Gluon-ModelZoo, +version 1.3: + + Perl interface to MXNet Gluon ModelZoo, a collection of pretrained machine learning models for computer vision. + +This library is licensed under Apache 2.0 license https://www.apache.org/licenses/LICENSE-2.0 + diff --git a/perl-package/AI-MXNet-Gluon-ModelZoo/examples/image_classification.pl b/perl-package/AI-MXNet-Gluon-ModelZoo/examples/image_classification.pl new file mode 100755 index 000000000000..008b33c9fb3b --- /dev/null +++ b/perl-package/AI-MXNet-Gluon-ModelZoo/examples/image_classification.pl @@ -0,0 +1,63 @@ +#!/usr/bin/env perl +use strict; +use warnings; +use AI::MXNet::Gluon::ModelZoo 'get_model'; +use AI::MXNet::Gluon::Utils 'download'; +use Getopt::Long qw(HelpMessage); + +GetOptions( + ## my Pembroke Welsh Corgi Kyuubi, enjoing Solar eclipse of August 21, 2017 + 'image=s' => \(my $image = 'https://scontent-sea1-1.cdninstagram.com/vp/'. + 'c6e923f45b408fb26077bb2079e2ffa8/5BDB8A7E/'. + 't51.2885-15/e35/'. + '23734897_1382606301862187_7379024371697844224_n.jpg'), + 'model=s' => \(my $model = 'resnet152_v2'), + 'help' => sub { HelpMessage(0) }, +) or HelpMessage(1); + +## get a pretrained model (download parameters file if necessary) +my $net = get_model($model, pretrained => 1); + +## ImageNet classes +my $fname = download('http://data.mxnet.io/models/imagenet/synset.txt'); +my @text_labels = map { chomp; s/^\S+\s+//; $_ } IO::File->new($fname)->getlines; + +## get the image from the disk or net +if($image =~ /^https/) +{ + eval { require IO::Socket::SSL; }; + die "Need to have IO::Socket::SSL installed for https images" if $@; +} +$image = $image =~ /^https?/ ? download($image) : $image; + +# Following the conventional way of preprocessing ImageNet data: +# Resize the short edge into 256 pixes, +# And then perform a center crop to obtain a 224-by-224 image. +# The following code uses the image processing functions provided +# in the AI::MXNet::Image module. + +$image = mx->image->imread($image); +$image = mx->image->resize_short($image, $model =~ /inception/ ? 330 : 256); +($image) = mx->image->center_crop($image, [($model =~ /inception/ ? 299 : 224)x2]); + +## CV that is used to read image is column major (as PDL) +$image = $image->transpose([2,0,1])->expand_dims(axis=>0); + +## normalizing the image +my $rgb_mean = nd->array([0.485, 0.456, 0.406])->reshape([1,3,1,1]); +my $rgb_std = nd->array([0.229, 0.224, 0.225])->reshape([1,3,1,1]); +$image = $image->astype('float32') / (255 - $rgb_mean) / $rgb_std; + +# Now we can recognize the object in the image. +# We perform an additional softmax on the output to obtain probability scores. +# And then print the top-5 recognized objects. +my $prob = $net->($image)->softmax; +my $idxs = $prob->topk(k=>5)->[0]; +for my $idx (@{ $idxs->reshape([5]) }) +{ + my $i = $idx->asscalar; + printf( + "With prob = %.5f, it contains %s\n", + $prob->at(0)->at($i)->asscalar, $text_labels[$i] + ); +} diff --git a/perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo.pm b/perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo.pm new file mode 100644 index 000000000000..842687c5de00 --- /dev/null +++ b/perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo.pm @@ -0,0 +1,128 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +package AI::MXNet::Gluon::ModelZoo; +use strict; +use warnings; +use AI::MXNet qw(mx); +use AI::MXNet::Gluon qw(gluon); +use AI::MXNet::Gluon::NN qw(nn); +use AI::MXNet::Gluon::Contrib; +use AI::MXNet::Gluon::ModelZoo::Vision; +use Exporter; +use base qw(Exporter); +@AI::MXNet::Gluon::ModelZoo::EXPORT_OK = qw(get_model); +our $VERSION = '1.3'; + +=head1 NAME + + AI::MXNet::Gluon::ModelZoo - A collection of pretrained MXNet Gluon models +=cut + +=head1 SYNOPSIS + + ## run forward prediction on random data + use AI::MXNet::Gluon::ModelZoo qw(get_model); + my $alexnet = get_model('alexnet', pretrained => 1); + my $out = $alexnet->(mx->nd->random->uniform(shape=>[1, 3, 224, 224])); + print $out->aspdl; +=cut + +=head1 DESCRIPTION + + This module houses a collection of pretrained modules (the parameters are hosted on public mxnet servers). + https://mxnet.incubator.apache.org/api/python/gluon/model_zoo.html +=cut + +our %models = qw/ + resnet18_v1 resnet18_v1 + resnet34_v1 resnet34_v1 + resnet50_v1 resnet50_v1 + resnet101_v1 resnet101_v1 + resnet152_v1 resnet152_v1 + resnet18_v2 resnet18_v2 + resnet34_v2 resnet34_v2 + resnet50_v2 resnet50_v2 + resnet101_v2 resnet101_v2 + resnet152_v2 resnet152_v2 + vgg11 vgg11 + vgg13 vgg13 + vgg16 vgg16 + vgg19 vgg19 + vgg11_bn vgg11_bn + vgg13_bn vgg13_bn + vgg16_bn vgg16_bn + vgg19_bn vgg19_bn + alexnet alexnet + densenet121 densenet121 + densenet161 densenet161 + densenet169 densenet169 + densenet201 densenet201 + squeezenet1.0 squeezenet1_0 + squeezenet1.1 squeezenet1_1 + inceptionv3 inception_v3 + mobilenet1.0 mobilenet1_0 + mobilenet0.75 mobilenet0_75 + mobilenet0.5 mobilenet0_5 + mobilenet0.25 mobilenet0_25 + mobilenetv2_1.0 mobilenet_v2_1_0 + mobilenetv2_0.75 mobilenet_v2_0_75 + mobilenetv2_0.5 mobilenet_v2_0_5 + mobilenetv2_0.25 mobilenet_v2_0_25 +/; + + +=head2 get_model + + Returns a pre-defined model by name + + Parameters + ---------- + $name : Str + Name of the model. + :$pretrained : Bool + Whether to load the pretrained weights for model. + :$classes : Int + Number of classes for the output layer. + :$ctx : AI::MXNet::Context, default CPU + The context in which to load the pretrained weights. + :$root : Str, default '~/.mxnet/models' + Location for keeping the model parameters. + + Returns + ------- + HybridBlock + The model. +=cut + +sub get_model +{ + if(exists $models{lc $_[1]}) + { + shift; + } + my ($name, %kwargs) = @_; + $name = lc $name; + Carp::confess( + "Model $name is not present in the zoo\nValid models are:\n". + join(', ', sort keys %models)."\n" + ) unless exists $models{$name}; + my $sub = $models{$name}; + AI::MXNet::Gluon::ModelZoo::Vision->$sub(%kwargs); +} + +1; diff --git a/perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo/ModelStore.pm b/perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo/ModelStore.pm new file mode 100644 index 000000000000..9269ee735663 --- /dev/null +++ b/perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo/ModelStore.pm @@ -0,0 +1,164 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +package AI::MXNet::Gluon::ModelZoo::ModelStore; +use strict; +use warnings; +use AI::MXNet::Function::Parameters; + +=head1 NAME + + AI::MXNet::Gluon::ModelZoo::ModelStore - Model zoo for pre-trained models. +=cut + +use AI::MXNet::Gluon::Utils qw(download check_sha1); +use IO::Uncompress::Unzip qw(unzip); +use File::Path qw(make_path); + +my %_model_sha1 = map { $_->[1] => $_->[0] } ( + ['44335d1f0046b328243b32a26a4fbd62d9057b45', 'alexnet'], + ['f27dbf2dbd5ce9a80b102d89c7483342cd33cb31', 'densenet121'], + ['b6c8a95717e3e761bd88d145f4d0a214aaa515dc', 'densenet161'], + ['2603f878403c6aa5a71a124c4a3307143d6820e9', 'densenet169'], + ['1cdbc116bc3a1b65832b18cf53e1cb8e7da017eb', 'densenet201'], + ['ed47ec45a937b656fcc94dabde85495bbef5ba1f', 'inceptionv3'], + ['9f83e440996887baf91a6aff1cccc1c903a64274', 'mobilenet0.25'], + ['8e9d539cc66aa5efa71c4b6af983b936ab8701c3', 'mobilenet0.5'], + ['529b2c7f4934e6cb851155b22c96c9ab0a7c4dc2', 'mobilenet0.75'], + ['6b8c5106c730e8750bcd82ceb75220a3351157cd', 'mobilenet1.0'], + ['36da4ff1867abccd32b29592d79fc753bca5a215', 'mobilenetv2_1.0'], + ['e2be7b72a79fe4a750d1dd415afedf01c3ea818d', 'mobilenetv2_0.75'], + ['aabd26cd335379fcb72ae6c8fac45a70eab11785', 'mobilenetv2_0.5'], + ['ae8f9392789b04822cbb1d98c27283fc5f8aa0a7', 'mobilenetv2_0.25'], + ['a0666292f0a30ff61f857b0b66efc0228eb6a54b', 'resnet18_v1'], + ['48216ba99a8b1005d75c0f3a0c422301a0473233', 'resnet34_v1'], + ['0aee57f96768c0a2d5b23a6ec91eb08dfb0a45ce', 'resnet50_v1'], + ['d988c13d6159779e907140a638c56f229634cb02', 'resnet101_v1'], + ['671c637a14387ab9e2654eafd0d493d86b1c8579', 'resnet152_v1'], + ['a81db45fd7b7a2d12ab97cd88ef0a5ac48b8f657', 'resnet18_v2'], + ['9d6b80bbc35169de6b6edecffdd6047c56fdd322', 'resnet34_v2'], + ['ecdde35339c1aadbec4f547857078e734a76fb49', 'resnet50_v2'], + ['18e93e4f48947e002547f50eabbcc9c83e516aa6', 'resnet101_v2'], + ['f2695542de38cf7e71ed58f02893d82bb409415e', 'resnet152_v2'], + ['264ba4970a0cc87a4f15c96e25246a1307caf523', 'squeezenet1.0'], + ['33ba0f93753c83d86e1eb397f38a667eaf2e9376', 'squeezenet1.1'], + ['dd221b160977f36a53f464cb54648d227c707a05', 'vgg11'], + ['ee79a8098a91fbe05b7a973fed2017a6117723a8', 'vgg11_bn'], + ['6bc5de58a05a5e2e7f493e2d75a580d83efde38c', 'vgg13'], + ['7d97a06c3c7a1aecc88b6e7385c2b373a249e95e', 'vgg13_bn'], + ['649467530119c0f78c4859999e264e7bf14471a9', 'vgg16'], + ['6b9dbe6194e5bfed30fd7a7c9a71f7e5a276cb14', 'vgg16_bn'], + ['f713436691eee9a20d70a145ce0d53ed24bf7399', 'vgg19'], + ['9730961c9cea43fd7eeefb00d792e386c45847d6', 'vgg19_bn'] +); + +my $apache_repo_url = 'http://apache-mxnet.s3-accelerate.dualstack.amazonaws.com/'; +my $_url_format = '%sgluon/models/%s.zip'; + +func short_hash($name) +{ + Carp::confess("model $name is not available in model zoo") unless exists $_model_sha1{$name}; + return substr($_model_sha1{$name}, 0, 8); +} + +=head2 get_model_file + + Return location for the pretrained on local file system. + + This function will download from online model zoo when model cannot be found or has mismatch. + The root directory will be created if it doesn't exist. + + Parameters + ---------- + $name : Str + Name of the model. + :$root : Str, default '~/.mxnet/models' + Location for keeping the model parameters. + + Returns + ------- + $file_path + Path to the requested pretrained model file. +=cut + +method get_model_file(Str $name, Str :$root='~/.mxnet/models') +{ + my $file_name = "$name-".short_hash($name); + $root =~ s/~/$ENV{HOME}/; + my $file_path = "$root/$file_name.params"; + my $sha1_hash = $_model_sha1{$name}; + if(-f $file_path) + { + if(check_sha1($file_path, $sha1_hash)) + { + return $file_path; + } + else + { + warn("Mismatch in the content of model file detected. Downloading again.\n"); + } + } + else + { + warn("Model file is not found. Downloading.\n"); + } + + if(not -d $root) + { + make_path($root); + } + + my $zip_file_path = "$root/$file_name.zip"; + my $repo_url = $ENV{MXNET_GLUON_REPO}//$apache_repo_url; + if($repo_url !~ /\/$/) + { + $repo_url .= '/'; + } + download( + sprintf($_url_format, $repo_url, $file_name), + path=>$zip_file_path, + overwrite=>1 + ); + unzip($zip_file_path, $file_path); + unlink $zip_file_path; + if(check_sha1($file_path, $sha1_hash)) + { + return $file_path; + } + else + { + Carp::confess("Downloaded file $file_path has different hash. Please try again."); + } +} + +=head2 purge + + Purge all pretrained model files in local file store. + + Parameters + ---------- + root : str, default '~/.mxnet/models' + Location for keeping the model parameters. +=cut + +method purge(Str $root='~/.mxnet/models') +{ + $root =~ s/~/$ENV{HOME}/; + map { unlink } glob("$root/*.params"); +} + +1; diff --git a/perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo/Vision.pm b/perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo/Vision.pm new file mode 100644 index 000000000000..d306c2d44895 --- /dev/null +++ b/perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo/Vision.pm @@ -0,0 +1,48 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +package AI::MXNet::Gluon::ModelZoo::Vision; +use strict; +use warnings; +use AI::MXNet::Gluon::ModelZoo::ModelStore; +use AI::MXNet::Gluon::ModelZoo::Vision::AlexNet; +use AI::MXNet::Gluon::ModelZoo::Vision::DenseNet; +use AI::MXNet::Gluon::ModelZoo::Vision::Inception; +use AI::MXNet::Gluon::ModelZoo::Vision::MobileNet; +use AI::MXNet::Gluon::ModelZoo::Vision::ResNet; +use AI::MXNet::Gluon::ModelZoo::Vision::SqueezeNet; +use AI::MXNet::Gluon::ModelZoo::Vision::VGG; + +sub import +{ + my ($class, $short_name) = @_; + if($short_name) + { + $short_name =~ s/[^\w:]//g; + if(length $short_name) + { + my $short_name_package =<<"EOP"; + package $short_name; + \@${short_name}::ISA = ('AI::MXNet::Gluon::ModelZoo::Vision'); + 1; +EOP + eval $short_name_package; + } + } +} + +1; diff --git a/perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo/Vision/AlexNet.pm b/perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo/Vision/AlexNet.pm new file mode 100644 index 000000000000..5c24f46dbf85 --- /dev/null +++ b/perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo/Vision/AlexNet.pm @@ -0,0 +1,115 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +package AI::MXNet::Gluon::ModelZoo::Vision::AlexNet; +use strict; +use warnings; +use AI::MXNet::Function::Parameters; +use AI::MXNet::Gluon::Mouse; +extends 'AI::MXNet::Gluon::HybridBlock'; + +=head1 NAME + + AI::MXNet::Gluon::ModelZoo::Vision::AlexNet - AlexNet model from the `"One weird trick..." +=cut + +=head1 DESCRIPTION + + AlexNet model from the "One weird trick..." paper. + + Parameters + ---------- + classes : Int, default 1000 + Number of classes for the output layer-> +=cut +has 'classes' => (is => 'ro', isa => 'Int', default => 1000); +method python_constructor_arguments() { ['classes'] } + +sub BUILD +{ + my $self = shift; + $self->name_scope(sub { + $self->features(nn->HybridSequential(prefix=>'')); + $self->features->name_scope(sub { + $self->features->add(nn->Conv2D(64, kernel_size=>11, strides=>4, + padding=>2, activation=>'relu')); + $self->features->add(nn->MaxPool2D(pool_size=>3, strides=>2)); + $self->features->add(nn->Conv2D(192, kernel_size=>5, padding=>2, + activation=>'relu')); + $self->features->add(nn->MaxPool2D(pool_size=>3, strides=>2)); + $self->features->add(nn->Conv2D(384, kernel_size=>3, padding=>1, + activation=>'relu')); + $self->features->add(nn->Conv2D(256, kernel_size=>3, padding=>1, + activation=>'relu')); + $self->features->add(nn->Conv2D(256, kernel_size=>3, padding=>1, + activation=>'relu')); + $self->features->add(nn->MaxPool2D(pool_size=>3, strides=>2)); + $self->features->add(nn->Flatten()); + $self->features->add(nn->Dense(4096, activation=>'relu')); + $self->features->add(nn->Dropout(0.5)); + $self->features->add(nn->Dense(4096, activation=>'relu')); + $self->features->add(nn->Dropout(0.5)); + }); + $self->output(nn->Dense($self->classes)); + }); +} + +method hybrid_forward(GluonClass $F, GluonInput $x) +{ + $x = $self->features->($x); + $x = $self->output->($x); + return $x; +} + +package AI::MXNet::Gluon::ModelZoo::Vision; + +=head2 alexnet + + AlexNet model from the `"One weird trick..." paper. + + Parameters + ---------- + :$pretrained : Bool, default 0 + Whether to load the pretrained weights for model. + :$ctx : AI::MXNet::Context, default AI::MXNet::Context->cpu + The context in which to load the pretrained weights. + :$root : Str, default '~/.mxnet/models' + Location for keeping the model parameters. +=cut + +method alexnet( + Bool :$pretrained=0, + AI::MXNet::Context :$ctx=AI::MXNet::Context->cpu(), + Str :$root='~/.mxnet/models', + Int :$classes=1000 +) +{ + my $net = AI::MXNet::Gluon::ModelZoo::Vision::AlexNet->new($classes); + if($pretrained) + { + $net->load_parameters( + AI::MXNet::Gluon::ModelZoo::ModelStore->get_model_file( + 'alexnet', + root=>$root + ), + ctx=>$ctx + ); + } + return $net; +} + +1; diff --git a/perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo/Vision/DenseNet.pm b/perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo/Vision/DenseNet.pm new file mode 100644 index 000000000000..cc69e7b5f40e --- /dev/null +++ b/perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo/Vision/DenseNet.pm @@ -0,0 +1,277 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +package AI::MXNet::Gluon::ModelZoo::Vision::DenseNet; +use strict; +use warnings; +use AI::MXNet::Base; +use AI::MXNet::Function::Parameters; +use AI::MXNet::Gluon::Mouse; +extends 'AI::MXNet::Gluon::HybridBlock'; + +func _make_dense_block($num_layers, $bn_size, $growth_rate, $dropout, $stage_index) +{ + my $out = nn->HybridSequential(prefix=>"stage${stage_index}_"); + $out->name_scope(sub { + for(1..$num_layers) + { + $out->add(_make_dense_layer($growth_rate, $bn_size, $dropout)); + } + }); + return $out; +} + +func _make_dense_layer($growth_rate, $bn_size, $dropout) +{ + my $new_features = nn->HybridSequential(prefix=>''); + $new_features->add(nn->BatchNorm()); + $new_features->add(nn->Activation('relu')); + $new_features->add(nn->Conv2D($bn_size * $growth_rate, kernel_size=>1, use_bias=>0)); + $new_features->add(nn->BatchNorm()); + $new_features->add(nn->Activation('relu')); + $new_features->add(nn->Conv2D($growth_rate, kernel_size=>3, padding=>1, use_bias=>0)); + if($dropout) + { + $new_features->add(nn->Dropout($dropout)); + } + + my $out = nn->HybridConcurrent(axis=>1, prefix=>''); + $out->add(nn->Identity()); + $out->add($new_features); + + return $out; +} + +func _make_transition($num_output_features) +{ + my $out = nn->HybridSequential(prefix=>''); + $out->add(nn->BatchNorm()); + $out->add(nn->Activation('relu')); + $out->add(nn->Conv2D($num_output_features, kernel_size=>1, use_bias=>0)); + $out->add(nn->AvgPool2D(pool_size=>2, strides=>2)); + return $out; +} + +=head1 NAME + + AI::MXNet::Gluon::ModelZoo::Vision::DenseNet - Densenet-BC model from the "Densely Connected Convolutional Networks" +=cut + +=head1 DESCRIPTION + + Densenet-BC model from the "Densely Connected Convolutional Networks" paper. + + Parameters + ---------- + num_init_features : Int + Number of filters to learn in the first convolution layer. + growth_rate : Int + Number of filters to add each layer (`k` in the paper). + block_config : array ref of Int + List of integers for numbers of layers in each pooling block. + bn_size : Int, default 4 + Multiplicative factor for number of bottle neck layers. + (i.e. bn_size * k features in the bottleneck layer) + dropout : float, default 0 + Rate of dropout after each dense layer. + classes : int, default 1000 + Number of classification classes. +=cut +has [qw/num_init_features + growth_rate/] => (is => 'ro', isa => 'Int', required => 1); +has 'block_config' => (is => 'ro', isa => 'ArrayRef[Int]', required => 1); +has 'bn_size' => (is => 'ro', isa => 'Int', default => 4); +has 'dropout' => (is => 'ro', isa => 'Num', default => 0); +has 'classes' => (is => 'ro', isa => 'Int', default => 1000); +method python_constructor_arguments(){ [qw/num_init_features growth_rate block_config bn_size dropout classes/] } + +sub BUILD +{ + my $self = shift; + $self->name_scope(sub { + $self->features(nn->HybridSequential(prefix=>'')); + $self->features->add( + nn->Conv2D( + $self->num_init_features, kernel_size=>7, + strides=>2, padding=>3, use_bias=>0 + ) + ); + $self->features->add(nn->BatchNorm()); + $self->features->add(nn->Activation('relu')); + $self->features->add(nn->MaxPool2D(pool_size=>3, strides=>2, padding=>1)); + # Add dense blocks + my $num_features = $self->num_init_features; + for(enumerate($self->block_config)) + { + my ($i, $num_layers) = @$_; + $self->features->add(_make_dense_block($num_layers, $self->bn_size, $self->growth_rate, $self->dropout, $i+1)); + $num_features += $num_layers * $self->growth_rate; + if($i != @{ $self->block_config } - 1) + { + $self->features->add(_make_transition(int($num_features/2))); + $num_features = int($num_features/2); + } + } + $self->features->add(nn->BatchNorm()); + $self->features->add(nn->Activation('relu')); + $self->features->add(nn->AvgPool2D(pool_size=>7)); + $self->features->add(nn->Flatten()); + + $self->output(nn->Dense($self->classes)); + }); +} + +method hybrid_forward(GluonClass $F, GluonInput $x) +{ + $x = $self->features->($x); + $x = $self->output->($x); + return $x; +} + +package AI::MXNet::Gluon::ModelZoo::Vision; + +my %densenet_spec = ( + 121 => [64, 32, [6, 12, 24, 16]], + 161 => [96, 48, [6, 12, 36, 24]], + 169 => [64, 32, [6, 12, 32, 32]], + 201 => [64, 32, [6, 12, 48, 32]] +); + +=head2 get_densenet + + Densenet-BC model from the + "Densely Connected Convolutional Networks" paper. + + Parameters + ---------- + $num_layers : Int + Number of layers for the variant of densenet. Options are 121, 161, 169, 201. + :$pretrained : Bool, default 0 + Whether to load the pretrained weights for model. + :$ctx : AI::MXNet::Context, default CPU + The context in which to load the pretrained weights. + :$root : Str, default '~/.mxnet/models' + Location for keeping the model parameters. +=cut + +method get_densenet( + Int $num_layers, Bool :$pretrained=0, :$ctx=AI::MXNet::Context->cpu(), + :$root='~/.mxnet/models', + Int :$bn_size=4, + Num :$dropout=0, + Int :$classes=1000 +) +{ + my ($num_init_features, $growth_rate, $block_config) = @{ $densenet_spec{$num_layers} }; + my $net = AI::MXNet::Gluon::ModelZoo::Vision::DenseNet->new( + $num_init_features, $growth_rate, $block_config, + $bn_size, $dropout, $classes + ); + if($pretrained) + { + $net->load_parameters( + AI::MXNet::Gluon::ModelZoo::ModelStore->get_model_file( + "densenet$num_layers", + root=>$root + ), + ctx=>$ctx + ); + } + return $net; +} + +=head2 densenet121 + + Densenet-BC 121-layer model from the + "Densely Connected Convolutional Networks" paper. + + Parameters + ---------- + :$pretrained : Bool, default 0 + Whether to load the pretrained weights for model. + :$ctx : AI::MXNet::Context, default CPU + The context in which to load the pretrained weights. + :$root : Str, default '~/.mxnet/models' + Location for keeping the model parameters. +=cut + +method densenet121(%kwargs) +{ + return __PACKAGE__->get_densenet(121, %kwargs) +} + +=head2 densenet161 + + Densenet-BC 161-layer model from the + "Densely Connected Convolutional Networks" paper. + + Parameters + ---------- + :$pretrained : Bool, default 0 + Whether to load the pretrained weights for model. + :$ctx : AI::MXNet::Context, default CPU + The context in which to load the pretrained weights. + :$root : Str, default '~/.mxnet/models' + Location for keeping the model parameters. +=cut + +method densenet161(%kwargs) +{ + return __PACKAGE__->get_densenet(161, %kwargs) +} + +=head2 densenet169 + + Densenet-BC 169-layer model from the + "Densely Connected Convolutional Networks" paper. + + Parameters + ---------- + :$pretrained : Bool, default 0 + Whether to load the pretrained weights for model. + :$ctx : AI::MXNet::Context, default CPU + The context in which to load the pretrained weights. + :$root : Str, default '~/.mxnet/models' + Location for keeping the model parameters. +=cut + +method densenet169(%kwargs) +{ + return __PACKAGE__->get_densenet(169, %kwargs) +} + +=head2 densenet201 + + Densenet-BC 201-layer model from the + "Densely Connected Convolutional Networks" paper. + + Parameters + ---------- + :$pretrained : Bool, default 0 + Whether to load the pretrained weights for model. + :$ctx : AI::MXNet::Context, default CPU + The context in which to load the pretrained weights. + :$root : Str, default '~/.mxnet/models' + Location for keeping the model parameters. +=cut + +method densenet201(%kwargs) +{ + return __PACKAGE__->get_densenet(201, %kwargs) +} + +1; diff --git a/perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo/Vision/Inception.pm b/perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo/Vision/Inception.pm new file mode 100644 index 000000000000..19bd8e1bfb45 --- /dev/null +++ b/perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo/Vision/Inception.pm @@ -0,0 +1,287 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +package AI::MXNet::Gluon::ModelZoo::Vision::Inception::V3; +use strict; +use warnings; +use AI::MXNet::Base; +use AI::MXNet::Function::Parameters; +use AI::MXNet::Gluon::Mouse; +extends 'AI::MXNet::Gluon::HybridBlock'; + +func _make_basic_conv(%kwargs) +{ + my $out = nn->HybridSequential(prefix=>''); + $out->add(nn->Conv2D(use_bias=>0, %kwargs)); + $out->add(nn->BatchNorm(epsilon=>0.001)); + $out->add(nn->Activation('relu')); + return $out; +} + +func _make_branch($use_pool, @conv_settings) +{ + my $out = nn->HybridSequential(prefix=>''); + if($use_pool eq 'avg') + { + $out->add(nn->AvgPool2D(pool_size=>3, strides=>1, padding=>1)); + } + elsif($use_pool eq 'max') + { + $out->add(nn->MaxPool2D(pool_size=>3, strides=>2)); + } + my @setting_names = ('channels', 'kernel_size', 'strides', 'padding'); + for my $setting (@conv_settings) + { + my %kwargs; + for(enumerate($setting)) + { + my ($i, $value) = @$_; + if(defined $value) + { + $kwargs{ $setting_names[$i] } = $value; + } + } + $out->add(_make_basic_conv(%kwargs)); + } + return $out; +} + +func _make_A($pool_features, $prefix) +{ + my $out = nn->HybridConcurrent(axis=>1, prefix=>$prefix); + $out->name_scope(sub { + $out->add(_make_branch('', [64, 1, undef, undef])); + $out->add(_make_branch( + '', + [48, 1, undef, undef], + [64, 5, undef, 2] + )); + $out->add(_make_branch( + '', + [64, 1, undef, undef], + [96, 3, undef, 1], + [96, 3, undef, 1] + )); + $out->add(_make_branch('avg', [$pool_features, 1, undef, undef])); + }); + return $out; +} + +func _make_B($prefix) +{ + my $out = nn->HybridConcurrent(axis=>1, prefix=>$prefix); + $out->name_scope(sub { + $out->add(_make_branch('', [384, 3, 2, undef])); + $out->add(_make_branch( + '', + [64, 1, undef, undef], + [96, 3, undef, 1], + [96, 3, 2, undef] + )); + $out->add(_make_branch('max')); + }); + return $out; +} + +func _make_C($channels_7x7, $prefix) +{ + my $out = nn->HybridConcurrent(axis=>1, prefix=>$prefix); + $out->name_scope(sub { + $out->add(_make_branch('', [192, 1, undef, undef])); + $out->add(_make_branch( + '', + [$channels_7x7, 1, undef, undef], + [$channels_7x7, [1, 7], undef, [0, 3]], + [192, [7, 1], undef, [3, 0]] + )); + $out->add(_make_branch( + '', + [$channels_7x7, 1, undef, undef], + [$channels_7x7, [7, 1], undef, [3, 0]], + [$channels_7x7, [1, 7], undef, [0, 3]], + [$channels_7x7, [7, 1], undef, [3, 0]], + [192, [1, 7], undef, [0, 3]] + )); + $out->add(_make_branch( + 'avg', + [192, 1, undef, undef] + )); + }); + return $out; +} + +func _make_D($prefix) +{ + my $out = nn->HybridConcurrent(axis=>1, prefix=>$prefix); + $out->name_scope(sub { + $out->add(_make_branch( + '', + [192, 1, undef, undef], + [320, 3, 2, undef] + )); + $out->add(_make_branch( + '', + [192, 1, undef, undef], + [192, [1, 7], undef, [0, 3]], + [192, [7, 1], undef, [3, 0]], + [192, 3, 2, undef] + )); + $out->add(_make_branch('max')); + }); + return $out; +} + +func _make_E($prefix) +{ + my $out = nn->HybridConcurrent(axis=>1, prefix=>$prefix); + $out->name_scope(sub { + $out->add(_make_branch('', [320, 1, undef, undef])); + + my $branch_3x3 = nn->HybridSequential(prefix=>''); + $out->add($branch_3x3); + $branch_3x3->add(_make_branch( + '', + [384, 1, undef, undef] + )); + my $branch_3x3_split = nn->HybridConcurrent(axis=>1, prefix=>''); + $branch_3x3_split->add(_make_branch('', [384, [1, 3], undef, [0, 1]])); + $branch_3x3_split->add(_make_branch('', [384, [3, 1], undef, [1, 0]])); + $branch_3x3->add($branch_3x3_split); + + my $branch_3x3dbl = nn->HybridSequential(prefix=>''); + $out->add($branch_3x3dbl); + $branch_3x3dbl->add(_make_branch( + '', + [448, 1, undef, undef], + [384, 3, undef, 1] + )); + my $branch_3x3dbl_split = nn->HybridConcurrent(axis=>1, prefix=>''); + $branch_3x3dbl->add($branch_3x3dbl_split); + $branch_3x3dbl_split->add(_make_branch('', [384, [1, 3], undef, [0, 1]])); + $branch_3x3dbl_split->add(_make_branch('', [384, [3, 1], undef, [1, 0]])); + + $out->add(_make_branch('avg', [192, 1, undef, undef])); + }); + return $out; +} + +func make_aux($classes) +{ + my $out = nn->HybridSequential(prefix=>''); + $out->add(nn->AvgPool2D(pool_size=>5, strides=>3)); + $out->add(_make_basic_conv(channels=>128, kernel_size=>1)); + $out->add(_make_basic_conv(channels=>768, kernel_size=>5)); + $out->add(nn->Flatten()); + $out->add(nn->Dense($classes)); + return $out; +} + +=head1 NAME + + AI::MXNet::Gluon::ModelZoo::Vision::Inception::V3 - Inception v3 model. +=cut + +=head1 DESCRIPTION + + Inception v3 model from + "Rethinking the Inception Architecture for Computer Vision" + paper. + + Parameters + ---------- + classes : Int, default 1000 + Number of classification classes. +=cut + +has 'classes' => (is => 'ro', isa => 'Int', default => 1000); +method python_constructor_arguments(){ ['classes'] } + +sub BUILD +{ + my $self = shift; + $self->name_scope(sub { + $self->features(nn->HybridSequential(prefix=>'')); + $self->features->add(_make_basic_conv(channels=>32, kernel_size=>3, strides=>2)); + $self->features->add(_make_basic_conv(channels=>32, kernel_size=>3)); + $self->features->add(_make_basic_conv(channels=>64, kernel_size=>3, padding=>1)); + $self->features->add(nn->MaxPool2D(pool_size=>3, strides=>2)); + $self->features->add(_make_basic_conv(channels=>80, kernel_size=>1)); + $self->features->add(_make_basic_conv(channels=>192, kernel_size=>3)); + $self->features->add(nn->MaxPool2D(pool_size=>3, strides=>2)); + $self->features->add(_make_A(32, 'A1_')); + $self->features->add(_make_A(64, 'A2_')); + $self->features->add(_make_A(64, 'A3_')); + $self->features->add(_make_B('B_')); + $self->features->add(_make_C(128, 'C1_')); + $self->features->add(_make_C(160, 'C2_')); + $self->features->add(_make_C(160, 'C3_')); + $self->features->add(_make_C(192, 'C4_')); + $self->features->add(_make_D('D_')); + $self->features->add(_make_E('E1_')); + $self->features->add(_make_E('E2_')); + $self->features->add(nn->AvgPool2D(pool_size=>8)); + $self->features->add(nn->Dropout(0.5)); + + $self->output(nn->Dense($self->classes)); + }); +} + +method hybrid_forward(GluonClass $F, GluonInput $x) +{ + $x = $self->features->($x); + $x = $self->output->($x); + return $x; +} + +package AI::MXNet::Gluon::ModelZoo::Vision; + +=head2 inception_v3 + + Inception v3 model from + "Rethinking the Inception Architecture for Computer Vision" + paper. + + Parameters + ---------- + :$pretrained : Bool, default 0 + Whether to load the pretrained weights for model. + :$ctx : AI::MXNet::Context, default CPU + The context in which to load the pretrained weights. + :$root : Str, default '~/.mxnet/models' + Location for keeping the model parameters. +=cut + +method inception_v3( + Bool :$pretrained=0, AI::MXNet::Context :$ctx=AI::MXNet::Context->cpu(), + Str :$root='~/.mxnet/models', Int :$classes=1000 +) +{ + my $net = AI::MXNet::Gluon::ModelZoo::Vision::Inception::V3->new($classes); + if($pretrained) + { + $net->load_parameters( + AI::MXNet::Gluon::ModelZoo::ModelStore->get_model_file( + "inceptionv3", + root=>$root + ), + ctx=>$ctx + ); + } + return $net; +} + +1; \ No newline at end of file diff --git a/perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo/Vision/MobileNet.pm b/perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo/Vision/MobileNet.pm new file mode 100644 index 000000000000..a0891c5d51da --- /dev/null +++ b/perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo/Vision/MobileNet.pm @@ -0,0 +1,518 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +use strict; +use warnings; +use AI::MXNet::Function::Parameters; +package AI::MXNet::Gluon::ModelZoo::Vision::MobileNet::RELU6; +use AI::MXNet::Gluon::Mouse; +extends 'AI::MXNet::Gluon::HybridBlock'; + +method hybrid_forward(GluonClass $F, GluonInput $x) +{ + return $F->clip($x, a_min => 0, a_max => 6, name=>"relu6"); +} + +package AI::MXNet::Gluon::ModelZoo::Vision::MobileNet::LinearBottleneck; +use AI::MXNet::Gluon::Mouse; +extends 'AI::MXNet::Gluon::HybridBlock'; +has [qw/in_channels channels t stride/] => (is => 'ro', isa => 'Int', required => 1); +method python_constructor_arguments(){ [qw/in_channels channels t stride/] } + +=head1 NAME + + AI::MXNet::Gluon::ModelZoo::Vision::MobileNet::LinearBottleneck - LinearBottleneck used in MobileNetV2 model +=cut + +=head1 DESCRIPTION + + LinearBottleneck used in MobileNetV2 model from the + "Inverted Residuals and Linear Bottlenecks: + Mobile Networks for Classification, Detection and Segmentation" + paper. + + Parameters + ---------- + in_channels : Int + Number of input channels. + channels : Int + Number of output channels. + t : Int + Layer expansion ratio. + stride : Int + stride +=cut + +func _add_conv( + $out, $channels, :$kernel=1, :$stride=1, :$pad=0, + :$num_group=1, :$active=1, :$relu6=0 +) +{ + $out->add(nn->Conv2D($channels, $kernel, $stride, $pad, groups=>$num_group, use_bias=>0)); + $out->add(nn->BatchNorm(scale=>1)); + if($active) + { + $out->add($relu6 ? AI::MXNet::Gluon::ModelZoo::Vision::MobileNet::RELU6->new : nn->Activation('relu')); + } +} + +sub BUILD +{ + my $self = shift; + $self->use_shortcut($self->stride == 1 and $self->in_channels == $self->channels); + $self->name_scope(sub { + $self->out(nn->HybridSequential()); + _add_conv($self->out, $self->in_channels * $self->t, relu6=>1); + _add_conv( + $self->out, $self->in_channels * $self->t, kernel=>3, stride=>$self->stride, + pad=>1, num_group=>$self->in_channels * $self->t, relu6=>1 + ); + _add_conv($self->out, $self->channels, active=>0, relu6=>1); + }); +} + +method hybrid_forward($F, $x) +{ + my $out = $self->out->($x); + if($self->use_shortcut) + { + $out = $F->elemwise_add($out, $x); + } + return $out; +} + +package AI::MXNet::Gluon::ModelZoo::Vision::MobileNet; +use AI::MXNet::Gluon::Mouse; +use AI::MXNet::Base; +extends 'AI::MXNet::Gluon::HybridBlock'; +has 'multiplier' => (is => 'ro', isa => 'Num', default => 1); +has 'classes' => (is => 'ro', isa => 'Int', default => 1000); +method python_constructor_arguments(){ [qw/multiplier classes/] } + +=head1 NAME + + AI::MXNet::Gluon::ModelZoo::Vision::MobileNet - MobileNet model from the + "MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications" +=cut + +=head1 DESCRIPTION + + MobileNet model from the + "MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications" + paper. + + Parameters + ---------- + multiplier : Num, default 1.0 + The width multiplier for controling the model size. Only multipliers that are no + less than 0.25 are supported. The actual number of channels is equal to the original + channel size multiplied by this multiplier. + classes : Int, default 1000 + Number of classes for the output layer. +=cut + +func _add_conv( + $out, :$channels=1, :$kernel=1, :$stride=1, :$pad=0, + :$num_group=1, :$active=1, :$relu6=0 +) +{ + $out->add(nn->Conv2D($channels, $kernel, $stride, $pad, groups=>$num_group, use_bias=>0)); + $out->add(nn->BatchNorm(scale=>1)); + if($active) + { + $out->add($relu6 ? AI::MXNet::Gluon::ModelZoo::Vision::MobileNet::RELU6->new : nn->Activation('relu')); + } +} + + +func _add_conv_dw($out, :$dw_channels=, :$channels=, :$stride=, :$relu6=0) +{ + _add_conv($out, channels=>$dw_channels, kernel=>3, stride=>$stride, + pad=>1, num_group=>$dw_channels, relu6=>$relu6); + _add_conv($out, channels=>$channels, relu6=>$relu6); +} + +sub BUILD +{ + my $self = shift; + $self->name_scope(sub { + $self->features(nn->HybridSequential(prefix=>'')); + $self->features->name_scope(sub { + _add_conv($self->features, channels=>int(32 * $self->multiplier), kernel=>3, pad=>1, stride=>2); + my $dw_channels = [map { int($_ * $self->multiplier) } (32, 64, (128)x2, (256)x2, (512)x6, 1024)]; + my $channels = [map { int($_ * $self->multiplier) } (64, (128)x2, (256)x2, (512)x6, (1024)x2)]; + my $strides = [(1, 2)x3, (1)x5, 2, 1]; + for(zip($dw_channels, $channels, $strides)) + { + my ($dwc, $c, $s) = @$_; + _add_conv_dw($self->features, dw_channels=>$dwc, channels=>$c, stride=>$s); + } + $self->features->add(nn->GlobalAvgPool2D()); + $self->features->add(nn->Flatten()); + }); + $self->output(nn->Dense($self->classes)); + }); +} + +method hybrid_forward(GluonClass $F, GluonInput $x) +{ + $x = $self->features->($x); + $x = $self->output->($x); + return $x; +} + +package AI::MXNet::Gluon::ModelZoo::Vision::MobileNetV2; +use AI::MXNet::Gluon::Mouse; +use AI::MXNet::Base; +extends 'AI::MXNet::Gluon::HybridBlock'; +has 'multiplier' => (is => 'ro', isa => 'Num', default => 1); +has 'classes' => (is => 'ro', isa => 'Int', default => 1000); +method python_constructor_arguments(){ [qw/multiplier classes/] } + +=head1 NAME + + AI::MXNet::Gluon::ModelZoo::Vision::MobileNetV2 - MobileNet model from the + "Inverted Residuals and Linear Bottlenecks: Mobile Networks for Classification, Detection and Segmentation" +=cut + +=head1 DESCRIPTION + + MobileNetV2 model from the + "Inverted Residuals and Linear Bottlenecks: + Mobile Networks for Classification, Detection and Segmentation" + paper. + + Parameters + ---------- + multiplier : Num, default 1.0 + The width multiplier for controling the model size. Only multipliers that are no + less than 0.25 are supported. The actual number of channels is equal to the original + channel size multiplied by this multiplier. + classes : Int, default 1000 + Number of classes for the output layer. +=cut + +func _add_conv( + $out, $channels, :$kernel=1, :$stride=1, :$pad=0, + :$num_group=1, :$active=1, :$relu6=0 +) +{ + $out->add(nn->Conv2D($channels, $kernel, $stride, $pad, groups=>$num_group, use_bias=>0)); + $out->add(nn->BatchNorm(scale=>1)); + if($active) + { + $out->add($relu6 ? AI::MXNet::Gluon::ModelZoo::Vision::MobileNet::RELU6->new : nn->Activation('relu')); + } +} + +sub BUILD +{ + my $self = shift; + $self->name_scope(sub { + $self->features(nn->HybridSequential(prefix=>'features_')); + $self->features->name_scope(sub { + _add_conv( + $self->features, int(32 * $self->multiplier), kernel=>3, + stride=>2, pad=>1, relu6=>1 + ); + + my $in_channels_group = [map { int($_ * $self->multiplier) } (32, 16, (24)x2, (32)x3, (64)x4, (96)x3, (160)x3)]; + my $channels_group = [map { int($_ * $self->multiplier) } (16, (24)x2, (32)x3, (64)x4, (96)x3, (160)x3, 320)]; + my $ts = [1, (6)x16]; + my $strides = [(1, 2)x2, 1, 1, 2, (1)x6, 2, (1)x3]; + + for(zip($in_channels_group, $channels_group, $ts, $strides)) + { + my ($in_c, $c, $t, $s) = @$_; + $self->features->add( + AI::MXNet::Gluon::ModelZoo::Vision::MobileNet::LinearBottleneck->new( + in_channels=>$in_c, channels=>$c, + t=>$t, stride=>$s + ) + ); + } + + my $last_channels = $self->multiplier > 1 ? int(1280 * $self->multiplier) : 1280; + _add_conv($self->features, $last_channels, relu6=>1); + $self->features->add(nn->GlobalAvgPool2D()); + }); + + $self->output(nn->HybridSequential(prefix=>'output_')); + $self->output->name_scope(sub { + $self->output->add( + nn->Conv2D($self->classes, 1, use_bias=>0, prefix=>'pred_'), + nn->Flatten() + ); + }); + }); +} + +method hybrid_forward(GluonClass $F, GluonInput $x) +{ + $x = $self->features->($x); + $x = $self->output->($x); + return $x; +} + +package AI::MXNet::Gluon::ModelZoo::Vision; + +=head2 get_mobilenet + + MobileNet model from the + "MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications" + paper. + + Parameters + ---------- + $multiplier : Num + The width multiplier for controling the model size. Only multipliers that are no + less than 0.25 are supported. The actual number of channels is equal to the original + channel size multiplied by this multiplier. + :$pretrained : Bool, default 0 + Whether to load the pretrained weights for model. + :$ctx : AI::MXNet::Context, default CPU + The context in which to load the pretrained weights. + :$root : Str, default '~/.mxnet/models' + Location for keeping the model parameters. +=cut + +method get_mobilenet( + Num $multiplier, Bool :$pretrained=0, AI::MXNet::Context :$ctx=AI::MXNet::Context->cpu(), + Str :$root='~/.mxnet/models' +) +{ + my $net = AI::MXNet::Gluon::ModelZoo::Vision::MobileNet->new($multiplier); + if($pretrained) + { + my $version_suffix = sprintf("%.2f", $multiplier); + if($version_suffix eq '1.00' or $version_suffix eq '0.50') + { + $version_suffix =~ s/.$//; + } + $net->load_parameters( + AI::MXNet::Gluon::ModelZoo::ModelStore->get_model_file( + "mobilenet$version_suffix", + root=>$root + ), + ctx=>$ctx + ); + } + return $net; +} + +=head2 get_mobilenet_v2 + + MobileNetV2 model from the + "Inverted Residuals and Linear Bottlenecks: + Mobile Networks for Classification, Detection and Segmentation" + paper. + + Parameters + ---------- + $multiplier : Num + The width multiplier for controling the model size. Only multipliers that are no + less than 0.25 are supported. The actual number of channels is equal to the original + channel size multiplied by this multiplier. + :$pretrained : Bool, default 0 + Whether to load the pretrained weights for model. + :$ctx : AI::MXNet::Context, default CPU + The context in which to load the pretrained weights. + :$root : Str, default '~/.mxnet/models' + Location for keeping the model parameters. +=cut + +method get_mobilenet_v2( + Num $multiplier, Bool :$pretrained=0, AI::MXNet::Context :$ctx=AI::MXNet::Context->cpu(), + Str :$root='~/.mxnet/models' +) +{ + my $net = AI::MXNet::Gluon::ModelZoo::Vision::MobileNetV2->new($multiplier); + if($pretrained) + { + my $version_suffix = sprintf("%.2f", $multiplier); + if($version_suffix eq '1.00' or $version_suffix eq '0.50') + { + $version_suffix =~ s/.$//; + } + $net->load_parameters( + AI::MXNet::Gluon::ModelZoo::ModelStore->get_model_file( + "mobilenetv2_$version_suffix", + root=>$root + ), + ctx=>$ctx + ); + } + return $net; +} + +=head2 mobilenet1_0 + + MobileNet model from the + "MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications" + paper, with width multiplier 1.0. + + Parameters + ---------- + :$pretrained : Bool, default 0 + Whether to load the pretrained weights for model. + :$ctx : AI::MXNet::Context, default CPU + The context in which to load the pretrained weights. +=cut + +method mobilenet1_0(%kwargs) +{ + return __PACKAGE__->get_mobilenet(1.0, %kwargs); +} + +=head2 mobilenet_v2_1_0 + + MobileNetV2 model from the + "Inverted Residuals and Linear Bottlenecks: + Mobile Networks for Classification, Detection and Segmentation" + paper. + + Parameters + ---------- + :$pretrained : Bool, default 0 + Whether to load the pretrained weights for model. + :$ctx : AI::MXNet::Context, default CPU + The context in which to load the pretrained weights. +=cut + +method mobilenet_v2_1_0(%kwargs) +{ + return __PACKAGE__->get_mobilenet_v2(1.0, %kwargs); +} + +=head2 mobilenet0_75 + + MobileNet model from the + "MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications" + paper, with width multiplier 0.75. + + Parameters + ---------- + :$pretrained : Bool, default 0 + Whether to load the pretrained weights for model. + :$ctx : AI::MXNet::Context, default CPU + The context in which to load the pretrained weights. +=cut + +method mobilenet0_75(%kwargs) +{ + return __PACKAGE__->get_mobilenet(0.75, %kwargs); +} + +=head2 mobilenet_v2_0_75 + + MobileNetV2 model from the + "Inverted Residuals and Linear Bottlenecks: + Mobile Networks for Classification, Detection and Segmentation" + paper. + + Parameters + ---------- + :$pretrained : Bool, default 0 + Whether to load the pretrained weights for model. + :$ctx : AI::MXNet::Context, default CPU + The context in which to load the pretrained weights. +=cut + +method mobilenet_v2_0_75(%kwargs) +{ + return __PACKAGE__->get_mobilenet_v2(0.75, %kwargs); +} + +=head2 mobilenet0_5 + + MobileNet model from the + "MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications" + paper, with width multiplier 0.5. + + Parameters + ---------- + :$pretrained : Bool, default 0 + Whether to load the pretrained weights for model. + :$ctx : AI::MXNet::Context, default CPU + The context in which to load the pretrained weights. +=cut + +method mobilenet0_5(%kwargs) +{ + return __PACKAGE__->get_mobilenet(0.5, %kwargs); +} + +=head2 mobilenet_v2_0_5 + + MobileNetV2 model from the + "Inverted Residuals and Linear Bottlenecks: + Mobile Networks for Classification, Detection and Segmentation" + paper. + + Parameters + ---------- + :$pretrained : Bool, default 0 + Whether to load the pretrained weights for model. + :$ctx : AI::MXNet::Context, default CPU + The context in which to load the pretrained weights. +=cut + +method mobilenet_v2_0_5(%kwargs) +{ + return __PACKAGE__->get_mobilenet_v2(0.5, %kwargs); +} + +=head2 mobilenet0_25 + + MobileNet model from the + "MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications" + paper, with width multiplier 0.25. + + Parameters + ---------- + :$pretrained : Bool, default 0 + Whether to load the pretrained weights for model. + :$ctx : AI::MXNet::Context, default CPU + The context in which to load the pretrained weights. +=cut + +method mobilenet0_25(%kwargs) +{ + return __PACKAGE__->get_mobilenet(0.25, %kwargs); +} + +=head2 mobilenet_v2_0_25 + + MobileNetV2 model from the + "Inverted Residuals and Linear Bottlenecks: + Mobile Networks for Classification, Detection and Segmentation" + paper. + + Parameters + ---------- + :$pretrained : Bool, default 0 + Whether to load the pretrained weights for model. + :$ctx : AI::MXNet::Context, default CPU + The context in which to load the pretrained weights. +=cut + +method mobilenet_v2_0_25(%kwargs) +{ + return __PACKAGE__->get_mobilenet_v2(0.25, %kwargs); +} + +1; diff --git a/perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo/Vision/ResNet.pm b/perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo/Vision/ResNet.pm new file mode 100644 index 000000000000..adf6e8aaaa8f --- /dev/null +++ b/perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo/Vision/ResNet.pm @@ -0,0 +1,828 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +use strict; +use warnings; +use AI::MXNet::Function::Parameters; + +package AI::MXNet::Gluon::ModelZoo::Vision::ResNet::BasicBlockV1; +use AI::MXNet::Gluon::Mouse; +extends 'AI::MXNet::Gluon::HybridBlock'; + +=head1 NAME + + AI::MXNet::Gluon::ModelZoo::Vision::ResNet::BasicBlockV1 - BasicBlock V1 from `"Deep Residual Learning for Image Recognition" +=cut + +=head1 DESCRIPTION + + BasicBlock V1 from `"Deep Residual Learning for Image Recognition" + `_ paper. + This is used for ResNet V1 for 18, 34 layers. + + Parameters + ---------- + channels : Int + Number of output channels. + stride : Int + Stride size. + downsample : Bool, default 0 + Whether to downsample the input. + in_channels : Int, default 0 + Number of input channels. Default is 0, to infer from the graph. +=cut + +has ['channels', + 'stride'] => (is => 'ro', isa => 'Int', required => 1); +has 'downsample' => (is => 'rw', default => 0); +has 'in_channels' => (is => 'ro', isa => 'Int', default => 0); +method python_constructor_arguments() { [qw/channels stride downsample/] } +func _conv3x3($channels, $stride, $in_channels) +{ + return nn->Conv2D( + $channels, kernel_size=>3, strides=>$stride, padding=>1, + use_bias=>0, in_channels=>$in_channels + ); +} + +sub BUILD +{ + my $self = shift; + $self->body(nn->HybridSequential(prefix=>'')); + $self->body->add(_conv3x3($self->channels, $self->stride, $self->in_channels)); + $self->body->add(nn->BatchNorm()); + $self->body->add(nn->Activation('relu')); + $self->body->add(_conv3x3($self->channels, 1, $self->channels)); + $self->body->add(nn->BatchNorm()); + if($self->downsample) + { + $self->downsample(nn->HybridSequential(prefix=>'')); + $self->downsample->add( + nn->Conv2D($self->channels, kernel_size=>1, strides=>$self->stride, + use_bias=>0, in_channels=>$self->in_channels) + ); + $self->downsample->add(nn->BatchNorm()); + } + else + { + $self->downsample(undef); + } +} + +method hybrid_forward(GluonClass $F, GluonInput $x) +{ + my $residual = $x; + $x = $self->body->($x); + if(defined $self->downsample) + { + $residual = $self->downsample->($residual); + } + $x = $F->Activation($residual+$x, act_type=>'relu'); + return $x; +} + +package AI::MXNet::Gluon::ModelZoo::Vision::ResNet::BottleneckV1; +use AI::MXNet::Gluon::Mouse; +extends 'AI::MXNet::Gluon::HybridBlock'; + +=head1 NAME + + AI::MXNet::Gluon::ModelZoo::Vision::ResNet::BottleneckV1 - Bottleneck V1 from "Deep Residual Learning for Image Recognition" +=cut + +=head1 DESCRIPTION + + Bottleneck V1 from "Deep Residual Learning for Image Recognition" + paper. + This is used for ResNet V1 for 50, 101, 152 layers. + + Parameters + ---------- + channels : int + Number of output channels. + stride : int + Stride size. + downsample : bool, default False + Whether to downsample the input. + in_channels : int, default 0 + Number of input channels. Default is 0, to infer from the graph. +=cut + +has ['channels', + 'stride'] => (is => 'ro', isa => 'Int', required => 1); +has 'downsample' => (is => 'rw', default => 0); +has 'in_channels' => (is => 'ro', isa => 'Int', default => 0); +method python_constructor_arguments() { [qw/channels stride downsample/] } +func _conv3x3($channels, $stride, $in_channels) +{ + return nn->Conv2D( + $channels, kernel_size=>3, strides=>$stride, padding=>1, + use_bias=>0, in_channels=>$in_channels + ); +} + +sub BUILD +{ + my $self = shift; + $self->body(nn->HybridSequential(prefix=>'')); + $self->body->add(nn->Conv2D(int($self->channels/4), kernel_size=>1, strides=>$self->stride)); + $self->body->add(nn->BatchNorm()); + $self->body->add(nn->Activation('relu')); + $self->body->add(_conv3x3(int($self->channels/4), 1, int($self->channels/4))); + $self->body->add(nn->BatchNorm()); + $self->body->add(nn->Activation('relu')); + $self->body->add(nn->Conv2D($self->channels, kernel_size=>1, strides=>1)); + $self->body->add(nn->BatchNorm()); + if($self->downsample) + { + $self->downsample(nn->HybridSequential(prefix=>'')); + $self->downsample->add( + nn->Conv2D($self->channels, kernel_size=>1, strides=>$self->stride, + use_bias=>0, in_channels=>$self->in_channels) + ); + $self->downsample->add(nn->BatchNorm()); + } + else + { + $self->downsample(undef); + } +} + +method hybrid_forward(GluonClass $F, GluonInput $x) +{ + my $residual = $x; + $x = $self->body->($x); + if(defined $self->downsample) + { + $residual = $self->downsample->($residual); + } + $x = $F->Activation($residual+$x, act_type=>'relu'); + return $x; +} + +package AI::MXNet::Gluon::ModelZoo::Vision::ResNet::BasicBlockV2; +use AI::MXNet::Gluon::Mouse; +extends 'AI::MXNet::Gluon::HybridBlock'; + +=head1 NAME + + AI::MXNet::Gluon::ModelZoo::Vision::ResNet::BasicBlockV2 - BasicBlock V2 from "Identity Mappings in Deep Residual Networks" +=cut + +=head1 DESCRIPTION + + Bottleneck V2 from "Identity Mappings in Deep Residual Networks" + paper. + This is used for ResNet V2 for 18, 34 layers. + + Parameters + ---------- + channels : Int + Number of output channels. + stride : Int + Stride size. + downsample : Bool, default 0 + Whether to downsample the input. + in_channels : Int, default 0 + Number of input channels. Default is 0, to infer from the graph. +=cut + +has ['channels', + 'stride'] => (is => 'ro', isa => 'Int', required => 1); +has 'downsample' => (is => 'rw', default => 0); +has 'in_channels' => (is => 'ro', isa => 'Int', default => 0); +method python_constructor_arguments() { [qw/channels stride downsample/] } +func _conv3x3($channels, $stride, $in_channels) +{ + return nn->Conv2D( + $channels, kernel_size=>3, strides=>$stride, padding=>1, + use_bias=>0, in_channels=>$in_channels + ); +} + +sub BUILD +{ + my $self = shift; + $self->bn1(nn->BatchNorm()); + $self->conv1(_conv3x3($self->channels, $self->stride, $self->in_channels)); + $self->bn2(nn->BatchNorm()); + $self->conv2(_conv3x3($self->channels, 1, $self->channels)); + if($self->downsample) + { + $self->downsample( + nn->Conv2D($self->channels, kernel_size=>1, strides=>$self->stride, + use_bias=>0, in_channels=>$self->in_channels) + ); + } + else + { + $self->downsample(undef); + } +} + +method hybrid_forward(GluonClass $F, GluonInput $x) +{ + my $residual = $x; + $x = $self->bn1->($x); + $x = $F->Activation($x, act_type=>'relu'); + if(defined $self->downsample) + { + $residual = $self->downsample->($x); + } + $x = $self->conv1->($x); + + $x = $self->bn2->($x); + $x = $F->Activation($x, act_type=>'relu'); + $x = $self->conv2->($x); + + return $x + $residual; +} + + +package AI::MXNet::Gluon::ModelZoo::Vision::ResNet::BottleneckV2; +use AI::MXNet::Gluon::Mouse; +extends 'AI::MXNet::Gluon::HybridBlock'; + +=head1 NAME + + AI::MXNet::Gluon::ModelZoo::Vision::ResNet::BottleneckV2 - Bottleneck V2 from "Identity Mappings in Deep Residual Networks" +=cut + +=head1 DESCRIPTION + + Bottleneck V2 from "Identity Mappings in Deep Residual Networks" + paper. + This is used for ResNet V2 for 50, 101, 152 layers. + + Parameters + ---------- + channels : int + Number of output channels. + stride : int + Stride size. + downsample : bool, default False + Whether to downsample the input. + in_channels : int, default 0 + Number of input channels. Default is 0, to infer from the graph. +=cut + +has ['channels', + 'stride'] => (is => 'ro', isa => 'Int', required => 1); +has 'downsample' => (is => 'rw', default => 0); +has 'in_channels' => (is => 'ro', isa => 'Int', default => 0); +method python_constructor_arguments() { [qw/channels stride downsample/] } +func _conv3x3($channels, $stride, $in_channels) +{ + return nn->Conv2D( + $channels, kernel_size=>3, strides=>$stride, padding=>1, + use_bias=>0, in_channels=>$in_channels + ); +} + +sub BUILD +{ + my $self = shift; + $self->bn1(nn->BatchNorm()); + $self->conv1(nn->Conv2D(int($self->channels/4), kernel_size=>1, strides=>1, use_bias=>0)); + $self->bn2(nn->BatchNorm()); + $self->conv2(_conv3x3(int($self->channels/4), $self->stride, int($self->channels/4))); + $self->bn3(nn->BatchNorm()); + $self->conv3(nn->Conv2D($self->channels, kernel_size=>1, strides=>1, use_bias=>0)); + if($self->downsample) + { + $self->downsample( + nn->Conv2D($self->channels, kernel_size=>1, strides=>$self->stride, + use_bias=>0, in_channels=>$self->in_channels) + ); + } + else + { + $self->downsample(undef); + } +} + +method hybrid_forward(GluonClass $F, GluonInput $x) +{ + my $residual = $x; + $x = $self->bn1->($x); + $x = $F->Activation($x, act_type=>'relu'); + if(defined $self->downsample) + { + $residual = $self->downsample->($x); + } + $x = $self->conv1->($x); + + $x = $self->bn2->($x); + $x = $F->Activation($x, act_type=>'relu'); + $x = $self->conv2->($x); + + $x = $self->bn3->($x); + $x = $F->Activation($x, act_type=>'relu'); + $x = $self->conv3->($x); + + return $x + $residual; +} + + +# Nets +package AI::MXNet::Gluon::ModelZoo::Vision::ResNet::V1; +use AI::MXNet::Gluon::Mouse; +extends 'AI::MXNet::Gluon::HybridBlock'; +use AI::MXNet::Base; + +=head1 NAME + + AI::MXNet::Gluon::ModelZoo::Vision::ResNet::V1 - ResNet V1 model from "Deep Residual Learning for Image Recognition" +=cut + +=head1 DESCRIPTION + + ResNet V1 model from from "Deep Residual Learning for Image Recognition" + paper. + + Parameters + ---------- + block : AI::MXNet::Gluon::HybridBlock + Class for the residual block. Options are AI::MXNet::Gluon::ModelZoo::Vision::ResNet::BasicBlockV1, + AI::MXNet::Gluon::ModelZoo::Vision::ResNet::BottleneckV1. + layers : array ref of Int + Numbers of layers in each block + channels : array ref of Int + Numbers of channels in each block. Length should be one larger than layers list. + classes : int, default 1000 + Number of classification classes. + thumbnail : bool, default 0 + Enable thumbnail. +=cut + +has 'block' => (is => 'ro', isa => 'Str', required => 1); +has ['layers', + 'channels'] => (is => 'ro', isa => 'ArrayRef[Int]', required => 1); +has 'classes' => (is => 'ro', isa => 'Int', default => 1000); +has 'thumbnail' => (is => 'ro', isa => 'Bool', default => 0); +method python_constructor_arguments() { [qw/block layers channels classes thumbnail/] } +func _conv3x3($channels, $stride, $in_channels) +{ + return nn->Conv2D( + $channels, kernel_size=>3, strides=>$stride, padding=>1, + use_bias=>0, in_channels=>$in_channels + ); +} + +sub BUILD +{ + my $self = shift; + assert(@{ $self->layers } == (@{ $self->channels } - 1)); + $self->name_scope(sub { + $self->features(nn->HybridSequential(prefix=>'')); + if($self->thumbnail) + { + $self->features->add(_conv3x3($self->channels->[0], 1, 0)); + } + else + { + $self->features->add(nn->Conv2D($self->channels->[0], 7, 2, 3, use_bias=>0)); + $self->features->add(nn->BatchNorm()); + $self->features->add(nn->Activation('relu')); + $self->features->add(nn->MaxPool2D(3, 2, 1)); + } + for(enumerate($self->layers)) + { + my ($i, $num_layer) = @$_; + my $stride = $i == 0 ? 1 : 2; + $self->features->add( + $self->_make_layer( + $self->block, $num_layer, $self->channels->[$i+1], + $stride, $i+1, in_channels=>$self->channels->[$i] + ) + ); + } + $self->features->add(nn->GlobalAvgPool2D()); + $self->output(nn->Dense($self->classes, in_units=>$self->channels->[-1])); + }); +} + +method _make_layer($block, $layers, $channels, $stride, $stage_index, :$in_channels=0) +{ + my $layer = nn->HybridSequential(prefix=>"stage${stage_index}_"); + $layer->name_scope(sub { + $layer->add( + $block->new( + $channels, $stride, $channels != $in_channels, in_channels=>$in_channels, + prefix=>'' + ) + ); + for(1..$layers-1) + { + $layer->add($block->new($channels, 1, 0, in_channels=>$channels, prefix=>'')); + } + }); + return $layer; +} + +method hybrid_forward(GluonClass $F, GluonInput $x) +{ + $x = $self->features->($x); + $x = $self->output->($x); + return $x; +} + + +package AI::MXNet::Gluon::ModelZoo::Vision::ResNet::V2; +use AI::MXNet::Gluon::Mouse; +extends 'AI::MXNet::Gluon::HybridBlock'; +use AI::MXNet::Base; + +=head1 NAME + + AI::MXNet::Gluon::ModelZoo::Vision::ResNet::V2 - ResNet V2 model from "Identity Mappings in Deep Residual Networks" +=cut + +=head1 DESCRIPTION + + ResNet V2 model from "Identity Mappings in Deep Residual Networks" + paper. + + Parameters + ---------- + block : AI::MXNet::Gluon::HybridBlock + Class for the residual block. Options are AI::MXNet::Gluon::ModelZoo::Vision::ResNet::BasicBlockV2, + AI::MXNet::Gluon::ModelZoo::Vision::ResNet::BottleneckV2. + layers : array ref of Int + Numbers of layers in each block + channels : array ref of Int + Numbers of channels in each block. Length should be one larger than layers list. + classes : int, default 1000 + Number of classification classes. + thumbnail : bool, default 0 + Enable thumbnail. +=cut + +has 'block' => (is => 'ro', isa => 'Str', required => 1); +has ['layers', + 'channels'] => (is => 'ro', isa => 'ArrayRef[Int]', required => 1); +has 'classes' => (is => 'ro', isa => 'Int', default => 1000); +has 'thumbnail' => (is => 'ro', isa => 'Bool', default => 0); +method python_constructor_arguments() { [qw/block layers channels classes thumbnail/] } +func _conv3x3($channels, $stride, $in_channels) +{ + return nn->Conv2D( + $channels, kernel_size=>3, strides=>$stride, padding=>1, + use_bias=>0, in_channels=>$in_channels + ); +} + +sub BUILD +{ + my $self = shift; + assert(@{ $self->layers } == (@{ $self->channels } - 1)); + $self->name_scope(sub { + $self->features(nn->HybridSequential(prefix=>'')); + $self->features->add(nn->BatchNorm(scale=>0, center=>0)); + if($self->thumbnail) + { + $self->features->add(_conv3x3($self->channels->[0], 1, 0)); + } + else + { + $self->features->add(nn->Conv2D($self->channels->[0], 7, 2, 3, use_bias=>0)); + $self->features->add(nn->BatchNorm()); + $self->features->add(nn->Activation('relu')); + $self->features->add(nn->MaxPool2D(3, 2, 1)); + } + my $in_channels = $self->channels->[0]; + for(enumerate($self->layers)) + { + my ($i, $num_layer) = @$_; + my $stride = $i == 0 ? 1 : 2; + $self->features->add( + $self->_make_layer( + $self->block, $num_layer, $self->channels->[$i+1], + $stride, $i+1, in_channels=>$in_channels + ) + ); + $in_channels = $self->channels->[$i+1]; + } + $self->features->add(nn->BatchNorm()); + $self->features->add(nn->Activation('relu')); + $self->features->add(nn->GlobalAvgPool2D()); + $self->features->add(nn->Flatten()); + $self->output(nn->Dense($self->classes, in_units=>$in_channels)); + }); +} + +method _make_layer($block, $layers, $channels, $stride, $stage_index, :$in_channels=0) +{ + my $layer = nn->HybridSequential(prefix=>"stage${stage_index}_"); + $layer->name_scope(sub { + $layer->add( + $block->new( + $channels, $stride, $channels != $in_channels, in_channels=>$in_channels, + prefix=>'' + ) + ); + for(1..$layers-1) + { + $layer->add($block->new($channels, 1, 0, in_channels=>$channels, prefix=>'')); + } + }); + return $layer; +} + +method hybrid_forward(GluonClass $F, GluonInput $x) +{ + $x = $self->features->($x); + $x = $self->output->($x); + return $x; +} + +package AI::MXNet::Gluon::ModelZoo::Vision; + +# Specification +my %resnet_spec = ( + 18 => ['basic_block', [2, 2, 2, 2], [64, 64, 128, 256, 512]], + 34 => ['basic_block', [3, 4, 6, 3], [64, 64, 128, 256, 512]], + 50 => ['bottle_neck', [3, 4, 6, 3], [64, 256, 512, 1024, 2048]], + 101 => ['bottle_neck', [3, 4, 23, 3], [64, 256, 512, 1024, 2048]], + 152 => ['bottle_neck', [3, 8, 36, 3], [64, 256, 512, 1024, 2048]] +); + +my @resnet_net_versions = qw(AI::MXNet::Gluon::ModelZoo::Vision::ResNet::V1 AI::MXNet::Gluon::ModelZoo::Vision::ResNet::V2); +my @resnet_block_versions = ( + { + basic_block => 'AI::MXNet::Gluon::ModelZoo::Vision::ResNet::BasicBlockV1', + bottle_neck => 'AI::MXNet::Gluon::ModelZoo::Vision::ResNet::BottleneckV1' + }, + { + basic_block => 'AI::MXNet::Gluon::ModelZoo::Vision::ResNet::BasicBlockV2', + bottle_neck => 'AI::MXNet::Gluon::ModelZoo::Vision::ResNet::BottleneckV2' + }, +); + +=head2 get_resnet + + ResNet V1 model from "Deep Residual Learning for Image Recognition" + paper. + ResNet V2 model from "Identity Mappings in Deep Residual Networks" + paper. + + Parameters + ---------- + $version : Int + Version of ResNet. Options are 1, 2. + $num_layers : Int + Numbers of layers. Options are 18, 34, 50, 101, 152. + :$pretrained : Bool, default 0 + Whether to load the pretrained weights for model. + :$ctx : AI::MXNet::Context, default CPU + The context in which to load the pretrained weights. + :$root : Str, default '~/.mxnet/models' + Location for keeping the model parameters. +=cut + +# Constructor +method get_resnet( + Int $version, Int $num_layers, Bool :$pretrained=0, + AI::MXNet::Context :$ctx=AI::MXNet::Context->cpu(), + Str :$root='~/.mxnet/models', + Maybe[Int] :$classes=, + Maybe[Bool] :$thumbnail= +) +{ + my ($block_type, $layers, $channels) = @{ $resnet_spec{$num_layers} }; + my $resnet_class = $resnet_net_versions[$version-1]; + confess("invalid resnet $version [$version], can be 1,2") unless $resnet_class; + my $block_class = $resnet_block_versions[$version-1]{$block_type}; + my $net = $resnet_class->new( + $block_class, $layers, $channels, + (defined($classes) ? (classes => $classes) : ()), + (defined($thumbnail) ? (thumbnail => $thumbnail) : ()) + ); + if($pretrained) + { + $net->load_parameters( + AI::MXNet::Gluon::ModelZoo::ModelStore->get_model_file( + "resnet${num_layers}_v$version", + root=>$root + ), + ctx=>$ctx + ); + } + return $net; +} + +=head2 resnet18_v1 + + ResNet-18 V1 model from "Deep Residual Learning for Image Recognition" + paper. + + Parameters + ---------- + :$pretrained : Bool, default 0 + Whether to load the pretrained weights for model. + :$ctx : AI::MXNet::Context, default CPU + The context in which to load the pretrained weights. + :$root : Str, default '~/.mxnet/models' + Location for keeping the model parameters. +=cut + +method resnet18_v1(%kwargs) +{ + return __PACKAGE__->get_resnet(1, 18, %kwargs); +} + +=head2 resnet34_v1 + + ResNet-34 V1 model from "Deep Residual Learning for Image Recognition" + paper. + + Parameters + ---------- + :$pretrained : Bool, default 0 + Whether to load the pretrained weights for model. + :$ctx : AI::MXNet::Context, default CPU + The context in which to load the pretrained weights. + :$root : Str, default '~/.mxnet/models' + Location for keeping the model parameters. +=cut + +method resnet34_v1(%kwargs) +{ + return __PACKAGE__->get_resnet(1, 34, %kwargs); +} + +=head2 resnet50_v1 + + ResNet-50 V1 model from "Deep Residual Learning for Image Recognition" + paper. + + Parameters + ---------- + :$pretrained : Bool, default 0 + Whether to load the pretrained weights for model. + :$ctx : AI::MXNet::Context, default CPU + The context in which to load the pretrained weights. + :$root : Str, default '~/.mxnet/models' + Location for keeping the model parameters. +=cut + +method resnet50_v1(%kwargs) +{ + return __PACKAGE__->get_resnet(1, 50, %kwargs); +} + +=head2 resnet101_v1 + + ResNet-101 V1 model from "Deep Residual Learning for Image Recognition" + paper. + + Parameters + ---------- + :$pretrained : Bool, default 0 + Whether to load the pretrained weights for model. + :$ctx : AI::MXNet::Context, default CPU + The context in which to load the pretrained weights. + :$root : Str, default '~/.mxnet/models' + Location for keeping the model parameters. +=cut + +method resnet101_v1(%kwargs) +{ + return __PACKAGE__->get_resnet(1, 101, %kwargs); +} + +=head2 resnet152_v1 + + ResNet-152 V1 model from "Deep Residual Learning for Image Recognition" + paper. + + Parameters + ---------- + :$pretrained : Bool, default 0 + Whether to load the pretrained weights for model. + :$ctx : AI::MXNet::Context, default CPU + The context in which to load the pretrained weights. + :$root : Str, default '~/.mxnet/models' + Location for keeping the model parameters. +=cut + +method resnet152_v1(%kwargs) +{ + return __PACKAGE__->get_resnet(1, 152, %kwargs); +} + +=head2 resnet18_v2 + + ResNet-18 V2 model from "Identity Mappings in Deep Residual Networks" + paper. + + Parameters + ---------- + :$pretrained : Bool, default 0 + Whether to load the pretrained weights for model. + :$ctx : AI::MXNet::Context, default CPU + The context in which to load the pretrained weights. + :$root : Str, default '~/.mxnet/models' + Location for keeping the model parameters. +=cut + +method resnet18_v2(%kwargs) +{ + return __PACKAGE__->get_resnet(2, 18, %kwargs); +} + +=head2 resnet34_v2 + + ResNet-34 V2 model from "Identity Mappings in Deep Residual Networks" + paper. + + Parameters + ---------- + :$pretrained : Bool, default 0 + Whether to load the pretrained weights for model. + :$ctx : AI::MXNet::Context, default CPU + The context in which to load the pretrained weights. + :$root : Str, default '~/.mxnet/models' + Location for keeping the model parameters. +=cut + +method resnet34_v2(%kwargs) +{ + return __PACKAGE__->get_resnet(2, 34, %kwargs); +} + +=head2 resnet50_v2 + + ResNet-50 V2 model from "Identity Mappings in Deep Residual Networks" + paper. + + Parameters + ---------- + :$pretrained : Bool, default 0 + Whether to load the pretrained weights for model. + :$ctx : AI::MXNet::Context, default CPU + The context in which to load the pretrained weights. + :$root : Str, default '~/.mxnet/models' + Location for keeping the model parameters. +=cut + +method resnet50_v2(%kwargs) +{ + return __PACKAGE__->get_resnet(2, 50, %kwargs); +} + +=head2 resnet101_v2 + + ResNet-101 V2 model from "Identity Mappings in Deep Residual Networks" + paper. + + Parameters + ---------- + :$pretrained : Bool, default 0 + Whether to load the pretrained weights for model. + :$ctx : AI::MXNet::Context, default CPU + The context in which to load the pretrained weights. + :$root : Str, default '~/.mxnet/models' + Location for keeping the model parameters. +=cut + +method resnet101_v2(%kwargs) +{ + return __PACKAGE__->get_resnet(2, 101, %kwargs); +} + +=head2 resnet152_v2 + + ResNet-152 V2 model from "Identity Mappings in Deep Residual Networks" + paper. + + Parameters + ---------- + :$pretrained : Bool, default 0 + Whether to load the pretrained weights for model. + :$ctx : AI::MXNet::Context, default CPU + The context in which to load the pretrained weights. + :$root : Str, default '~/.mxnet/models' + Location for keeping the model parameters. +=cut + +method resnet152_v2(%kwargs) +{ + return __PACKAGE__->get_resnet(2, 152, %kwargs); +} + +1; diff --git a/perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo/Vision/SqueezeNet.pm b/perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo/Vision/SqueezeNet.pm new file mode 100644 index 000000000000..3cbe8dd9e8f3 --- /dev/null +++ b/perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo/Vision/SqueezeNet.pm @@ -0,0 +1,212 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +package AI::MXNet::Gluon::ModelZoo::Vision::SqueezeNet; +use strict; +use warnings; +use AI::MXNet::Function::Parameters; +use AI::MXNet::Gluon::Mouse; +use AI::MXNet::Types; +extends 'AI::MXNet::Gluon::HybridBlock'; + +func _make_fire($squeeze_channels, $expand1x1_channels, $expand3x3_channels) +{ + my $out = nn->HybridSequential(prefix=>''); + $out->add(_make_fire_conv($squeeze_channels, 1)); + + my $paths = nn->HybridConcurrent(axis=>1, prefix=>''); + $paths->add(_make_fire_conv($expand1x1_channels, 1)); + $paths->add(_make_fire_conv($expand3x3_channels, 3, 1)); + $out->add($paths); + + return $out; +} + +func _make_fire_conv($channels, $kernel_size, $padding=0) +{ + my $out = nn->HybridSequential(prefix=>''); + $out->add(nn->Conv2D($channels, $kernel_size, padding=>$padding)); + $out->add(nn->Activation('relu')); + return $out; +} + +=head1 NAME + + AI::MXNet::Gluon::ModelZoo::Vision::SqueezeNet - SqueezeNet model from the "SqueezeNet: AlexNet-level accuracy with 50x fewer parameters and <0.5MB model size" +=cut + +=head1 DESCRIPTION + + SqueezeNet model from the "SqueezeNet: AlexNet-level accuracy with 50x fewer parameters + and <0.5MB model size" paper. + SqueezeNet 1.1 model from the official SqueezeNet repo + . + SqueezeNet 1.1 has 2.4x less computation and slightly fewer parameters + than SqueezeNet 1.0, without sacrificing accuracy. + + Parameters + ---------- + version : Str + Version of squeezenet. Options are '1.0', '1.1'. + classes : Int, default 1000 + Number of classification classes. +=cut + +has 'version' => (is => 'ro', isa => enum([qw[1.0 1.1]]), required => 1); +has 'classes' => (is => 'ro', isa => 'Int', default => 1000); +method python_constructor_arguments() { [qw/version classes/] } + +sub BUILD +{ + my $self = shift; + $self->name_scope(sub { + $self->features(nn->HybridSequential(prefix=>'')); + if($self->version eq '1.0') + { + $self->features->add(nn->Conv2D(96, kernel_size=>7, strides=>2)); + $self->features->add(nn->Activation('relu')); + $self->features->add(nn->MaxPool2D(pool_size=>3, strides=>2, ceil_mode=>1)); + $self->features->add(_make_fire(16, 64, 64)); + $self->features->add(_make_fire(16, 64, 64)); + $self->features->add(_make_fire(32, 128, 128)); + $self->features->add(nn->MaxPool2D(pool_size=>3, strides=>2, ceil_mode=>1)); + $self->features->add(_make_fire(32, 128, 128)); + $self->features->add(_make_fire(48, 192, 192)); + $self->features->add(_make_fire(48, 192, 192)); + $self->features->add(_make_fire(64, 256, 256)); + $self->features->add(nn->MaxPool2D(pool_size=>3, strides=>2, ceil_mode=>1)); + $self->features->add(_make_fire(64, 256, 256)); + } + else + { + $self->features->add(nn->Conv2D(64, kernel_size=>3, strides=>2)); + $self->features->add(nn->Activation('relu')); + $self->features->add(nn->MaxPool2D(pool_size=>3, strides=>2, ceil_mode=>1)); + $self->features->add(_make_fire(16, 64, 64)); + $self->features->add(_make_fire(16, 64, 64)); + $self->features->add(nn->MaxPool2D(pool_size=>3, strides=>2, ceil_mode=>1)); + $self->features->add(_make_fire(32, 128, 128)); + $self->features->add(_make_fire(32, 128, 128)); + $self->features->add(nn->MaxPool2D(pool_size=>3, strides=>2, ceil_mode=>1)); + $self->features->add(_make_fire(48, 192, 192)); + $self->features->add(_make_fire(48, 192, 192)); + $self->features->add(_make_fire(64, 256, 256)); + $self->features->add(_make_fire(64, 256, 256)); + } + $self->features->add(nn->Dropout(0.5)); + + $self->output(nn->HybridSequential(prefix=>'')); + $self->output->add(nn->Conv2D($self->classes, kernel_size=>1)); + $self->output->add(nn->Activation('relu')); + $self->output->add(nn->AvgPool2D(13)); + $self->output->add(nn->Flatten()); + }); +} + +method hybrid_forward(GluonClass $F, GluonInput $x) +{ + $x = $self->features->($x); + $x = $self->output->($x); + return $x; +} + + +package AI::MXNet::Gluon::ModelZoo::Vision; + +=head2 get_squeezenet + + SqueezeNet model from the "SqueezeNet: AlexNet-level accuracy with 50x fewer parameters + and <0.5MB model size" paper. + SqueezeNet 1.1 model from the official SqueezeNet repo + . + SqueezeNet 1.1 has 2.4x less computation and slightly fewer parameters + than SqueezeNet 1.0, without sacrificing accuracy. + + Parameters + ---------- + $version : Str + Version of squeezenet. Options are '1.0', '1.1'. + :$pretrained : Bool, default 0 + Whether to load the pretrained weights for model. + :$ctx : AI::MXNet::Context, default CPU + The context in which to load the pretrained weights. + :$root : Str, default '~/.mxnet/models' + Location for keeping the model parameters. +=cut + +method get_squeezenet( + Str $version, Bool :$pretrained=0, AI::MXNet::Context :$ctx=AI::MXNet::Context->cpu(), + Str :$root='~/.mxnet/models', Int :$classes=1000 +) +{ + my $net = AI::MXNet::Gluon::ModelZoo::Vision::SqueezeNet->new($version, $classes); + if($pretrained) + { + $net->load_parameters( + AI::MXNet::Gluon::ModelZoo::ModelStore->get_model_file( + "squeezenet$version", + root=>$root + ), + ctx=>$ctx + ); + } + return $net; +} + +=head2 squeezenet1_0 + + SqueezeNet 1.0 model from the "SqueezeNet: AlexNet-level accuracy with 50x fewer parameters + and <0.5MB model size" paper. + + Parameters + ---------- + :$pretrained : Bool, default 0 + Whether to load the pretrained weights for model. + :$ctx : AI::MXNet::Context, default CPU + The context in which to load the pretrained weights. + :$root : Str, default '~/.mxnet/models' + Location for keeping the model parameters. +=cut + +method squeezenet1_0(%kwargs) +{ + return __PACKAGE__->get_squeezenet('1.0', %kwargs); +} + +=head2 squeezenet1_1 + + SqueezeNet 1.1 model from the official SqueezeNet repo + . + SqueezeNet 1.1 has 2.4x less computation and slightly fewer parameters + than SqueezeNet 1.0, without sacrificing accuracy. + + Parameters + ---------- + :$pretrained : Bool, default 0 + Whether to load the pretrained weights for model. + :$ctx : AI::MXNet::Context, default CPU + The context in which to load the pretrained weights. + :$root : Str, default '~/.mxnet/models' + Location for keeping the model parameters. +=cut + +method squeezenet1_1(%kwargs) +{ + return __PACKAGE__->get_squeezenet('1.1', %kwargs); +} + +1; diff --git a/perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo/Vision/VGG.pm b/perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo/Vision/VGG.pm new file mode 100644 index 000000000000..03443a78b02e --- /dev/null +++ b/perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo/Vision/VGG.pm @@ -0,0 +1,321 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +use strict; +use warnings; +use AI::MXNet::Function::Parameters; +package AI::MXNet::Gluon::ModelZoo::Vision::VGG; +use AI::MXNet::Gluon::Mouse; +extends 'AI::MXNet::Gluon::HybridBlock'; +use AI::MXNet::Base; + +=head1 NAME + + AI::MXNet::Gluon::ModelZoo::Vision::VGG - VGG model from the "Very Deep Convolutional Networks for Large-Scale Image Recognition" +=cut + +=head1 DESCRIPTION + + VGG model from the "Very Deep Convolutional Networks for Large-Scale Image Recognition" + paper. + + Parameters + ---------- + layers : array ref of Int + Numbers of layers in each feature block. + filters : array ref of Int + Numbers of filters in each feature block. List length should match the layers. + classes : Int, default 1000 + Number of classification classes. + batch_norm : Bool, default 0 + Use batch normalization. +=cut +method python_constructor_arguments() { [qw/layers filters classes batch_norm/] } +has ['layers', + 'filters'] => (is => 'ro', isa => 'ArrayRef[Int]', required => 1); +has 'classes' => (is => 'ro', isa => 'Int', default => 1000); +has 'batch_norm' => (is => 'ro', isa => 'Bool', default => 0); + +sub BUILD +{ + my $self = shift; + assert(@{ $self->layers } == @{ $self->filters }); + $self->name_scope(sub { + $self->features($self->_make_features()); + $self->features->add(nn->Dense(4096, activation=>'relu', + weight_initializer=>'normal', + bias_initializer=>'zeros')); + $self->features->add(nn->Dropout(rate=>0.5)); + $self->features->add(nn->Dense(4096, activation=>'relu', + weight_initializer=>'normal', + bias_initializer=>'zeros')); + $self->features->add(nn->Dropout(rate=>0.5)); + $self->output(nn->Dense($self->classes, + weight_initializer=>'normal', + bias_initializer=>'zeros')); + }); +} + +method _make_features() +{ + my $featurizer = nn->HybridSequential(prefix=>''); + for(enumerate($self->layers)) + { + my ($i, $num) = @$_; + for(0..$num-1) + { + $featurizer->add( + nn->Conv2D( + $self->filters->[$i], kernel_size => 3, padding => 1, + weight_initializer => mx->init->Xavier( + rnd_type => 'gaussian', + factor_type => 'out', + magnitude => 2 + ), + bias_initializer=>'zeros' + ) + ); + if($self->batch_norm) + { + $featurizer->add(nn->BatchNorm()); + } + $featurizer->add(nn->Activation('relu')); + } + $featurizer->add(nn->MaxPool2D(strides=>2)); + } + return $featurizer; +} + +method hybrid_forward(GluonClass $F, GluonInput $x) +{ + $x = $self->features->($x); + $x = $self->output->($x); + return $x; +} + +package AI::MXNet::Gluon::ModelZoo::Vision; + +# Specification +my %vgg_spec = ( + 11 => [[1, 1, 2, 2, 2], [64, 128, 256, 512, 512]], + 13 => [[2, 2, 2, 2, 2], [64, 128, 256, 512, 512]], + 16 => [[2, 2, 3, 3, 3], [64, 128, 256, 512, 512]], + 19 => [[2, 2, 4, 4, 4], [64, 128, 256, 512, 512]] +); + +=head2 get_vgg + + VGG model from the "Very Deep Convolutional Networks for Large-Scale Image Recognition" + paper. + + Parameters + ---------- + $num_layers : Int + Number of layers for the variant of densenet. Options are 11, 13, 16, 19. + :$pretrained : Bool, default 0 + Whether to load the pretrained weights for model. + :$ctx : AI::MXNet::Context, default AI::MXNet::Context->cpu + The context in which to load the pretrained weights. + :$root : Str, default '~/.mxnet/models' + Location for keeping the model parameters. +=cut + +method get_vgg( + Int $num_layers, Bool :$pretrained=0, AI::MXNet::Context :$ctx=AI::MXNet::Context->cpu(), + Str :$root='~/.mxnet/models', Int :$classes=1000, Bool :$batch_norm=0 +) +{ + my ($layers, $filters) = @{ $vgg_spec{$num_layers} }; + my $net = AI::MXNet::Gluon::ModelZoo::Vision::VGG->new($layers, $filters, $classes, $batch_norm); + if($pretrained) + { + $net->load_parameters( + AI::MXNet::Gluon::ModelZoo::ModelStore->get_model_file( + "vgg$num_layers".($batch_norm ? '_bn' : ''), + root=>$root + ), + ctx=>$ctx + ); + } + return $net; +} + +=head2 vgg11 + + VGG-11 model from the "Very Deep Convolutional Networks for Large-Scale Image Recognition" + paper. + + Parameters + ---------- + :$pretrained : Bool, default 0 + Whether to load the pretrained weights for model. + :$ctx : AI::MXNet::Context, default AI::MXNet::Context->cpu + The context in which to load the pretrained weights. + :$root : Str, default '~/.mxnet/models' + Location for keeping the model parameters. +=cut + +method vgg11(%kwargs) +{ + return __PACKAGE__->get_vgg(11, %kwargs); +} + +=head2 vgg13 + + VGG-13 model from the "Very Deep Convolutional Networks for Large-Scale Image Recognition" + paper. + + Parameters + ---------- + :$pretrained : Bool, default 0 + Whether to load the pretrained weights for model. + :$ctx : AI::MXNet::Context, default AI::MXNet::Context->cpu + The context in which to load the pretrained weights. + :$root : Str, default '~/.mxnet/models' + Location for keeping the model parameters. +=cut + +method vgg13(%kwargs) +{ + return __PACKAGE__->get_vgg(13, %kwargs); +} + +=head2 vgg16 + + VGG-16 model from the "Very Deep Convolutional Networks for Large-Scale Image Recognition" + paper. + + Parameters + ---------- + :$pretrained : Bool, default 0 + Whether to load the pretrained weights for model. + :$ctx : AI::MXNet::Context, default AI::MXNet::Context->cpu + The context in which to load the pretrained weights. + :$root : Str, default '~/.mxnet/models' + Location for keeping the model parameters. +=cut + +method vgg16(%kwargs) +{ + return __PACKAGE__->get_vgg(16, %kwargs); +} + +=head2 vgg19 + + VGG-19 model from the "Very Deep Convolutional Networks for Large-Scale Image Recognition" + paper. + + Parameters + ---------- + :$pretrained : Bool, default 0 + Whether to load the pretrained weights for model. + :$ctx : AI::MXNet::Context, default AI::MXNet::Context->cpu + The context in which to load the pretrained weights. + :$root : Str, default '~/.mxnet/models' + Location for keeping the model parameters. +=cut + +method vgg19(%kwargs) +{ + return __PACKAGE__->get_vgg(19, %kwargs); +} + +=head2 vgg11_bn + + VGG-11 model with batch normalization from the "Very Deep Convolutional Networks for Large-Scale Image Recognition" + paper. + + Parameters + ---------- + :$pretrained : Bool, default 0 + Whether to load the pretrained weights for model. + :$ctx : AI::MXNet::Context, default AI::MXNet::Context->cpu + The context in which to load the pretrained weights. + :$root : Str, default '~/.mxnet/models' + Location for keeping the model parameters. +=cut + +method vgg11_bn(%kwargs) +{ + $kwargs{batch_norm} = 1; + return __PACKAGE__->get_vgg(11, %kwargs); +} + +=head2 vgg13_bn + + VGG-13 model with batch normalization from the "Very Deep Convolutional Networks for Large-Scale Image Recognition" + paper. + + Parameters + ---------- + :$pretrained : Bool, default 0 + Whether to load the pretrained weights for model. + :$ctx : AI::MXNet::Context, default AI::MXNet::Context->cpu + The context in which to load the pretrained weights. + :$root : Str, default '~/.mxnet/models' + Location for keeping the model parameters. +=cut + +method vgg13_bn(%kwargs) +{ + $kwargs{batch_norm} = 1; + return __PACKAGE__->get_vgg(13, %kwargs); +} + +=head2 vgg16_bn + + VGG-16 model with batch normalization from the "Very Deep Convolutional Networks for Large-Scale Image Recognition" + paper. + + Parameters + ---------- + :$pretrained : Bool, default 0 + Whether to load the pretrained weights for model. + :$ctx : AI::MXNet::Context, default AI::MXNet::Context->cpu + The context in which to load the pretrained weights. + :$root : Str, default '~/.mxnet/models' + Location for keeping the model parameters. +=cut + +method vgg16_bn(%kwargs) +{ + $kwargs{batch_norm} = 1; + return __PACKAGE__->get_vgg(16, %kwargs); +} + +=head2 vgg19_bn + + VGG-19 model with batch normalization from the "Very Deep Convolutional Networks for Large-Scale Image Recognition" + paper. + + Parameters + ---------- + :$pretrained : Bool, default 0 + Whether to load the pretrained weights for model. + :$ctx : AI::MXNet::Context, default AI::MXNet::Context->cpu + The context in which to load the pretrained weights. + :$root : Str, default '~/.mxnet/models' + Location for keeping the model parameters. +=cut + +method vgg19_bn(%kwargs) +{ + $kwargs{batch_norm} = 1; + return __PACKAGE__->get_vgg(19, %kwargs); +} + +1; \ No newline at end of file diff --git a/perl-package/AI-MXNet-Gluon-ModelZoo/t/AI-MXNet-Gluon-ModelZoo.t b/perl-package/AI-MXNet-Gluon-ModelZoo/t/AI-MXNet-Gluon-ModelZoo.t new file mode 100644 index 000000000000..30014547759e --- /dev/null +++ b/perl-package/AI-MXNet-Gluon-ModelZoo/t/AI-MXNet-Gluon-ModelZoo.t @@ -0,0 +1,21 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +use strict; +use warnings; +use Test::More tests => 1; +BEGIN { use_ok('AI::MXNet::Gluon::ModelZoo') }; diff --git a/perl-package/AI-MXNet-Gluon-ModelZoo/t/test_gluon_model_zoo.t b/perl-package/AI-MXNet-Gluon-ModelZoo/t/test_gluon_model_zoo.t new file mode 100644 index 000000000000..d782c2027583 --- /dev/null +++ b/perl-package/AI-MXNet-Gluon-ModelZoo/t/test_gluon_model_zoo.t @@ -0,0 +1,51 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +use strict; +use warnings; +use AI::MXNet::Gluon::ModelZoo qw(get_model); +use Test::More tests => 34; + +sub test_models +{ + my @all_models = ('resnet34_v1', 'resnet18_v1', 'resnet50_v1', 'resnet101_v1', 'resnet152_v1', + 'resnet18_v2', 'resnet34_v2', 'resnet50_v2', 'resnet101_v2', 'resnet152_v2', + 'vgg11', 'vgg13', 'vgg16', 'vgg19', + 'vgg11_bn', 'vgg13_bn', 'vgg16_bn', 'vgg19_bn', + 'alexnet', 'inceptionv3', + 'densenet121', 'densenet161', 'densenet169', 'densenet201', + 'squeezenet1.0', 'squeezenet1.1', + 'mobilenet1.0', 'mobilenet0.75', 'mobilenet0.5', 'mobilenet0.25', + 'mobilenetv2_1.0', 'mobilenetv2_0.75', 'mobilenetv2_0.5', 'mobilenetv2_0.25'); + my %pretrained_to_test = ('squeezenet1.1' => 1); + + for my $model_name (@all_models) + { + my $test_pretrain = exists $pretrained_to_test{ $model_name }; + my $model = get_model($model_name, pretrained=>$test_pretrain, root=>'model/'); + my $data_shape = $model_name !~ /inception/ ? [2, 3, 224, 224] : [2, 3, 299, 299]; + if(not $test_pretrain) + { + $model->collect_params()->initialize(); + } + $model->hybridize(); + $model->(mx->nd->random->uniform(shape=>$data_shape))->wait_to_read; + ok(1, "forward for $model_name"); + } +} + +test_models(); diff --git a/perl-package/AI-MXNet/Changes b/perl-package/AI-MXNet/Changes index 393987257565..b522759529a0 100644 --- a/perl-package/AI-MXNet/Changes +++ b/perl-package/AI-MXNet/Changes @@ -1,4 +1,9 @@ Revision history for Perl extension AI::MXNet +1.31 Tue Jul 10 21:19:13 PDT 2018 + - Memory leak fix for Gluon API + - Added summary function for Gluon models + - Improved graphviz vizualizations + - Added artistic style transfer example for Gluon API 1.3 Tue Jun 26 20:57:40 PDT 2018 - Major Gluon update towards parity with Python's API. diff --git a/perl-package/AI-MXNet/MANIFEST b/perl-package/AI-MXNet/MANIFEST index 22a162495a7e..14290227a65f 100644 --- a/perl-package/AI-MXNet/MANIFEST +++ b/perl-package/AI-MXNet/MANIFEST @@ -2,9 +2,14 @@ Changes examples/calculator.pl examples/char_lstm.pl examples/cudnn_lstm_bucketing.pl -examples/get_ptb_data.sh +examples/get_sherlockholmes_data.sh examples/gluon/dcgan.pl examples/gluon/mnist.pl +examples/gluon/style_transfer/get_data.sh +examples/gluon/style_transfer/net.pl +examples/gluon/style_transfer/README.md +examples/gluon/style_transfer/style_transfer.pl +examples/gluon/style_transfer/utils.pl examples/lstm_bucketing.pl examples/mnist.pl examples/plot_network.pl diff --git a/perl-package/AI-MXNet/META.json b/perl-package/AI-MXNet/META.json index fabbd775eb73..a43f77d3662a 100644 --- a/perl-package/AI-MXNet/META.json +++ b/perl-package/AI-MXNet/META.json @@ -45,5 +45,5 @@ } }, "release_status" : "stable", - "version" : "1.3" + "version" : "1.31" } diff --git a/perl-package/AI-MXNet/META.yml b/perl-package/AI-MXNet/META.yml index 610704f1b703..642f370ee81f 100644 --- a/perl-package/AI-MXNet/META.yml +++ b/perl-package/AI-MXNet/META.yml @@ -25,4 +25,4 @@ requires: Mouse: v2.1.0 PDL: '2.007' PDL::CCS: '1.23.4' -version: '1.3' +version: '1.31' diff --git a/perl-package/AI-MXNet/Makefile.PL b/perl-package/AI-MXNet/Makefile.PL index 8ffa715bd45c..f8f0d9c63fe0 100644 --- a/perl-package/AI-MXNet/Makefile.PL +++ b/perl-package/AI-MXNet/Makefile.PL @@ -36,8 +36,8 @@ my %WriteMakefileArgs = ( "LICENSE" => "apache_2_0", "NAME" => "AI::MXNet", "PREREQ_PM" => { - "AI::MXNetCAPI" => "1.2", - "AI::NNVMCAPI" => "1.2", + "AI::MXNetCAPI" => "1.3", + "AI::NNVMCAPI" => "1.3", "Function::Parameters" => "1.0705", "Hash::Ordered" => "0.012", "Mouse" => "v2.1.0", @@ -46,7 +46,7 @@ my %WriteMakefileArgs = ( "GraphViz" => "2.14" }, "TEST_REQUIRES" => {}, - "VERSION" => "1.22", + "VERSION" => "1.31", "test" => { "TESTS" => "t/*.t" } @@ -54,8 +54,8 @@ my %WriteMakefileArgs = ( my %FallbackPrereqs = ( - "AI::MXNetCAPI" => "1.2", - "AI::NNVMCAPI" => "1.2", + "AI::MXNetCAPI" => "1.3", + "AI::NNVMCAPI" => "1.3", "Function::Parameters" => "1.0705", "Hash::Ordered" => "0.012", "Mouse" => "v2.1.0", diff --git a/perl-package/AI-MXNet/README b/perl-package/AI-MXNet/README index cc6372caa9a5..e34970a79723 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.3: +version 1.31: Perl interface to MXNet machine learning library diff --git a/perl-package/AI-MXNet/examples/gluon/style_transfer/README.md b/perl-package/AI-MXNet/examples/gluon/style_transfer/README.md new file mode 100644 index 000000000000..13001b16bd77 --- /dev/null +++ b/perl-package/AI-MXNet/examples/gluon/style_transfer/README.md @@ -0,0 +1,31 @@ +This directory provides AI::MXNet Implementation of MSG-Net real time style transfer, https://arxiv.org/abs/1703.06953 + +### Stylize Images Using Pre-trained MSG-Net +Download the pre-trained model + ``` + ./get_data.sh + ``` + +Test the model + ``` + ./style_transfer.pl --content-image --style-image < path or url> --content-size 512 + ``` + +More options: + + * --content-image: path or url to content image you want to stylize. + * --style-image: path or url to style image. + * --model: path to the pre-trained model to be used for stylizing the image if you use your custom model + * --output-image: path for saving the output image, default is 'out.jpg' + * --content-size: the content image size to test on, default is 512 pixels for the shorter side, + decrease the size if your computer is low on RAM and the script fails + + + + + + + + + + diff --git a/perl-package/AI-MXNet/examples/gluon/style_transfer/get_data.sh b/perl-package/AI-MXNet/examples/gluon/style_transfer/get_data.sh new file mode 100755 index 000000000000..73465f1a3e7f --- /dev/null +++ b/perl-package/AI-MXNet/examples/gluon/style_transfer/get_data.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +DIR=$(cd `dirname $0`; pwd) +DATA_DIR="${DIR}/data/" + +if [[ ! -d "${DATA_DIR}" ]]; then + echo "${DATA_DIR} doesn't exist, will create one"; + mkdir -p ${DATA_DIR} +fi + +wget -P ${DATA_DIR} https://apache-mxnet.s3-accelerate.amazonaws.com/gluon/models/msgnet_21styles-2cb88353.zip +cd ${DATA_DIR} +unzip msgnet_21styles-2cb88353.zip +rm msgnet_21styles-2cb88353.zip diff --git a/perl-package/AI-MXNet/examples/gluon/style_transfer/net.pl b/perl-package/AI-MXNet/examples/gluon/style_transfer/net.pl new file mode 100644 index 000000000000..1e98502ab126 --- /dev/null +++ b/perl-package/AI-MXNet/examples/gluon/style_transfer/net.pl @@ -0,0 +1,325 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +use strict; +use warnings; +use AI::MXNet::Function::Parameters; + +package Bottleneck { + # Pre-activation residual block + # Identity Mapping in Deep Residual Networks + # ref https://arxiv.org/abs/1603.05027 + use AI::MXNet::Gluon::Mouse; + extends 'AI::MXNet::Gluon::Block'; + has ['inplanes', + 'planes'] => (is => 'rw', required => 1); + has 'stride' => (is => 'rw', default => 1); + has 'downsample' => (is => 'rw'); + has 'norm_layer' => (is => 'rw', default => 'AI::MXNet::Gluon::NN::InstanceNorm'); + method python_constructor_arguments(){ [qw/inplanes planes stride downsample norm_layer/] } + sub BUILD + { + my $self = shift; + $self->expansion(4); + if(defined $self->downsample) + { + $self->residual_layer( + nn->Conv2D( + in_channels=>$self->inplanes, + channels=>$self->planes * $self->expansion, + kernel_size=>1, strides=>[$self->stride, $self->stride] + ) + ); + } + $self->conv_block(nn->Sequential()); + $self->conv_block->name_scope(sub { + $self->conv_block->add( + $self->norm_layer->new(in_channels=>$self->inplanes) + ); + $self->conv_block->add(nn->Activation('relu')); + $self->conv_block->add( + nn->Conv2D(in_channels=>$self->inplanes, + channels=>$self->planes, + kernel_size=>1 + ) + ); + $self->conv_block->add($self->norm_layer->new(in_channels=>$self->planes)); + $self->conv_block->add(nn->Activation('relu')); + $self->conv_block->add( + ConvLayer->new( + $self->planes, $self->planes, kernel_size=>3, + stride=>$self->stride + ) + ); + $self->conv_block->add($self->norm_layer->new(in_channels=>$self->planes)); + $self->conv_block->add(nn->Activation('relu')); + $self->conv_block->add( + nn->Conv2D( + in_channels=>$self->planes, + channels=>$self->planes * $self->expansion, + kernel_size=>1 + ) + ); + }); + } + + method forward($x) + { + my $residual; + if(defined $self->downsample) + { + $residual = $self->residual_layer->($x); + } + else + { + $residual = $x; + } + return $residual + $self->conv_block->($x); + } +} + +package UpBottleneck { + # Up-sample residual block (from MSG-Net paper) + # Enables passing identity all the way through the generator + # ref https://arxiv.org/abs/1703.06953 + use AI::MXNet::Gluon::Mouse; + extends 'AI::MXNet::Gluon::Block'; + has ['inplanes', + 'planes'] => (is => 'rw', required => 1); + has 'stride' => (is => 'rw', default => 2); + has 'norm_layer' => (is => 'rw', default => 'AI::MXNet::Gluon::NN::InstanceNorm'); + method python_constructor_arguments(){ [qw/inplanes planes stride norm_layer/] } + sub BUILD + { + my $self = shift; + $self->expansion(4); + $self->residual_layer( + UpsampleConvLayer->new( + $self->inplanes, + $self->planes * $self->expansion, + kernel_size=>1, stride=>1, + upsample=>$self->stride + ) + ); + $self->conv_block(nn->Sequential()); + $self->conv_block->name_scope(sub { + $self->conv_block->add($self->norm_layer->new(in_channels=>$self->inplanes)); + $self->conv_block->add(nn->Activation('relu')); + $self->conv_block->add( + nn->Conv2D( + in_channels=>$self->inplanes, + channels=>$self->planes, + kernel_size=>1 + ) + ); + $self->conv_block->add($self->norm_layer->new(in_channels=>$self->planes)); + $self->conv_block->add(nn->Activation('relu')); + $self->conv_block->add( + UpsampleConvLayer->new( + $self->planes, $self->planes, + kernel_size=>3, stride=>1, + upsample=>$self->stride + ) + ); + $self->conv_block->add($self->norm_layer->new(in_channels=>$self->planes)); + $self->conv_block->add(nn->Activation('relu')); + $self->conv_block->add( + nn->Conv2D( + in_channels=>$self->planes, + channels=>$self->planes * $self->expansion, + kernel_size=>1 + ) + ); + }); + } + + method forward($x) + { + return $self->residual_layer->($x) + $self->conv_block->($x); + } +} + +package ConvLayer { + use AI::MXNet::Gluon::Mouse; + use POSIX qw(floor); + extends 'AI::MXNet::Gluon::Block'; + has [qw/in_channels out_channels kernel_size stride/] => (is => 'rw'); + method python_constructor_arguments(){ [qw/in_channels out_channels kernel_size stride/] } + sub BUILD + { + my $self = shift; + $self->pad(nn->ReflectionPad2D(floor($self->kernel_size/2))); + $self->conv2d( + nn->Conv2D( + in_channels=>$self->in_channels, + channels=>$self->out_channels, + kernel_size=>$self->kernel_size, + strides=>[$self->stride, $self->stride], + padding=>0 + ) + ); + } + + method forward($x) + { + $x = $self->pad->($x); + my $out = $self->conv2d->($x); + return $out; + } +} + + +package UpsampleConvLayer { + # UpsampleConvLayer + # Upsamples the input and then does a convolution. This method gives better results + # compared to ConvTranspose2d. + # ref: http://distill.pub/2016/deconv-checkerboard/ + use AI::MXNet::Gluon::Mouse; + use POSIX qw(floor); + extends 'AI::MXNet::Gluon::Block'; + has [qw/in_channels out_channels kernel_size stride upsample/] => (is => 'rw'); + method python_constructor_arguments(){ [qw/in_channels out_channels kernel_size stride upsample/] } + sub BUILD + { + my $self = shift; + $self->conv2d( + nn->Conv2D( + in_channels=>$self->in_channels, + channels=>$self->out_channels, + kernel_size=>$self->kernel_size, + strides=>[$self->stride, $self->stride], + padding=>floor($self->kernel_size/2) + ) + ); + } + + method forward($x) + { + if($self->upsample) + { + $x = nd->UpSampling($x, scale=>$self->upsample, sample_type=>'nearest'); + } + my $out = $self->conv2d->($x); + return $out; + } +} + +package GramMatrix { + use AI::MXNet::Gluon::Mouse; + extends 'AI::MXNet::Gluon::Block'; + method forward($x) + { + my ($b, $ch, $h, $w) = @{ $x->shape }; + my $features = $x->reshape([$b, $ch, $w * $h]); + my $gram = nd->batch_dot($features, $features, transpose_b=>1) / ($ch * $h * $w); + return $gram; + } +}; + +package Inspiration { + # Inspiration Layer (from MSG-Net paper) + # tuning the featuremap with target Gram Matrix + # ref https://arxiv.org/abs/1703.06953 + use AI::MXNet::Gluon::Mouse; + extends 'AI::MXNet::Gluon::Block'; + has 'C' => (is => 'rw', required => 1); + has 'B' => (is => 'rw', default => 1); + method python_constructor_arguments(){ [qw/C B/] } + sub BUILD + { + my $self = shift; + $self->weight($self->params->get('weight', shape=>[1,$self->C,$self->C], + init=>mx->initializer->Uniform(), + allow_deferred_init=>1)); + $self->gram(nd->random->uniform(shape=>[$self->B, $self->C, $self->C])); + } + + method set_target($target) + { + $self->gram($target); + } + + method forward($x) + { + $self->P(nd->batch_dot($self->weight->data->broadcast_to($self->gram->shape), $self->gram)); + return nd->batch_dot( + nd->SwapAxis($self->P,1,2)->broadcast_to([$x->shape->[0], $self->C, $self->C]), + $x->reshape([0, 0, $x->shape->[2]*$x->shape->[3]]) + )->reshape($x->shape); + } +} + +package Net { + use AI::MXNet::Gluon::Mouse; + extends 'AI::MXNet::Gluon::Block'; + has 'input_nc' => (is => 'rw', default => 3); + has 'output_nc' => (is => 'rw', default => 3); + has 'ngf' => (is => 'rw', default => 64); + has 'norm_layer' => (is => 'rw', default => 'AI::MXNet::Gluon::NN::InstanceNorm'); + has 'n_blocks' => (is => 'rw', default => 6); + has 'gpu_ids' => (is => 'rw', default => sub { [] }); + method python_constructor_arguments(){ [qw/input_nc output_nc ngf norm_layer n_blocks gpu_ids/] } + sub BUILD + { + my $self = shift; + $self->gram(GramMatrix->new); + + my $block = 'Bottleneck'; + my $upblock = 'UpBottleneck'; + my $expansion = 4; + + $self->name_scope(sub { + $self->model1(nn->Sequential()); + $self->ins(Inspiration->new($self->ngf*$expansion)); + $self->model(nn->Sequential()); + + $self->model1->add(ConvLayer->new($self->input_nc, 64, kernel_size=>7, stride=>1)); + $self->model1->add($self->norm_layer->new(in_channels=>64)); + $self->model1->add(nn->Activation('relu')); + $self->model1->add($block->new(64, 32, 2, 1, $self->norm_layer)); + $self->model1->add($block->new(32*$expansion, $self->ngf, 2, 1, $self->norm_layer)); + + $self->model->add($self->model1); + $self->model->add($self->ins); + + for(1..$self->n_blocks) + { + $self->model->add($block->new($self->ngf*$expansion, $self->ngf, 1, undef, $self->norm_layer)); + } + + $self->model->add($upblock->new($self->ngf*$expansion, 32, 2, $self->norm_layer)); + $self->model->add($upblock->new(32*$expansion, 16, 2, $self->norm_layer)); + $self->model->add($self->norm_layer->new(in_channels=>16*$expansion)); + $self->model->add(nn->Activation('relu')); + $self->model->add(ConvLayer->new(16*$expansion, $self->output_nc, kernel_size=>7, stride=>1)); + }); + } + + method set_target($x) + { + my $F = $self->model1->($x); + my $G = $self->gram->($F); + $self->ins->set_target($G); + } + + method forward($input) + { + return $self->model->($input); + } +} + +1; diff --git a/perl-package/AI-MXNet/examples/gluon/style_transfer/style_transfer.pl b/perl-package/AI-MXNet/examples/gluon/style_transfer/style_transfer.pl new file mode 100755 index 000000000000..d85db2652f9a --- /dev/null +++ b/perl-package/AI-MXNet/examples/gluon/style_transfer/style_transfer.pl @@ -0,0 +1,58 @@ +#!/usr/bin/env perl + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +use strict; +use warnings; +use AI::MXNet::Gluon::Utils qw(download); +use AI::MXNet 'mx'; +use AI::MXNet::Gluon::NN 'nn'; +use PDL::IO::Pic; +require './net.pl'; +require './utils.pl'; +use Getopt::Long qw(HelpMessage); + +GetOptions( + 'content-image=s' => \(my $content_image), + 'style-image=s' => \(my $style_image), + 'model=s' => \(my $model = './data/msgnet_21styles-2cb88353.params'), + 'output-image=s' => \(my $output_image = 'out.jpg'), + 'content-size=i' => \(my $content_size = 512), + 'ngf' => \(my $ngf = 128), ## number of convolutional filters for the model + 'help' => sub { HelpMessage(0) }, +) or HelpMessage(1); + +die "Please supply --content-image and --style-image " + unless (defined $content_image and defined $style_image); +if($content_image =~ /^https:/ or $style_image =~ /^https:/) +{ + eval { require IO::Socket::SSL; }; + die "You need to have IO::Socket::SSL installed for https images" if $@; +} +$content_image = download($content_image) if $content_image =~ /^https?:/; +$style_image = download($style_image) if $style_image =~ /^https?:/; + +evaluate( + content_image => $content_image, + style_image => $style_image, + content_size => $content_size, + style_size => $content_size, + output_image => $output_image, + ngf => $ngf, + model => $model +); diff --git a/perl-package/AI-MXNet/examples/gluon/style_transfer/utils.pl b/perl-package/AI-MXNet/examples/gluon/style_transfer/utils.pl new file mode 100644 index 000000000000..f17cd623e017 --- /dev/null +++ b/perl-package/AI-MXNet/examples/gluon/style_transfer/utils.pl @@ -0,0 +1,73 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +use strict; +use warnings; +use AI::MXNet::Function::Parameters; + +func tensor_load_rgbimage($filename, $size=) +{ + my $img = mx->image->imread($filename); + if($size) + { + $img = mx->image->resize_short($img, $size); + } + return $img->transpose([2,0,1])->expand_dims(axis=>0)->astype('float32'); +} + +func tensor_save_rgbimage($img, $filename) +{ + $img = nd->clip($img, a_min => 0, a_max => 255)->transpose([1,2,0])->aspdl; + $img->slice('X', 'X', '-1:0')->byte->wpic($filename); +} + +func tensor_save_bgrimage($tensor, $filename) +{ + $tensor = $tensor->at(0); + my ($b, $g, $r) = @{ nd->split($tensor, num_outputs=>3, axis=>0) }; + $tensor = nd->concat($r, $g, $b, dim=>0); + tensor_save_rgbimage($tensor, $filename); +} + + +func preprocess_batch($batch) +{ + $batch = nd->swapaxes($batch, 0, 1); + my ($r, $g, $b) = @{ nd->split($batch, num_outputs=>3, axis=>0) }; + $batch = nd->concat($b, $g, $r, dim=>0); + $batch = nd->swapaxes($batch, 0, 1); + return $batch; +} + +func evaluate(%args) +{ + my $ctx = mx->cpu; + # images + my $content_image = tensor_load_rgbimage($args{content_image}, $args{content_size}); + my $style_image = tensor_load_rgbimage($args{style_image}, $args{style_size}); + $style_image = preprocess_batch($style_image); + # model + my $style_model = Net->new(ngf=>$args{ngf}); + $style_model->load_parameters($args{model}, ctx=>$ctx); + + # forward + $style_model->set_target($style_image); + my $output = $style_model->($content_image); + tensor_save_bgrimage($output->[0], $args{output_image}); +} + +1; diff --git a/perl-package/AI-MXNet/lib/AI/MXNet.pm b/perl-package/AI-MXNet/lib/AI/MXNet.pm index b9ae39c029d2..4e40fd7298b2 100644 --- a/perl-package/AI-MXNet/lib/AI/MXNet.pm +++ b/perl-package/AI-MXNet/lib/AI/MXNet.pm @@ -51,7 +51,7 @@ use AI::MXNet::Gluon; use AI::MXNet::NDArray::Sparse; use AI::MXNet::Symbol::Sparse; use AI::MXNet::Engine; -our $VERSION = '1.3'; +our $VERSION = '1.31'; 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 8e6546833873..f7daea2a7871 100644 --- a/perl-package/AI-MXNet/lib/AI/MXNet/Base.pm +++ b/perl-package/AI-MXNet/lib/AI/MXNet/Base.pm @@ -30,6 +30,7 @@ use Carp; use Exporter; use base qw(Exporter); use List::Util qw(shuffle); +use Data::Dumper; @AI::MXNet::Base::EXPORT = qw(product enumerate assert zip check_call build_param_doc pdl cat dog svd bisect_left pdl_shuffle as_array ascsr rand_sparse @@ -355,7 +356,7 @@ sub process_arguments %{ $attributes_per_class{$class} } = map { $_->name => 1 } $class->meta->get_all_attributes; } my %kwargs; - while(@_ >= 2 and not ref $_[-2] and (exists $attributes_per_class{$class}{ $_[-2] } or exists $internal_arguments{ $_[-2] })) + while(@_ >= 2 and defined $_[-2] and not ref $_[-2] and (exists $attributes_per_class{$class}{ $_[-2] } or exists $internal_arguments{ $_[-2] })) { my $v = pop(@_); my $k = pop(@_); @@ -363,7 +364,10 @@ sub process_arguments } if(@_) { - @kwargs{ @{ $class->python_constructor_arguments }[0..@_-1] } = @_; + my @named_params = @{ $class->python_constructor_arguments }; + Carp::confess("Paramers mismatch expected ".Dumper(\@named_params).", but got ".Dumper(\@_)) + if @_ > @named_params; + @kwargs{ @named_params[0..@_-1] } = @_; } return $class->$orig(%kwargs); } diff --git a/perl-package/AI-MXNet/lib/AI/MXNet/Gluon/Block.pm b/perl-package/AI-MXNet/lib/AI/MXNet/Gluon/Block.pm index 60c62cf8cfc2..be819ac9d4e5 100644 --- a/perl-package/AI-MXNet/lib/AI/MXNet/Gluon/Block.pm +++ b/perl-package/AI-MXNet/lib/AI/MXNet/Gluon/Block.pm @@ -24,7 +24,7 @@ package AI::MXNet::Gluon::BlockScope; use AI::MXNet::Function::Parameters; my $_current; use Mouse; -has '_block' => (is => 'ro', init_arg => 'block'); +has '_block' => (is => 'ro', init_arg => 'block', weak_ref => 1); has [qw/_counter _old_scope _name_scope/] => (is => 'rw', init_arg => undef); @@ -700,12 +700,12 @@ method call(@args) { for my $hook ($self->_forward_pre_hooks->values) { - $hook->($self, @args); + $hook->($self, \@args); } my @out = $self->forward(@args); for my $hook ($self->_forward_hooks->values) { - $hook->($self, @args); + $hook->($self, \@args, \@out); } return wantarray ? @out : $out[0]; } @@ -733,6 +733,119 @@ method register(Str $container) *{$container.'_::'.$sub_name} = sub { shift; $self->new(@_) }; } +=head2 summary + + Print the summary of the model's output and parameters. + + The network must have been initialized, and must not have been hybridized. + + Parameters + ---------- + @inputs : objects + Any inputs that the model supports. For any tensor in the input, only + AI::MXNet::NDArray is supported. +=cut + +method summary(@inputs) +{ + my $summary = Hash::Ordered->new; + my %seen; + my @hooks; + my $stringify; + $stringify = sub { + my $in = shift; + if(ref($in) eq 'ARRAY') + { + return '('.join(', ', map { $stringify->($_) } @$in).')'; + } + else + { + return "$in"; + } + }; + my $_get_shape_str = sub { my ($args) = @_; + $args = $args->[0] if(ref $args eq 'ARRAY' and @$args == 1); + my ($flat_args, $fmts) = __PACKAGE__->_flatten($args); + my $flat_arg_shapes = [map { (blessed($_) and $_->isa('AI::MXNet::NDArray')) ? $_->shape : $_ } @$flat_args]; + my $shapes = (__PACKAGE__->_regroup($flat_arg_shapes, $fmts))[0]; + my $shape_str = $stringify->($shapes); + $shape_str =~ s/L//g; + return $shape_str; + }; + + my $_register_summary_hook = sub { my ($block) = @_; + unless(not $block->isa('AI::MXNet::Gluon:::HybridBlock') or not $block->_active) + { + confess("\"${\ $block->name }\" must not be hybridized to print summary."); + } + my $_summary_hook = sub { my ($block, undef, $outputs) = @_; + my $class_name = $block->_class_name; + my $block_idx = $summary->keys - 1; + + my $m_key = sprintf('%s-%i', $class_name, $block_idx+1); + $summary->set($m_key, Hash::Ordered->new); + $summary->get($m_key)->set('output_shape', $_get_shape_str->($outputs)); + + my $params = 0; + $summary->get($m_key)->set('trainable', 0); + $summary->get($m_key)->set('shared', 0); + for my $p (values %{ $block->_reg_params }) + { + $params += $p->data->size; + $summary->get($m_key)->set('trainable', $summary->get($m_key)->get('trainable') + ($p->grad_req eq 'null' ? 0 : $p->data->size)); + if(exists $seen{$p}) + { + $summary->get($m_key)->set('shared', $summary->get($m_key)->get('shared') + $p->data->size); + } + else + { + $seen{$p} = 1; + } + } + $summary->get($m_key)->set('n_params', $params); + }; + + if(not $block->isa('AI::MXNet::Gluon::NN::Sequential') and not $block->isa('AI::MXNet::Gluon::NN::HybridSequential')) + { + push @hooks, $block->register_forward_hook($_summary_hook); + } + }; + + my $input = Hash::Ordered->new; + $summary->set('Input', $input); + $input->set('output_shape', $_get_shape_str->(\@inputs)); + $input->set('n_params', 0); + $input->set('trainable', 0); + $input->set('shared', 0); + + eval { + $self->apply($_register_summary_hook); + $self->(@inputs); + + my $line_format = "%20s %42s %15s\n"; + print (('-')x80, "\n"); + printf($line_format, 'Layer (type)', 'Output Shape', 'Param #'); + print (('=')x80, "\n"); + my $total_params = 0; + my $trainable_params = 0; + my $shared_params = 0; + for my $layer ($summary->keys) + { + printf($line_format, $layer, $summary->get($layer)->get('output_shape'), $summary->get($layer)->get('n_params')); + $total_params += $summary->get($layer)->get('n_params'); + $trainable_params += $summary->get($layer)->get('trainable'); + $shared_params += $summary->get($layer)->get('shared'); + } + print (('=')x80, "\n"); + print "Total params: $total_params\n"; + print "Trainable params: $trainable_params\n"; + print "Non-trainable params: ", $total_params - $trainable_params, "\n"; + print "Shared params: $shared_params\n"; + print (('-')x80, "\n"); + }; + $_->detach for @hooks; +} + __PACKAGE__->register('AI::MXNet::Gluon'); package AI::MXNet::Gluon::HybridBlock; diff --git a/perl-package/AI-MXNet/lib/AI/MXNet/Gluon/Utils.pm b/perl-package/AI-MXNet/lib/AI/MXNet/Gluon/Utils.pm index 393f4fc2e6d7..497356da89a8 100644 --- a/perl-package/AI-MXNet/lib/AI/MXNet/Gluon/Utils.pm +++ b/perl-package/AI-MXNet/lib/AI/MXNet/Gluon/Utils.pm @@ -25,7 +25,7 @@ use File::Path qw(make_path); use HTTP::Tiny; use Exporter; use base qw(Exporter); -@AI::MXNet::Gluon::Utils::EXPORT_OK = qw(download); +@AI::MXNet::Gluon::Utils::EXPORT_OK = qw(download check_sha1); =head1 NAME diff --git a/perl-package/AI-MXNet/lib/AI/MXNet/Image.pm b/perl-package/AI-MXNet/lib/AI/MXNet/Image.pm index dbfa5893d576..4f670b0e8e36 100644 --- a/perl-package/AI-MXNet/lib/AI/MXNet/Image.pm +++ b/perl-package/AI-MXNet/lib/AI/MXNet/Image.pm @@ -162,7 +162,7 @@ method resize_short(AI::MXNet::NDArray $src, Int $size, Int $interp=2) { ($new_h, $new_w) = ($size, $size*$w/$h); } - return AI::MXNet::NDArray->_cvimresize($src, $new_w, $new_h, { interp=>$interp }); + return AI::MXNet::NDArray->_cvimresize($src, int $new_w, int $new_h, { interp=>$interp }); } =head2 fixed_crop @@ -189,7 +189,7 @@ method fixed_crop(AI::MXNet::NDArray $src, Int $x0, Int $y0, Int $w, Int $h, May my $out = AI::MXNet::NDArray->crop($src, { begin=>[$y0, $x0, 0], end=>[$y0+$h, $x0+$w, $src->shape->[2]] }); if(defined $size and join(',', $w, $h) ne join(',', @{ $size })) { - $out = AI::MXNet::NDArray->_cvimresize($out, @{ $size }, { interp=>$interp }); + $out = AI::MXNet::NDArray->_cvimresize($out, (map { int } @{ $size }), { interp=>$interp }); } return $out; } @@ -696,7 +696,7 @@ Int :$inter_method=2 method imresize(AI::MXNet::NDArray $src, Int $w, Int $h, Int $interp=2) { - return AI::MXNet::NDArray->_cvimresize($src, $w, $h, { interp=>$interp }); + return AI::MXNet::NDArray->_cvimresize($src, int $w, int $h, { interp=>$interp }); } method ImageIter(@args) { AI::MXNet::ImageIter->new(@args) } diff --git a/perl-package/AI-MXNet/lib/AI/MXNet/Visualization.pm b/perl-package/AI-MXNet/lib/AI/MXNet/Visualization.pm index 47905601b9b2..20811f10fedf 100644 --- a/perl-package/AI-MXNet/lib/AI/MXNet/Visualization.pm +++ b/perl-package/AI-MXNet/lib/AI/MXNet/Visualization.pm @@ -258,7 +258,6 @@ method print_summary( dot object of symbol =cut - method plot_network( AI::MXNet::Symbol $symbol, Str :$title='plot', @@ -290,7 +289,7 @@ method plot_network( ); my $dot = AI::MXNet::Visualization::PythonGraphviz->new( graph => GraphViz->new(name => $title), - format => $save_format + format => $save_format, ); # color map my @cm = ( @@ -308,7 +307,7 @@ method plot_network( my $label = $name; if($op eq 'null') { - if($name =~ /(?:_weight|_bias|_beta|_gamma|_moving_var|_moving_mean)$/) + if($name =~ /(?:_weight|_bias|_beta|_gamma|_moving_var|_moving_mean|running_var|running_mean)$/) { if($hide_weights) { @@ -321,6 +320,7 @@ method plot_network( } $attr{shape} = 'ellipse'; # inputs get their own shape $label = $name; + $label = 'plus' if $label =~ /plus\d+$/; $attr{fillcolor} = $cm[0]; } elsif($op eq 'Convolution') @@ -339,10 +339,17 @@ method plot_network( elsif($op eq 'BatchNorm') { $attr{fillcolor} = $cm[3]; + $label = $op; } elsif($op eq 'Flatten') { $label = $op; + $attr{fillcolor} = $cm[5]; + } + elsif($op eq 'elemwise_add' or $op eq 'clip' or $op eq 'Concat') + { + $label = $op; + $attr{fillcolor} = $cm[5]; } elsif($op eq 'Dropout') { @@ -351,6 +358,7 @@ method plot_network( elsif($op eq 'Reshape') { $label = "$op $node->{attrs}{shape}"; + $attr{fillcolor} = $cm[5]; } elsif($op eq 'Activation' or $op eq 'LeakyReLU') { @@ -365,10 +373,6 @@ method plot_network( $label = "Pooling\n$node->{attrs}{pool_type}, ".join('x',@k).'/'.join('x',@stride); $attr{fillcolor} = $cm[4]; } - elsif($op eq 'Concat' or $op eq 'Flatten' or $op eq 'Reshape') - { - $attr{fillcolor} = $cm[5]; - } elsif($op eq 'Softmax') { $attr{fillcolor} = $cm[6]; diff --git a/perl-package/test.sh b/perl-package/test.sh index 417e00a03cdf..87101a389480 100755 --- a/perl-package/test.sh +++ b/perl-package/test.sh @@ -30,3 +30,13 @@ make install || exit -1 cd ${MXNET_HOME}/perl-package/AI-MXNet/ perl Makefile.PL INSTALL_BASE=${MXNET_HOME}/perl5 make test TEST_VERBOSE=1 || exit -1 # Add debug output to test log +make install || exit -1 + +cd ${MXNET_HOME}/perl-package/AI-MXNet-Gluon-Contrib/ +perl Makefile.PL INSTALL_BASE=${MXNET_HOME}/perl5 +make install || exit -1 + +cd ${MXNET_HOME}/perl-package/AI-MXNet-Gluon-ModelZoo/ +perl Makefile.PL INSTALL_BASE=${MXNET_HOME}/perl5 +make test TEST_VERBOSE=1 || exit -1 + From e9bfbefd3f8dc88b4cdac223ff48bfbf2b04a9ea Mon Sep 17 00:00:00 2001 From: Sergey Kolychev Date: Tue, 10 Jul 2018 23:30:59 -0700 Subject: [PATCH 2/7] added license. --- .../examples/image_classification.pl | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/perl-package/AI-MXNet-Gluon-ModelZoo/examples/image_classification.pl b/perl-package/AI-MXNet-Gluon-ModelZoo/examples/image_classification.pl index 008b33c9fb3b..dafd5cccdcca 100755 --- a/perl-package/AI-MXNet-Gluon-ModelZoo/examples/image_classification.pl +++ b/perl-package/AI-MXNet-Gluon-ModelZoo/examples/image_classification.pl @@ -1,4 +1,21 @@ #!/usr/bin/env perl +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + use strict; use warnings; use AI::MXNet::Gluon::ModelZoo 'get_model'; From 0bc428db2241ed515684efbce54d46d93c185e97 Mon Sep 17 00:00:00 2001 From: Sergey Kolychev Date: Wed, 11 Jul 2018 09:09:13 -0700 Subject: [PATCH 3/7] a few typos/bugfix/doc improvement. --- .../AI-MXNet-Gluon-ModelZoo/examples/image_classification.pl | 2 +- .../AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo.pm | 4 +++- .../lib/AI/MXNet/Gluon/ModelZoo/Vision/AlexNet.pm | 2 +- perl-package/AI-MXNet/t/test_ndarray.t | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/perl-package/AI-MXNet-Gluon-ModelZoo/examples/image_classification.pl b/perl-package/AI-MXNet-Gluon-ModelZoo/examples/image_classification.pl index dafd5cccdcca..72d384c2e204 100755 --- a/perl-package/AI-MXNet-Gluon-ModelZoo/examples/image_classification.pl +++ b/perl-package/AI-MXNet-Gluon-ModelZoo/examples/image_classification.pl @@ -63,7 +63,7 @@ ## normalizing the image my $rgb_mean = nd->array([0.485, 0.456, 0.406])->reshape([1,3,1,1]); my $rgb_std = nd->array([0.229, 0.224, 0.225])->reshape([1,3,1,1]); -$image = $image->astype('float32') / (255 - $rgb_mean) / $rgb_std; +$image = ($image->astype('float32') / 255 - $rgb_mean) / $rgb_std; # Now we can recognize the object in the image. # We perform an additional softmax on the output to obtain probability scores. diff --git a/perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo.pm b/perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo.pm index 842687c5de00..967815c2fb6f 100644 --- a/perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo.pm +++ b/perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo.pm @@ -44,8 +44,10 @@ our $VERSION = '1.3'; =head1 DESCRIPTION - This module houses a collection of pretrained modules (the parameters are hosted on public mxnet servers). + This module houses a collection of pretrained models (the parameters are hosted on public mxnet servers). https://mxnet.incubator.apache.org/api/python/gluon/model_zoo.html + See examples/image_classification.pl for the example of real time image classification + using a pretrained model from the ModelZoo =cut our %models = qw/ diff --git a/perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo/Vision/AlexNet.pm b/perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo/Vision/AlexNet.pm index 5c24f46dbf85..48afe8b39f63 100644 --- a/perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo/Vision/AlexNet.pm +++ b/perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo/Vision/AlexNet.pm @@ -34,7 +34,7 @@ extends 'AI::MXNet::Gluon::HybridBlock'; Parameters ---------- classes : Int, default 1000 - Number of classes for the output layer-> + Number of classes for the output layer. =cut has 'classes' => (is => 'ro', isa => 'Int', default => 1000); method python_constructor_arguments() { ['classes'] } diff --git a/perl-package/AI-MXNet/t/test_ndarray.t b/perl-package/AI-MXNet/t/test_ndarray.t index 1190d52264d0..a6cd113c3f89 100644 --- a/perl-package/AI-MXNet/t/test_ndarray.t +++ b/perl-package/AI-MXNet/t/test_ndarray.t @@ -200,7 +200,7 @@ sub test_buffer_load mx->nd->save($fname, \%hash); $buf_data = join('',IO::File->new($fname)->getlines); my $hash2 = mx->nd->load_frombuffer($buf_data); - ok(%hash == %$hash2); + ok(keys %hash == keys %$hash2); while(my ($k, $v) = each %hash) { ok(same($v->aspdl, $hash2->{$k}->aspdl)); From 4dacd8de90b3b72e09098e17f0c67c4968b28ccf Mon Sep 17 00:00:00 2001 From: Sergey Kolychev Date: Thu, 12 Jul 2018 10:08:25 -0700 Subject: [PATCH 4/7] switched default images for the style transfer example for images of my cute dog. optimized image_classification.pl script a bit. --- .../examples/image_classification.pl | 9 ++--- .../examples/gluon/style_transfer/README.md | 34 ++++++++++++------- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/perl-package/AI-MXNet-Gluon-ModelZoo/examples/image_classification.pl b/perl-package/AI-MXNet-Gluon-ModelZoo/examples/image_classification.pl index 72d384c2e204..4dbf890b4ff0 100755 --- a/perl-package/AI-MXNet-Gluon-ModelZoo/examples/image_classification.pl +++ b/perl-package/AI-MXNet-Gluon-ModelZoo/examples/image_classification.pl @@ -24,10 +24,8 @@ GetOptions( ## my Pembroke Welsh Corgi Kyuubi, enjoing Solar eclipse of August 21, 2017 - 'image=s' => \(my $image = 'https://scontent-sea1-1.cdninstagram.com/vp/'. - 'c6e923f45b408fb26077bb2079e2ffa8/5BDB8A7E/'. - 't51.2885-15/e35/'. - '23734897_1382606301862187_7379024371697844224_n.jpg'), + 'image=s' => \(my $image = 'http://apache-mxnet.s3-accelerate.dualstack.amazonaws.com/'. + 'gluon/dataset/kyuubi.jpg'), 'model=s' => \(my $model = 'resnet152_v2'), 'help' => sub { HelpMessage(0) }, ) or HelpMessage(1); @@ -69,8 +67,7 @@ # We perform an additional softmax on the output to obtain probability scores. # And then print the top-5 recognized objects. my $prob = $net->($image)->softmax; -my $idxs = $prob->topk(k=>5)->[0]; -for my $idx (@{ $idxs->reshape([5]) }) +for my $idx (@{ $prob->topk(k=>5)->at(0) }) { my $i = $idx->asscalar; printf( diff --git a/perl-package/AI-MXNet/examples/gluon/style_transfer/README.md b/perl-package/AI-MXNet/examples/gluon/style_transfer/README.md index 13001b16bd77..8965af44bf4e 100644 --- a/perl-package/AI-MXNet/examples/gluon/style_transfer/README.md +++ b/perl-package/AI-MXNet/examples/gluon/style_transfer/README.md @@ -1,12 +1,14 @@ This directory provides AI::MXNet Implementation of MSG-Net real time style transfer, https://arxiv.org/abs/1703.06953 ### Stylize Images Using Pre-trained MSG-Net -Download the pre-trained model +Download the pre-trained model: + ``` ./get_data.sh ``` -Test the model +Test the model: + ``` ./style_transfer.pl --content-image --style-image < path or url> --content-size 512 ``` @@ -17,15 +19,21 @@ More options: * --style-image: path or url to style image. * --model: path to the pre-trained model to be used for stylizing the image if you use your custom model * --output-image: path for saving the output image, default is 'out.jpg' - * --content-size: the content image size to test on, default is 512 pixels for the shorter side, - decrease the size if your computer is low on RAM and the script fails + * --content-size: the output image size, default is 512 pixels for the shorter side, + decrease the size if your computer is low on RAM and the script fails. - - - - - - - - - +Pembroke Welsh Corgi Kyuubi is enjoying Total Solar Eclipse of Aug 2017 in Salem, OR +Style image: Kazimir Malevich, Black Square +Style image: random ornate stone wall image +Style image: Salvador Dali, The Enigma of Desire +Style image: Vincent van Gogh, The Starry Night From af8eaa077231c667a53e677c97fd87146f860207 Mon Sep 17 00:00:00 2001 From: Sergey Kolychev Date: Thu, 12 Jul 2018 10:25:55 -0700 Subject: [PATCH 5/7] fixed readme. --- perl-package/AI-MXNet/examples/gluon/style_transfer/README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/perl-package/AI-MXNet/examples/gluon/style_transfer/README.md b/perl-package/AI-MXNet/examples/gluon/style_transfer/README.md index 8965af44bf4e..658a77530a92 100644 --- a/perl-package/AI-MXNet/examples/gluon/style_transfer/README.md +++ b/perl-package/AI-MXNet/examples/gluon/style_transfer/README.md @@ -3,15 +3,11 @@ This directory provides AI::MXNet Implementation of MSG-Net real time style tran ### Stylize Images Using Pre-trained MSG-Net Download the pre-trained model: - ``` ./get_data.sh - ``` Test the model: - ``` ./style_transfer.pl --content-image --style-image < path or url> --content-size 512 - ``` More options: From ef96ad3c11abed719951ba70833cb273c47104fa Mon Sep 17 00:00:00 2001 From: Sergey Kolychev Date: Thu, 12 Jul 2018 19:22:19 -0700 Subject: [PATCH 6/7] using static seed in the perl space as well (shiffling in NDArrayIter) to provide consistence in the loss tests. --- perl-package/AI-MXNet/t/test_loss.t | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/perl-package/AI-MXNet/t/test_loss.t b/perl-package/AI-MXNet/t/test_loss.t index 3a136f471d7d..7fc7ee81d0de 100644 --- a/perl-package/AI-MXNet/t/test_loss.t +++ b/perl-package/AI-MXNet/t/test_loss.t @@ -26,6 +26,7 @@ use Hash::Ordered; sub test_loss_ndarray { mx->random->seed(1234); + srand(1234); my $output = mx->nd->array([1, 2, 3, 4]); my $label = mx->nd->array([1, 3, 5, 7]); my $weighting = mx->nd->array([0.5, 1, 0.5, 1]); @@ -74,6 +75,7 @@ sub get_net sub test_ce_loss { mx->random->seed(1234); + srand(1234); my $nclass = 10; my $N = 20; my $data = mx->random->uniform(-1, 1, shape=>[$N, $nclass]); @@ -96,6 +98,7 @@ test_ce_loss(); sub test_bce_loss { mx->random->seed(1234); + srand(1234); my $N = 20; my $data = mx->random->uniform(-1, 1, shape=>[$N, 20]); my $label = mx->nd->array([qw/1 1 0 1 0 0 0 1 1 1 1 1 0 0 1 0 0 0 0 0/], dtype=>'float32'); @@ -118,6 +121,7 @@ test_bce_loss(); sub test_bce_equal_ce2 { mx->random->seed(1234); + srand(1234); my $N = 100; my $loss1 = gluon->loss->SigmoidBCELoss(from_sigmoid=>1); my $loss2 = gluon->loss->SoftmaxCELoss(from_logits=>1); @@ -132,6 +136,7 @@ test_bce_equal_ce2(); sub test_kl_loss { mx->random->seed(1234); + srand(1234); my $N = 20; my $data = mx->random->uniform(-1, 1, shape=>[$N, 10]); my $label = mx->nd->softmax(mx->random->uniform(0, 1, shape=>[$N, 2])); @@ -153,6 +158,7 @@ test_kl_loss(); sub test_l2_loss { mx->random->seed(1234); + srand(1234); my $N = 20; my $data = mx->random->uniform(-1, 1, shape=>[$N, 10]); my $label = mx->nd->softmax(mx->random->uniform(-1, 1, shape=>[$N, 1])); @@ -174,6 +180,7 @@ test_l2_loss(); sub test_l1_loss { mx->random->seed(1234); + srand(1234); my $N = 20; my $data = mx->random->uniform(-1, 1, shape=>[$N, 10]); my $label = mx->nd->softmax(mx->random->uniform(-1, 1, shape=>[$N, 1])); @@ -195,6 +202,7 @@ test_l1_loss(); sub test_ctc_loss { mx->random->seed(1234); + srand(1234); my $loss = gluon->loss->CTCLoss(); my $l = $loss->(mx->nd->ones([2,20,4]), mx->nd->array([[1,0,-1,-1],[2,1,1,-1]])); ok(almost_equal($l->aspdl, mx->nd->array([18.82820702, 16.50581741])->aspdl)); @@ -225,6 +233,7 @@ test_ctc_loss(); sub test_ctc_loss_train { mx->random->seed(1234); + srand(1234); my $N = 20; my $data = mx->random->uniform(-1, 1, shape=>[$N, 20, 10]); my $label = mx->nd->arange(start => 4, repeat=>$N)->reshape([$N, 4]); @@ -247,6 +256,7 @@ test_ctc_loss_train(); sub test_sample_weight_loss { mx->random->seed(1234); + srand(1234); my $nclass = 10; my $N = 20; my $data = mx->random->uniform(-1, 1, shape=>[$N, $nclass]); @@ -288,6 +298,7 @@ test_sample_weight_loss(); sub test_saveload { mx->random->seed(1234); + srand(1234); my $nclass = 10; my $N = 20; my $data = mx->random->uniform(-1, 1, shape=>[$N, $nclass]); @@ -316,6 +327,7 @@ test_saveload(); sub test_logistic_loss_equal_bce { mx->random->seed(1234); + srand(1234); my $N = 100; my $loss_binary = gluon->loss->LogisticLoss(label_format=>'binary'); my $loss_signed = gluon->loss->LogisticLoss(label_format=>'signed'); @@ -331,6 +343,7 @@ test_logistic_loss_equal_bce(); sub test_huber_loss { mx->random->seed(1234); + srand(1234); my $N = 20; my $data = mx->random->uniform(-1, 1, shape=>[$N, 10]); my $label = mx->random->uniform(-1, 1, shape=>[$N, 1]); @@ -353,6 +366,7 @@ test_huber_loss(); sub test_hinge_loss { mx->random->seed(1234); + srand(1234); my $N = 20; my $data = mx->random->uniform(-1, 1, shape=>[$N, 10]); my $label = mx->random->uniform(-1, 1, shape=>[$N, 1]); @@ -375,6 +389,7 @@ test_hinge_loss(); sub test_squared_hinge_loss { mx->random->seed(1234); + srand(1234); my $N = 20; my $data = mx->random->uniform(-1, 1, shape=>[$N, 10]); my $label = mx->random->uniform(-1, 1, shape=>[$N, 1]); @@ -397,6 +412,7 @@ test_squared_hinge_loss(); sub test_triplet_loss { mx->random->seed(1234); + srand(1234); my $N = 20; my $data = mx->random->uniform(-1, 1, shape=>[$N, 10]); my $pos = mx->random->uniform(-1, 1, shape=>[$N, 10]); From 43fd30126fe9b5dcf7d8b1ab4dcf112ad0125456 Mon Sep 17 00:00:00 2001 From: Sergey Kolychev Date: Fri, 13 Jul 2018 11:27:32 -0700 Subject: [PATCH 7/7] additional pythonification. --- .../lib/AI/MXNet/Gluon/ModelZoo.pm | 2 ++ perl-package/AI-MXNet/lib/AI/MXNet/Gluon.pm | 8 +++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo.pm b/perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo.pm index 967815c2fb6f..64ccd4601cf4 100644 --- a/perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo.pm +++ b/perl-package/AI-MXNet-Gluon-ModelZoo/lib/AI/MXNet/Gluon/ModelZoo.pm @@ -127,4 +127,6 @@ sub get_model AI::MXNet::Gluon::ModelZoo::Vision->$sub(%kwargs); } +sub vision { 'AI::MXNet::Gluon::ModelZoo::Vision' } + 1; diff --git a/perl-package/AI-MXNet/lib/AI/MXNet/Gluon.pm b/perl-package/AI-MXNet/lib/AI/MXNet/Gluon.pm index 687cd8c3a3e4..7f92378c0823 100644 --- a/perl-package/AI-MXNet/lib/AI/MXNet/Gluon.pm +++ b/perl-package/AI-MXNet/lib/AI/MXNet/Gluon.pm @@ -35,11 +35,13 @@ sub import { my $short_name_package =<<"EOP"; package $short_name; + no warnings 'redefine'; sub data { 'AI::MXNet::Gluon::Data' } sub nn { 'AI::MXNet::Gluon::NN_' } sub rnn { 'AI::MXNet::Gluon::RNN_' } sub loss { 'AI::MXNet::Gluon::Loss_' } sub utils { 'AI::MXNet::Gluon::Utils' } + sub model_zoo { require AI::MXNet::Gluon::ModelZoo; 'AI::MXNet::Gluon::ModelZoo' } sub Trainer { shift; AI::MXNet::Gluon::Trainer->new(\@_); } sub Parameter { shift; AI::MXNet::Gluon::Parameter->new(\@_); } sub ParameterDict { shift; AI::MXNet::Gluon::ParameterDict->new(\@_); } @@ -51,4 +53,8 @@ EOP } } -1; \ No newline at end of file +sub data { 'AI::MXNet::Gluon::Data' } +sub utils { 'AI::MXNet::Gluon::Utils' } +sub model_zoo { require AI::MXNet::Gluon::ModelZoo; 'AI::MXNet::Gluon::ModelZoo' } + +1;