Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

4.6. Programmatic API

Simple facade (LoraTrainer.open with basic args)

Same single-shard layout as ./juno lora. Train from code, then call save():

import java.nio.file.Path;

import cab.ml.juno.player.ChatModelType;
import cab.ml.juno.player.LoraTrainer;

Path model = Path.of("/path/to/model.gguf");
Path adapter = Path.of("/path/to/model.lora");

try (var trainer = LoraTrainer.open(model, adapter, /*rank*/ 8, /*alpha*/ 8f, /*lr*/ 1e-4)) {
    LoraTrainer.TrainUntilResult textResult = trainer.trainRawTextUntil(
            "Some prose to adapt style.", /*lossTarget*/ 1.8f, /*maxIters*/ 50, /*chunkTokens*/ 32);
    String modelKey = ChatModelType.fromPath(model.toString());
    LoraTrainer.TrainUntilResult qaResult = trainer.trainQaPairUntil(
            "What is my favorite color?", "Blue.", modelKey, /*lossTarget*/ 1.2f, /*maxIters*/ 50);
    trainer.save();
}

Config-based facade (preferred)

Use LoraTrainingConfig when you need control over targets, accumulation, clipping, or scheduling:

Programmatic API

import cab.ml.juno.lora.*;
import cab.ml.juno.node.*;
import cab.ml.juno.player.LoraTrainer;
import cab.ml.juno.player.LoraTrainingConfig;

// Config-based open (preferred: targets, accumulation, clipping, scheduling)
LoraTrainingConfig cfg = LoraTrainingConfig.builder()
    .rank(8).alpha(8f).learningRate(1e-4)
    .targets("qv")
    .gradientAccumulationSteps(4)
    .maxGradNorm(1.0f)
    .chunkTokens(128)
    .maxTrainTokens(0)      // 0 = unlimited
    .lrSchedule("cosine").warmupSteps(20).minLr(1e-5f)
    .loraMode("lora")       // or "dora", "qa-lora"
    .scaling("standard")    // or "rslora"
    .seed(42)
    .build();
try (LoraTrainer trainer = LoraTrainer.open(modelPath, adapterPath, cfg)) {
    trainer.trainQaPairUntil("What is my name?", "Dima", "tinyllama", 1.2f, 50);
    trainer.save();
}

// Multi-pair from a list
List<String[]> pairs = List.of(
    new String[]{"What is my name?", "Dima"},
    new String[]{"Where do I live?",  "Kyiv"}
);
try (LoraTrainer trainer = LoraTrainer.open(modelPath, adapterPath, cfg)) {
    trainer.trainQaPairsUntilResult(pairs, "tinyllama");
    trainer.save();
}

// Low-level: computeGradients + prepare + step
LoraAdapterSet adapters = LoraInitializer.create(llamaCfg, LoraProjection.qv(), 8, 8f, new Random(42));
LoraTrainableHandler handler = LoraTrainableHandler.load(modelPath, ctx, adapters);
adapters.zeroAllGrads();
LoraGradientResult r = handler.computeGradients(tokens);
LoraGradients.prepare(adapters, r.predictionCount(), 1.0f);
LoraAdamOptimizer.defaults(1e-4).step(adapters);

See also


<- 4.5 Merging Adapters  |  Table of Contents  |  4.7 Common Pitfalls ->