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 )