AI-ANN
view release on metacpan or search on metacpan
lib/AI/ANN.pm view on Meta::CPAN
delete $net->[$i]->{'done'};
delete $net->[$i]->{'state'};
}
my $progress = 0;
do {
$progress = 0;
foreach my $i (0..$lastneuron) {
if ($net->[$i]->{'done'}) {next}
if ($net->[$i]->{'object'}->ready($inputs, \@neurons)) {
my $potential = $net->[$i]->{'object'}->execute($inputs, \@neurons);
$self->{'rawpotentials'}->[$i] = $potential;
$potential = $self->{'maxvalue'} if $potential > $self->{'maxvalue'};
$potential = $self->{'minvalue'} if $potential < $self->{'minvalue'};
$potential = &{$self->{'afunc'}}($potential);
$neurons[$i] = $net->[$i]->{'state'} = $potential;
$net->[$i]->{'done'} = 1;
$progress++;
}
}
} while ($progress); # If the network is feed-forward, we are now finished.
my @notdone = grep {not (defined $net->[$_]->{'done'} &&
$net->[$_]->{'done'} == 1)} 0..$lastneuron;
my @neuronstemp = ();
if ($#notdone > 0) { #This is the part where we deal with loops and bad things
my $maxerror = 0;
my $loopcounter = 1;
while (1) {
foreach my $i (@notdone) { # Only bother iterating over the
# ones we couldn't solve exactly
# We don't care if it's ready now, we're just going to interate
# until it stabilizes.
if (not defined $neurons[$i] && $i <= $lastneuron) {
# Fixes warnings about uninitialized values, but we make
# sure $i is valid first.
$neurons[$i] = 0;
}
my $potential = $net->[$i]->{'object'}->execute($inputs, \@neurons);
$self->{'rawpotentials'}->[$i] = $potential;
$potential = &{$self->{'afunc'}}($potential);
$potential = $self->{'maxvalue'} if $potential > $self->{'maxvalue'};
$potential = $self->{'minvalue'} if $potential < $self->{'minvalue'};
$neuronstemp[$i] = $net->[$i]->{'state'} = $potential;
# We want to know the absolute change
if (abs($neurons[$i]-$neuronstemp[$i])>$maxerror) {
$maxerror = abs($neurons[$i]-$neuronstemp[$i]);
}
}
foreach my $i (0..$lastneuron) {
# Update $neurons, since that is what gets passed to execute
$neurons[$i] = $neuronstemp[$i];
}
if (($maxerror < 0.0001 && $loopcounter >= 5) || $loopcounter > 250) {last}
$loopcounter++;
$maxerror=0;
}
}
# Ok, hopefully all the neurons have happy values by now.
# Get the output values for neurons corresponding to outputneurons
my @output = map {$neurons[$_]} @{$self->{'outputneurons'}};
return \@output;
}
sub get_state {
my $self = shift;
my $net = $self->{'network'}; # For less typing
my @neurons = map {$net->[$_]->{'state'}} 0..$#{$self->{'network'}};
my @output = map {$net->[$_]->{'state'}} @{$self->{'outputneurons'}};
return $self->{'inputs'}, \@neurons, \@output;
}
sub get_internals {
my $self = shift;
my $net = $self->{'network'}; # For less typing
my $retval = [];
for (my $i = 0; $i <= $#{$self->{'network'}}; $i++) {
$retval->[$i] = { iamanoutput => 0,
inputs => $net->[$i]->{'object'}->inputs(),
neurons => $net->[$i]->{'object'}->neurons(),
eta_inputs => $net->[$i]->{'object'}->eta_inputs(),
eta_neurons => $net->[$i]->{'object'}->eta_neurons()
};
}
foreach my $i (@{$self->{'outputneurons'}}) {
$retval->[$i]->{'iamanoutput'} = 1;
}
return dclone($retval); # Dclone for safety.
}
sub readable {
my $self = shift;
my $retval = "This network has ". $self->{'inputcount'} ." inputs and ".
scalar(@{$self->{'network'}}) ." neurons.\n";
for (my $i = 0; $i <= $#{$self->{'network'}}; $i++) {
$retval .= "Neuron $i\n";
while (my ($k, $v) = each %{$self->{'network'}->[$i]->{'object'}->inputs()}) {
$retval .= "\tInput from input $k, weight is $v\n";
}
while (my ($k, $v) = each %{$self->{'network'}->[$i]->{'object'}->neurons()}) {
$retval .= "\tInput from neuron $k, weight is $v\n";
}
if (map {$_ == $i} $self->{'outputneurons'}) {
$retval .= "\tThis neuron is a network output\n";
}
}
return $retval;
}
sub backprop {
my $self = shift;
my $inputs = shift;
my $desired = shift;
my $actual = $self->execute($inputs);
my $net = $self->{'network'};
my $lastneuron = $#{$net};
my $deltas = [];
my $i = 0;
foreach my $neuron (@{$self->outputneurons()}) {
$deltas->[$neuron] = $desired->[$i] - $actual->[$i];
$i++;
}
my $progress = 0;
foreach my $neuron (reverse 0..$lastneuron) {
foreach my $i (reverse $neuron..$lastneuron) {
my $weight = $net->[$i]->{'object'}->neurons()->[$neuron];
if (defined $weight && $weight != 0 && $deltas->[$i]) {
$deltas->[$neuron] += $weight * $deltas->[$i];
}
}
} # Finished generating deltas
foreach my $neuron (0..$lastneuron) {
my $inputinputs = $net->[$neuron]->{'object'}->inputs();
my $neuroninputs = $net->[$neuron]->{'object'}->neurons();
my $dafunc = &{$self->{'dafunc'}}($self->{'rawpotentials'}->[$neuron]);
my $delta = $deltas->[$neuron] || 0;
foreach my $i (0..$#{$inputinputs}) {
$inputinputs->[$i] += $inputs->[$i]*$self->{'backprop_eta'}*$delta*$dafunc;
}
foreach my $i (0..$#{$neuroninputs}) {
$neuroninputs->[$i] += $net->[$i]->{'state'}*$self->{'backprop_eta'}*$delta*$dafunc;
}
$net->[$neuron]->{'object'}->inputs($inputinputs);
$net->[$neuron]->{'object'}->neurons($neuroninputs);
} # Finished changing weights.
}
__PACKAGE__->meta->make_immutable;
1;
__END__
=pod
=head1 NAME
AI::ANN - an artificial neural network simulator
=head1 VERSION
version 0.008
( run in 1.602 second using v1.01-cache-2.11-cpan-140bd7fdf52 )