sklearn-onnx

repository·main·Indexed 20 days ago

https://github.com/onnx/sklearn-onnx

A library to convert scikit-learn models and pipelines into the ONNX format for high-performance inference using ONNX Runtime. It provides tools like to_onnx for automatic type inference and convert_sklearn for manual input definition, supporting up to ONNX opset 21. The package includes utilities for registering custom converters, manipulating ONNX graphs via helpers, and parsing submodels within meta-estimators.

Tokens
10.8K
Snippets
34
Records
53
Agent score
70%

What's inside skl2onnx

  1. Overview of sklearn-onnx

    main

    The sklearn-onnx project (package skl2onnx) provides converters to transform scikit-learn models into the ONNX (Open Neural Network Exchange) format.

    Key features:

    • High Performance: Converted models can be executed using ONNX Runtime for optimized scoring.
    • Extensibility: You can register external converters to handle scikit-learn pipelines that include transformers or models from third-party libraries.
    • Compatibility: Supports up to ONNX opset 21.

    For a full list of supported models, visit the Supported scikit-learn Models documentation.

  2. How skl2onnx wraps scikit-learn models

    main
    The skl2onnx library converts scikit-learn models by wrapping existing scikit-learn classes. It dynamically creates new classes that inherit from OnnxOperatorMixin, which implements the necessary to_onnx methods. This allows standard scikit-learn models and specialized containers like OnnxSklearnPipeline to be converted into the ONNX format.
  3. Understand the 'easy case' for ONNX conversion

    main

    The 'easy case' for converting a machine learning model to ONNX occurs when you can perform the conversion using a library without writing custom conversion code. This is possible if:

    1. A converter exists for the model or every individual component of the model.
    2. The converter produces an ONNX graph where every node adheres to existing ONNX specifications.
    3. The target runtime (e.g., onnxruntime) implements every node used in the resulting ONNX graph.
  4. How to write converters for other libraries in skl2onnx

    main

    While skl2onnx primarily converts scikit-learn models, it provides a registration mechanism to allow developers to implement and register converters for models from other libraries.

    Note that skl2onnx does not include converters for third-party libraries by default to avoid dependency bloat and maintenance complexity. If you need to convert models from a library other than scikit-learn, you must implement the converter yourself using the provided registration mechanism.

  5. Understand available ONNX operators in skl2onnx

    main

    skl2onnx maps ONNX operators into classes that can be easily inserted into a computational graph. The specific list of available operators is dynamic and depends on the version of the ONNX package installed in your environment.

    To verify which operators are available or to understand their behavior, you can refer to the official ONNX documentation for standard and ML-specific operators:

  6. How the conversion process works (Parser, Shape Calculator, Converter)

    main

    The conversion of a scikit-learn pipeline to ONNX involves three distinct stages executed in order:

    1. Parser: Builds the expected outputs of the model. It uses a scope to ensure unique node names and a custom_parser map to handle non-standard model types. It defines the initial topology.
    2. Shape Calculator: Refines the shapes and types of the outputs defined by the parser. This step defines the final graph structure. Note: The shape calculator should not change types, as many C++ runtimes do not support implicit type casting between nodes.
    3. Converter: Transforms the individual transformers or predictors into specific ONNX nodes (standard operators, ML operators, or custom operators).

    If you are using models from external libraries (like XGBoost), you must provide the corresponding parser, shape calculator, or converter via convert_sklearn arguments or by registering them globally.

  7. Understand ONNX opset versions in sklearn-onnx

    main

    The converter allows you to target a specific ONNX version using the target_opset parameter.

    • Every ONNX release is identified by an opset number.
    • The library selects the most recent version of an operator that is less than or equal to your specified target_opset.
    • The resulting ONNX model will have an opset number for every operator domain, representing the maximum opset number among all nodes in the graph.

    You can check the current version of sklearn-onnx and the maximum supported opset using the following code:

    from skl2onnx import __max_supported_opset__, __version__
    print("documentation for version:", __version__)
    print("Last supported opset:", __max_supported_opset__)
  8. How skl2onnx handles model conversion

    main
    When skl2onnx converts a scikit-learn pipeline, it iterates through every transformer and predictor in the pipeline to fetch its associated converter. The final ONNX graph is a combination of the outputs from all individual converters. If a model in the pipeline lacks a registered converter, skl2onnx will raise an error indicating the missing converter.
  9. Customize converter behavior using options

    main

    While most converters produce a standard ONNX graph, certain models allow you to alter the conversion process by providing additional information via the options parameter in convert_sklearn or to_onnx. These options can change the underlying ONNX operators used, which can affect model size, performance, or output format.

    # Example pattern for passing options
    options = {type(model): {'option_name': value}}
    convert_sklearn(model, target_opset, options=options)
  10. Define initial_types for ONNX conversion

    main

    When using convert_sklearn, you must define initial_types to describe the expected input schema. Each entry in the list is a tuple: (name, tensor_type).

    Numerical Inputs

    For a matrix of floats with a variable number of rows and a fixed number of features: ('name', FloatTensorType([None, num_features]))

    Mixed Inputs

    You can define multiple inputs, such as a combination of strings and numerical matrices:

    initial_type = [
        ('S', StringTensorType([None, 1])),
        ('X', FloatTensorType([None, num_features]))
    ]
    from skl2onnx.common.data_types import FloatTensorType, StringTensorType
    
    # Example for a mixed input schema
    initial_type = [
        ('S', StringTensorType([None, 1])),
        ('X', FloatTensorType([None, 4])),
    ]
  11. Investigate conversion discrepancies with collect_intermediate_steps

    main

    If a converted ONNX model produces different results than the original scikit-learn model, use skl2onnx.helpers.collect_intermediate_steps to isolate the source of the error.

    This function modifies the pipeline to keep intermediate inputs and outputs. You can then iterate through each operator, run the corresponding ONNX sub-graph using onnxruntime, and compare the results against the scikit-learn component's debug outputs using compare_objects.

    import numpy
    from sklearn.pipeline import Pipeline
    from sklearn.preprocessing import StandardScaler
    import onnxruntime
    from skl2onnx.helpers import collect_intermediate_steps, compare_objects
    from skl2onnx.common.data_types import FloatTensorType
    
    # 1. Setup and fit model
    data = numpy.array([[0, 0], [0, 0], [2, 1], [2, 1]], dtype=numpy.float32)
    model = Pipeline([("scaler1", StandardScaler()), ("scaler2", StandardScaler())])
    model.fit(data)
    
    # 2. Collect intermediate steps
    operators = collect_intermediate_steps(model, "pipeline", [("input", FloatTensorType([None, 2]))])
    
    # 3. Trigger transform to populate debug info
    model.transform(data)
    
    # 4. Iterate and compare
    for op in operators:
        onnx_step = op['onnx_step']
        sess = onnxruntime.InferenceSession(onnx_step.SerializeToString(), providers=["CPUExecutionProvider"])
        
        onnx_outputs = sess.run(None, {'input': data})
        onnx_output = onnx_outputs[0]
        skl_outputs = op['model']._debug.outputs['transform']
    
        # Compare outputs
        compare_objects(onnx_output, skl_outputs)