Lugh

 view release on metacpan or  search on metacpan

t/0044-full-weight-training.t  view on Meta::CPAN

    for my $step (1..3) {
        $optimizer->zero_grad();
        
        my $ctx = Lugh::Context->new(size => 64 * 1024 * 1024);
        my $logits = Lugh::Train->forward(
            inference  => $opt_inference,
            context    => $ctx,
            tokens     => [1, 72, 101],
            train_lora => 0,
            train_full => 1,
        );
        
        Lugh::Train->register_weight_tensors($logits, \@opt_weights);
        
        my $loss = Lugh::Train->cross_entropy_loss($ctx, $logits, [72, 101, 108]);
        $loss->backward();
        $optimizer->step();
    }
    
    pass('completed 3 optimizer training steps');
    
    done_testing();
};

# =============================================================================
# GRADIENT ACCUMULATION
# =============================================================================

subtest 'gradient accumulation' => sub {
    my $optimizer = Lugh::Optimizer::AdamW->new(lr => 0.01);
    $optimizer->add_param($_) for @weights;
    
    $optimizer->zero_grad();
    
    # Accumulate gradients from multiple forward/backward
    for my $i (1..3) {
        my $ctx = Lugh::Context->new(size => 64 * 1024 * 1024);
        my $logits = Lugh::Train->forward(
            inference  => $inference,
            context    => $ctx,
            tokens     => [1, 72, 101],
            train_lora => 0,
            train_full => 1,
        );
        
        Lugh::Train->register_weight_tensors($logits, \@weights);
        
        my $loss = Lugh::Train->cross_entropy_loss($ctx, $logits, [72, 101, 108]);
        lives_ok { $loss->backward() } "accumulation step $i backward";
    }
    
    lives_ok { $optimizer->step() } 'optimizer step after accumulation';
    
    done_testing();
};

# =============================================================================
# STRESS TEST - MANY ITERATIONS (REGRESSION TEST FOR MEMORY CORRUPTION)
# =============================================================================

subtest 'stress test many iterations' => sub {
    # This catches memory corruption that manifests after many iterations
    my $iterations = 50;
    
    for my $i (1..$iterations) {
        my $ctx = Lugh::Context->new(size => 64 * 1024 * 1024);
        
        my $logits = Lugh::Train->forward(
            inference  => $inference,
            context    => $ctx,
            tokens     => [1, 65, 66, 67],  # 4 tokens
            train_lora => 0,
            train_full => 1,
        );
        
        Lugh::Train->register_weight_tensors($logits, \@weights);
        
        my $loss = Lugh::Train->cross_entropy_loss($ctx, $logits, [65, 66, 67, 68]);
        
        eval { $loss->backward() };
        if ($@) {
            fail("iteration $i crashed: $@");
            last;
        }
    }
    
    pass("completed $iterations iterations without crash");
    
    done_testing();
};

sub min { $_[0] < $_[1] ? $_[0] : $_[1] }

done_testing();



( run in 0.982 second using v1.01-cache-2.11-cpan-4ab04211f4c )