Skip to content

Data Processing

The Bring Your Own Model (BYOM) feature requires data processing scripts to ensure that model inputs and outputs are compatible with the model architecture and the target task.

Data processing consists of two script types:

  • Pre-processing scripts transform raw input files (for example, .JPG, .PNG, .WAV) into the input shape and data format expected by the model.
  • Post-processing scripts transform raw model outputs into human‑readable and task‑appropriate results for presentation and analysis.

This guide provides guidelines on creating custom data processing scripts, which are mandatory for all BYOM workflows.

Script Editor Environment

The script editor operates within a Jupyter-like Python environment, these three imports are mandatory for data processing script files:

  • argparse
  • numpy
  • pathlib

These are the only supported libraries in the runtime environment:

  • torch (2.1.2+cpu)
  • torchvision (0.16.2+cpu)
  • tensorflow-cpu (2.15.0)
  • requests (2.32.5)
  • pyyaml (6.0.3)
  • pandas (2.3.3)
  • scipy (1.15.3)
  • matplotlib (3.10.8)
  • opencv-python (4.11.0.86)
  • librosa (0.9.1)

Develop your script outside EdgeBench before uploading it for validation. This environment allows users to test custom post-processing logic directly within the platform.

Creating a Pre-processing Script

When entering the pre‑processing step, the script editor displays a template similar to the following:

import argparse               # Standard library for parsing command-line arguments
from pathlib import Path      # Cross-platform, safer file path handling
import numpy as np            # NumPy for numerical operations and .npy files
                              # These three imports are mandatory for all postprocessing script files

def preprocess(file_path: str, quant_type: str) -> np.ndarray:
    """
    Main preprocess function which applies the preprocessing logic
    on the input files

    This function should:
    1. Load the raw input data from file path
    2. Apply domain-specific preprocessing
    3. Return a NumPy array ready to send to model for inference

    Parameters
    ----------
    1. file_path : str
        Absolute Path to the input file.
        This may be an audio file, image, or time-series file.
        Example: "data/sample.wav" or "images/input.jpg"

    2. quant_type: str
        Quantization type to use while processing the input files
        Example: "int8"

    Returns
    -------
    np.ndarray
        Preprocessed data formatted to pass to model for inference.

    Notes
    -----
    Implement task-specific preprocessing logic here, such as:
    - Audio: loading waveform, resampling, Mel spectrograms
    - Vision: resizing, normalization, channel reordering
    - Time series: windowing, smoothing, feature extraction
    """
    # TODO: Load file and implement real preprocessing logic
    # Example:
    # data = load_audio(file_path)
    # features = compute_mel_spectrogram(data)
    # return features

    return  # Placeholder return


if __name__ == "__main__":
    """
    Script entry point.

    Example command-line usage:
    ---------------------------
    python preprocess.py \
        --source data/input.wav \
        --output_file_path outputs/preprocessed.npy \
        --quant_type float32
    """

    # Create an argument parser that defines expected CLI inputs
    parser = argparse.ArgumentParser(
        description="Preprocess an input file and save the result as a NumPy (.npy) array"
    )

    # Required argument: path to the raw input file
    parser.add_argument(
        "--source",
        required=True,
        help=(
            "Absolute Path to the input file to be preprocessed."
        )
    )

    # Required argument: output .npy file path (including filename)
    parser.add_argument(
        "--output_file_path",
        required=True,
        help=(
            "Absolute Path where the preprocessed .npy file will be saved"
        )
    )

    # Required argument: quantization type
    parser.add_argument(
        "--quant_type",
        required=True,
        help=(
            "Quantization type: int8, int16, float32"
        )
    )

    # Parse command-line arguments into a namespace object
    args = parser.parse_args()

    # Extract parsed arguments
    source_path = args.source
    output_file_path = Path(args.output_file_path)
    quant_type = args.quant_type

    # Run preprocessing on the input file
    x = preprocess(source_path, quant_type)

    # Save the resulting NumPy array to disk
    np.save(output_file_path, x)

    # Print helpful confirmation messages
    print(f"Processed data shape: {x.shape}")
    print(f"Saved preprocessed data to: {output_file_path}")

The core of this template is the preprocess() function, which accepts the following parameters:

  1. file_path: str - Absolute path to the input file. Supported inputs include image, audio, and time‑series files. An example is "images/input.jpg".
  2. quant_type: str - Quantization type applied to the processed data. An example is "int8".

The pre‑processing script must output a NumPy array and write it to a .NPY file for inference.

Pre-processing scripts typically perform the following steps:

  1. Load raw data from the input file
  2. Extract task‑specific features
  3. Transform data to match the model’s input shape
  4. Apply quantization (if required)

The following discussion uses an image classification pre‑processing script as an example.

Loading Raw Data

Load the raw data from the file_path argument using commonly supported libraries:

  • Images (.PNG, .JPG): PIL.Image.open(file_path)
  • Audio (.WAV, .MP3): librosa.load(file_path)
  • Time series (.CSV): pandas.read_csv(file_path)

Example for loading an image:

from PIL import Image

p = Path(file_path)
if not p.exists():
    raise FileNotFoundError(f"Input file not found: {file_path}")

img = Image.open(p)

Feature Extraction

Extract the desired features from the raw data. Implementation here depends on the task and model training pipeline.

Common operations include:

  • For image tasks, resizing, normalization, channel reordering
  • For audio tasks, resampling, conversion to mel spectrograms
  • For time-series tasks, windowing, smoothing, feature extraction

Example for image classification:

import numpy as np

img = img.convert('L')
img = img.resize((28, 28), Image.BILINEAR)
arr = np.asarray(img).astype(np.float32) / 255.0

Transforming the Data

Processed data must match the exact input shape expected by the model.

Example for an image classification model expecting (1, 1, 28, 28) input shape:

arr = arr[np.newaxis, np.newaxis, :, :]

Quantizing Inputs

If required, quantize data based on quant_type parameter:

q = quant_type.lower()
if q == 'float32':
    return arr.astype(np.float32)
elif q == 'int8':
    # Map float [-1,1] -> int8 range [-127,127]
    out = np.round(arr * 127.0).astype(np.int8)
    return out
elif q == 'int16':
    # Map float [-1,1] -> int16 range [-32767,32767]
    out = np.round(arr * 32767.0).astype(np.int16)
    return out
else:
    raise ValueError(f"Unsupported quant_type: {quant_type}. Use float32, int8 or int16.")

Validating the Pre-processing Script

To validate your pre-processing script on EdgeBench, go to Adding a Pre-processing Script.

Creating a Post-processing Script

When entering the post-processing step, the script editor displays a template similar to the following:

import argparse               # Standard library for parsing command-line arguments
from pathlib import Path      # Safer, cross-platform path handling
import numpy as np            # Numerical operations and .npy file handling
                              # These three imports are mandatory for all postprocessing script files

def postprocess(file_path: str,quant_type: str) -> np.ndarray:
    """
    Main postprocess function which applies the postprocessing logic
    on the model output

    This function is expected to:
    1. Load a NumPy (.npy) file from path
    2. Apply any task-specific transformations
    3. Return a NumPy array ready to be passed in the pipeline

    Parameters
    ----------
    1. file_path : str
        Absolute Path to the input .npy file containing raw model outputs.
        Example: "outputs/raw_output.npy"

    2. quant_type: str
        Quantization type to use while processing the model outputs
        Example: "int8"


    Returns
    -------
    np.ndarray
        The postprocessed NumPy array.

    Notes
    -----
    Replace the placeholder logic below with real postprocessing steps,
    such as:
    - Normalization or scaling
    - Casting to a specific dtype (e.g., int8, int16, float32)
    - Reshaping or filtering values
    - Converting logits to probabilities
    """
    # TODO: Load file and apply real postprocessing logic
    # Example:
    # data = np.load(file_path)
    # processed = data / np.max(data)
    # return processed

    return  # Placeholder return


if __name__ == "__main__":
    """
    Entry point when running this file as a script.

    Example command-line usage:
    ---------------------------
    python postprocess.py \
        --source outputs/raw_output.npy \
        --output_file_path outputs/final_output.npy \
        --quant_type float32
    """

    # Create the argument parser object
    # This defines which command-line arguments the script accepts
    parser = argparse.ArgumentParser(
        description="Postprocess model outputs and save them as a NumPy (.npy) file"
    )

    # Required argument: input model output file
    parser.add_argument(
        "--source",
        required=True,
        help=(
            "Absolute Path to the input .npy file containing raw model outputs."
        )
    )

    # Required argument: output file path (including filename)
    parser.add_argument(
        "--output_file_path",
        required=True,
        help=(
            "Absolute Path where the postprocessed .npy file will be saved."
        )
    )

    # Required argument: quantization type
    parser.add_argument(
        "--quant_type",
        required=True,
        help=(
            "Quantization type: int8, int16, float32"
        )
    )

    # Parse arguments provided via the command line
    args = parser.parse_args()

    # Extract argument values
    source_path = args.source
    output_file_path = Path(args.output_file_path)
    quant_type = args.quant_type

    # Run postprocessing on the input file
    result = postprocess(source_path, quant_type)

    # Print array shape for debugging or validation
    print(f"Shape of postprocessed data: {result.shape}")

    # Save the final NumPy array to disk
    np.save(output_file_path, result)

    print(f"Saved postprocessed data to: {output_file_path}")

The core of this template is the postprocess() function, which accepts the following parameters:

  1. file_path: str - Absolute path to the raw output file. An example is "outputs/raw_output.npy".
  2. quant_type: str - Quantization type applied to dequantize the data. An example is "int8".

The post‑processing script must output a NumPy array and write it to a .NPY file for UI presentation.

Pre-processing typically performs the following steps:

  1. Loading raw model outputs
  2. Dequantizing values (if applicable)
  3. Transforming results for task-specific presentation

The following discussion uses an image classification post‑processing script as an example.

Loading Model Outputs

Load the raw model output data from inference using np.load.

import numpy as np

p = Path(file_path)
if not p.exists():
    raise FileNotFoundError(f"Model output file not found: {file_path}")

# Load raw model outputs
data = np.load(p)

Dequantizing Outputs

If applicable, dequantize data as shown below:

q = quant_type.lower()
if q == 'float32':
    outputs = data.astype(np.float32)
elif q == 'int8':
    # int8 was encoded as round(float * 127)
    outputs = data.astype(np.float32) / 127.0
elif q == 'int16':
    # int16 was encoded as round(float * 32767)
    outputs = data.astype(np.float32) / 32767.0
else:
    raise ValueError(f"Unsupported quant_type: {quant_type}. Use float32, int8 or int16.")

Transforming Outputs for Display

The post-processing logic to prepare output data for UI display vary by task. The table below shows the expected post-processing output for each task:

Domain Task Expected Output Structure
Vision Classification Probabilities mapped to the model’s labels np.array([prob_1, prob_2, prob_3])

Shape: (1, n), n being the number of classes
Vision Image Segmentation Two image masks (such as, one for background, one for subject) np.array([[[val_1, val_2, ...], [val_1, val_2, ...], ...], [[val_1, val_2, ...], [val_1, val_2, ...], ...]])

Shape: 2, H, W, corresponds to 2 masks of input image with height H and width W
Vision Object Detection Array of dictionaries for each detected object specifying bounding box, label, and confidence score np.array([{"box": box, "label": label, "score": score}])

Shape: (n, 1), n being the number of objects detected
Bounding box format: (x_ref, x_length, y_ref, y_length)
Vision Visual Wake Word Probabilities mapped to the model’s labels np.array([prob_1, prob_2, prob_3])

Shape: (1, 2), corresponding to detected or not detected
Audio Denoising Audio data in NumPy format np.array([data_1, data_2, data_3...])

Shape: (n,), n being the number of audio samples
Audio Genre Identification Probabilities mapped to the model’s labels np.array([prob_1, prob_2, prob_3])

Shape: (1, n), n being the number of genres
Audio Keyword Spotting Probabilities mapped to the model’s labels np.array([prob_1, prob_2, prob_3])

Shape: (1, n), n being the number of keywords
Time Series Motor Fault Detection Time‑series data np.array([<same shape as input>])

Shape: Same shape as the input data. The UI adapter computes Mean Squared Error (MSE) loss between input data and the output reconstructed data, hence, they must have the same shape.
Time Series Anomaly Detection Time‑series data np.array([<same shape as input>])

Shape: Same shape as the input data. The UI adapter computes Mean Squared Error (MSE) loss between input data and the output reconstructed data, hence, they must have the same shape.

Example logic for image classification task, converting from logits to probabilities:

exp_shift = np.exp(outputs - np.max(outputs, axis=1, keepdims=True))
probs = exp_shift / np.sum(exp_shift, axis=1, keepdims=True)

return probs

Validating the Post-processing Script

To validate your post-processing script on EdgeBench, go to Adding a Post-processing Script.