AI-Nerl
view release on metacpan or search on metacpan
lib/AI/Nerl/Network.pm view on Meta::CPAN
=head1 DESCRIPTION
=head1 METHODS
=head2 train($x,$y, %params)
Train with backpropagation using $x as input & $y as target.
$x and $y are both pdls. If there are multiple cases, each one will
occupy a column (dimension 2) of the pdl. If your dimensions are off,
you will experience an pdl error of some sort.
=head3 %params
=head4 passes
number of passes.
=head2 run($x)
$output = $nn->run($x);
=head2 cost($x,$y)
($cost,$num_correct) = $nn->cost($x,$y);
Calculate the 'cost' of the network. This is basically the difference between the
actual output ($nn->run($x)) and the the target output($y), added to the sum of
the neural weights if you're penalizing weights with lambda. The cost should
B<Always> decrease after being trained with ($x,$y).
This function returns both the cost, and the number of "correct" responses
if using output neurons for classification.
=head1 SEE ALSO
L<http://en.wikipedia.org/wiki/Feedforward_neural_network#Multi-layer_perceptron>
L<http://en.wikipedia.org/wiki/Backpropagation>
=head1 AUTHOR
Zach Morgan C<< <zpmorgan@gmail.com> >>
=head1 COPYRIGHT
Copyright 2012 by Zach Morgan
This package is free software; you can redistribute it and/or modify it under the
same terms as Perl itself.
=cut
# Simple nn with 1 hidden layer
# train with $nn->train(data,labels);
has scale_input => (
is => 'ro',
required => 0,
isa => 'Num',
default => 0,
);
# number of input,hidden,output neurons
has [qw/ l1 l2 l3 /] => (
is => 'ro',
isa => 'Int',
);
has theta1 => (
is => 'ro',
isa => 'PDL',
lazy => 1,
builder => '_mk_theta1',
);
has theta2 => (
is => 'ro',
isa => 'PDL',
lazy => 1,
builder => '_mk_theta2',
);
has b1 => (
is => 'ro',
isa => 'PDL',
lazy => 1,
builder => '_mk_b1',
);
has b2 => (
is => 'ro',
isa => 'PDL',
lazy => 1,
builder => '_mk_b2',
);
has alpha => ( #learning rate
isa => 'Num',
is => 'rw',
default => .6,
);
has lambda => (
isa => 'Num',
is => 'rw',
default => .01,
);
sub _mk_theta1{
my $self = shift;
return grandom($self->l1, $self->l2) * .01;
}
sub _mk_theta2{
my $self = shift;
return grandom($self->l2, $self->l3) * .01;
}
sub _mk_b1{
my $self = shift;
return grandom($self->l2) * .01;
}
sub _mk_b2{
my $self = shift;
return grandom($self->l3) * .01;
}
sub train{
my ($self,$x,$y, %params) = @_;
$x->sever();
my $passes = $params{passes} // 10;
if ($self->scale_input){
$x *= $self->scale_input;
}
my $num_examples = $x->dim(0);
for my $pass (1..$passes){
# warn 'blah:'. $self->theta1->slice(':,2')->flat->sum;
show784($self->theta1->slice(':,0')) if $pass%30==29 and $DEBUG;
my $delta1 = $self->theta1->copy * 0;
my $delta2 = $self->theta2->copy * 0;
my $deltab1 = $self->b1->copy * 0;
my $deltab2 = $self->b2->copy * 0;
#iterate over examples :(
for my $i (0..$num_examples-1){
my $a1 = $x(($i));
my $z2 = ($self->theta1 x $a1->transpose)->squeeze;
$z2 += $self->b1; #add bias.
my $a2 = $z2->tanh;
my $z3 = ($self->theta2 x $a2->transpose)->squeeze;
$z3 += $self->b2; #add bias.
my $a3 = $z3->tanh;
# warn $y(($i)) - $a3;
my $d3= -($y(($i)) - $a3) * tanhxderivative($a3);
#warn $d3;
$delta2 += $d3->transpose x $a2;
my $d2 = ($self->theta2->transpose x $d3->transpose)->squeeze * tanhxderivative($a2);
$delta1 += $d2->transpose x $a1;
#warn $delta2(4);
$deltab1 += $d2;
$deltab2 += $d3;
if($DEBUG==1){
warn "z2: $z2\n$z3: $z3\n";
warn "d3:$d3\n";
( run in 1.976 second using v1.01-cache-2.11-cpan-39bf76dae61 )