diff --git a/docs/customizer/about.mdx b/docs/customizer/about.mdx index 68840658c2..fb99db75c8 100644 --- a/docs/customizer/about.mdx +++ b/docs/customizer/about.mdx @@ -212,7 +212,7 @@ SFT and LoRA both learn from examples of the right answer. Reinforcement learnin DPO trains on preference pairs. Each example is a prompt with a **chosen** response and a **rejected** one, and training pushes the model toward the chosen style without needing a separate reward model. Use it when you have human or model-generated preference data and want to shape tone, formatting, or helpfulness. -The main control is `ref_policy_kl_penalty` (β), which sets how far the policy may drift from the model it started as. +The main control is `ref_policy_kl_penalty` (β), which sets how far the policy may drift from the model it started as. See [DPO Customization](/documentation/customizer-reference/tutorials/dpo-customization-job). ### Group Relative Policy Optimization diff --git a/docs/customizer/grpo-training.mdx b/docs/customizer/grpo-training.mdx index 05f0a33eb1..b4e5084565 100644 --- a/docs/customizer/grpo-training.mdx +++ b/docs/customizer/grpo-training.mdx @@ -373,7 +373,7 @@ A full-weight GRPO job instead registers a new model entity, which does need its ## Key hyperparameters -All GRPO knobs live under `training`, except `policy_backend`, which is under `training.parallelism`. +All GRPO knobs live under `training`, including `policy_backend` (a sibling of `parallelism`, not a field on it). | Field | Default | Notes | | --- | --- | --- | @@ -384,7 +384,7 @@ All GRPO knobs live under `training`, except `policy_backend`, which is under `t | `overlong_filtering` | `false` | Zero the loss contribution of responses truncated at the generation cap. | | `ref_policy_kl_penalty` | `0.0` | KL coefficient against the reference policy. | | `val_at_start` | `false` | Run validation before the first step, so baseline and result come from one job. | -| `parallelism.policy_backend` | `automodel` | The NeMo-RL worker that trains the model, chosen explicitly and never inferred. `automodel` supports LoRA, `expert_parallel_size` above 1, and `automodel_kwargs`, and needs Transformer Engine (Hopper or newer). `dtensor` runs stock HuggingFace modules on PyTorch FSDP2 for pre-Hopper GPUs, and supports none of those three. Requesting an `automodel`-only feature under `dtensor` is rejected at submit, with every conflict reported at once. | +| `policy_backend` | `automodel` | The NeMo-RL worker that trains the model, chosen explicitly and never inferred. Set it on `training` (for example `training.policy_backend`), not under `training.parallelism`. `automodel` supports LoRA, `expert_parallel_size` above 1, and `automodel_kwargs`, and needs Transformer Engine (Hopper or newer). `dtensor` runs stock HuggingFace modules on PyTorch FSDP2 for pre-Hopper GPUs, and supports none of those three. Requesting an `automodel`-only feature under `dtensor` is rejected at submit, with every conflict reported at once. | | `batching_strategy` | `dynamic` | How rollouts are grouped into training micro-batches. `dynamic` fills each micro-batch to a token budget, so short rollouts share a batch instead of each paying for a full-length pad. `static` puts one rollout per slot. `sequence_packing` concatenates rollouts, and is rejected for VLM, multimodal, and context-parallel runs. | | `train_mb_tokens` | Derived | Token budget per micro-batch, read by `dynamic` and `sequence_packing`. Defaults to `max_seq_length × micro_batch_size`. | | `sequence_length_round` | `64` | Round bucketed sequence lengths up to a multiple of this. Read only by `dynamic`. | diff --git a/docs/customizer/index.mdx b/docs/customizer/index.mdx index b7a9f60aa1..3a8fe00faa 100644 --- a/docs/customizer/index.mdx +++ b/docs/customizer/index.mdx @@ -133,6 +133,13 @@ Learn how to start a SFT customization job using a custom dataset. nemo-customizer + + + +Learn how to start a DPO customization job using preference data. + +nemo-customizer dpo + diff --git a/docs/customizer/manage-customization-jobs/cancel-job.mdx b/docs/customizer/manage-customization-jobs/cancel-job.mdx index 1ad6d63472..15d0e9fc10 100644 --- a/docs/customizer/manage-customization-jobs/cancel-job.mdx +++ b/docs/customizer/manage-customization-jobs/cancel-job.mdx @@ -54,7 +54,7 @@ print(f"Updated at: {cancelled_job.updated_at}") "name": "automodel-a1b2c3d4e5f6", "workspace": "default", "id": "platform-job-2k8i3i1HqJHHPVB5M6Bk9Z", - "source": "automodel", + "source": "customization", "status": "cancelled", "spec": { "model": "default/qwen3-1.7b", diff --git a/docs/customizer/manage-customization-jobs/create-job.mdx b/docs/customizer/manage-customization-jobs/create-job.mdx index be5f46dcab..ac2397ae11 100644 --- a/docs/customizer/manage-customization-jobs/create-job.mdx +++ b/docs/customizer/manage-customization-jobs/create-job.mdx @@ -143,6 +143,15 @@ print(f"Submitted job: {job.job.name}") Knowledge distillation is an Automodel feature. Set `training.training_type` to `"distillation"` and provide a `teacher_model` that references a second Model Entity. The `model` field is the student model being trained. ```python +import os +from nemo_platform import NeMoPlatform +from nemo_automodel_plugin.schema import AutomodelJobInput + +client = NeMoPlatform( + base_url=os.environ.get("NMP_BASE_URL", "http://localhost:8080"), + workspace="default", +) + spec = AutomodelJobInput( model="default/qwen3-1.7b", # Student model dataset={"training": "default/my-training-dataset"}, diff --git a/docs/customizer/manage-customization-jobs/list-active-jobs.mdx b/docs/customizer/manage-customization-jobs/list-active-jobs.mdx index 25054f79c7..2e054030bd 100644 --- a/docs/customizer/manage-customization-jobs/list-active-jobs.mdx +++ b/docs/customizer/manage-customization-jobs/list-active-jobs.mdx @@ -6,7 +6,7 @@ title: "List Active Jobs" description: "" --- -List active customization jobs and their high-level status. Customization jobs run on the platform's Jobs service, so you list them through that service and filter by `source` and `status`. The `source` scopes results to a backend (`automodel` or `unsloth`), while `status="active"` excludes completed, failed, and cancelled jobs. Each entry includes the job definition (model, dataset, training configuration) and overall status. +List active customization jobs and their high-level status. Customization jobs run on the platform's Jobs service, so you list them through that service and filter by `source` and `status`. Platform job records use `source="customization"` for Automodel, Unsloth, and RL (the Jobs service does not store the training backend as `source`). `status="active"` excludes completed, failed, and cancelled jobs. Tell backends apart from the job name prefix (`automodel-…`, `unsloth-…`, `rl-…`) or from the backend you submitted to. Each entry includes the job definition (model, dataset, training configuration) and overall status. @@ -28,7 +28,7 @@ export NMP_BASE_URL="https://your-nmp-base-url" ## To List Active Customization Jobs -Use the SDK to list jobs, filtering by `source` to scope the results to a customization backend and by `status` to return only active jobs: +Use the SDK to list jobs, filtering by `source="customization"` and by `status` to return only active customization jobs: ```python import os @@ -40,11 +40,11 @@ client = NeMoPlatform( workspace="default", ) -# List active automodel customization jobs +# List active customization jobs jobs = client.jobs.list( workspace="default", filter={ - "source": "automodel", # Use "unsloth" for the Unsloth backend + "source": "customization", "status": "active", }, page=1, @@ -61,7 +61,7 @@ for job in jobs.data: filtered_jobs = client.jobs.list( workspace="default", filter={ - "source": "automodel", + "source": "customization", "status": "active", "project": "my-finetuning-project", }, @@ -84,7 +84,7 @@ for job in filtered_jobs.data: "id": "platform-job-QtyhRY5ub4t4tTLPY4sTkz", "name": "automodel-99da3f7c1b2e", "workspace": "default", - "source": "automodel", + "source": "customization", "created_at": "2026-02-09T22:12:45", "updated_at": "2026-02-09T22:12:45", "status": "active", @@ -126,7 +126,7 @@ for job in filtered_jobs.data: }, "sort": "created_at", "filter": { - "source": "automodel", + "source": "customization", "status": "active" }, "search": {} diff --git a/docs/customizer/models/data-format.mdx b/docs/customizer/models/data-format.mdx index fa24f93d55..96b7934f73 100644 --- a/docs/customizer/models/data-format.mdx +++ b/docs/customizer/models/data-format.mdx @@ -175,31 +175,4 @@ Each line in your JSONL file must contain a JSON object with these required fiel } ``` -### SFT Legacy Conversational - -#### Required Schema - -Each line in your JSONL file must contain a JSON object with these required fields: - -- **`system`** (string): The system message that defines the assistant's role or behavior. -- **`conversations`** (array of objects): The conversation turns between user and assistant. - - **`from`** (string): The role of the message sender (`User` or `Assistant`). - - **`value`** (string): The content of the message. - -#### Example Dataset Entry - -```json -{ - "system": "You are a helpful assistant.", - "conversations": [ - { - "from": "User", - "value": "Choose a number that is greater than 0 and less than 2." - }, - { - "from": "Assistant", - "value": "1" - } - ] -} -``` +The `system` plus `conversations: [{from, value}]` layout is **not** a supported SFT schema. Automodel detects chat data from a `messages` array (`role` / `content`), prompt/completion pairs, embedding triplets, or a custom two-column `prompt_template`. Convert conversational examples to [OpenAI Chat Model](#openai-chat-model) format before training; otherwise the job fails at start with `DatasetFormatError`. diff --git a/docs/customizer/tutorials/distillation-customization-job.ipynb b/docs/customizer/tutorials/distillation-customization-job.ipynb index 59b68cd619..bfcc3b6b8d 100644 --- a/docs/customizer/tutorials/distillation-customization-job.ipynb +++ b/docs/customizer/tutorials/distillation-customization-job.ipynb @@ -94,7 +94,9 @@ }, { "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ "import json\n", "import os\n", @@ -109,9 +111,7 @@ " base_url=NMP_BASE_URL,\n", " workspace=\"default\"\n", ")" - ], - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "markdown", @@ -126,7 +126,9 @@ }, { "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ "from datasets import load_dataset, DatasetDict\n", "\n", @@ -189,13 +191,13 @@ " sample = json.loads(f.readline())\n", " print(f\"\\nSample prompt: {sample['prompt'][:150]}...\")\n", " print(f\"Sample completion: {sample['completion']}\")" - ], - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ "DATASET_NAME = \"kd-dataset\"\n", "\n", @@ -218,9 +220,7 @@ "\n", "print(\"Uploaded files:\")\n", "print(json.dumps([f.model_dump() for f in client.files.list(fileset=DATASET_NAME, workspace=\"default\").data], indent=2))" - ], - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "markdown", @@ -244,7 +244,9 @@ }, { "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ "HF_TOKEN = os.getenv(\"HF_TOKEN\")\n", "\n", @@ -268,9 +270,7 @@ "hf_secret = create_or_get_secret(\"hf-token\", HF_TOKEN, \"HF_TOKEN\")\n", "print(\"HF_TOKEN secret:\")\n", "print(hf_secret.model_dump_json(indent=2))" - ], - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "markdown", @@ -287,7 +287,9 @@ }, { "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ "from nemo_platform.types.files import HuggingfaceStorageConfigParam\n", "\n", @@ -351,9 +353,7 @@ " model_name=\"llama-3-2-3b-teacher\",\n", " description=\"Llama 3.2 3B Instruct teacher model\",\n", ")" - ], - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "markdown", @@ -372,7 +372,9 @@ }, { "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ "from nemo_automodel_plugin.schema import AutomodelJobInput\n", "\n", @@ -403,13 +405,13 @@ "TRAINED_TEACHER_NAME = TEACHER_OUTPUT_NAME\n", "print(f\"Teacher training job: {teacher_job.job.name}\")\n", "print(f\"Output teacher model: {TRAINED_TEACHER_NAME}\")\n" - ], - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ "from IPython.display import clear_output\n", "\n", @@ -445,9 +447,7 @@ "\n", "teacher_status = wait_for_job(TEACHER_JOB_NAME)\n", "assert teacher_status.status == \"completed\"" - ], - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "markdown", @@ -464,7 +464,9 @@ }, { "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ "baseline_suffix = uuid.uuid4().hex[:4]\n", "BASELINE_DEPLOYMENT_CONFIG = f\"baseline-student-cfg-{baseline_suffix}\"\n", @@ -492,13 +494,13 @@ ")\n", "\n", "print(f\"Baseline student deployment: {baseline_deployment.name}\")\n" - ], - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ "def wait_for_deployment(deployment_name: str, timeout_minutes: int = 30):\n", " \"\"\"Poll deployment until ready.\"\"\"\n", @@ -553,9 +555,7 @@ " except Exception as cleanup_error:\n", " print(f\"Baseline cleanup after readiness failure also failed: {cleanup_error}\")\n", " raise" - ], - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "markdown", @@ -566,7 +566,9 @@ }, { "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ "with open(f\"{DATASET_PATH}/testing.jsonl\", \"r\", encoding=\"utf-8\") as f:\n", " test_data = [json.loads(line) for line in f]\n", @@ -579,13 +581,13 @@ "print(f\"Sample context: {contexts[0]}\")\n", "print(f\"Sample question: {questions[0]}\")\n", "print(f\"Sample reference: {reference_completions[0]}\")" - ], - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ "def generate_completions(\n", " deployment_name: str,\n", @@ -621,9 +623,7 @@ "baseline_completions = generate_completions(BASELINE_DEPLOYMENT_NAME, student_model.name, contexts, questions)\n", "print(f\"Generated {len(baseline_completions)} baseline predictions\")\n", "print(f\"\\nSample baseline output: {baseline_completions[0]}\")" - ], - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "markdown", @@ -636,7 +636,9 @@ }, { "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ "client.inference.deployments.delete(name=BASELINE_DEPLOYMENT_NAME, workspace=\"default\")\n", "print(f\"Deleted baseline deployment: {BASELINE_DEPLOYMENT_NAME}\")\n", @@ -653,9 +655,7 @@ "\n", "client.inference.deployment_configs.delete(name=BASELINE_DEPLOYMENT_CONFIG, workspace=\"default\")\n", "print(f\"Deleted baseline deployment config: {BASELINE_DEPLOYMENT_CONFIG}\")" - ], - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "markdown", @@ -686,7 +686,9 @@ }, { "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ "from nemo_automodel_plugin.schema import AutomodelJobInput\n", "\n", @@ -719,9 +721,7 @@ "DISTILLED_STUDENT_NAME = KD_OUTPUT_NAME\n", "print(f\"Distillation job: {kd_job.job.name}\")\n", "print(f\"Output student model: {DISTILLED_STUDENT_NAME}\")\n" - ], - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "markdown", @@ -732,13 +732,13 @@ }, { "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ "kd_status = wait_for_job(KD_JOB_NAME)\n", "assert kd_status.status == \"completed\"" - ], - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "markdown", @@ -755,7 +755,9 @@ }, { "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ "deploy_suffix_2 = uuid.uuid4().hex[:4]\n", "STUDENT_DEPLOYMENT_CONFIG = f\"kd-student-deploy-cfg-{deploy_suffix_2}\"\n", @@ -783,18 +785,16 @@ ")\n", "\n", "print(f\"Student deployment: {student_deployment.name}\")\n" - ], - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ "wait_for_deployment(STUDENT_DEPLOYMENT_NAME)" - ], - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "markdown", @@ -805,15 +805,15 @@ }, { "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ "print(\"Generating distilled student predictions...\")\n", "student_completions = generate_completions(STUDENT_DEPLOYMENT_NAME, DISTILLED_STUDENT_NAME, contexts, questions)\n", "print(f\"Generated {len(student_completions)} student predictions\")\n", "print(f\"\\nSample student output: {student_completions[0]}\")" - ], - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "markdown", @@ -826,7 +826,9 @@ }, { "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ "import evaluate\n", "\n", @@ -846,13 +848,13 @@ "print(separator)\n", "print(f\"{'Base Student (1B, no training)':<35} \" + \" \".join(f\"{baseline_scores[m]:>10.4f}\" for m in metrics))\n", "print(f\"{'Distilled Student (1B, KD)':<35} \" + \" \".join(f\"{student_scores[m]:>10.4f}\" for m in metrics))" - ], - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ "print(\"Sample predictions (first 3):\\n\")\n", "for i in range(min(3, len(contexts))):\n", @@ -862,9 +864,7 @@ " print(f\"Baseline: {baseline_completions[i][:200]}\")\n", " print(f\"Distilled: {student_completions[i][:200]}\")\n", " print()" - ], - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "markdown", @@ -922,7 +922,8 @@ "\n", "**Deployment fails:**\n", "- Verify output model exists: `client.models.retrieve(name=DISTILLED_STUDENT_NAME, workspace=\"default\")`\n", - "- Check deployment logs: `client.inference.deployments.get_logs(name=deployment.name, workspace=\"default\")`\n", + "- Check job logs: `client.jobs.get_logs(name=kd_job.job.name, workspace=\"default\")`\n", + "- Check student deployment status: `client.inference.deployments.retrieve(name=student_deployment.name, workspace=\"default\")`\n", "- The distilled model has the same size as the student, so GPU requirements match the student model\n", "\n", "\n", @@ -956,4 +957,4 @@ }, "nbformat": 4, "nbformat_minor": 4 -} \ No newline at end of file +} diff --git a/docs/customizer/tutorials/distillation-customization-job.mdx b/docs/customizer/tutorials/distillation-customization-job.mdx index 77bf3a21c2..359251c933 100644 --- a/docs/customizer/tutorials/distillation-customization-job.mdx +++ b/docs/customizer/tutorials/distillation-customization-job.mdx @@ -1,7 +1,6 @@ --- # SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 - title: "Knowledge Distillation Customization" description: "" --- @@ -127,17 +126,20 @@ train_ds = split['train'].select(range(min(TRAINING_SIZE, len(split['train'])))) val_ds = split['test'].select(range(min(VALIDATION_SIZE, len(split['test'])))) test_ds = split['test'].select(range(VALIDATION_SIZE, min(VALIDATION_SIZE + TEST_SIZE, len(split['test'])))) + def convert_squad(example): """Convert SQuAD format to prompt/completion format.""" prompt = f"Context: {example['context']} Question: {example['question']} Answer:" completion = example["answers"]["text"][0] return {"prompt": prompt, "completion": completion} + def write_jsonl(dataset, path): with open(path, "w", encoding="utf-8") as f: for example in dataset: f.write(json.dumps(convert_squad(example)) + "\n") + def write_test_jsonl(dataset, path): """Save test split with raw context/question for chat-style evaluation.""" with open(path, "w", encoding="utf-8") as f: @@ -148,6 +150,7 @@ def write_test_jsonl(dataset, path): "completion": example["answers"]["text"][0], }) + "\n") + write_jsonl(train_ds, f"{DATASET_PATH}/training.jsonl") write_jsonl(val_ds, f"{DATASET_PATH}/validation.jsonl") write_test_jsonl(test_ds, f"{DATASET_PATH}/testing.jsonl") @@ -204,6 +207,7 @@ Both models share the same tokenizer/vocabulary (required for knowledge distilla ```python HF_TOKEN = os.getenv("HF_TOKEN") + def create_or_get_secret(name: str, value: str | None, label: str): if not value: raise ValueError(f"{label} is not set") @@ -219,6 +223,7 @@ def create_or_get_secret(name: str, value: str | None, label: str): print(f"Secret '{name}' already exists, continuing...") return client.secrets.retrieve(name=name, workspace="default") + hf_secret = create_or_get_secret("hf-token", HF_TOKEN, "HF_TOKEN") print("HF_TOKEN secret:") print(hf_secret.model_dump_json(indent=2)) @@ -237,6 +242,7 @@ from nemo_platform.types.files import HuggingfaceStorageConfigParam SPEC_TIMEOUT_SECONDS = 120 + def create_model(hf_repo: str, model_name: str, description: str): """Create a fileset + model entity and wait for ModelSpec.""" try: @@ -280,6 +286,7 @@ def create_model(hf_repo: str, model_name: str, description: str): print(f"ModelSpec populated: {model.spec}") return model + student_model = create_model( hf_repo="meta-llama/Llama-3.2-1B-Instruct", model_name="llama-3-2-1b-student", @@ -341,6 +348,7 @@ print(f"Output teacher model: {TRAINED_TEACHER_NAME}") ```python from IPython.display import clear_output + def wait_for_job(job_name: str): """Poll job status until completion.""" while True: @@ -369,6 +377,7 @@ def wait_for_job(job_name: str): time.sleep(10) + teacher_status = wait_for_job(TEACHER_JOB_NAME) assert teacher_status.status == "completed" ``` @@ -444,6 +453,7 @@ def wait_for_deployment(deployment_name: str, timeout_minutes: int = 30): raise TimeoutError(f"Deployment timeout after {timeout_minutes} minutes") time.sleep(15) + try: dep_status = wait_for_deployment(BASELINE_DEPLOYMENT_NAME) assert dep_status.status == "READY" @@ -512,6 +522,7 @@ def generate_completions( completions.append(response["choices"][0]["message"]["content"]) return completions + print("Generating baseline (base student) predictions...") baseline_completions = generate_completions(BASELINE_DEPLOYMENT_NAME, student_model.name, contexts, questions) print(f"Generated {len(baseline_completions)} baseline predictions") @@ -738,9 +749,11 @@ KD loads both models, so OOM is more likely than with SFT: **Deployment fails:** - Verify output model exists: `client.models.retrieve(name=DISTILLED_STUDENT_NAME, workspace="default")` -- Check deployment logs: `client.inference.deployments.get_logs(name=deployment.name, workspace="default")` +- Check job logs: `client.jobs.get_logs(name=kd_job.job.name, workspace="default")` +- Check student deployment status: `client.inference.deployments.retrieve(name=student_deployment.name, workspace="default")` - The distilled model has the same size as the student, so GPU requirements match the student model + ## Next Steps - [Monitor training metrics](/documentation/customizer-reference/tutorials/metrics) in detail diff --git a/docs/customizer/tutorials/dpo-customization-job.ipynb b/docs/customizer/tutorials/dpo-customization-job.ipynb index 5c8437dfcb..5aead80134 100644 --- a/docs/customizer/tutorials/dpo-customization-job.ipynb +++ b/docs/customizer/tutorials/dpo-customization-job.ipynb @@ -26,7 +26,7 @@ "Before starting this tutorial, ensure you have:\n", "\n", "1. **Completed the [Quickstart](/documentation/get-started)** to install the NeMo Platform and Python SDK.\n", - "2. **Installed the Python SDK** (PyPI wrapper: `pip install \"nemo-platform[all]\"`; source checkout: run `make bootstrap` from the repository root).\n", + "2. **Installed the Python SDK** (PyPI wrapper: `pip install \"nemo-platform[all,nemo-rl-plugin]\"` so `RlJobInput` is available; source checkout: run `make bootstrap` from the repository root). `nemo-platform[all]` does not include the RL plugin.\n", "3. **Installed the `datasets` package**: `pip install datasets`.\n", "4. **A platform configured with `platform.runtime: kubernetes`.** The `rl` (DPO) backend provisions a Ray cluster and has **no local Docker fallback** — `submit` fails fast on a Docker-runtime platform. Multi-node jobs (`parallelism.num_nodes > 1`) additionally require the platform-side `NMP_RL_MULTINODE_SHARED_STORAGE_PATH`.\n", "5. **A Hugging Face token** with access to the gated base model (this tutorial uses `meta-llama/Llama-3.2-1B-Instruct`). Export it as `HF_TOKEN`.\n", diff --git a/docs/customizer/tutorials/dpo-customization-job.mdx b/docs/customizer/tutorials/dpo-customization-job.mdx index 254dca67bf..beee8a285c 100644 --- a/docs/customizer/tutorials/dpo-customization-job.mdx +++ b/docs/customizer/tutorials/dpo-customization-job.mdx @@ -1,12 +1,360 @@ --- # SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 - title: "DPO Customization" description: "" --- - +[Run in Google Colab](https://colab.research.google.com/github/NVIDIA-NeMo/nemo-platform/blob/release/0.5/docs/customizer/tutorials/dpo-customization-job.ipynb) + +Learn how to use the NeMo Platform to align a model with **DPO** (Direct Preference Optimization) on a preference dataset. For each prompt, DPO trains on a *chosen* (preferred) and a *rejected* response so the model prefers the chosen style — no separate reward model required. + +This tutorial uses the `rl` customization backend (powered by [NVIDIA NeMo-RL](https://github.com/NVIDIA-NeMo/RL)), which runs DPO on a **Ray** cluster. Unlike the [SFT](/documentation/customizer-reference/tutorials/sft-customization-job) and [LoRA](/documentation/customizer-reference/tutorials/lora-customization-job) tutorials (Docker GPU jobs), `rl` requires a **Kubernetes-backed** NeMo Platform. DPO here is **full-weight** (no LoRA/adapter); the output is a full model entity. + +**Time to complete:** approximately 45-60 minutes. Job duration increases with model and dataset size. + +## Prerequisites + +Before starting this tutorial, ensure you have: + +1. **Completed the [Quickstart](/documentation/get-started)** to install the NeMo Platform and Python SDK. +2. **Installed the Python SDK** (PyPI wrapper: `pip install "nemo-platform[all,nemo-rl-plugin]"` so `RlJobInput` is available; source checkout: run `make bootstrap` from the repository root). `nemo-platform[all]` does not include the RL plugin. +3. **Installed the `datasets` package**: `pip install datasets`. +4. **A platform configured with `platform.runtime: kubernetes`.** The `rl` (DPO) backend provisions a Ray cluster and has **no local Docker fallback** — `submit` fails fast on a Docker-runtime platform. Multi-node jobs (`parallelism.num_nodes > 1`) additionally require the platform-side `NMP_RL_MULTINODE_SHARED_STORAGE_PATH`. +5. **A Hugging Face token** with access to the gated base model (this tutorial uses `meta-llama/Llama-3.2-1B-Instruct`). Export it as `HF_TOKEN`. +6. **At least one GPU with CUDA 13+** and a GPU execution profile (`nemo jobs list-execution-profiles`). + +## Quick Start + +### 1. Initialize the SDK + +The SDK needs your NeMo Platform server URL. By default `http://localhost:8080` is used; set `NMP_BASE_URL` to override: + +```sh +export NMP_BASE_URL= +``` + +```python +import json +import os +import time +import uuid +from pathlib import Path +from nemo_platform import NeMoPlatform, ConflictError +from nemo_platform.types.secrets import PlatformSecretResponse +from nemo_platform.types.files import HuggingfaceStorageConfigParam +from nemo_rl_plugin.schema import RlJobInput + + +def max_wait_time_checker(seconds: int, label: str = ""): + """Return a check() that raises TimeoutError once `seconds` have elapsed.""" + start = time.time() + + def check(): + if time.time() - start > seconds: + raise TimeoutError(f"{label} took longer than {seconds} seconds") + + return check + + +NMP_BASE_URL = os.environ.get("NMP_BASE_URL", "http://localhost:8080") +sdk = NeMoPlatform(base_url=NMP_BASE_URL, workspace="default") +``` + +### 2. Prepare the Preference Dataset + +DPO trains on **preference data**. The `rl` backend takes a **single** dataset fileset that holds both `training.jsonl` and `validation.jsonl`, and auto-detects the row schema from the first line. Three preference formats are supported (see the platform's `BinaryPreferenceDatasetItemSchema` / `HelpSteer3DatasetItemSchema` / `Tulu3PreferenceDatasetItemSchema`): + +#### Binary Preference Format + +Simple `prompt` / `chosen` / `rejected` (the `prompt` may be a string or a list of chat messages): + +```json +{"prompt": "What is the capital of France?", "chosen": "The capital of France is Paris.", "rejected": "I'm not sure."} +``` + +#### HelpSteer3 Format (used here) + +A conversation `context` (string or chat messages), two candidate `response1` / `response2`, and a signed `overall_preference` in -3..3 — **negative** means response 1 is preferred, **positive** means response 2, **0** is a tie. This is the **raw** schema of `nvidia/HelpSteer3`, so no conversion is needed: + +```json +{"context": [{"role": "user", "content": "Explain how to use git rebase"}], "response1": "...", "response2": "...", "overall_preference": -2} +``` + +#### Tulu3 Preference Format + +Full chat conversations for both the chosen and rejected branches (each a list of messages ending with the assistant turn): + +```json +{"chosen": [{"role": "user", "content": "..."}, {"role": "assistant", "content": "preferred"}], "rejected": [{"role": "user", "content": "..."}, {"role": "assistant", "content": "dispreferred"}]} +``` + +#### Download nvidia/HelpSteer3 + +We use [nvidia/HelpSteer3](https://huggingface.co/datasets/nvidia/HelpSteer3) (the `preference` subset), NVIDIA's open preference dataset. It ships native `train` and `validation` splits and matches the HelpSteer3 schema above, so we upload the rows **as-is** — the platform's `HelpSteer3Dataset` loader handles the `overall_preference` semantics (including ties) at training time. + +```python +from datasets import load_dataset, Dataset + +print("Loading dataset nvidia/HelpSteer3 (preference subset)") +ds = load_dataset("nvidia/HelpSteer3", "preference") + +# Small subsets keep the tutorial fast; larger sets train better but take longer. +training_size = 3000 +validation_size = 300 +DATASET_NAME = "dpo-dataset" +DATASET_PATH = Path("dpo-dataset").absolute() +os.makedirs(DATASET_PATH, exist_ok=True) + +train_dataset = ds["train"] +validation_dataset = ds["validation"] +assert isinstance(train_dataset, Dataset) and isinstance(validation_dataset, Dataset) + +# Save raw HelpSteer3 rows directly — no conversion. The platform detects the +# HelpSteer3 schema from the row keys (context / response1 / response2 / overall_preference). +train_dataset.select(range(training_size)).to_json(f"{DATASET_PATH}/training.jsonl") +validation_dataset.select(range(validation_size)).to_json(f"{DATASET_PATH}/validation.jsonl") + +print(f"Saved training.jsonl ({training_size} rows) and validation.jsonl ({validation_size} rows)") +with open(f"{DATASET_PATH}/training.jsonl") as f: + sample = json.loads(f.readline()) +print("Sample keys:", sorted(sample.keys())) +print("overall_preference:", sample["overall_preference"]) +``` + +### 3. Create FileSet and Upload Preference Data + +Upload both JSONL files to a single FileSet so the DPO job can read them. + +```python +try: + sdk.files.filesets.create(workspace="default", name=DATASET_NAME, description="DPO preference data") + print(f"Created fileset: {DATASET_NAME}") +except ConflictError: + print(f"Fileset '{DATASET_NAME}' already exists, continuing...") + +sdk.files.upload(local_path=DATASET_PATH, remote_path="", fileset=DATASET_NAME, workspace="default") + +print("Preference data:") +print(json.dumps([f.model_dump() for f in sdk.files.list(fileset=DATASET_NAME, workspace="default").data], indent=2, default=str)) +``` + +### 4. Secrets Setup + +The base model (`meta-llama/Llama-3.2-1B-Instruct`) is gated, so store your Hugging Face token as a platform secret named `hf-token` and reference it on the model fileset. + +```python +HF_TOKEN = os.getenv("HF_TOKEN") +if not HF_TOKEN: + raise RuntimeError("Set HF_TOKEN before running this tutorial.") + +def create_or_get_secret(name: str, value: str, label: str) -> PlatformSecretResponse: + try: + secret = sdk.secrets.create(name=name, workspace="default", value=value) + print(f"Created secret: {name}") + return secret + except ConflictError: + print(f"Secret '{name}' already exists, continuing...") + return sdk.secrets.retrieve(name=name, workspace="default") + + +hf_secret = create_or_get_secret("hf-token", HF_TOKEN, "HF_TOKEN") +``` + +### 5. Create Base Model FileSet and Model Entity + +DPO starts from an instruction-tuned base model. The model entity's spec is inferred asynchronously after creation. + +```python +HF_REPO_ID = "meta-llama/Llama-3.2-1B-Instruct" +MODEL_NAME = "llama-3-2-1b-instruct" + +storage = HuggingfaceStorageConfigParam( + type="huggingface", + repo_id=HF_REPO_ID, + repo_type="model", + token_secret=hf_secret.name, +) + +try: + base_model_fs = sdk.files.filesets.create( + workspace="default", name=MODEL_NAME, description="Llama 3.2 1B Instruct base model", storage=storage + ) + print(f"Created base model fileset: {MODEL_NAME}") +except ConflictError: + base_model_fs = sdk.files.filesets.retrieve(workspace="default", name=MODEL_NAME) + print("Base model fileset already exists.") + +try: + base_model = sdk.models.create(workspace="default", name=MODEL_NAME, fileset=f"default/{MODEL_NAME}") +except ConflictError: + base_model = sdk.models.retrieve(workspace="default", name=MODEL_NAME) + +print(f"Base model fileset: fileset://default/{base_model.name}") + +# Wait for the ModelSpec to be inferred from the checkpoint. +check = max_wait_time_checker(600, "Model spec") +while not base_model.spec: + check() + time.sleep(10) + base_model = sdk.models.retrieve(workspace="default", name=MODEL_NAME) +print("Model spec ready") +``` + +### 6. Create the DPO Customization Job + +Submit a DPO job to the `rl` backend with `RlJobInput`. Note the DPO-specific shape: + +- `model` is a string ref to the model entity; `dataset` is a **single** string ref to the preference fileset (holding both files). +- The training method is `{"type": "dpo", ...}` — full-weight, no `finetuning_type`/LoRA. +- `ref_policy_kl_penalty` is **β** (DPO paper): how strongly the policy stays tied to the reference model. +- `rl` auto-generates the job id (`rl-`); read it back from the response. + +Other configurable knobs: `optimizer_type`, `adam_eps`, `activation_checkpointing`, `keep_top_k`, `val_at_end`, `preference_loss_weight`, `sft_loss_weight`. Run `nemo customization rl explain` for the live schema. + +```python +job_suffix = uuid.uuid4().hex[:8] +OUTPUT_NAME = f"llama-3-2-1b-dpo-{job_suffix}" + +spec = RlJobInput( + model=f"default/{base_model.name}", + dataset=f"default/{DATASET_NAME}", + training={ + "type": "dpo", + "epochs": 1, + "batch_size": 16, + "micro_batch_size": 1, + "learning_rate": 5e-6, + "max_seq_length": 4096, + "ref_policy_kl_penalty": 0.1, + "parallelism": { + "num_nodes": 1, + "num_gpus_per_node": 1, + "tensor_parallel_size": 1, + "pipeline_parallel_size": 1, + }, + }, + output={"name": OUTPUT_NAME}, +) + +# `rl` auto-generates the job id (rl-); do not pass name=. +job = sdk.customization.rl.jobs.create(spec=spec, workspace="default") +print(f"Job ID: {job.job.name}") +print(f"Output model: {OUTPUT_NAME}") +``` + +### 7. Track Training Progress + +The DPO job runs four steps: download -> **dpo-training** (Ray) -> upload -> model-entity. We poll the top-level job status and surface the training step's progress. + +```python +from IPython.display import clear_output + +check = max_wait_time_checker(7200, "DPO job") +while True: + check() + status = sdk.jobs.get_status(name=job.job.name, workspace="default") + clear_output(wait=True) + print(f"Job Status: {status.status}") + + step = max_steps = phase = None + for job_step in status.steps or []: + if job_step.name == "dpo-training": + for task in job_step.tasks or []: + d = task.status_details or {} + step, max_steps, phase = d.get("step"), d.get("max_steps"), d.get("phase") + break + break + if step is not None and max_steps: + print(f"Training: Step {step}/{max_steps} ({100 * step / max_steps:.1f}%)") + if phase: + print(f"Phase: {phase}") + + if status.status in ("completed", "failed", "cancelled", "error"): + print(f"\nJob finished: {status.status}") + break + time.sleep(15) + +assert status.status == "completed" +``` + +**Interpreting DPO training metrics** (in `status_details.metrics`): + +- **`loss`** — the DPO loss; should trend down as the policy learns to separate chosen from rejected. +- **Reward margin** (chosen minus rejected reward) — should trend **up**: the model increasingly prefers chosen responses. +- **Validation `loss`** — watch for divergence from training loss (overfitting). Raise `ref_policy_kl_penalty` (β) or add `sft_loss_weight` if the policy drifts too far from the reference. + +### 8. Validate the Output Model + +DPO produces a **full-weight model entity** (not an adapter). Confirm it was registered. + +```python +model_entity = sdk.models.retrieve(workspace="default", name=OUTPUT_NAME) +print(model_entity.model_dump_json(indent=2)) +``` + +### 9. Deploy and Evaluate (optional) + +The DPO output is a full model, so it deploys like any full-weight checkpoint (see the [Full SFT](/documentation/customizer-reference/tutorials/sft-customization-job) tutorial for details). We deploy with vLLM and send a chat completion. + +```python +deploy_suffix = uuid.uuid4().hex[:8] +DEPLOYMENT_CONFIG_NAME = f"dpo-deployment-cfg-{deploy_suffix}" +DEPLOYMENT_NAME = f"dpo-deployment-{deploy_suffix}" + +deployment_config = sdk.inference.deployment_configs.create( + workspace="default", + name=DEPLOYMENT_CONFIG_NAME, + engine="vllm", + model_spec={"model_namespace": "default", "model_name": OUTPUT_NAME}, + executor_config={"gpu": 1, "image_name": "vllm/vllm-openai", "image_tag": "v0.22.1"}, +) + +deployment = sdk.inference.deployments.create( + workspace="default", name=DEPLOYMENT_NAME, config=deployment_config.name +) +print(f"Deployment name: {deployment.name}") +``` + +```python +check = max_wait_time_checker(1800, "Deployment") +while True: + check() + deployment_status = sdk.inference.deployments.retrieve(name=deployment.name, workspace="default") + clear_output(wait=True) + print(f"Deployment status: {deployment_status.status}") + deployment_state = str(deployment_status.status).lower() + if deployment_state in ("ready", "running"): + if not sdk.models.wait_for_gateway(deployment.name, workspace="default", timeout=60): + raise RuntimeError("Inference gateway did not become ready") + break + if deployment_state in ("failed", "error", "terminated", "lost"): + raise RuntimeError(f"Deployment failed with status: {deployment_status.status}") + time.sleep(15) +``` + +```python +messages = [ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "user", "content": "Write a short, friendly email to a colleague asking to reschedule our meeting to Thursday."}, +] + +response = sdk.inference.gateway.provider.post( + "v1/chat/completions", + name=deployment.name, + workspace="default", + body={"model": f"default/{OUTPUT_NAME}", "messages": messages, "temperature": 0.7, "max_tokens": 256}, +) +print("Model output:\n") +print(response["choices"][0]["message"]["content"]) +``` + +## Conclusion + +You aligned a base model with **DPO** on the NeMo Platform using the `rl` backend: + +- Uploaded a HelpSteer3 preference dataset **as-is** (the platform detects the schema natively). +- Submitted a full-weight DPO job that ran on a Ray cluster via the Kubernetes executor. +- Registered the output as a full model entity and (optionally) deployed it for inference. + +**Next steps:** tune the alignment strength with `ref_policy_kl_penalty` (β), add `sft_loss_weight` to anchor the policy to the chosen responses, enable `activation_checkpointing` for memory headroom, or scale up with `parallelism`. See the [Training Configuration](/documentation/customizer-reference/manage-customization-jobs/training-configuration) reference for the full hyperparameter set. diff --git a/docs/customizer/tutorials/embedding-customization-job.ipynb b/docs/customizer/tutorials/embedding-customization-job.ipynb index b552332593..bd2ee1e759 100644 --- a/docs/customizer/tutorials/embedding-customization-job.ipynb +++ b/docs/customizer/tutorials/embedding-customization-job.ipynb @@ -105,14 +105,15 @@ ] }, { - "cell_type": "code", + "cell_type": "markdown", "metadata": {}, "source": [ - "# Install required packages for dataset preparation\n", - "%pip install -q datasets huggingface_hub" - ], - "execution_count": null, - "outputs": [] + "Install required packages for dataset preparation if they are not installed in your Python environment:\n", + "\n", + "```sh\n", + "pip install datasets huggingface_hub\n", + "```" + ] }, { "cell_type": "code", diff --git a/docs/customizer/tutorials/embedding-customization-job.mdx b/docs/customizer/tutorials/embedding-customization-job.mdx index 84d52077e5..5e970005c4 100644 --- a/docs/customizer/tutorials/embedding-customization-job.mdx +++ b/docs/customizer/tutorials/embedding-customization-job.mdx @@ -1,7 +1,6 @@ --- # SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 - title: "Embedding Model Customization" description: "" --- @@ -82,9 +81,10 @@ Before fine-tuning, establish baseline performance with the pretrained model. De - **Trap:** "Random Forests" shares the word "random" but is an unrelated tree-based algorithm - **Goal:** The model should distinguish between them. -```python -# Install required packages for dataset preparation -%pip install -q datasets huggingface_hub +Install required packages for dataset preparation if they are not installed in your Python environment: + +```sh +pip install datasets huggingface_hub ``` ```python @@ -188,6 +188,7 @@ while True: time.sleep(10) + ``` ```python @@ -371,6 +372,7 @@ This tutorial fine-tunes [nvidia/llama-nemotron-embed-1b-v2](https://huggingface # Note: NGC_API_KEY secret was already created in the baseline step (Step 2) HF_TOKEN = os.getenv("HF_TOKEN") + def create_or_get_secret(name: str, value: str | None, label: str): if not value: raise ValueError(f"{label} is not set") @@ -386,6 +388,7 @@ def create_or_get_secret(name: str, value: str | None, label: str): print(f"Secret '{name}' already exists, continuing...") return client.secrets.retrieve(name=name, workspace="default") + # Public Hugging Face models need no token. Create a secret only when HF_TOKEN is set. hf_secret = create_or_get_secret("hf-token", HF_TOKEN, "HF_TOKEN") if HF_TOKEN else None if hf_secret: diff --git a/docs/customizer/tutorials/index.mdx b/docs/customizer/tutorials/index.mdx index 455bba6f24..b1cfe1b9b2 100644 --- a/docs/customizer/tutorials/index.mdx +++ b/docs/customizer/tutorials/index.mdx @@ -67,6 +67,13 @@ Learn how to perform supervised fine-tuning using custom data by modifying all t nemo-customizer + + + +Learn how to run a DPO customization job with preference pairs on the RL backend. + +nemo-customizer dpo + diff --git a/docs/customizer/tutorials/sft-customization-job.ipynb b/docs/customizer/tutorials/sft-customization-job.ipynb index 91d3c25899..a7a0146b32 100644 --- a/docs/customizer/tutorials/sft-customization-job.ipynb +++ b/docs/customizer/tutorials/sft-customization-job.ipynb @@ -79,7 +79,9 @@ }, { "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ "import json\n", "import os\n", @@ -90,9 +92,7 @@ " base_url=NMP_BASE_URL,\n", " workspace=\"default\"\n", ")" - ], - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "markdown", @@ -201,7 +201,9 @@ }, { "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ "from pathlib import Path\n", "from datasets import load_dataset, DatasetDict\n", @@ -265,13 +267,13 @@ " sample = json.loads(first_line)\n", " print(f\"Prompt: {sample['prompt'][:200]}...\")\n", " print(f\"Completion: {sample['completion']}\")" - ], - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ "# Create fileset to store SFT training data\n", "DATASET_NAME = \"sft-dataset\"\n", @@ -297,9 +299,7 @@ "# Validate training data is uploaded correctly\n", "print(\"Training data:\")\n", "print(json.dumps([f.model_dump() for f in client.files.list(fileset=DATASET_NAME, workspace=\"default\").data], indent=2))" - ], - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "markdown", @@ -335,7 +335,9 @@ }, { "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ "# Export the HF_TOKEN and NGC_API_KEY environment variables if they are not already set.\n", "# This tutorial's default model (meta-llama/Llama-3.2-1B-Instruct) is gated and requires HF_TOKEN.\n", @@ -370,9 +372,7 @@ "# Create NGC API key secret\n", "# Uncomment the line below if you have NGC API Key and want to finetune NGC models\n", "# ngc_api_key = create_or_get_secret(\"ngc-api-key\", NGC_API_KEY, \"NGC_API_KEY\")" - ], - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "markdown", @@ -387,7 +387,9 @@ }, { "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ "import time\n", "\n", @@ -454,9 +456,7 @@ " )\n", "\n", "print(f\"ModelSpec populated: {base_model.spec}\")" - ], - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "markdown", @@ -482,7 +482,9 @@ }, { "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ "import uuid\n", "from nemo_automodel_plugin.schema import AutomodelJobInput\n", @@ -518,9 +520,7 @@ "\n", "print(f\"Submitted job: {job.job.name}\")\n", "print(f\"Output model: {OUTPUT_NAME}\")\n" - ], - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "markdown", @@ -531,7 +531,9 @@ }, { "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ "import time\n", "from IPython.display import clear_output\n", @@ -578,9 +580,7 @@ "\n", "if status.status != \"completed\":\n", " raise RuntimeError(f\"Training job finished with status: {status.status}\")" - ], - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "markdown", @@ -611,18 +611,20 @@ }, { "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ "# Validate model entity exists\n", "model_entity = client.models.retrieve(workspace='default', name=OUTPUT_NAME)\n", "print(model_entity.model_dump_json(indent=2))" - ], - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ "# Create deployment config\n", "deploy_suffix = uuid.uuid4().hex[:4]\n", @@ -660,9 +662,7 @@ "\n", "print(f\"Deployment name: {deployment.name}\")\n", "print(f\"Deployment status: {deployment_status.status}\")\n" - ], - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "markdown", @@ -706,7 +706,9 @@ }, { "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ "import time\n", "from IPython.display import clear_output\n", @@ -750,9 +752,7 @@ " raise TimeoutError(f\"Deployment timeout after {TIMEOUT_MINUTES} minutes\")\n", "\n", " time.sleep(15)" - ], - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "markdown", @@ -767,7 +767,9 @@ }, { "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ "# Wait for deployment to be ready, then test\n", "# Test the fine-tuned model with a question answering prompt\n", @@ -794,9 +796,7 @@ "print(f\"Question: {question}\")\n", "print(f\"Expected: Neil Armstrong\")\n", "print(f\"Model output: {response['choices'][0]['text']}\")" - ], - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "markdown", @@ -863,7 +863,8 @@ "\n", "**Deployment fails:**\n", "- Verify output model exists: `client.models.retrieve(name=OUTPUT_NAME, workspace=\"default\")`\n", - "- Check deployment logs: `client.inference.deployments.get_logs(name=deployment.name, workspace=\"default\")`\n", + "- Check job logs: `client.jobs.get_logs(name=job.job.name, workspace=\"default\")`\n", + "- Check deployment status: `client.inference.deployments.retrieve(name=deployment.name, workspace=\"default\")`\n", "- Ensure sufficient GPU resources for `executor_config={\"gpu\": 1, ...}`\n", "- Verify the deployment config matches this tutorial: `engine=\"vllm\"` with `vllm/vllm-openai:v0.22.1`\n", "\n", @@ -889,4 +890,4 @@ }, "nbformat": 4, "nbformat_minor": 2 -} \ No newline at end of file +} diff --git a/docs/customizer/tutorials/sft-customization-job.mdx b/docs/customizer/tutorials/sft-customization-job.mdx index 60b3928efa..8be12692b3 100644 --- a/docs/customizer/tutorials/sft-customization-job.mdx +++ b/docs/customizer/tutorials/sft-customization-job.mdx @@ -1,7 +1,6 @@ --- # SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 - title: "Full SFT Customization" description: "" --- @@ -231,12 +230,14 @@ If you plan to use NGC or Hugging Face models, you will need to configure authen - **NGC models** (`ngc://` URIs): Requires NGC API key - **Hugging Face models** (`hf://` URIs): Requires HF token for gated/private models + Configure these as secrets in your platform. Refer to [Managing Secrets](/documentation/get-started/core-concepts/manage-secrets) for detailed instructions. Get your credentials to access base models: - [NGC API Key](https://ngc.nvidia.com/) (Setup → Generate API Key) - [Hugging Face Token](https://huggingface.co/settings/tokens) (Create token with Read access) + --- #### Quick Setup Example @@ -260,6 +261,7 @@ if not HF_TOKEN: "The default model meta-llama/Llama-3.2-1B-Instruct is gated." ) + def create_or_get_secret(name: str, value: str, label: str): try: secret = client.secrets.create( @@ -273,6 +275,7 @@ def create_or_get_secret(name: str, value: str, label: str): print(f"Secret '{name}' already exists, continuing...") return client.secrets.retrieve(name=name, workspace="default") + # Create Hugging Face token secret hf_secret = create_or_get_secret("hf-token", HF_TOKEN, "HF_TOKEN") print("HF_TOKEN secret:") @@ -507,6 +510,7 @@ deployment = client.inference.deployments.create( config=deployment_config.name ) + # Check deployment status deployment_status = client.inference.deployments.retrieve( name=deployment.name, @@ -651,6 +655,7 @@ For detailed information on all available hyperparameters, recommended values, a --- + ## Troubleshooting **Job fails during model download:** @@ -687,10 +692,12 @@ For detailed information on all available hyperparameters, recommended values, a **Deployment fails:** - Verify output model exists: `client.models.retrieve(name=OUTPUT_NAME, workspace="default")` -- Check deployment logs: `client.inference.deployments.get_logs(name=deployment.name, workspace="default")` +- Check job logs: `client.jobs.get_logs(name=job.job.name, workspace="default")` +- Check deployment status: `client.inference.deployments.retrieve(name=deployment.name, workspace="default")` - Ensure sufficient GPU resources for `executor_config={"gpu": 1, ...}` - Verify the deployment config matches this tutorial: `engine="vllm"` with `vllm/vllm-openai:v0.22.1` + ## Next Steps - [Monitor training metrics](/documentation/customizer-reference/tutorials/metrics) in detail diff --git a/docs/fern/components/notebooks/distillation-customization-job.json b/docs/fern/components/notebooks/distillation-customization-job.json index 3ae0a8e082..01a2b891b7 100644 --- a/docs/fern/components/notebooks/distillation-customization-job.json +++ b/docs/fern/components/notebooks/distillation-customization-job.json @@ -196,8 +196,8 @@ }, { "type": "markdown", - "source": "**Interpreting ROUGE Scores:**\n\n| Metric | Measures |\n|--------|----------|\n| **ROUGE-1** | Unigram overlap between prediction and reference |\n| **ROUGE-2** | Bigram overlap (captures phrase-level similarity) |\n| **ROUGE-L** | Longest common subsequence (captures sentence structure) |\n| **ROUGE-Lsum** | ROUGE-L computed over full summaries |\n\n**What to expect:**\n- The base student (1B, no training) provides a lower bound since it has not seen the task data\n- The distilled student (1B, KD) should significantly outperform the base student, demonstrating the knowledge transferred from the 3B teacher\n- If the distilled student scores are not much higher than the baseline, try increasing `distillation_temperature`, adjusting `distillation_ratio`, or training for more epochs\n\n---\n\n## Hyperparameters\n\nFor detailed information on all available hyperparameters, recommended values, and tuning guidance, refer to the [Hyperparameter Reference](../manage-customization-jobs/hyperparameters.md).\n\n---\n\n## Troubleshooting\n\n**Job fails during model download:**\n- Verify authentication secrets are configured (refer to [Managing Secrets](../../get-started/concepts/manage-secrets.md))\n- For gated Hugging Face models (Llama, Gemma), accept the license on the model page\n- Check both `model` (student) and `teacher_model` URNs are correct\n- Ensure both model entities exist: `client.models.retrieve(name=..., workspace=\"default\")`\n\n**Job fails with OOM (Out of Memory) error:**\n\nKD loads both models, so OOM is more likely than with SFT:\n1. **First try:** Use `teacher_precision=\"bf16\"` to reduce teacher memory\n2. **Still OOM:** Reduce `micro_batch_size` to 1\n3. **Still OOM:** Reduce `global_batch_size` and `max_seq_length`\n4. **Last resort:** Increase `num_gpus_per_node`\n\n**No chat template / `/chat/completions` fails:**\n- Use Instruct model variants (e.g., `Llama-3.2-1B-Instruct`) instead of base models (`Llama-3.2-1B`). Base models do not include a chat template in their tokenizer, so the output model will also lack one.\n\n**Distilled model quality is poor:**\n- Increase `distillation_temperature` (try 2.0–5.0) to transfer more nuanced knowledge\n- Adjust `distillation_ratio`—if dataset labels are high-quality, lower the ratio; if the teacher is strong, raise it\n- Increase `epochs` or `max_steps` for more training\n- Verify teacher and student share the same vocabulary\n\n**Vocabulary mismatch error:**\n- Teacher and student must use the same tokenizer. Use models from the same family (e.g., Llama 3.2 1B Instruct + Llama 3.2 3B Instruct)\n\n**Deployment fails:**\n- Verify output model exists: `client.models.retrieve(name=DISTILLED_STUDENT_NAME, workspace=\"default\")`\n- Check deployment logs: `client.inference.deployments.get_logs(name=deployment.name, workspace=\"default\")`\n- The distilled model has the same size as the student, so GPU requirements match the student model\n\n\n## Next Steps\n\n- [Monitor training metrics](fine-tune-metrics) in detail\n- [Evaluate your fine-tuned model](../../evaluator/index) using the Evaluator service\n- Learn about [LoRA customization](./lora-customization-job) for resource-efficient fine-tuning\n- Learn about [Full SFT](./sft-customization-job) for direct supervised fine-tuning", - "source_html": "

Interpreting ROUGE Scores:

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
MetricMeasures
ROUGE-1Unigram overlap between prediction and reference
ROUGE-2Bigram overlap (captures phrase-level similarity)
ROUGE-LLongest common subsequence (captures sentence structure)
ROUGE-LsumROUGE-L computed over full summaries
\n

What to expect:

\n\n
\n

Hyperparameters

\n

For detailed information on all available hyperparameters, recommended values, and tuning guidance, refer to the Hyperparameter Reference.

\n
\n

Troubleshooting

\n

Job fails during model download:

\n\n

Job fails with OOM (Out of Memory) error:

\n

KD loads both models, so OOM is more likely than with SFT:

\n
    \n
  1. First try: Use teacher_precision="bf16" to reduce teacher memory
  2. \n
  3. Still OOM: Reduce micro_batch_size to 1
  4. \n
  5. Still OOM: Reduce global_batch_size and max_seq_length
  6. \n
  7. Last resort: Increase num_gpus_per_node
  8. \n
\n

No chat template / /chat/completions fails:

\n\n

Distilled model quality is poor:

\n\n

Vocabulary mismatch error:

\n\n

Deployment fails:

\n\n

Next Steps

\n\n" + "source": "**Interpreting ROUGE Scores:**\n\n| Metric | Measures |\n|--------|----------|\n| **ROUGE-1** | Unigram overlap between prediction and reference |\n| **ROUGE-2** | Bigram overlap (captures phrase-level similarity) |\n| **ROUGE-L** | Longest common subsequence (captures sentence structure) |\n| **ROUGE-Lsum** | ROUGE-L computed over full summaries |\n\n**What to expect:**\n- The base student (1B, no training) provides a lower bound since it has not seen the task data\n- The distilled student (1B, KD) should significantly outperform the base student, demonstrating the knowledge transferred from the 3B teacher\n- If the distilled student scores are not much higher than the baseline, try increasing `distillation_temperature`, adjusting `distillation_ratio`, or training for more epochs\n\n---\n\n## Hyperparameters\n\nFor detailed information on all available hyperparameters, recommended values, and tuning guidance, refer to the [Hyperparameter Reference](../manage-customization-jobs/hyperparameters.md).\n\n---\n\n## Troubleshooting\n\n**Job fails during model download:**\n- Verify authentication secrets are configured (refer to [Managing Secrets](../../get-started/concepts/manage-secrets.md))\n- For gated Hugging Face models (Llama, Gemma), accept the license on the model page\n- Check both `model` (student) and `teacher_model` URNs are correct\n- Ensure both model entities exist: `client.models.retrieve(name=..., workspace=\"default\")`\n\n**Job fails with OOM (Out of Memory) error:**\n\nKD loads both models, so OOM is more likely than with SFT:\n1. **First try:** Use `teacher_precision=\"bf16\"` to reduce teacher memory\n2. **Still OOM:** Reduce `micro_batch_size` to 1\n3. **Still OOM:** Reduce `global_batch_size` and `max_seq_length`\n4. **Last resort:** Increase `num_gpus_per_node`\n\n**No chat template / `/chat/completions` fails:**\n- Use Instruct model variants (e.g., `Llama-3.2-1B-Instruct`) instead of base models (`Llama-3.2-1B`). Base models do not include a chat template in their tokenizer, so the output model will also lack one.\n\n**Distilled model quality is poor:**\n- Increase `distillation_temperature` (try 2.0–5.0) to transfer more nuanced knowledge\n- Adjust `distillation_ratio`—if dataset labels are high-quality, lower the ratio; if the teacher is strong, raise it\n- Increase `epochs` or `max_steps` for more training\n- Verify teacher and student share the same vocabulary\n\n**Vocabulary mismatch error:**\n- Teacher and student must use the same tokenizer. Use models from the same family (e.g., Llama 3.2 1B Instruct + Llama 3.2 3B Instruct)\n\n**Deployment fails:**\n- Verify output model exists: `client.models.retrieve(name=DISTILLED_STUDENT_NAME, workspace=\"default\")`\n- Check job logs: `client.jobs.get_logs(name=kd_job.job.name, workspace=\"default\")`\n- Check student deployment status: `client.inference.deployments.retrieve(name=student_deployment.name, workspace=\"default\")`\n- The distilled model has the same size as the student, so GPU requirements match the student model\n\n\n## Next Steps\n\n- [Monitor training metrics](fine-tune-metrics) in detail\n- [Evaluate your fine-tuned model](../../evaluator/index) using the Evaluator service\n- Learn about [LoRA customization](./lora-customization-job) for resource-efficient fine-tuning\n- Learn about [Full SFT](./sft-customization-job) for direct supervised fine-tuning", + "source_html": "

Interpreting ROUGE Scores:

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
MetricMeasures
ROUGE-1Unigram overlap between prediction and reference
ROUGE-2Bigram overlap (captures phrase-level similarity)
ROUGE-LLongest common subsequence (captures sentence structure)
ROUGE-LsumROUGE-L computed over full summaries
\n

What to expect:

\n\n
\n

Hyperparameters

\n

For detailed information on all available hyperparameters, recommended values, and tuning guidance, refer to the Hyperparameter Reference.

\n
\n

Troubleshooting

\n

Job fails during model download:

\n\n

Job fails with OOM (Out of Memory) error:

\n

KD loads both models, so OOM is more likely than with SFT:

\n
    \n
  1. First try: Use teacher_precision="bf16" to reduce teacher memory
  2. \n
  3. Still OOM: Reduce micro_batch_size to 1
  4. \n
  5. Still OOM: Reduce global_batch_size and max_seq_length
  6. \n
  7. Last resort: Increase num_gpus_per_node
  8. \n
\n

No chat template / /chat/completions fails:

\n\n

Distilled model quality is poor:

\n\n

Vocabulary mismatch error:

\n\n

Deployment fails:

\n\n

Next Steps

\n\n" } ] } \ No newline at end of file diff --git a/docs/fern/components/notebooks/distillation-customization-job.ts b/docs/fern/components/notebooks/distillation-customization-job.ts index 83d6f3a29e..1e75137fbb 100644 --- a/docs/fern/components/notebooks/distillation-customization-job.ts +++ b/docs/fern/components/notebooks/distillation-customization-job.ts @@ -201,7 +201,7 @@ export default { cells: [ }, { "type": "markdown", - "source": "**Interpreting ROUGE Scores:**\n\n| Metric | Measures |\n|--------|----------|\n| **ROUGE-1** | Unigram overlap between prediction and reference |\n| **ROUGE-2** | Bigram overlap (captures phrase-level similarity) |\n| **ROUGE-L** | Longest common subsequence (captures sentence structure) |\n| **ROUGE-Lsum** | ROUGE-L computed over full summaries |\n\n**What to expect:**\n- The base student (1B, no training) provides a lower bound since it has not seen the task data\n- The distilled student (1B, KD) should significantly outperform the base student, demonstrating the knowledge transferred from the 3B teacher\n- If the distilled student scores are not much higher than the baseline, try increasing `distillation_temperature`, adjusting `distillation_ratio`, or training for more epochs\n\n---\n\n## Hyperparameters\n\nFor detailed information on all available hyperparameters, recommended values, and tuning guidance, refer to the [Hyperparameter Reference](../manage-customization-jobs/hyperparameters.md).\n\n---\n\n## Troubleshooting\n\n**Job fails during model download:**\n- Verify authentication secrets are configured (refer to [Managing Secrets](../../get-started/concepts/manage-secrets.md))\n- For gated Hugging Face models (Llama, Gemma), accept the license on the model page\n- Check both `model` (student) and `teacher_model` URNs are correct\n- Ensure both model entities exist: `client.models.retrieve(name=..., workspace=\"default\")`\n\n**Job fails with OOM (Out of Memory) error:**\n\nKD loads both models, so OOM is more likely than with SFT:\n1. **First try:** Use `teacher_precision=\"bf16\"` to reduce teacher memory\n2. **Still OOM:** Reduce `micro_batch_size` to 1\n3. **Still OOM:** Reduce `global_batch_size` and `max_seq_length`\n4. **Last resort:** Increase `num_gpus_per_node`\n\n**No chat template / `/chat/completions` fails:**\n- Use Instruct model variants (e.g., `Llama-3.2-1B-Instruct`) instead of base models (`Llama-3.2-1B`). Base models do not include a chat template in their tokenizer, so the output model will also lack one.\n\n**Distilled model quality is poor:**\n- Increase `distillation_temperature` (try 2.0–5.0) to transfer more nuanced knowledge\n- Adjust `distillation_ratio`—if dataset labels are high-quality, lower the ratio; if the teacher is strong, raise it\n- Increase `epochs` or `max_steps` for more training\n- Verify teacher and student share the same vocabulary\n\n**Vocabulary mismatch error:**\n- Teacher and student must use the same tokenizer. Use models from the same family (e.g., Llama 3.2 1B Instruct + Llama 3.2 3B Instruct)\n\n**Deployment fails:**\n- Verify output model exists: `client.models.retrieve(name=DISTILLED_STUDENT_NAME, workspace=\"default\")`\n- Check deployment logs: `client.inference.deployments.get_logs(name=deployment.name, workspace=\"default\")`\n- The distilled model has the same size as the student, so GPU requirements match the student model\n\n\n## Next Steps\n\n- [Monitor training metrics](fine-tune-metrics) in detail\n- [Evaluate your fine-tuned model](../../evaluator/index) using the Evaluator service\n- Learn about [LoRA customization](./lora-customization-job) for resource-efficient fine-tuning\n- Learn about [Full SFT](./sft-customization-job) for direct supervised fine-tuning", - "source_html": "

Interpreting ROUGE Scores:

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
MetricMeasures
ROUGE-1Unigram overlap between prediction and reference
ROUGE-2Bigram overlap (captures phrase-level similarity)
ROUGE-LLongest common subsequence (captures sentence structure)
ROUGE-LsumROUGE-L computed over full summaries
\n

What to expect:

\n\n
\n

Hyperparameters

\n

For detailed information on all available hyperparameters, recommended values, and tuning guidance, refer to the Hyperparameter Reference.

\n
\n

Troubleshooting

\n

Job fails during model download:

\n\n

Job fails with OOM (Out of Memory) error:

\n

KD loads both models, so OOM is more likely than with SFT:

\n
    \n
  1. First try: Use teacher_precision="bf16" to reduce teacher memory
  2. \n
  3. Still OOM: Reduce micro_batch_size to 1
  4. \n
  5. Still OOM: Reduce global_batch_size and max_seq_length
  6. \n
  7. Last resort: Increase num_gpus_per_node
  8. \n
\n

No chat template / /chat/completions fails:

\n\n

Distilled model quality is poor:

\n\n

Vocabulary mismatch error:

\n\n

Deployment fails:

\n\n

Next Steps

\n\n" + "source": "**Interpreting ROUGE Scores:**\n\n| Metric | Measures |\n|--------|----------|\n| **ROUGE-1** | Unigram overlap between prediction and reference |\n| **ROUGE-2** | Bigram overlap (captures phrase-level similarity) |\n| **ROUGE-L** | Longest common subsequence (captures sentence structure) |\n| **ROUGE-Lsum** | ROUGE-L computed over full summaries |\n\n**What to expect:**\n- The base student (1B, no training) provides a lower bound since it has not seen the task data\n- The distilled student (1B, KD) should significantly outperform the base student, demonstrating the knowledge transferred from the 3B teacher\n- If the distilled student scores are not much higher than the baseline, try increasing `distillation_temperature`, adjusting `distillation_ratio`, or training for more epochs\n\n---\n\n## Hyperparameters\n\nFor detailed information on all available hyperparameters, recommended values, and tuning guidance, refer to the [Hyperparameter Reference](../manage-customization-jobs/hyperparameters.md).\n\n---\n\n## Troubleshooting\n\n**Job fails during model download:**\n- Verify authentication secrets are configured (refer to [Managing Secrets](../../get-started/concepts/manage-secrets.md))\n- For gated Hugging Face models (Llama, Gemma), accept the license on the model page\n- Check both `model` (student) and `teacher_model` URNs are correct\n- Ensure both model entities exist: `client.models.retrieve(name=..., workspace=\"default\")`\n\n**Job fails with OOM (Out of Memory) error:**\n\nKD loads both models, so OOM is more likely than with SFT:\n1. **First try:** Use `teacher_precision=\"bf16\"` to reduce teacher memory\n2. **Still OOM:** Reduce `micro_batch_size` to 1\n3. **Still OOM:** Reduce `global_batch_size` and `max_seq_length`\n4. **Last resort:** Increase `num_gpus_per_node`\n\n**No chat template / `/chat/completions` fails:**\n- Use Instruct model variants (e.g., `Llama-3.2-1B-Instruct`) instead of base models (`Llama-3.2-1B`). Base models do not include a chat template in their tokenizer, so the output model will also lack one.\n\n**Distilled model quality is poor:**\n- Increase `distillation_temperature` (try 2.0–5.0) to transfer more nuanced knowledge\n- Adjust `distillation_ratio`—if dataset labels are high-quality, lower the ratio; if the teacher is strong, raise it\n- Increase `epochs` or `max_steps` for more training\n- Verify teacher and student share the same vocabulary\n\n**Vocabulary mismatch error:**\n- Teacher and student must use the same tokenizer. Use models from the same family (e.g., Llama 3.2 1B Instruct + Llama 3.2 3B Instruct)\n\n**Deployment fails:**\n- Verify output model exists: `client.models.retrieve(name=DISTILLED_STUDENT_NAME, workspace=\"default\")`\n- Check job logs: `client.jobs.get_logs(name=kd_job.job.name, workspace=\"default\")`\n- Check student deployment status: `client.inference.deployments.retrieve(name=student_deployment.name, workspace=\"default\")`\n- The distilled model has the same size as the student, so GPU requirements match the student model\n\n\n## Next Steps\n\n- [Monitor training metrics](fine-tune-metrics) in detail\n- [Evaluate your fine-tuned model](../../evaluator/index) using the Evaluator service\n- Learn about [LoRA customization](./lora-customization-job) for resource-efficient fine-tuning\n- Learn about [Full SFT](./sft-customization-job) for direct supervised fine-tuning", + "source_html": "

Interpreting ROUGE Scores:

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
MetricMeasures
ROUGE-1Unigram overlap between prediction and reference
ROUGE-2Bigram overlap (captures phrase-level similarity)
ROUGE-LLongest common subsequence (captures sentence structure)
ROUGE-LsumROUGE-L computed over full summaries
\n

What to expect:

\n\n
\n

Hyperparameters

\n

For detailed information on all available hyperparameters, recommended values, and tuning guidance, refer to the Hyperparameter Reference.

\n
\n

Troubleshooting

\n

Job fails during model download:

\n\n

Job fails with OOM (Out of Memory) error:

\n

KD loads both models, so OOM is more likely than with SFT:

\n
    \n
  1. First try: Use teacher_precision="bf16" to reduce teacher memory
  2. \n
  3. Still OOM: Reduce micro_batch_size to 1
  4. \n
  5. Still OOM: Reduce global_batch_size and max_seq_length
  6. \n
  7. Last resort: Increase num_gpus_per_node
  8. \n
\n

No chat template / /chat/completions fails:

\n\n

Distilled model quality is poor:

\n\n

Vocabulary mismatch error:

\n\n

Deployment fails:

\n\n

Next Steps

\n\n" } ] }; diff --git a/docs/fern/components/notebooks/dpo-customization-job.json b/docs/fern/components/notebooks/dpo-customization-job.json index ab69dab155..01a864b23a 100644 --- a/docs/fern/components/notebooks/dpo-customization-job.json +++ b/docs/fern/components/notebooks/dpo-customization-job.json @@ -7,8 +7,8 @@ }, { "type": "markdown", - "source": "## Prerequisites\n\nBefore starting this tutorial, ensure you have:\n\n1. **Completed the [Quickstart](/documentation/get-started)** to install the NeMo Platform and Python SDK.\n2. **Installed the Python SDK** (PyPI wrapper: `pip install \"nemo-platform[all]\"`; source checkout: run `make bootstrap` from the repository root).\n3. **Installed the `datasets` package**: `pip install datasets`.\n4. **A platform configured with `platform.runtime: kubernetes`.** The `rl` (DPO) backend provisions a Ray cluster and has **no local Docker fallback** — `submit` fails fast on a Docker-runtime platform. Multi-node jobs (`parallelism.num_nodes > 1`) additionally require the platform-side `NMP_RL_MULTINODE_SHARED_STORAGE_PATH`.\n5. **A Hugging Face token** with access to the gated base model (this tutorial uses `meta-llama/Llama-3.2-1B-Instruct`). Export it as `HF_TOKEN`.\n6. **At least one GPU with CUDA 13+** and a GPU execution profile (`nemo jobs list-execution-profiles`).", - "source_html": "

Prerequisites

\n

Before starting this tutorial, ensure you have:

\n
    \n
  1. Completed the Quickstart to install the NeMo Platform and Python SDK.
  2. \n
  3. Installed the Python SDK (PyPI wrapper: pip install "nemo-platform[all]"; source checkout: run make bootstrap from the repository root).
  4. \n
  5. Installed the datasets package: pip install datasets.
  6. \n
  7. A platform configured with platform.runtime: kubernetes. The rl (DPO) backend provisions a Ray cluster and has no local Docker fallbacksubmit fails fast on a Docker-runtime platform. Multi-node jobs (parallelism.num_nodes > 1) additionally require the platform-side NMP_RL_MULTINODE_SHARED_STORAGE_PATH.
  8. \n
  9. A Hugging Face token with access to the gated base model (this tutorial uses meta-llama/Llama-3.2-1B-Instruct). Export it as HF_TOKEN.
  10. \n
  11. At least one GPU with CUDA 13+ and a GPU execution profile (nemo jobs list-execution-profiles).
  12. \n
\n" + "source": "## Prerequisites\n\nBefore starting this tutorial, ensure you have:\n\n1. **Completed the [Quickstart](/documentation/get-started)** to install the NeMo Platform and Python SDK.\n2. **Installed the Python SDK** (PyPI wrapper: `pip install \"nemo-platform[all,nemo-rl-plugin]\"` so `RlJobInput` is available; source checkout: run `make bootstrap` from the repository root). `nemo-platform[all]` does not include the RL plugin.\n3. **Installed the `datasets` package**: `pip install datasets`.\n4. **A platform configured with `platform.runtime: kubernetes`.** The `rl` (DPO) backend provisions a Ray cluster and has **no local Docker fallback** — `submit` fails fast on a Docker-runtime platform. Multi-node jobs (`parallelism.num_nodes > 1`) additionally require the platform-side `NMP_RL_MULTINODE_SHARED_STORAGE_PATH`.\n5. **A Hugging Face token** with access to the gated base model (this tutorial uses `meta-llama/Llama-3.2-1B-Instruct`). Export it as `HF_TOKEN`.\n6. **At least one GPU with CUDA 13+** and a GPU execution profile (`nemo jobs list-execution-profiles`).", + "source_html": "

Prerequisites

\n

Before starting this tutorial, ensure you have:

\n
    \n
  1. Completed the Quickstart to install the NeMo Platform and Python SDK.
  2. \n
  3. Installed the Python SDK (PyPI wrapper: pip install "nemo-platform[all,nemo-rl-plugin]" so RlJobInput is available; source checkout: run make bootstrap from the repository root). nemo-platform[all] does not include the RL plugin.
  4. \n
  5. Installed the datasets package: pip install datasets.
  6. \n
  7. A platform configured with platform.runtime: kubernetes. The rl (DPO) backend provisions a Ray cluster and has no local Docker fallbacksubmit fails fast on a Docker-runtime platform. Multi-node jobs (parallelism.num_nodes > 1) additionally require the platform-side NMP_RL_MULTINODE_SHARED_STORAGE_PATH.
  8. \n
  9. A Hugging Face token with access to the gated base model (this tutorial uses meta-llama/Llama-3.2-1B-Instruct). Export it as HF_TOKEN.
  10. \n
  11. At least one GPU with CUDA 13+ and a GPU execution profile (nemo jobs list-execution-profiles).
  12. \n
\n" }, { "type": "markdown", diff --git a/docs/fern/components/notebooks/dpo-customization-job.ts b/docs/fern/components/notebooks/dpo-customization-job.ts index fc8e558903..c0a3dc97ac 100644 --- a/docs/fern/components/notebooks/dpo-customization-job.ts +++ b/docs/fern/components/notebooks/dpo-customization-job.ts @@ -12,8 +12,8 @@ export default { cells: [ }, { "type": "markdown", - "source": "## Prerequisites\n\nBefore starting this tutorial, ensure you have:\n\n1. **Completed the [Quickstart](/documentation/get-started)** to install the NeMo Platform and Python SDK.\n2. **Installed the Python SDK** (PyPI wrapper: `pip install \"nemo-platform[all]\"`; source checkout: run `make bootstrap` from the repository root).\n3. **Installed the `datasets` package**: `pip install datasets`.\n4. **A platform configured with `platform.runtime: kubernetes`.** The `rl` (DPO) backend provisions a Ray cluster and has **no local Docker fallback** — `submit` fails fast on a Docker-runtime platform. Multi-node jobs (`parallelism.num_nodes > 1`) additionally require the platform-side `NMP_RL_MULTINODE_SHARED_STORAGE_PATH`.\n5. **A Hugging Face token** with access to the gated base model (this tutorial uses `meta-llama/Llama-3.2-1B-Instruct`). Export it as `HF_TOKEN`.\n6. **At least one GPU with CUDA 13+** and a GPU execution profile (`nemo jobs list-execution-profiles`).", - "source_html": "

Prerequisites

\n

Before starting this tutorial, ensure you have:

\n
    \n
  1. Completed the Quickstart to install the NeMo Platform and Python SDK.
  2. \n
  3. Installed the Python SDK (PyPI wrapper: pip install "nemo-platform[all]"; source checkout: run make bootstrap from the repository root).
  4. \n
  5. Installed the datasets package: pip install datasets.
  6. \n
  7. A platform configured with platform.runtime: kubernetes. The rl (DPO) backend provisions a Ray cluster and has no local Docker fallbacksubmit fails fast on a Docker-runtime platform. Multi-node jobs (parallelism.num_nodes > 1) additionally require the platform-side NMP_RL_MULTINODE_SHARED_STORAGE_PATH.
  8. \n
  9. A Hugging Face token with access to the gated base model (this tutorial uses meta-llama/Llama-3.2-1B-Instruct). Export it as HF_TOKEN.
  10. \n
  11. At least one GPU with CUDA 13+ and a GPU execution profile (nemo jobs list-execution-profiles).
  12. \n
\n" + "source": "## Prerequisites\n\nBefore starting this tutorial, ensure you have:\n\n1. **Completed the [Quickstart](/documentation/get-started)** to install the NeMo Platform and Python SDK.\n2. **Installed the Python SDK** (PyPI wrapper: `pip install \"nemo-platform[all,nemo-rl-plugin]\"` so `RlJobInput` is available; source checkout: run `make bootstrap` from the repository root). `nemo-platform[all]` does not include the RL plugin.\n3. **Installed the `datasets` package**: `pip install datasets`.\n4. **A platform configured with `platform.runtime: kubernetes`.** The `rl` (DPO) backend provisions a Ray cluster and has **no local Docker fallback** — `submit` fails fast on a Docker-runtime platform. Multi-node jobs (`parallelism.num_nodes > 1`) additionally require the platform-side `NMP_RL_MULTINODE_SHARED_STORAGE_PATH`.\n5. **A Hugging Face token** with access to the gated base model (this tutorial uses `meta-llama/Llama-3.2-1B-Instruct`). Export it as `HF_TOKEN`.\n6. **At least one GPU with CUDA 13+** and a GPU execution profile (`nemo jobs list-execution-profiles`).", + "source_html": "

Prerequisites

\n

Before starting this tutorial, ensure you have:

\n
    \n
  1. Completed the Quickstart to install the NeMo Platform and Python SDK.
  2. \n
  3. Installed the Python SDK (PyPI wrapper: pip install "nemo-platform[all,nemo-rl-plugin]" so RlJobInput is available; source checkout: run make bootstrap from the repository root). nemo-platform[all] does not include the RL plugin.
  4. \n
  5. Installed the datasets package: pip install datasets.
  6. \n
  7. A platform configured with platform.runtime: kubernetes. The rl (DPO) backend provisions a Ray cluster and has no local Docker fallbacksubmit fails fast on a Docker-runtime platform. Multi-node jobs (parallelism.num_nodes > 1) additionally require the platform-side NMP_RL_MULTINODE_SHARED_STORAGE_PATH.
  8. \n
  9. A Hugging Face token with access to the gated base model (this tutorial uses meta-llama/Llama-3.2-1B-Instruct). Export it as HF_TOKEN.
  10. \n
  11. At least one GPU with CUDA 13+ and a GPU execution profile (nemo jobs list-execution-profiles).
  12. \n
\n" }, { "type": "markdown", diff --git a/docs/fern/components/notebooks/embedding-customization-job.json b/docs/fern/components/notebooks/embedding-customization-job.json index 82e62a10c9..05b9ae4c21 100644 --- a/docs/fern/components/notebooks/embedding-customization-job.json +++ b/docs/fern/components/notebooks/embedding-customization-job.json @@ -27,10 +27,9 @@ "source_html": "

2. Establish Baseline Performance

\n

Before fine-tuning, establish baseline performance with the pretrained model. Deploy it, run a test query, and observe where it struggles. Following fine-tuning, compare the results.

\n

Scenario: Searching scientific papers by meaning, not keywords.

\n

Demo setup:

\n\n" }, { - "type": "code", - "source": "# Install required packages for dataset preparation\n%pip install -q datasets huggingface_hub", - "language": "python", - "source_html": "# Install required packages for dataset preparation\n%pip install -q datasets huggingface_hub\n" + "type": "markdown", + "source": "Install required packages for dataset preparation if they are not installed in your Python environment:\n\n```sh\npip install datasets huggingface_hub\n```", + "source_html": "

Install required packages for dataset preparation if they are not installed in your Python environment:

\n
pip install datasets huggingface_hub\n
\n" }, { "type": "code", diff --git a/docs/fern/components/notebooks/embedding-customization-job.ts b/docs/fern/components/notebooks/embedding-customization-job.ts index 50c25536cf..f6d8265362 100644 --- a/docs/fern/components/notebooks/embedding-customization-job.ts +++ b/docs/fern/components/notebooks/embedding-customization-job.ts @@ -32,10 +32,9 @@ export default { cells: [ "source_html": "

2. Establish Baseline Performance

\n

Before fine-tuning, establish baseline performance with the pretrained model. Deploy it, run a test query, and observe where it struggles. Following fine-tuning, compare the results.

\n

Scenario: Searching scientific papers by meaning, not keywords.

\n

Demo setup:

\n\n" }, { - "type": "code", - "source": "# Install required packages for dataset preparation\n%pip install -q datasets huggingface_hub", - "language": "python", - "source_html": "# Install required packages for dataset preparation\n%pip install -q datasets huggingface_hub\n" + "type": "markdown", + "source": "Install required packages for dataset preparation if they are not installed in your Python environment:\n\n```sh\npip install datasets huggingface_hub\n```", + "source_html": "

Install required packages for dataset preparation if they are not installed in your Python environment:

\n
pip install datasets huggingface_hub\n
\n" }, { "type": "code", diff --git a/docs/fern/components/notebooks/sft-customization-job.json b/docs/fern/components/notebooks/sft-customization-job.json index 33c6a61148..471dcedea5 100644 --- a/docs/fern/components/notebooks/sft-customization-job.json +++ b/docs/fern/components/notebooks/sft-customization-job.json @@ -183,8 +183,8 @@ }, { "type": "markdown", - "source": "#### Evaluation Best Practices\n\n**Manual Evaluation** (Recommended)\n- Test with real-world examples from your use case\n- Compare responses to base model and expected outputs\n- Verify the model exhibits desired behavior changes\n- Check edge cases and error handling\n\n**What to look for:**\n- ✅ Model follows your desired output format\n- ✅ Applies domain knowledge correctly\n- ✅ Maintains general language capabilities\n- ✅ Avoids unwanted behaviors or biases\n- ❌ Doesn't hallucinate facts not in training data\n- ❌ Doesn't produce repetitive or nonsensical outputs\n\n---\n\n## Hyperparameters\n\nFor detailed information on all available hyperparameters, recommended values, and tuning guidance, refer to the [Hyperparameter Reference](../manage-customization-jobs/hyperparameters.md).\n\n---\n\n\n## Troubleshooting\n\n**Job fails during model download:**\n- Verify authentication secrets are configured (refer to [Managing Secrets](../../get-started/concepts/manage-secrets.md))\n- For gated Hugging Face models (Llama, Gemma), accept the license on the model page (for example, [meta-llama/Llama-3.2-1B-Instruct](https://huggingface.co/meta-llama/Llama-3.2-1B-Instruct))\n- Confirm the model fileset uses `token_secret=hf_secret.name` for gated models\n- Check `AutomodelJobInput` references use the `workspace/name` format: `model=f\"default/{MODEL_NAME}\"` and `dataset={\"training\": f\"default/{DATASET_NAME}\"}` (for example, `default/llama-3-2-1b-base`, `default/sft-dataset`)\n- Verify the model entity points at the fileset: `fileset=f\"default/{MODEL_NAME}\"`\n- Check job status: `client.jobs.get_status(name=job.job.name, workspace=\"default\")`\n\n**Job fails with OOM (Out of Memory) error:**\n1. **First try:** Reduce `global_batch_size` from 64 to 32 or 16 in `batch={...}`\n2. **Still OOM:** Keep `micro_batch_size` at 1 (already the minimum in this tutorial)\n3. **Still OOM:** Reduce `max_seq_length` from 2048 to 1024 or 512 in `training={...}`\n4. **Last resort:** Increase `num_gpus_per_node` and `tensor_parallel_size` in `parallelism={...}`\n\n**Loss curves not decreasing (underfitting):**\n- Increase training duration: raise `epochs` from 2 to 3-5 in `schedule={...}`\n- Adjust learning rate: try `1e-4` or `1e-5` instead of the default `5e-5` in `optimizer={...}`\n- Check data quality: Verify formatting, remove duplicates, ensure diversity\n\n**Training loss decreases but validation loss increases (overfitting):**\n- Reduce `epochs` from 2 to 1 in `schedule={...}`\n- Lower `learning_rate` from `5e-5` to `2e-5` or `1e-5` in `optimizer={...}`\n- Increase dataset size and diversity\n- Verify train/validation split has no data leakage\n\n**Model output quality is poor despite good training metrics:**\n- Training metrics optimize for loss, not your actual task—evaluate on real use cases\n- Review data quality, format, and diversity—metrics can be misleading with poor data\n- Try a different base model size or architecture\n- Adjust `learning_rate` and `global_batch_size`\n- Compare to baseline: Test base model to ensure fine-tuning improved performance\n\n**Deployment fails:**\n- Verify output model exists: `client.models.retrieve(name=OUTPUT_NAME, workspace=\"default\")`\n- Check deployment logs: `client.inference.deployments.get_logs(name=deployment.name, workspace=\"default\")`\n- Ensure sufficient GPU resources for `executor_config={\"gpu\": 1, ...}`\n- Verify the deployment config matches this tutorial: `engine=\"vllm\"` with `vllm/vllm-openai:v0.22.1`\n\n\n## Next Steps\n\n- [Monitor training metrics](fine-tune-metrics) in detail\n- [Evaluate your fine-tuned model](../../evaluator/index) using the Evaluator service\n- Learn about [LoRA customization](./lora-customization-job) for resource-efficient fine-tuning", - "source_html": "

Evaluation Best Practices

\n

Manual Evaluation (Recommended)

\n\n

What to look for:

\n\n
\n

Hyperparameters

\n

For detailed information on all available hyperparameters, recommended values, and tuning guidance, refer to the Hyperparameter Reference.

\n
\n

Troubleshooting

\n

Job fails during model download:

\n\n

Job fails with OOM (Out of Memory) error:

\n
    \n
  1. First try: Reduce global_batch_size from 64 to 32 or 16 in batch={...}
  2. \n
  3. Still OOM: Keep micro_batch_size at 1 (already the minimum in this tutorial)
  4. \n
  5. Still OOM: Reduce max_seq_length from 2048 to 1024 or 512 in training={...}
  6. \n
  7. Last resort: Increase num_gpus_per_node and tensor_parallel_size in parallelism={...}
  8. \n
\n

Loss curves not decreasing (underfitting):

\n\n

Training loss decreases but validation loss increases (overfitting):

\n\n

Model output quality is poor despite good training metrics:

\n\n

Deployment fails:

\n\n

Next Steps

\n\n" + "source": "#### Evaluation Best Practices\n\n**Manual Evaluation** (Recommended)\n- Test with real-world examples from your use case\n- Compare responses to base model and expected outputs\n- Verify the model exhibits desired behavior changes\n- Check edge cases and error handling\n\n**What to look for:**\n- ✅ Model follows your desired output format\n- ✅ Applies domain knowledge correctly\n- ✅ Maintains general language capabilities\n- ✅ Avoids unwanted behaviors or biases\n- ❌ Doesn't hallucinate facts not in training data\n- ❌ Doesn't produce repetitive or nonsensical outputs\n\n---\n\n## Hyperparameters\n\nFor detailed information on all available hyperparameters, recommended values, and tuning guidance, refer to the [Hyperparameter Reference](../manage-customization-jobs/hyperparameters.md).\n\n---\n\n\n## Troubleshooting\n\n**Job fails during model download:**\n- Verify authentication secrets are configured (refer to [Managing Secrets](../../get-started/concepts/manage-secrets.md))\n- For gated Hugging Face models (Llama, Gemma), accept the license on the model page (for example, [meta-llama/Llama-3.2-1B-Instruct](https://huggingface.co/meta-llama/Llama-3.2-1B-Instruct))\n- Confirm the model fileset uses `token_secret=hf_secret.name` for gated models\n- Check `AutomodelJobInput` references use the `workspace/name` format: `model=f\"default/{MODEL_NAME}\"` and `dataset={\"training\": f\"default/{DATASET_NAME}\"}` (for example, `default/llama-3-2-1b-base`, `default/sft-dataset`)\n- Verify the model entity points at the fileset: `fileset=f\"default/{MODEL_NAME}\"`\n- Check job status: `client.jobs.get_status(name=job.job.name, workspace=\"default\")`\n\n**Job fails with OOM (Out of Memory) error:**\n1. **First try:** Reduce `global_batch_size` from 64 to 32 or 16 in `batch={...}`\n2. **Still OOM:** Keep `micro_batch_size` at 1 (already the minimum in this tutorial)\n3. **Still OOM:** Reduce `max_seq_length` from 2048 to 1024 or 512 in `training={...}`\n4. **Last resort:** Increase `num_gpus_per_node` and `tensor_parallel_size` in `parallelism={...}`\n\n**Loss curves not decreasing (underfitting):**\n- Increase training duration: raise `epochs` from 2 to 3-5 in `schedule={...}`\n- Adjust learning rate: try `1e-4` or `1e-5` instead of the default `5e-5` in `optimizer={...}`\n- Check data quality: Verify formatting, remove duplicates, ensure diversity\n\n**Training loss decreases but validation loss increases (overfitting):**\n- Reduce `epochs` from 2 to 1 in `schedule={...}`\n- Lower `learning_rate` from `5e-5` to `2e-5` or `1e-5` in `optimizer={...}`\n- Increase dataset size and diversity\n- Verify train/validation split has no data leakage\n\n**Model output quality is poor despite good training metrics:**\n- Training metrics optimize for loss, not your actual task—evaluate on real use cases\n- Review data quality, format, and diversity—metrics can be misleading with poor data\n- Try a different base model size or architecture\n- Adjust `learning_rate` and `global_batch_size`\n- Compare to baseline: Test base model to ensure fine-tuning improved performance\n\n**Deployment fails:**\n- Verify output model exists: `client.models.retrieve(name=OUTPUT_NAME, workspace=\"default\")`\n- Check job logs: `client.jobs.get_logs(name=job.job.name, workspace=\"default\")`\n- Check deployment status: `client.inference.deployments.retrieve(name=deployment.name, workspace=\"default\")`\n- Ensure sufficient GPU resources for `executor_config={\"gpu\": 1, ...}`\n- Verify the deployment config matches this tutorial: `engine=\"vllm\"` with `vllm/vllm-openai:v0.22.1`\n\n\n## Next Steps\n\n- [Monitor training metrics](fine-tune-metrics) in detail\n- [Evaluate your fine-tuned model](../../evaluator/index) using the Evaluator service\n- Learn about [LoRA customization](./lora-customization-job) for resource-efficient fine-tuning", + "source_html": "

Evaluation Best Practices

\n

Manual Evaluation (Recommended)

\n\n

What to look for:

\n\n
\n

Hyperparameters

\n

For detailed information on all available hyperparameters, recommended values, and tuning guidance, refer to the Hyperparameter Reference.

\n
\n

Troubleshooting

\n

Job fails during model download:

\n\n

Job fails with OOM (Out of Memory) error:

\n
    \n
  1. First try: Reduce global_batch_size from 64 to 32 or 16 in batch={...}
  2. \n
  3. Still OOM: Keep micro_batch_size at 1 (already the minimum in this tutorial)
  4. \n
  5. Still OOM: Reduce max_seq_length from 2048 to 1024 or 512 in training={...}
  6. \n
  7. Last resort: Increase num_gpus_per_node and tensor_parallel_size in parallelism={...}
  8. \n
\n

Loss curves not decreasing (underfitting):

\n\n

Training loss decreases but validation loss increases (overfitting):

\n\n

Model output quality is poor despite good training metrics:

\n\n

Deployment fails:

\n\n

Next Steps

\n\n" } ] } \ No newline at end of file diff --git a/docs/fern/components/notebooks/sft-customization-job.ts b/docs/fern/components/notebooks/sft-customization-job.ts index 5b804ed7f2..a57f08e764 100644 --- a/docs/fern/components/notebooks/sft-customization-job.ts +++ b/docs/fern/components/notebooks/sft-customization-job.ts @@ -188,7 +188,7 @@ export default { cells: [ }, { "type": "markdown", - "source": "#### Evaluation Best Practices\n\n**Manual Evaluation** (Recommended)\n- Test with real-world examples from your use case\n- Compare responses to base model and expected outputs\n- Verify the model exhibits desired behavior changes\n- Check edge cases and error handling\n\n**What to look for:**\n- ✅ Model follows your desired output format\n- ✅ Applies domain knowledge correctly\n- ✅ Maintains general language capabilities\n- ✅ Avoids unwanted behaviors or biases\n- ❌ Doesn't hallucinate facts not in training data\n- ❌ Doesn't produce repetitive or nonsensical outputs\n\n---\n\n## Hyperparameters\n\nFor detailed information on all available hyperparameters, recommended values, and tuning guidance, refer to the [Hyperparameter Reference](../manage-customization-jobs/hyperparameters.md).\n\n---\n\n\n## Troubleshooting\n\n**Job fails during model download:**\n- Verify authentication secrets are configured (refer to [Managing Secrets](../../get-started/concepts/manage-secrets.md))\n- For gated Hugging Face models (Llama, Gemma), accept the license on the model page (for example, [meta-llama/Llama-3.2-1B-Instruct](https://huggingface.co/meta-llama/Llama-3.2-1B-Instruct))\n- Confirm the model fileset uses `token_secret=hf_secret.name` for gated models\n- Check `AutomodelJobInput` references use the `workspace/name` format: `model=f\"default/{MODEL_NAME}\"` and `dataset={\"training\": f\"default/{DATASET_NAME}\"}` (for example, `default/llama-3-2-1b-base`, `default/sft-dataset`)\n- Verify the model entity points at the fileset: `fileset=f\"default/{MODEL_NAME}\"`\n- Check job status: `client.jobs.get_status(name=job.job.name, workspace=\"default\")`\n\n**Job fails with OOM (Out of Memory) error:**\n1. **First try:** Reduce `global_batch_size` from 64 to 32 or 16 in `batch={...}`\n2. **Still OOM:** Keep `micro_batch_size` at 1 (already the minimum in this tutorial)\n3. **Still OOM:** Reduce `max_seq_length` from 2048 to 1024 or 512 in `training={...}`\n4. **Last resort:** Increase `num_gpus_per_node` and `tensor_parallel_size` in `parallelism={...}`\n\n**Loss curves not decreasing (underfitting):**\n- Increase training duration: raise `epochs` from 2 to 3-5 in `schedule={...}`\n- Adjust learning rate: try `1e-4` or `1e-5` instead of the default `5e-5` in `optimizer={...}`\n- Check data quality: Verify formatting, remove duplicates, ensure diversity\n\n**Training loss decreases but validation loss increases (overfitting):**\n- Reduce `epochs` from 2 to 1 in `schedule={...}`\n- Lower `learning_rate` from `5e-5` to `2e-5` or `1e-5` in `optimizer={...}`\n- Increase dataset size and diversity\n- Verify train/validation split has no data leakage\n\n**Model output quality is poor despite good training metrics:**\n- Training metrics optimize for loss, not your actual task—evaluate on real use cases\n- Review data quality, format, and diversity—metrics can be misleading with poor data\n- Try a different base model size or architecture\n- Adjust `learning_rate` and `global_batch_size`\n- Compare to baseline: Test base model to ensure fine-tuning improved performance\n\n**Deployment fails:**\n- Verify output model exists: `client.models.retrieve(name=OUTPUT_NAME, workspace=\"default\")`\n- Check deployment logs: `client.inference.deployments.get_logs(name=deployment.name, workspace=\"default\")`\n- Ensure sufficient GPU resources for `executor_config={\"gpu\": 1, ...}`\n- Verify the deployment config matches this tutorial: `engine=\"vllm\"` with `vllm/vllm-openai:v0.22.1`\n\n\n## Next Steps\n\n- [Monitor training metrics](fine-tune-metrics) in detail\n- [Evaluate your fine-tuned model](../../evaluator/index) using the Evaluator service\n- Learn about [LoRA customization](./lora-customization-job) for resource-efficient fine-tuning", - "source_html": "

Evaluation Best Practices

\n

Manual Evaluation (Recommended)

\n\n

What to look for:

\n\n
\n

Hyperparameters

\n

For detailed information on all available hyperparameters, recommended values, and tuning guidance, refer to the Hyperparameter Reference.

\n
\n

Troubleshooting

\n

Job fails during model download:

\n\n

Job fails with OOM (Out of Memory) error:

\n
    \n
  1. First try: Reduce global_batch_size from 64 to 32 or 16 in batch={...}
  2. \n
  3. Still OOM: Keep micro_batch_size at 1 (already the minimum in this tutorial)
  4. \n
  5. Still OOM: Reduce max_seq_length from 2048 to 1024 or 512 in training={...}
  6. \n
  7. Last resort: Increase num_gpus_per_node and tensor_parallel_size in parallelism={...}
  8. \n
\n

Loss curves not decreasing (underfitting):

\n\n

Training loss decreases but validation loss increases (overfitting):

\n\n

Model output quality is poor despite good training metrics:

\n\n

Deployment fails:

\n\n

Next Steps

\n\n" + "source": "#### Evaluation Best Practices\n\n**Manual Evaluation** (Recommended)\n- Test with real-world examples from your use case\n- Compare responses to base model and expected outputs\n- Verify the model exhibits desired behavior changes\n- Check edge cases and error handling\n\n**What to look for:**\n- ✅ Model follows your desired output format\n- ✅ Applies domain knowledge correctly\n- ✅ Maintains general language capabilities\n- ✅ Avoids unwanted behaviors or biases\n- ❌ Doesn't hallucinate facts not in training data\n- ❌ Doesn't produce repetitive or nonsensical outputs\n\n---\n\n## Hyperparameters\n\nFor detailed information on all available hyperparameters, recommended values, and tuning guidance, refer to the [Hyperparameter Reference](../manage-customization-jobs/hyperparameters.md).\n\n---\n\n\n## Troubleshooting\n\n**Job fails during model download:**\n- Verify authentication secrets are configured (refer to [Managing Secrets](../../get-started/concepts/manage-secrets.md))\n- For gated Hugging Face models (Llama, Gemma), accept the license on the model page (for example, [meta-llama/Llama-3.2-1B-Instruct](https://huggingface.co/meta-llama/Llama-3.2-1B-Instruct))\n- Confirm the model fileset uses `token_secret=hf_secret.name` for gated models\n- Check `AutomodelJobInput` references use the `workspace/name` format: `model=f\"default/{MODEL_NAME}\"` and `dataset={\"training\": f\"default/{DATASET_NAME}\"}` (for example, `default/llama-3-2-1b-base`, `default/sft-dataset`)\n- Verify the model entity points at the fileset: `fileset=f\"default/{MODEL_NAME}\"`\n- Check job status: `client.jobs.get_status(name=job.job.name, workspace=\"default\")`\n\n**Job fails with OOM (Out of Memory) error:**\n1. **First try:** Reduce `global_batch_size` from 64 to 32 or 16 in `batch={...}`\n2. **Still OOM:** Keep `micro_batch_size` at 1 (already the minimum in this tutorial)\n3. **Still OOM:** Reduce `max_seq_length` from 2048 to 1024 or 512 in `training={...}`\n4. **Last resort:** Increase `num_gpus_per_node` and `tensor_parallel_size` in `parallelism={...}`\n\n**Loss curves not decreasing (underfitting):**\n- Increase training duration: raise `epochs` from 2 to 3-5 in `schedule={...}`\n- Adjust learning rate: try `1e-4` or `1e-5` instead of the default `5e-5` in `optimizer={...}`\n- Check data quality: Verify formatting, remove duplicates, ensure diversity\n\n**Training loss decreases but validation loss increases (overfitting):**\n- Reduce `epochs` from 2 to 1 in `schedule={...}`\n- Lower `learning_rate` from `5e-5` to `2e-5` or `1e-5` in `optimizer={...}`\n- Increase dataset size and diversity\n- Verify train/validation split has no data leakage\n\n**Model output quality is poor despite good training metrics:**\n- Training metrics optimize for loss, not your actual task—evaluate on real use cases\n- Review data quality, format, and diversity—metrics can be misleading with poor data\n- Try a different base model size or architecture\n- Adjust `learning_rate` and `global_batch_size`\n- Compare to baseline: Test base model to ensure fine-tuning improved performance\n\n**Deployment fails:**\n- Verify output model exists: `client.models.retrieve(name=OUTPUT_NAME, workspace=\"default\")`\n- Check job logs: `client.jobs.get_logs(name=job.job.name, workspace=\"default\")`\n- Check deployment status: `client.inference.deployments.retrieve(name=deployment.name, workspace=\"default\")`\n- Ensure sufficient GPU resources for `executor_config={\"gpu\": 1, ...}`\n- Verify the deployment config matches this tutorial: `engine=\"vllm\"` with `vllm/vllm-openai:v0.22.1`\n\n\n## Next Steps\n\n- [Monitor training metrics](fine-tune-metrics) in detail\n- [Evaluate your fine-tuned model](../../evaluator/index) using the Evaluator service\n- Learn about [LoRA customization](./lora-customization-job) for resource-efficient fine-tuning", + "source_html": "

Evaluation Best Practices

\n

Manual Evaluation (Recommended)

\n\n

What to look for:

\n\n
\n

Hyperparameters

\n

For detailed information on all available hyperparameters, recommended values, and tuning guidance, refer to the Hyperparameter Reference.

\n
\n

Troubleshooting

\n

Job fails during model download:

\n\n

Job fails with OOM (Out of Memory) error:

\n
    \n
  1. First try: Reduce global_batch_size from 64 to 32 or 16 in batch={...}
  2. \n
  3. Still OOM: Keep micro_batch_size at 1 (already the minimum in this tutorial)
  4. \n
  5. Still OOM: Reduce max_seq_length from 2048 to 1024 or 512 in training={...}
  6. \n
  7. Last resort: Increase num_gpus_per_node and tensor_parallel_size in parallelism={...}
  8. \n
\n

Loss curves not decreasing (underfitting):

\n\n

Training loss decreases but validation loss increases (overfitting):

\n\n

Model output quality is poor despite good training metrics:

\n\n

Deployment fails:

\n\n

Next Steps

\n\n" } ] }; diff --git a/docs/fern/versions/latest.yml b/docs/fern/versions/latest.yml index 7a3ce91903..d7c870a4cb 100644 --- a/docs/fern/versions/latest.yml +++ b/docs/fern/versions/latest.yml @@ -213,6 +213,9 @@ navigation: - page: SFT Customization Job path: ../../customizer/tutorials/sft-customization-job.mdx slug: sft-customization-job + - page: DPO Customization Job + path: ../../customizer/tutorials/dpo-customization-job.mdx + slug: dpo-customization-job - page: LoRA Customization Job path: ../../customizer/tutorials/lora-customization-job.mdx slug: lora-customization-job diff --git a/docs/get-started/concepts/filtering.mdx b/docs/get-started/concepts/filtering.mdx index e41524373c..8e99441450 100644 --- a/docs/get-started/concepts/filtering.mdx +++ b/docs/get-started/concepts/filtering.mdx @@ -185,6 +185,6 @@ metrics = client.evaluation.metrics.list( ```python jobs = client.jobs.list( workspace="default", - filter={"source": "automodel", "status": "completed"}, + filter={"source": "customization", "status": "completed"}, ) ``` diff --git a/docs/troubleshooting/customizer.mdx b/docs/troubleshooting/customizer.mdx index d1dabbfedb..c59e681959 100644 --- a/docs/troubleshooting/customizer.mdx +++ b/docs/troubleshooting/customizer.mdx @@ -49,5 +49,6 @@ Batch and sequence-length fields differ by backend. Use the fully qualified path **Deployment fails:** - Verify the base model and adapter exist: `client.models.retrieve(name=MODEL_NAME, workspace="default")` -- the LoRA adapter appears in the base model's `adapters` list, not as a separate model entity -- Check deployment logs: `client.inference.deployments.get_logs(name=deployment.name, workspace="default")` +- Check job logs: `client.jobs.get_logs(name=job.name, workspace="default")` +- Check deployment status: `client.inference.deployments.retrieve(name=DEPLOYMENT_NAME, workspace="default")` - Ensure sufficient GPU resources for the model size