AI-MXNet-Gluon-ModelZoo

 view release on metacpan or  search on metacpan

lib/AI/MXNet/Gluon/ModelZoo/Vision/ResNet.pm  view on Meta::CPAN

    $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"
    <http://arxiv.org/abs/1512.03385> 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"
    <https://arxiv.org/abs/1603.05027> 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"
    <http://arxiv.org/abs/1512.03385> paper.
    ResNet V2 model from "Identity Mappings in Deep Residual Networks"
    <https://arxiv.org/abs/1603.05027> 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;



( run in 1.227 second using v1.01-cache-2.11-cpan-39bf76dae61 )