このコンテンツは選択した言語では利用できません。

Chapter 6. Running Kubeflow Trainer v2-based distributed training workloads


You can run distributed training workloads on Red Hat OpenShift AI by using Kubeflow Trainer v2. Kubeflow Trainer v2 replaces the framework-specific custom resource definitions (CRDs) from Training Operator v1, such as PyTorchJob, with a unified TrainJob API and pre-built ClusterTrainingRuntime infrastructure templates.

6.1. Understanding and using training runtimes

Kubeflow Trainer v2 uses training runtimes to define the distributed training infrastructure for your training jobs. Runtimes encapsulate best practices for distributed training, including PyTorch distributed configuration, torchrun setup, environment variables, and container image defaults.

This section describes how to view the pre-built ClusterTrainingRuntime resources provided by Red Hat OpenShift AI, and how to create custom namespace-scoped TrainingRuntime resources when you need project-specific configuration.

6.1.1. Understanding ClusterTrainingRuntimes

ClusterTrainingRuntimes are cluster-scoped infrastructure templates that define the distributed training environment for your training jobs. They are created by the platform administrator and are available to all projects in the cluster. They replace the per-job infrastructure configuration that was required in Training Operator v1 (PyTorchJob), where you had to specify the full pod specification, environment variables, and distributed training setup for every job.

With Kubeflow Trainer v2, your TrainJob resource references a ClusterTrainingRuntime through the runtimeRef field. The runtime handles the complexity of configuring torchrun, environment variables(MASTER_ADDR, MASTER_PORT), node coordination, and container image defaults, so you can focus on your training code and resource requirements.

Red Hat OpenShift AI provides the following pre-built ClusterTrainingRuntimes:

Expand
RuntimeDescription

torch-distributed

General-purpose PyTorch distributed training with CUDA support

torch-distributed-rocm

PyTorch distributed training for AMD ROCm GPUs

torch-distributed-cuda128-torch29-py312

PyTorch 2.9 with CUDA 12.8, Python 3.12

training-hub

Training Hub runtime with built-in fine-tuning algorithms (OSFT, SFT)

training-hub-th05-cuda128-torch29-py312

Training Hub runtime with CUDA 12.8, PyTorch 2.9, Python 3.12

6.1.1.1. Viewing available runtimes

Prerequisites

  • You have installed the OpenShift CLI (oc).
  • You have access to an OpenShift cluster with Red Hat OpenShift AI installed.

Procedure

To list the available ClusterTrainingRuntimes in your cluster, run the following command:

oc get clustertrainingruntime

Example output:

NAME                                             AGE
torch-distributed                                5m
torch-distributed-rocm                           5m
torch-distributed-th03-cuda128-torch28-py312     5m
training-hub                                     5m
training-hub03-cuda128-torch28-py312             5m

To view the details of a specific runtime, run the following command:

oc get clustertrainingruntime torch-distributed -o yaml

6.1.2. ClusterTrainingRuntime structure

The following example shows the structure of the torch-distributed ClusterTrainingRuntime. This runtime is pre-installed in Red Hat OpenShift AI and handles PyTorch distributed training configuration, including torchrun setup, environment variables, and node coordination:

apiVersion: trainer.kubeflow.org/v1alpha1
kind: ClusterTrainingRuntime
metadata:
 name: torch-distributed
spec:
 mlPolicy:
   torch:
     numProcPerNode: auto
 template:
   spec:
     replicatedJobs:
       - name: Node
         template:
           spec:
             template:
               spec:
                 containers:
                   - name: trainer
                     env:
                       - name: MASTER_ADDR
                         value: "$(JOB_NAME)-node-0-0.$(JOB_NAME)"
                       - name: MASTER_PORT
                         value: "29500"
                     command:
                       - torchrun
Note

You do not need to create the torch-distributed ClusterTrainingRuntime. It is provided out of the box when the platform administrator enables the Kubeflow Trainer component in the DataScienceCluster. Only the platform administrator creates or modifies ClusterTrainingRuntimes.

6.1.3. How runtimeRef connects a TrainJob to a runtime

In your TrainJob resource, you reference a runtime using the runtimeRef field. This field specifies the name and kind of the runtime to use:

spec:
 runtimeRef:
   name: torch-distributed
   kind: ClusterTrainingRuntime

where:

kind
  • ClusterTrainingRuntime A cluster-scoped runtime available to all projects. These are the pre-built runtimes provided by Red Hat OpenShift AI.
  • TrainingRuntime A namespace-scoped runtime that you create in your own project. See Creating a custom TrainingRuntime resource below for details.

6.1.4. Creating a custom TrainingRuntime resource

A project administrator can create a namespace-scoped TrainingRuntime resource when they need a custom training environment that differs from the pre-built ClusterTrainingRuntime resources provided by the platform.

A TrainingRuntime is useful when you need to:

  • Use a custom container image with additional libraries or dependencies pre-installed.
  • Set custom environment variables for all training pods.
  • Define default resource requests and limits specific to your project.
  • Configure custom volume mounts or init containers.
Note

For most use cases, the pre-built ClusterTrainingRuntime resources are sufficient. Create a custom TrainingRuntime only when you need project-specific configuration that cannot be overridden in the TrainJob resource.

Prerequisites

  • Your platform administrator has installed Red Hat OpenShift AI with the required distributed training components as described in Installing the distributed workloads components (for disconnected environments, see Installing the distributed workloads components). *You can access the OpenShift Console for the cluster where OpenShift AI is installed.
  • You have project administrator access for the project where you want to create the TrainingRuntime.

    • If you created the project, you automatically have administrator access.
    • If you did not create the project, the platform administrator must give you project administrator access.

Procedure

  1. Log in to the OpenShift Console.
  2. Create a TrainingRuntime resource, as follows:

    1. In the Administrator perspective, click Home > Search.
    2. From the Project list, select your project.
    3. Click the Resources list, and in the search field, type TrainingRuntime.
    4. Select TrainingRuntime, and click Create TrainingRuntime. The Create TrainingRuntime page opens, with default YAML code automatically added.
    5. Replace the default YAML code with your custom runtime definition. The following example shows a custom TrainingRuntime with a custom container image and default resource configuration:

      apiVersion: trainer.kubeflow.org/v1alpha1
      kind: TrainingRuntime
      metadata:
        name: custom-torch-runtime
        namespace: my-project
      spec:
        mlPolicy:
          torch:
            numProcPerNode: auto
        template:
          spec:
            replicatedJobs:
              - name: Node
                template:
                  spec:
                    template:
                      spec:
                        containers:
                          - name: trainer
                            image: registry.redhat.io/rhoai/odh-training-cuda128-torch28-py312-rhel9:v3.0
                            env:
                              - name: MASTER_ADDR
                                value: "$(JOB_NAME)-node-0-0.$(JOB_NAME)"
                              - name: MASTER_PORT
                                value: "29500"
                              - name: MY_CUSTOM_ENV_VAR
                                value: "my-custom-value"
                            command:
                              - torchrun
                            resources:
                              requests:
                                cpu: "4"
                                memory: "16Gi"
                              limits:
                                cpu: "4"
                                memory: "16Gi"
    6. Click Create.

Verification

  • Using the user interface:

    • In the OpenShift Console, in the Administrator perspective, click Home > Search.
    • From the Project list, select your project.
    • Click the Resources list and search for TrainingRuntime.
    • Verify that your custom runtime appears in the list.
  • Using the CLI:
oc get trainingruntime -n <your-namespace>

6.2. Using Kubeflow Trainer v2 to run distributed training workloads

You can use Kubeflow Trainer v2 to run distributed PyTorch training workloads on Red Hat OpenShift AI. This section describes how to create a TrainJob resource by using the OpenShift Console or the OpenShift CLI (oc).

A TrainJob resource references a ClusterTrainingRuntime (or custom TrainingRuntime) that provides the distributed training infrastructure, so you only need to specify your training code, the number of nodes, and resource requirements.

6.2.1. Creating a Kubeflow Trainer TrainJob resource

You can create a TrainJob resource to run a distributed PyTorch training job by using the OpenShift Console.

Prerequisites

  • You can access an OpenShift cluster that has multiple worker nodes with supported NVIDIA GPUs or AMD GPUs.
  • Your cluster administrator has configured the cluster as follows:

    • Installed Red Hat OpenShift AI with the required distributed training components.
    • Configured the distributed training resources.
    • Installed the JobSet Operator from OLM.
    • A ClusterTrainingRuntime is available in your cluster (for example, torch-distributed). To verify, run oc get clustertrainingruntime.
  • You have administrator access for the project.

    • If you created the project, you automatically have administrator access.
    • If you did not create the project, your cluster administrator must give you administrator access.

Procedure

  1. Log in to the OpenShift Console.
  2. Create a TrainJob resource, as follows:

    1. In the Administrator perspective, click Home Search.
    2. From the Project list, select your project.
    3. Click the Resources list, and in the search field, type Job.
  3. Select TrainJob, and click Create TrainJob. The Create TrainJob page opens, with default YAML code automatically added.
  4. Update the metadata to replace the name and namespace values with the values for your environment, as shown in the following example:

    metadata:
      name: pytorch-multi-node-job
      namespace: test-namespace
  5. Configure the runtime reference to specify which ClusterTrainingRuntime to use, as shown in the following example:

    spec:
      runtimeRef:
        name: torch-distributed
        kind: ClusterTrainingRuntime
  6. Configure the trainer section with your ClusterTrainingRuntime name, command, and the number of distributed training nodes, as shown in the following example:

    spec:
      runtimeRef:
        name: torch-distributed
        kind: ClusterTrainingRuntime
      trainer:
        command: ["python", "/workspace/scripts/train.py"]
        numNodes: 3

    where:

    numNodes
    Specifies the total number of distributed training nodes. This replaces the separate Master and Worker replica specifications used in Training Operator v1. For example, a v1 job with 1 Master and 2 Workers is equivalent to numNodes: 3 in v2.
  7. To use a ConfigMap resource to provide the training script for the TrainJob pods, add the ConfigMap volume mount information, as shown in the following example:

    spec:
      runtimeRef:
        name: torch-distributed
        kind: ClusterTrainingRuntime
      trainer:
        command: ["python", "/workspace/scripts/train.py"]
        numNodes: 3
      podTemplateOverrides:
        - targetJobs:
            - name: node
          spec:
            volumes:
              - name: training-script-volume
                configMap:
                  name: training-script-configmap
            containers:
              - name: node
                volumeMounts:
                  - name: training-script-volume
                    mountPath: /workspace/scripts
  8. Add the appropriate resource constraints for your environment, as shown in the following example:

    spec:
      runtimeRef:
        name: torch-distributed
        kind: ClusterTrainingRuntime
      trainer:
        command: ["python", "/workspace/scripts/train.py"]
        numNodes: 3
        resourcesPerNode:
          requests:
            cpu: "4"
            memory: "8Gi"
            nvidia.com/gpu: 2
          limits:
            cpu: "4"
            memory: "8Gi"
            nvidia.com/gpu: 2
      podTemplateOverrides:
        - targetJobs:
            - name: node
          spec:
            volumes:
              - name: training-script-volume
                configMap:
                  name: training-script-configmap
            containers:
              - name: node
                volumeMounts:
                  - name: training-script-volume
                    mountPath: /workspace/scripts

    where:

    resourcesPerNode
    applies the same resource requests and limits to every training node. This replaces the per-replica resource configuration used in Training Operator v1, where you had to specify resources separately for the Master and each Worker.
  9. Click Create.

Verification

  1. In the OpenShift Console, open the Administrator perspective.
  2. From the Project list, select your project.

    1. Click Home > Search > TrainJob and verify that the job was created.
    2. Click Workloads > Pods and verify that the requested training node pods are running.
  3. (Optional) If you enabled progress tracking, navigate to the OpenShift AI dashboard, click Model training, and verify that real-time progress metrics are displayed.

6.2.2. Creating a Kubeflow Trainer TrainJob resource by using the CLI

You can use the OpenShift CLI (oc) to create a TrainJob resource to run a distributed PyTorch training job.

Prerequisites

  • You can access an OpenShift cluster that has multiple worker nodes with supported NVIDIA GPUs or AMD GPUs.
  • Your cluster administrator has configured the cluster as follows:

  • A ClusterTrainingRuntime is available in your cluster (for example, torch-distributed). To verify, run:

    oc get clustertrainingruntime
  • You have administrator access for the project.

    • If you created the project, you automatically have administrator access.
    • If you did not create the project, your cluster administrator must give you administrator access.
  • You have installed the OpenShift CLI (oc) as described in the appropriate documentation for your cluster:

    • Installing the OpenShift CLI for OpenShift Container Platform
    • Installing the OpenShift CLI for Red Hat OpenShift Service on AWS

Procedure

  1. Log in to the OpenShift CLI (oc):

    oc login --token=<token> --server=<server>
  2. Create a file named train.py and populate it with your training script, as follows:

    cat <<EOF > train.py
    <paste your content here>
    EOF
  3. Create a ConfigMap resource to store the training script, as follows:

    oc create configmap training-script-configmap
    --from-file=train.py -n <your-namespace>

    Replace <your-namespace> with the name of your project.

  4. Create a file named trainjob.yaml to define the distributed training job setup, as follows:

    cat <<'EOF' > trainjob.yaml
    apiVersion: trainer.kubeflow.org/v1alpha1
    kind: TrainJob
    metadata:
      name: pytorch-multi-node-job
      namespace: <your-namespace>
    spec:
      runtimeRef:
        name: torch-distributed
        kind: ClusterTrainingRuntime
      trainer:
        command: ["python", "/workspace/scripts/train.py"]
        numNodes: 3
        resourcesPerNode:
          requests:
            cpu: "4"
            memory: "8Gi"
            nvidia.com/gpu: 2
          limits:
            cpu: "4"
            memory: "8Gi"
            nvidia.com/gpu: 2
      podTemplateOverrides:
        - targetJobs:
            - name: node
          spec:
            volumes:
              - name: training-script-volume
                configMap:
                  name: training-script-configmap
            containers:
              - name: node
                volumeMounts:
                  - name: training-script-volume
                    mountPath: /workspace/scripts
    EOF

    Replace <your-namespace> with the name of your project. Update the numNodes, resource constraints, and image as needed for your environment.

  5. Create the distributed training job, as follows:

    oc apply -f trainjob.yaml
  6. Monitor the running distributed training job, as follows:

    oc get trainjob -n <your-namespace>

    Replace <your-namespace> with the name of your project.

  7. Check the pod status, as follows:

    oc logs <pod-name> -n <your-namespace>
  8. Replace <pod-name> with the name of the pod and <your-namespace> with the name of your project.

6.2.3. Suspending a training job

You can suspend a running TrainJob to pause training and free up cluster resources. If JIT checkpointing is configured, the training state is automatically saved before the pods are terminated.

Prerequisites

  1. You have an active TrainJob currently running on the OpenShift cluster.
  2. You have configured JIT (Just-In-Time) checkpointing in your training script.
  3. You have confirmed that your training script is configured to handle the SIGTERM signal.
  4. You have the OpenShift CLI (oc) installed and are logged in with a user that has patch permissions for the TrainJob resource in the target namespace.

Procedure

Run the following command:

oc patch trainjob pytorch-multi-node-job -n <your-namespace> --type=merge -p {"spec":{"suspend":true}}

6.2.4. Resuming a training job

Prerequisites

  1. You have a TrainJob that is currently in a suspended state (spec.suspend: true).
  2. You have a Persistent Volume Claim (PVC) or S3-compatible storage containing the latest saved checkpoint files from the previous session.
  3. Your training script is configured to automatically detect and load the most recent checkpoint from the designated storage path upon startup.
  4. The cluster has sufficient available resources to fulfill the original resource requests of the job.

Procedure

Run the following command:

oc patch trainjob pytorch-multi-node-job -n <your-namespace> --type=merge -p {"spec":{"suspend":false}}

When resumed, the job automatically loads the latest checkpoint and continues training from where it stopped.

For more information about JIT checkpointing and suspend/resume, see Using RHAI trainers for progress tracking and checkpointing.

6.2.5. Deleting a training job

Prerequisites

  1. You have identified the name of the TrainJob and the namespace where it is deployed.
  2. You have the OpenShift CLI (oc) installed and are logged in with a user that has delete permissions for the TrainJob resource.

Procedure

Run the following command:

oc delete trainjob/pytorch-multi-node-job -n <your-namespace>

where

<your-namespace>
is the name of your project.

6.3. Using the Kubeflow SDK to run distributed training workloads

You can use the Kubeflow Python SDK to create and manage TrainJob resources programmatically from an OpenShift AI workbench. The SDK provides a higher-level interface that handles the creation of the TrainJob resource, configures the distributed training environment, and sets up communication between nodes.

When you use the SDK with Red Hat OpenShift AI trainers (TransformersTrainer or TrainingHubTrainer), progress tracking and JIT checkpointing are enabled by default.

6.3.1. Creating a Kubeflow Trainer TrainJob resource by using the SDK

You can create a TrainJob resource programmatically by using the Kubeflow SDK from an OpenShift AI workbench.

Prerequisites

  • You can access an OpenShift cluster that has multiple worker nodes with supported NVIDIA GPUs or AMD GPUs.
  • Your cluster administrator has configured the cluster as follows:

  • A ClusterTrainingRuntime is available in your cluster (for example, torch-distributed). To verify, run oc get clustertrainingruntime.
  • You can access an OpenShift AI workbench with Python 3.9 or later.
  • You have installed the Kubeflow SDK:

    pip install kubeflow --index-url
    https://console.redhat.com/api/pypi/public-rhai/rhoai/3.2/cuda12.9-ubi9/simple/

    Verify the installation:

    pip show kubeflow

    Example output:

    Name: kubeflow
    Version: 0.2.1+rhai0

Procedure

  1. Obtain the API server URL and authentication token from your OpenShift cluster, as follows:

    # Get the API server URL
    oc whoami --show-server
    
    # Get your authentication token
    oc whoami --show-token
    Note

    If your cluster uses a self-signed certificate, you must set configuration.verify_ssl = False in your code to avoid SSL verification errors.

  2. In your workbench, create a Python script or notebook cell and define your training function, configure authentication, and submit the training job, as follows:

    from kubernetes import client
    from kubeflow.trainer import TrainerClient
    from kubeflow.trainer.rhai import TransformersTrainer
    from kubeflow.common.types import KubernetesBackendConfig
    
    
    # Define your training function
    def train_func():
        from transformers import (
            AutoModelForSequenceClassification,
            AutoTokenizer,
            Trainer,
            TrainingArguments,
        )
        from datasets import load_dataset
    
        # Load model and tokenizer
        model = AutoModelForSequenceClassification.from_pretrained(
            "bert-base-uncased", num_labels=2
        )
        tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")
    
        # Load and tokenize dataset
        dataset = load_dataset("imdb", split="train[:1000]")
    
        def tokenize_function(examples):
            return tokenizer(
                examples["text"], padding="max_length", truncation=True, max_length=512
            )
    
        tokenized_dataset = dataset.map(tokenize_function, batched=True)
    
        # Configure training
        training_args = TrainingArguments(
            output_dir="/tmp/output",
            num_train_epochs=3,
            per_device_train_batch_size=8,
        )
    
        # Train
        trainer = Trainer(
            model=model, args=training_args, train_dataset=tokenized_dataset
        )
        trainer.train()
    
    
    # Configure authentication for the workbench
    api_server = "<your-api-server-url>"  # e.g., "https://api.cluster.example.com:6443"
    token = "<your-bearer-token>"
    
    configuration = client.Configuration()
    configuration.host = api_server
    configuration.api_key = {"authorization": f"Bearer {token}"}
    # Uncomment the following line if your cluster uses a self-signed certificate
    # configuration.verify_ssl = False
    
    # Create the client with authentication
    trainer_client = TrainerClient(
        backend_config=KubernetesBackendConfig(client_configuration=configuration)
    )
    
    # Create the trainer configuration
    trainer = TransformersTrainer(
        func=train_func,
        num_nodes=2,
        resources_per_node={"nvidia.com/gpu": 1},
    )
    
    # Get the runtime and submit the training job
    runtime = trainer_client.backend.get_runtime("torch-distributed")
    job_name = trainer_client.train(trainer=trainer, runtime=runtime)
    print(f"Job {job_name} submitted!")

    Replace <your-api-server-url> with the API server URL from step 1 and <your-bearer-token> with the authentication token from step 1.

    The TransformersTrainer parameters are:

    Expand
    ParameterTypeDescription

    func

    Callable

    The Python function contains your training code.

    num_nodes

    int

    Number of distributed training nodes (default: 1).

    resources_per_node

    dict

    Resource requests per node, for example "nvidia.com/gpu": 1, "memory": "16Gi".

Verification

  1. Verify that the job was submitted by checking the job status:

    job = trainer_client.get_job(job_name)
    print(f"   Status: {job.status}")
  2. Verify that the training pods are running:

    oc get pods -n <your-namespace> -l job-name=<job-name>

    Replace <your-namespace> with the name of your project and <job-name> with the name of your training job.

  3. (Optional) Verify that real-time progress metrics are displayed for your training job. Navigate to the OpenShift AI dashboard and click TrainingJobs to view these metrics.

6.3.2. Configuring JIT checkpointing with a PVC

To enable JIT checkpointing with persistent storage, use the pvc:// URI scheme in the output_dir parameter of the TransformersTrainer. The SDK automatically mounts the PVC on all training pods.

Note

Your project must have a PersistentVolumeClaim (PVC) with ReadWriteMany (RWX) access mode.

trainer = TransformersTrainer(
    func=train_func,
    num_nodes=2,
    resources_per_node={"nvidia.com/gpu": 1},
    output_dir="pvc://training-checkpoints/checkpoints",
)

When JIT checkpointing is configured:

If pods receive a termination signal (SIGTERM), the trainer automatically saves a checkpoint. When the job restarts, training automatically resumes from the latest checkpoint. For more information about progress tracking and JIT checkpointing, see Using RHAI trainers for progress tracking and checkpointing.

6.3.3. Disabling progress tracking

Progress tracking is enabled by default when using RHAI trainers. To disable progress tracking, set enable_progression_tracking=False:

trainer = TransformersTrainer(
    func=train_func,
    num_nodes=2,
    resources_per_node={"nvidia.com/gpu": 1},
    enable_progression_tracking=False,
)

6.4. Fine-tuning a model by using Kubeflow Trainer v2

Fine-tuning is the process of customizing a Large Language Model (LLM) for a specific task by using labelled data. You can use Kubeflow Trainer v2 with Training Hub runtimes and the Kubeflow Python SDK to fine-tune LLMs in Red Hat OpenShift AI.

Red Hat OpenShift AI supports the following fine-tuning algorithms through Training Hub:

Orthogonal Subspace Fine-Tuning (OSFT)
Enables continual learning of pre-trained or instruction-tuned models without catastrophic forgetting. OSFT does not require a supplementary dataset to maintain the distribution of the original model.
Supervised Fine-Tuning (SFT)
Standard fine-tuning for task adaptation using PyTorch Fully Sharded Data Parallel (FSDP) to distribute training across multiple GPUs and nodes.

6.4.1. Configuring the fine-tuning job

Before you can use a training job to fine-tune a model, you must configure the training job. You must set the training parameters, select the fine-tuning algorithm, and configure the Kubeflow SDK.

Note

The code in this procedure specifies how to configure an example fine-tuning job. If you have the specified resources, you can run the example code without editing it. Alternatively, you can modify the example code to specify the appropriate configuration for your fine-tuning job.

Prerequisites

  • You can access an OpenShift cluster that has sufficient worker nodes with supported accelerators to run your training or tuning job.

    • For the OSFT example: 2 nodes with 2 NVIDIA L40/L40S GPUs each (4 GPUs total), 4 CPUs and 32 GiB memory per node.
    • For the SFT example: 2 nodes with 2 NVIDIA GPUs each (Ampere-based or newer recommended), 4 CPUs and 64 GiB memory per node.
  • Your cluster administrator has configured the cluster as follows:

  • A training-hub ClusterTrainingRuntime is available in your cluster. To verify, run oc get clustertrainingruntime training-hub.
  • You can access a workbench that is suitable for distributed training, as described in Creating a workbench for distributed training.
  • You can access a dynamic storage provisioner that supports ReadWriteMany (RWX) Persistent Volume Claim (PVC) provisioning, such as Red Hat OpenShift Data Foundation.
  • You have administrator access for the project.

    • If you created the project, you automatically have administrator access.
    • If you did not create the project, your cluster administrator must give you administrator access.

Procedure

  1. Open the workbench:
  2. Log in to the Red Hat OpenShift AI web console.
  3. Click Projects and click your project.
  4. Click the Workbenches tab.
  5. Ensure that the workbench uses a storage class with RWX capability.
  6. If your workbench is not already running, start the workbench.
  7. Click the Open link to open the IDE in a new window.
  8. Click File > New > Notebook.
  9. Install the required packages. In a notebook cell, add the code to install the dependencies, as follows:

    !pip install kubeflow --index-url https://console.redhat.com/api/pypi/public-rhai/rhoai/3.2/cuda12.9-ubi9/simple/
    
    !pip install training-hub==0.3.0
  10. Select the cell, and click Run > Run selected cell.
  11. Configure the Kubeflow SDK client authentication by creating a cell with the following content:

    from kubernetes import client as k8s
    from kubeflow.common.types import KubernetesBackendConfig
    from kubeflow.trainer import TrainerClient
    
    api_server = "<API_SERVER>"
    token = "<TOKEN>"
    
    configuration = k8s.Configuration()
    configuration.host = api_server
    configuration.api_key = {"authorization": f"Bearer {token}"}
    # Uncomment if your cluster API server uses a self-signed certificate
    # configuration.verify_ssl = False
    
    backend_cfg = KubernetesBackendConfig(client_configuration=configuration)
    client = TrainerClient(backend_cfg)
  12. Edit the api_server and token parameters to enter the values to authenticate to your OpenShift cluster. Run the cell to configure the SDK client authentication.
  13. Verify that a Training Hub runtime is available, as follows:

    for runtime in client.list_runtimes():
        print("Found runtime: " + str(runtime))
        if runtime.name == "training-hub":
            th_runtime = runtime
            print("Selected runtime: " + str(th_runtime))
  14. Set the training parameters. The training parameters depend on the fine-tuning algorithm you choose. See the following sections for algorithm-specific configuration:

    • OSFT training parameters for OSFT fine-tuning
    • SFT training parameters for SFT fine-tuning
  15. Click File > Save Notebook As, enter an appropriate file name, and click Save.

6.4.1.1. OSFT training parameters for OSFT fine-tuning

Create a cell with the following content to set the OSFT training parameters:

params = {
    # Model + Data Paths
    "model_path": "Qwen/Qwen2.5-1.5B-Instruct",
    "data_path": "/mnt/shared/table-gpt-data/train/train_All_5000.jsonl",
    "ckpt_output_dir": "/mnt/shared/checkpoints-logs-dir",
    "data_output_path": "/mnt/shared/osft-json/_data",

    # Training Hyperparameters
    "unfreeze_rank_ratio": 0.25,   # OSFT-specific: controls the subspace dimension
    "effective_batch_size": 128,
    "learning_rate": 5.0e-6,
    "num_epochs": 1,
    "lr_scheduler": "cosine",
    "warmup_steps": 0,
    "seed": 42,

    # Performance Hyperparameters
    "use_liger": True,             # OSFT-specific: enables Liger kernel optimization
    "max_tokens_per_gpu": 64000,
    "max_seq_len": 8192,

    # Checkpointing Settings
    "save_final_checkpoint": True,
    "checkpoint_at_epoch": False,

    # Distributed training (delegated to Kubeflow Trainer)
    "nproc_per_node": 2,
    "nnodes": 2,
}

Optional: Edit the parameters to suit your model, dataset, and resources.

6.4.1.2. SFT training parameters for SFT fine-tuning

Create a cell with the following content to set the SFT training parameters:

training_parameters = {
    # Model + Data Paths
    "model_path": "/mnt/shared/Qwen/Qwen2.5-1.5B-Instruct",
    "data_path": "/mnt/shared/table-gpt-data/train/train_All_5000.jsonl",
    "ckpt_output_dir": "/mnt/shared/checkpoints",
    "data_output_dir": "/mnt/shared/traininghub-sft-data",

    # Training Hyperparameters
    "effective_batch_size": 128,
    "learning_rate": 5e-6,
    "num_epochs": 1,
    "lr_scheduler": "cosine",
    "warmup_steps": 0,
    "seed": 42,

    # Performance Hyperparameters
    "max_tokens_per_gpu": 10000,
    "max_seq_len": 8192,

    # Checkpointing Settings
    "checkpoint_at_epoch": True,
    "accelerate_full_state_at_epoch": False,

    # FSDP Configuration
    "fsdp_options": {
        "sharding_strategy": "FULL_SHARD",
    },
    "nproc_per_node": 1,
    "nnodes": 2,
}

Optional: Edit the parameters to suit your model, dataset, and resources.

6.4.1.3. Example fine-tuning notebooks

For complete, runnable example notebooks, see the following resources:

  • OSFT Continual Learning example — Fine-tune Qwen/Qwen2.5-1.5B-Instruct with OSFT for continual learning without catastrophic forgetting. Includes model evaluation before and after fine-tuning.

    • Notebook: osft-example.ipynb
    • Hardware: 2 nodes x 2 GPUs (L40/L40S or equivalent), 4 GPUs total
  • SFT with Training Hub example — Fine-tune Qwen/Qwen2.5-1.5B-Instruct with SFT and PyTorch FSDP for distributed training.

    • Notebook: sft.ipynb
    • Hardware: validated with Qwen2.5 1.5B, 7B, and 14B models on 4x NVIDIA A100/80GB

6.4.1.4. Training data format

Training Hub fine-tuning algorithms support training data in JSON Lines (.jsonl) format with a messages structure:

{"messages": [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Hello!"}, {"role": "assistant", "content": "Hi there! How can I help you?"}]}
{"messages": [{"role": "user", "content": "What is OSFT?"}, {"role": "assistant", "content": "OSFT stands for Orthogonal Subspace Fine-Tuning..."}]}

6.4.2. Running the fine-tuning job

When you run a training job to fine-tune a model, you must specify the resources needed, provide any authorization credentials required, and configure shared storage for datasets, models, and checkpoints.

Note

The code in this procedure specifies how to run the example fine-tuning job. If you have the specified resources, you can run the example code without editing it. Alternatively, you can modify the example code to specify the appropriate details for your fine-tuning job.

Prerequisites

  • You have acces to an OpenShift cluster that has sufficient worker nodes with supported accelerators to run your training or tuning job.
  • You have access to a workbench that is suitable for distributed training, as described in Creating a workbench for distributed training.
  • You have administrator access for the project.

    • If you created the project, you automatically have administrator access.
    • If you did not create the project, your cluster administrator must give you administrator access.
  • You have access to a model.
  • You have access to data that you can use to fine-tune the model.
  • You have configured the fine-tuning job as described in Configuring the fine-tuning job.
  • You can access a dynamic storage provisioner that supports ReadWriteMany (RWX) Persistent Volume Claim (PVC) provisioning, such as Red Hat OpenShift Data Foundation.
  • A PersistentVolumeClaim resource named shared with RWX access mode is attached to your workbench.
  • If using a gated model from the Hugging Face Hub, you have a Hugging Face account and access token. For more information, search for "user access tokens" in the Hugging Face documentation.

Procedure

  1. Open the workbench, as follows:

    1. Log in to the Red Hat OpenShift AI web console.
    2. Click Projects and click your project.
    3. Click the Workbenches tab. If your workbench is not already running, start the workbench.
    4. Click the Open link to open the IDE in a new window.
    5. Click File > Open, and open the Jupyter notebook that you used to configure the fine-tuning job.
  2. Create a cell to run the job.
  3. Depending on your fine-tuning method, add the code for either OSFT or SFT:

    1. To enable OSFT fine-tuning, add:

      from kubeflow.trainer.rhai import TrainingHubAlgorithms, TrainingHubTrainer
      from kubeflow.trainer.options.kubernetes import (
          ContainerOverride,
          PodSpecOverride,
          PodTemplateOverride,
          PodTemplateOverrides,
      )
      
      PVC_NAME = "shared"
      
      job_name = client.train(
          trainer=TrainingHubTrainer(
              algorithm=TrainingHubAlgorithms.OSFT,
              func_args=params,
              env={
                  "HF_HOME": "/mnt/shared/huggingface",
                  "TRITON_CACHE_DIR": "/mnt/shared/.triton",
                  "XDG_CACHE_HOME": "/opt/app-root/src/.cache",
                  "NCCL_DEBUG": "INFO",
              },
              resources_per_node={
                  "nvidia.com/gpu": 2,
                  "memory": "32Gi",
                  "cpu": 4,
              },
          ),
          options=[
              PodTemplateOverrides(
                  PodTemplateOverride(
                      target_jobs=["node"],
                      spec=PodSpecOverride(
                          volumes=[
                              {
                                  "name": "work",
                                  "persistentVolumeClaim": {"claimName": PVC_NAME},
                              },
                          ],
                          containers=[
                              ContainerOverride(
                                  name="node",
                                  volume_mounts=[
                                      {
                                          "name": "work",
                                          "mountPath": "/mnt/shared",
                                      },
                                  ],
                              ),
                          ],
                      ),
                  )
              )
          ],
          runtime=th_runtime,
      )
    2. To enable SFT fine-tuning, add:

      from kubeflow.trainer.rhai import TrainingHubAlgorithms, TrainingHubTrainer
      from kubeflow.trainer.options.kubernetes import (
          ContainerOverride,
          PodSpecOverride,
          PodTemplateOverride,
          PodTemplateOverrides,
      )
      
      PVC_NAME = "shared"
      
      job_name = client.train(
          trainer=TrainingHubTrainer(
              algorithm=TrainingHubAlgorithms.SFT,
              func_args=training_parameters,
              env={
                  "HF_HOME": "/mnt/shared/huggingface",
                  "TRITON_CACHE_DIR": "/mnt/shared/.triton",
                  "XDG_CACHE_HOME": "/opt/app-root/src/.cache",
                  "NCCL_DEBUG": "INFO",
              },
              resources_per_node={
                  "nvidia.com/gpu": 2,
                  "memory": "64Gi",
                  "cpu": 4,
              },
          ),
          options=[
              PodTemplateOverrides(
                  PodTemplateOverride(
                      target_jobs=["node"],
                      spec=PodSpecOverride(
                          volumes=[
                              {
                                  "name": "work",
                                  "persistentVolumeClaim": {"claimName": PVC_NAME},
                              },
                          ],
                          containers=[
                              ContainerOverride(
                                  name="node",
                                  volume_mounts=[
                                      {
                                          "name": "work",
                                          "mountPath": "/mnt/shared",
                                      },
                                  ],
                              ),
                          ],
                      ),
                  )
              )
          ],
          runtime=th_runtime,
      )

where:

resources_per_node
values should be updated according to the job requirements and the resources available. If you use AMD accelerators, in the resources_per_node entry, change nvidia.com/gpu to amd.com/gpu. If the RWX PersistentVolumeClaim resource attached to your workbench has a different name instead of shared, update the PVC_NAME value and the mountPath as needed. If using a gated Hugging Face model, add "HF_TOKEN": "<your-token>" to the env dictionary.

Verification

View the progress of the job as follows:

  1. Create a cell with the following content:

    for logline in client.get_job_logs(job_name, follow=True):
        print(logline, end="")
  2. Run the cell to view the job progress.
  3. (Optional) Check the final job status:

    job = client.get_job(job_name)
    print(f"Name: {job.name}")
    print(f"Status: {job.status}")
    print(f"Nodes: {job.num_nodes}")
    print(f"Runtime: {job.runtime.name}")

6.4.3. Deleting the fine-tuning job

When you no longer need the fine-tuning job, delete the job to release the resources.

Procedure

  1. Log in to the Red Hat OpenShift AI web console.
  2. Click Projects and click your project.
  3. Click the Workbenches tab. If your workbench is not already running, start the workbench.
  4. Click the Open link to open the IDE in a new window.
  5. Click File > Open, and open the Jupyter notebook that you used to configure and run the fine-tuning job.
  6. Create a cell with the following content:

    client.delete_job(name=job_name)
  7. Run the cell to delete the job.

Verification

  1. In the OpenShift Console, in the Administrator perspective, click Workloads > Jobs.
  2. From the Project list, select your project.
  3. Verify that the specified job is not listed.

6.5. Example Kubeflow Trainer TrainJob resources

This section provides example TrainJob resources and training scripts for common distributed training scenarios with Kubeflow Trainer v2.

6.5.1. Example TrainJob resource for multi-node training

The following example shows a complete TrainJob resource for multi-node distributed PyTorch training using a ConfigMap to provide the training script:

apiVersion: trainer.kubeflow.org/v1alpha1
kind: TrainJob
metadata:
  name: pytorch-multi-node-job
  namespace: test-namespace
  annotations:
    trainer.opendatahub.io/progression-tracking: "true"
spec:
  runtimeRef:
    name: torch-distributed
    kind: ClusterTrainingRuntime
  trainer:
    command: ["torchrun", "/workspace/train.py"]
    numNodes: 3
    resourcesPerNode:
      requests:
        cpu: "4"
        memory: "8Gi"
        nvidia.com/gpu: 2
      limits:
        cpu: "4"
        memory: "8Gi"
        nvidia.com/gpu: 2
  podTemplateOverrides:
    - targetJobs:
        - name: node
      spec:
        volumes:
          - name: training-script-volume
            configMap:
              name: training-script-configmap
        containers:
          - name: node
            volumeMounts:
              - name: training-script-volume
                mountPath: /workspace

6.5.2. Example TrainJob with a minimal training script

The following example uses a small training script in a ConfigMap and runs it with torchrun. The runtime sets up distributed training for you, so you do not need to set any environment variables. The script is mounted into the pod using podTemplateOverrides, which works with the default torch-distributed runtime with container name node.

Apply the ConfigMap first, then the TrainJob.

  1. Create a ConfigMap with the training script:

    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: minimal-train-script
      namespace: test-namespace
    data:
      train.py: |
        import torch
        import torch.distributed as dist
    
        dist.init_process_group(backend="nccl")
        rank = dist.get_rank()
        world_size = dist.get_world_size()
    
        print(f"Rank {rank}/{world_size}")
        print(f"PyTorch: {torch.version}, CUDA: {torch.cuda.is_available()}")
        if torch.cuda.is_available():
            print(f"GPU: {torch.cuda.get_device_name(0)}")
    
        tensor = torch.ones(1).cuda() * rank
        dist.all_reduce(tensor)
        print(f"Rank {rank}: all_reduce result = {tensor.item()}")
    
        dist.destroy_process_group()
        print("Done.")
  2. Create the TrainJob:

    Use podTemplateOverrides to add the ConfigMap volume and mount it into the trainer container. The target job name node matches the default torch-distributed runtime.

    apiVersion: trainer.kubeflow.org/v1alpha1
    kind: TrainJob
    metadata:
      name: pytorch-minimal-example
      namespace: test-namespace
    spec:
      runtimeRef:
        name: torch-distributed
        kind: ClusterTrainingRuntime
      trainer:
        command: ["torchrun", "/workspace/train.py"]
        numNodes: 2
        resourcesPerNode:
          requests:
            nvidia.com/gpu: 1
          limits:
            nvidia.com/gpu: 1
      podTemplateOverrides:
        - targetJobs:
            - name: node
          spec:
            volumes:
              - name: script-volume
                configMap:
                  name: minimal-train-script
            containers:
              - name: node
                volumeMounts:
                  - name: script-volume
                    mountPath: /workspace

6.5.3. Example TrainJob resource with custom TrainingRuntime (no environment variables needed)

The following example shows a TrainJob resource that references a custom namespace-scoped TrainingRuntime. Use podTemplateOverrides to mount the script. Set targetJobs and the container name to match your custom runtime template. Here, node is used as the trainer job and container name:

apiVersion: trainer.kubeflow.org/v1alpha1
kind: TrainJob
metadata:
  name: pytorch-custom-runtime-example
  namespace: my-project
spec:
  runtimeRef:
    name: custom-torch-runtime
    kind: TrainingRuntime
  trainer:
    command: ["torchrun", "/workspace/train.py"]
    numNodes: 2
    resourcesPerNode:
      requests:
        cpu: "8"
        memory: "32Gi"
        nvidia.com/gpu: 4
      limits:
        cpu: "8"
        memory: "32Gi"
        nvidia.com/gpu: 4
  podTemplateOverrides:
    - targetJobs:
        - name: node
      spec:
        volumes:
          - name: training-script-volume
            configMap:
              name: training-script-configmap
        containers:
          - name: node
            volumeMounts:
              - name: training-script-volume
                mountPath: /workspace

where:

kind: TrainingRuntime
indicates that the runtime is namespace-scoped. Ensure that you have created the custom TrainingRuntime resource in the same namespace. See Understanding and using training runtimes for details. If your runtime uses a different replicated job or container name, change targetJobs and containers[].name accordingly.

6.5.4. Example TrainJob resource with suspend enabled

The following example shows a TrainJob resource that is created in a suspended state. This is useful when you want to define a job before starting it:

apiVersion: trainer.kubeflow.org/v1alpha1
kind: TrainJob
metadata:
  name: pytorch-suspended-example
  namespace: test-namespace
  annotations:
    trainer.opendatahub.io/progression-tracking: "true"
spec:
  suspend: true
  runtimeRef:
    name: torch-distributed
    kind: ClusterTrainingRuntime
  trainer:
    command: ["python", "/workspace/scripts/train.py"]
    numNodes: 2
    resourcesPerNode:
      requests:
        cpu: "4"
        memory: "8Gi"
        nvidia.com/gpu: 2
      limits:
        cpu: "4"
        memory: "8Gi"
        nvidia.com/gpu: 2
    volumeMounts:
      - name: training-script-volume
        mountPath: /workspace
  volumes:
    - name: training-script-volume
      configMap:
        name: training-script-configmap

To start the job:

oc patch trainjob pytorch-suspended-example -n test-namespace --type=merge -p {"spec":{"suspend":false}}

6.5.5. Example PyTorch training script

The following example shows a simple distributed PyTorch training script that you can store in a ConfigMap:

apiVersion: v1
kind: ConfigMap
metadata:
  name: training-script-configmap
  namespace: test-namespace
data:
  train.py: |
    import os
    import torch
    import torch.nn as nn
    import torch.distributed as dist
    from torch.nn.parallel import DistributedDataParallel as DDP
    from torch.utils.data import DataLoader, TensorDataset, DistributedSampler

    def main():
        # Initialize distributed training
        dist.init_process_group(backend="nccl")
        rank = dist.get_rank()
        world_size = dist.get_world_size()
        local_rank = int(os.environ.get("LOCAL_RANK", 0))
        torch.cuda.set_device(local_rank)

        print(f"Rank {rank}/{world_size}, Local Rank: {local_rank}")

        # Create a simple model
        model = nn.Sequential(
            nn.Linear(10, 128),
            nn.ReLU(),
            nn.Linear(128, 1),
        ).cuda(local_rank)

        model = DDP(model, device_ids=[local_rank])

        # Create synthetic dataset
        X = torch.randn(1000, 10)
        y = torch.randn(1000, 1)
        dataset = TensorDataset(X, y)
        sampler = DistributedSampler(dataset, num_replicas=world_size, rank=rank)
        dataloader = DataLoader(dataset, batch_size=32, sampler=sampler)

        # Training loop
        optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
        criterion = nn.MSELoss()

        for epoch in range(10):
            sampler.set_epoch(epoch)
            total_loss = 0
            for batch_X, batch_y in dataloader:
                batch_X = batch_X.cuda(local_rank)
                batch_y = batch_y.cuda(local_rank)

                optimizer.zero_grad()
                output = model(batch_X)
                loss = criterion(output, batch_y)
                loss.backward()
                optimizer.step()

                total_loss += loss.item()

            if rank == 0:
                print(f"Epoch {epoch+1}/10, Loss: {total_loss/len(dataloader):.4f}")

        dist.destroy_process_group()
        if rank == 0:
            print("Training complete!")

    if name == "main":
        main()

6.5.6. Example HuggingFace Transformers training script

The following example shows a HuggingFace Transformers training script for fine-tuning BERT on the IMDB dataset:

apiVersion: v1
kind: ConfigMap
metadata:
  name: transformers-training-script
  namespace: test-namespace
data:
  train.py: |
    from transformers import (
        AutoModelForSequenceClassification,
        AutoTokenizer,
        Trainer,
        TrainingArguments,
    )
    from datasets import load_dataset

    def main():
        # Load model and tokenizer
        model_name = "bert-base-uncased"
        model = AutoModelForSequenceClassification.from_pretrained(
            model_name, num_labels=2
        )
        tokenizer = AutoTokenizer.from_pretrained(model_name)

        # Load and tokenize dataset
        dataset = load_dataset("imdb", split="train[:2000]")

        def tokenize_function(examples):
            return tokenizer(
                examples["text"],
                padding="max_length",
                truncation=True,
                max_length=512,
            )

        tokenized_dataset = dataset.map(tokenize_function, batched=True)

        # Configure training
        training_args = TrainingArguments(
            output_dir="/tmp/output",
            num_train_epochs=3,
            per_device_train_batch_size=8,
            logging_steps=10,
            save_strategy="epoch",
        )

        # Create trainer and train
        trainer = Trainer(
            model=model,
            args=training_args,
            train_dataset=tokenized_dataset,
        )
        trainer.train()

        print("Training complete!")

    if name == "main":
        main()

6.5.7. Example TrainJob resource for AMD ROCm GPUs

The following example shows a TrainJob resource configured for AMD ROCm GPUs. The script is mounted using podTemplateOverrides because the target job name node matches the default ROCm runtime:

apiVersion: trainer.kubeflow.org/v1alpha1
kind: TrainJob
metadata:
  name: pytorch-rocm-example
  namespace: test-namespace
spec:
  runtimeRef:
    name: torch-distributed-rocm
    kind: ClusterTrainingRuntime
  trainer:
    command: ["torchrun", "/workspace/train.py"]
    numNodes: 2
    resourcesPerNode:
      requests:
        cpu: "4"
        memory: "8Gi"
        amd.com/gpu: 1
      limits:
        cpu: "4"
        memory: "8Gi"
        amd.com/gpu: 1
  podTemplateOverrides:
    - targetJobs:
        - name: node
      spec:
        volumes:
          - name: training-script-volume
            configMap:
              name: training-script-configmap
        containers:
          - name: node
            volumeMounts:
              - name: training-script-volume
                mountPath: /workspace

where:

torch-distributed-rocm
ClusterTrainingRuntime is specifically configured for AMD ROCm GPUs. The GPU resource request uses amd.com/gpu instead of nvidia.com/gpu.
Red Hat logoGithubredditYoutubeTwitter

詳細情報

試用、購入および販売

コミュニティー

会社概要

Red Hat は、企業がコアとなるデータセンターからネットワークエッジに至るまで、各種プラットフォームや環境全体で作業を簡素化できるように、強化されたソリューションを提供しています。

多様性を受け入れるオープンソースの強化

Red Hat では、コード、ドキュメント、Web プロパティーにおける配慮に欠ける用語の置き換えに取り組んでいます。このような変更は、段階的に実施される予定です。詳細情報: Red Hat ブログ.

Red Hat ドキュメントについて

Legal Notice

Theme

© 2026 Red Hat
トップに戻る