Ancient
view release on metacpan or search on metacpan
t/4012-object-neuralnet.t view on Meta::CPAN
ok(defined($net3->loss_fn), 'loss singleton cached');
isa_ok($net3->act, 'NeuralNet::Activation');
isa_ok($net3->loss_fn, 'NeuralNet::Loss');
# Test forward pass
my $out = $net3->forward([0.5, 0.5]);
is(scalar(@$out), 1, 'forward produces 1 output');
ok($out->[0] >= 0 && $out->[0] <= 1, 'output in [0,1] due to sigmoid');
# Test summary
my $summary = $net3->summary;
like($summary, qr/Learning rate: 0\.5/, 'summary includes learning rate');
like($summary, qr/2 -> 4/, 'summary includes layer dimensions');
like($summary, qr/4 -> 1/, 'summary includes second layer');
};
# ============================================
# Test NeuralNet::Network training (simple linear task)
# ============================================
subtest 'NeuralNet::Network training' => sub {
require NeuralNet::Network;
# Seed random for reproducibility
srand(42);
# Create simple network
my $net = NeuralNet::Network->new(learning_rate => 0.1);
$net->add_layer(2, 2);
$net->add_layer(2, 1);
# Simple AND-like training data (easier than XOR)
my @data = (
[[0, 0], [0]],
[[0, 1], [0]],
[[1, 0], [0]],
[[1, 1], [1]],
);
# Get initial loss
my $initial_loss = 0;
for my $sample (@data) {
my $pred = $net->predict($sample->[0]);
$initial_loss += $net->loss_fn->mse->($pred, $sample->[1]);
}
# Train for several epochs
for my $epoch (1 .. 500) {
for my $sample (@data) {
$net->train_step($sample->[0], $sample->[1]);
}
}
# Get final loss
my $final_loss = 0;
for my $sample (@data) {
my $pred = $net->predict($sample->[0]);
next unless defined $pred->[0] && $pred->[0] == $pred->[0]; # skip NaN
$final_loss += $net->loss_fn->mse->($pred, $sample->[1]);
}
# Just verify training ran without crashing and loss is finite
ok(defined $final_loss, 'training completed');
ok($final_loss == $final_loss, 'final loss is not NaN'); # NaN != NaN
# Test that predict returns valid output
my $out = $net->predict([1, 1]);
ok(ref($out) eq 'ARRAY', 'predict returns array');
ok(scalar(@$out) == 1, 'predict returns correct size');
ok(defined $out->[0], 'output is defined');
};
# ============================================
# Test function-style accessors work correctly
# ============================================
subtest 'function-style accessors' => sub {
require NeuralNet::Layer;
my $layer = NeuralNet::Layer->new(
input_size => 2,
output_size => 3,
);
# Use function-style accessors (imported in the package)
package NeuralNet::Layer;
::is(input_size($layer), 2, 'function-style getter works');
# Test setter
biases($layer, [1, 2, 3]);
::is_deeply(biases($layer), [1, 2, 3], 'function-style setter works');
package main;
# Method-style still works
is($layer->input_size, 2, 'method-style getter still works');
is_deeply($layer->biases, [1, 2, 3], 'method-style sees function-style changes');
};
done_testing;
( run in 0.442 second using v1.01-cache-2.11-cpan-13bb782fe5a )