LEAP-Finetune

repository·main·Indexed 41 days ago

https://github.com/liquid4all/leap-finetune

A minimal open-source fine-tuning repository for LFM2 models (version 0.1.0). It provides orchestration for dataset formatting, checkpointing, and model export, with support for distributed training across Ray, SLURM, Modal, and Kubernetes. Supports various training types including SFT, DPO, KTO, GRPO, and embedding/colbert, as well as Vision-Language Model (VLM) fine-tuning.

Tokens
40.6K
Snippets
142
Records
178
Agent score
53%

What's inside LEAP-Finetune

  1. Dataset formats for VLM (Vision-Language Models)

    main

    VLM SFT

    Uses a messages format where content can include image references.

    {
      "messages": [
        { "role": "user", "content": [{ "type": "image", "image": "/path/to/image.jpg" }, { "type": "text", "text": "What do you see?" }] }
      ]
    }

    VLM DPO

    Requires training_type: "vlm_dpo". Each row needs prompt, chosen, and rejected message lists, plus an image or images column.

    {
      "image": "images/chart.png",
      "prompt": [{ "role": "user", "content": [{ "type": "text", "text": "..." }] }],
      "chosen": [{ "role": "assistant", "content": "..." }],
      "rejected": [{ "role": "assistant", "content": "..." }]
    }

    Note: Relative image paths are resolved against dataset.image_root if set.

  2. When to use RL environments (rl_env) vs Rewards (rewards)

    main

    Use the rewards: block for tasks where a completion can be scored purely as a function of its text (e.g., math, code, grounding, format, or schema).

    Use the rl_env: block for agentic, stateful, multi-turn tasks where the environment evolves based on agent actions, such as real tool use, game simulators, web browsing, or stateful feedback. rl_env uses the OpenEnv standard.

  3. Configure vLLM rollout modes for GRPO

    main

    GRPO uses vLLM for rollouts. You can choose between two main modes:

    1. Colocate Mode (vllm_mode: "colocate"): Runs vLLM inside each training worker. This is the default.
    2. Server Mode (vllm_mode: "server"): Starts trl vllm-serve on driver GPUs before Ray initializes. This is better for multi-node training if you point vllm_server_base_url to an external server.

    GPU Management: Configure GPU counts using specific keys rather than device IDs. Use server_gpus to reserve GPUs for the vLLM server and training_gpus for the training process. If server_gpus is set, the remaining GPUs are allocated to training.

    Note on Multi-node: Local server partitioning is single-node only. For multi-node, use colocate mode or an externally managed vLLM server.

    grpo_rollout:
      server_gpus: 1
      judge_gpus: 1
      tensor_parallel_size: 1
      dtype: "bfloat16"
      gpu_memory_utilization: 0.9
    
    training_config:
      extends: "DEFAULT_GRPO"
      vllm_mode: "server"
      vllm_server_host: "auto"
      vllm_server_port: 8000
  4. Dataset formats for DPO, KTO, and Retrieval

    main

    Different training types require specific JSON/JSONL data structures.

    DPO (Direct Preference Optimization)

    Uses preference columns. For multi-turn, prompt is the shared history, while chosen and rejected are the completions.

    {
      "prompt": [{"role": "user", "content": "..."}],
      "chosen": [{"role": "assistant", "content": "..."}],
      "rejected": [{"role": "assistant", "content": "..."}]
    }

    KTO (Kahneman-Tversky Optimization)

    Learns from unpaired binary feedback. Requires prompt, completion, and a boolean label (true = desirable).

    {
      "prompt": [{"role": "user", "content": "..."}],
      "completion": [{"role": "assistant", "content": "..."}],
      "label": false
    }

    Note: per_device_train_batch_size must be > 1 for KTO.

    Retrieval and Embedding Models

    Use training_type: "embedding" or training_type: "colbert". Requires query, positive, and optionally negative columns.

    {
      "query": "search text",
      "positive": "relevant document",
      "negative": "irrelevant document"
    }
  5. Format benchmark data for evaluation

    main

    Benchmark data must follow the HuggingFace messages schema. Supported formats include JSONL, JSON, Parquet, and CSV.

    Generation Benchmarks

    The last assistant turn is treated as the ground truth. Everything preceding it is the prompt.

    {
      "messages": [
        {
          "role": "user",
          "content": [
            { "type": "image", "image": "/path/to/img.jpg" },
            { "type": "text", "text": "What is shown in this image?" }
          ]
        },
        {
          "role": "assistant",
          "content": [{ "type": "text", "text": "A dog sitting on a beach." }]
        }
      ]
    }

    Logprob MCQ Benchmarks

    No assistant turn is required. The model scores each option provided in the options list by log-probability and selects the highest. The correct answer is specified by answer_id.

    {
      "messages": [
        {
          "role": "user",
          "content": [
            { "type": "image", "image": "/path/to/img.jpg" },
            { "type": "text", "text": "What animal is in this image?" }
          ]
        }
      ],
      "options": ["cat", "dog", "bird"],
      "answer_id": 1
    }
  6. Use Async Evaluation (vLLM) to prevent training pauses

    main

    By default, training pauses during evaluation (sync mode). To run benchmarks without blocking training, use the async_eval block with one of the following modes:

    ModeEnginePauses Training?Best For
    syncHF transformersYesSmall/fast eval suites
    sidecarvLLMNoTight clusters; eval is free of training cost
    reservedvLLMNoSingle-node only; uses spare GPUs for predictable latency

    Mode Details:

    • sidecar: Launches a short sbatch job per eval_steps. Scales to multi-node training.
    • reserved: Carves GPUs off the same SLURM allocation via CUDA_VISIBLE_DEVICES. Single-node only. If used in multi-node training, it will raise NotImplementedError.

    Failures in async eval are isolated; training continues unless failure.max_consecutive is reached.

    async_eval:
      mode: sidecar # sync (default) | sidecar | reserved
      vllm_gpus: 1
      tensor_parallel_size: 1
      gpu_memory_utilization: 0.9
    
      sbatch:
        time: "00:30:00"
    
      reserved:
        weight_reload: respawn
        server_port: 8100
  7. Configure GRPO training for text and VLM models

    main

    Group Relative Policy Optimization (GRPO) is supported via TRL v1's GRPOTrainer.

    To use GRPO, set the following in your configuration:

    • For text models: training_type: "grpo"
    • For vision-language models: training_type: "vlm_grpo"

    Both modes use the same YAML entrypoint as SFT/DPO, the same Ray Train launcher, and vLLM rollouts by default.

    Dataset Contract:

    • Text GRPO: Can reuse the SFT messages format. The loader constructs a prompt from non-assistant turns and a solution from the last assistant message. Native prompt and solution columns are also supported.
    • VLM GRPO: Uses the same multimodal messages shape as VLM SFT.
    • Extra Columns: Any additional dataset columns are forwarded to reward functions as keyword arguments.
    training_type: "grpo" # for text
    training_type: "vlm_grpo" # for VLM
  8. Format Tool Calling datasets

    main

    Tool calls must use LFM bracket notation in the assistant content field. Tool definitions belong in the system prompt, and responses use role: "tool".

    Required Format:

    • Assistant content: <|tool_call_start|>[func(args)]<|tool_call_end|>
    • Tool response: Do not include <|tool_response_start|> or <|tool_response_end|> markers; the LFM2 template adds these automatically.
    • Tool definitions: For LFM2, wrap definitions in <|tool_list_start|> and <|tool_list_end|>. For LFM2.5, omit them (the pipeline will auto-strip them if present).

    Example:

    {
      "messages": [
        { "role": "system", "content": "List of tools: [\"...\"]" },
        { "role": "user", "content": "What's the weather in Boston?" },
        { "role": "assistant", "content": "<|tool_call_start|>[get_weather(location=\"Boston\")]<|tool_call_end|>" },
        { "role": "tool", "content": "{\"temperature\": 72}" },
        { "role": "assistant", "content": "It's 72 F and sunny in Boston." }
      ]
    }
  9. Reference a task recipe in YAML

    main

    To use a specific task's reward logic, reference its recipe.py file and the specific Recipe class name using the following syntax in your configuration YAML:

    rewards:
      recipe: "./rewards/tasks/<task_folder>/recipe.py::<RecipeClassName>"
    rewards:
      recipe: "./rewards/tasks/vlm_grounding/recipe.py::VLMGroundingIoURecipe"
  10. Use IFEval instruction-following recipes

    main

    The IFEvalRecipe provides a dense reward in the range [0, 1], representing the fraction of supported constraints satisfied by the completion.

    Supported Constraints

    • punctuation:no_comma
    • length_constraints:number_words
    • detectable_format:number_highlighted_sections
    • count:keywords_multiple
    • counting:letter_count_in_word

    Note: Unknown constraint IDs are silently skipped. Samples with zero supported constraints return None, causing GRPO to drop them from advantage computation.

    Requirements

    • Columns: prompt (instruction) and solution (a JSON constraint spec).
    • Example Config:
    project_name: "grpo_ifeval"
    model_name: "LFM2-1.2B"
    training_type: "grpo"
    
    dataset:
      path: "/path/to/ifeval/train.parquet"
      type: "grpo"
    
    rewards:
      recipe: "./rewards/tasks/ifeval/recipe.py::IFEvalRecipe"
    
    training_config:
      extends: "DEFAULT_GRPO"
  11. Create a custom reward primitive

    main

    To create a simple reward primitive, drop a .py file in the rewards/ directory and reference it in your YAML by its function name.

    # rewards/my_primitive.py
    def my_primitive_reward(completions, **kwargs):
        contents = [c[0]["content"] if isinstance(c, list) else c for c in completions]
        return [1.0 if len(t) >= 50 else 0.0 for t in contents]
    # In your config
    rewards:
      funcs:
        - "my_primitive_reward"