chore(v2): vendor dependencies for offline/China builds

go mod vendor pins onnxruntime_go v1.12.1, Gio and the rest into v2/vendor
so go run/build work without hitting proxy.golang.org (blocked/slow in
China). Verified: CGO_ENABLED=1 go build -mod=vendor ./internal/spike and
GOOS=windows go build -mod=vendor ./internal/ui both pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
ila
2026-07-23 16:35:01 +08:00
co-authored by Claude Opus 4.8
parent 97c1c4a974
commit f58728cddd
972 changed files with 597802 additions and 0 deletions
@@ -0,0 +1,3 @@
onnxruntime_c_api.h linguist-vendored
onnxruntime_training_c_api.h linguist-vendored
+138
View File
@@ -0,0 +1,138 @@
Contribution Guidelines
=======================
This library began as a personal project, and is primarily still maintained as
such. The following list of guidelines is not necessarily exhaustive, and,
ultimately, any contribution is subject to the maintainer's discretion. That
being said, contributions are welcome, and most recent new features have been
added by users who need them!
Coding Style
------------
- Go code must be formatted using the official `gofmt` tool.
- C code should adhere to the portions of Google's C++ style guide that
apply to C.
- If at all possible, any Go or C code should have at most 80 character lines.
(This may not be enforced very strictly.)
- Purely stylistic changes are unlikely to be accepted. Instead, the
maintainer or other contributers may make small stylistic adjustments to
surrounding code as part of other contributions.
- Attempt to mimic the existing style of the surrounding code.
Documentation
-------------
- All Go types, public-facing functions, and nontrivial internal functions
must include a comment on their intended usage, to be parsed by godoc.
- As per the google C++ style guide, all C functions must be documented with a
comment as well. If a C function is defined in a header file, the comment
should appear with the definition in the header. If it's a static function
in a `.c` file, the comment should appear with the function definition.
Tests
-----
- All new features and bugfixes must include a basic unit test (in
`onnxruntime_test.go` or `onnxruntime_training_test.go`) to serve as a
sanity check.
- If a test is for a platform-dependent or execution-provider-dependent
feature, the test must be skipped if run on an unsupported system.
- No tests should panic. Always check errors and fail rather than allowing
tests to panic.
- Every change must ensure that `go test -v -bench=.` passes on every
supported platform.
- Every test failure should be accompanied by a message containing the reason,
either using `t.Logf()`, `t.Errorf()`, or `t.Fatalf()`.
Adding New Files
----------------
- Apart from testing data, try not to add new source files.
- Do not add third-party code or headers. The only exceptions for now are
`onnxruntime_c_api.h` and `onnxruntime_training_c_api.h`.
- No C++ at all. Developing Go-to-C wrappers is annoying enough as it is.
- Do not add any new `onnxruntime` shared libraries under `test_data`. I know
there are additional platforms that would be nice to include (such as
`x86_64` Linux), but I do not want this project turning into an unofficial
distribution channel for onnxruntime libraries. It also clogs up the git
repo with large files, and increases the size of the history every time
these files are updated. The libraries that are included were only intended
to allow a majority of users to run `go test -v -bench=.` without further
setup or modification. Currently: amd64 Windows, arm64 Linux (I wish I
hadn't included this!), arm64 osx, and amd64 osx. All other users must set
the `ONNXRUNTIME_SHARED_LIBRARY_PATH` environment variable to a valid path
to the correct `onnxruntime` shared library file prior to running tests.
- If you need to add a .onnx file for a test, place both the .onnx file
_and_ the script used to generate it into `test_data/`.
- Keep any testing .onnx files as small as possible.
- Without a good reason (i.e., implementing an entire class of APIs such as
training), avoid adding new Go files---just add to `onnxruntime_go.go`.
Dependencies
------------
- Avoid Go or C dependencies outside of the language's standard libraries.
This package currently does not depend on any third-party Go modules, and
it would be great to keep it this way.
- Python scripts within `test_data/` can use whatever dependencies they need,
because end users should not be required to run the python files, and the
`.onnx` file they produce should already be included.
C-Specific Stuff
----------------
- Minimize Go management of C-allocated memory as much as possible. For
example, see the `convertORTString` function on `onnxruntime_go.go`, which
copies a C-allocated string into a garbage-collected go `string`.
- If you need to use a `OrtAllocator` in onnxruntime's C API, always use the
default `OrtAllocator` returned by
`ort_api->GetAllocatorWithDefaultOptions()`.
- ONNXRuntime APIs requiring file paths typically use `ORTCHAR_T*`
strings. On Linux/OSX/etc, these should be UTF-8, but on Windows they will
be wide-character strings. (Our tricks with `#include` to make them look
like `char*` to C code even on Windows, but the DLL still expects a
`wchar_t*`.) The important takeaway: when passing `ORTCHAR_T*`
values to the onnxruntime C API, use the `createOrtCharString(...)`
function. It converts a Go string to a C string, but unlike `C.CString`, it
will do UTF8 to UTF16 conversion on Windows. (On Linux, it simply wraps
`C.CString`.)
A Few Notes on Organization
---------------------------
- The `onnxruntime` C API uses a struct containing function pointers. Cgo
can't directly invoke functions via pointers, so `onnxruntime_wrapper.c`
(along with the associated header file) are used to provide top-level C
functions that call the function pointers within the `OrtApi` struct.
- Linux and OSX use `dlopen` to load the onnxruntime shared library, but this
isn't possible on Windows, which instead can use the `syscall.LoadLibrary()`
function from Go's standard library. This different behavior is locked
behind build constraints in `setup_env.go` and `setup_env_windows.go`,
respectively.
+19
View File
@@ -0,0 +1,19 @@
Copyright (c) 2023 Nathan Otterness
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+216
View File
@@ -0,0 +1,216 @@
Cross-Platform `onnxruntime` Wrapper for Go
===========================================
About
-----
This library seeks to provide an interface for loading and executing neural
networks from Go(lang) code, while remaining as simple to use as possible.
A few example applications using this library can be found in the
[`onnxruntime_go_examples` repository](https://github.com/yalue/onnxruntime_go_examples).
The [onnxruntime](https://github.com/microsoft/onnxruntime) library provides a
way to load and execute ONNX-format neural networks, though the library
primarily supports C and C++ APIs. Several efforts exist to have written
Go(lang) wrappers for the `onnxruntime` library, but as far as I can tell, none
of these existing Go wrappers support Windows. This is due to the fact that
Microsoft's `onnxruntime` library assumes the user will be using the MSVC
compiler on Windows systems, while CGo on Windows requires using Mingw.
This wrapper works around the issues by manually loading the `onnxruntime`
shared library, removing any dependency on the `onnxruntime` source code beyond
the header files. Naturally, this approach works equally well on non-Windows
systems.
Additionally, this library uses Go's recent addition of generics to support
multiple Tensor data types; see the `NewTensor` or `NewEmptyTensor` functions.
**IMPORTANT:** As of onnxruntime_go v1.12.0 or above, for CUDA acceleration we
now require the use of CUDA 12.x and CuDNN 9.x (as required by onnxruntime
v1.19.0+). Those wishing to stay on CUDA 11.8 should remain on onnxruntime_go
v1.11.0 or below.
Note on onnxruntime Library Versions
------------------------------------
At the time of writing, this library uses version 1.19.0 of the onnxruntime
C API headers. So, it will probably only work with version 1.19.0 of the
onnxruntime shared libraries, as well. If you need to use a different version,
or if I get behind on updating this repository, updating or changing the
onnxruntime version should be fairly easy:
1. Replace the `onnxruntime_c_api.h` file with the version corresponding to
the onnxruntime version you wish to use.
2. Replace the `test_data/onnxruntime.dll` (or `test_data/onnxruntime*.so`)
file with the version corresponding to the onnxruntime version you wish to
use.
3. (If you care about DirectML support) Verify that the entries in the
`DummyOrtDMLAPI` struct in `onnxruntime_wrapper.c` match the order in which
they appear in the `OrtDmlApi` struct from the `dml_provider_factory.h`
header in the official repo. See the comment on this struct in
`onnxruntime_wrapper.c` for more information.
Note that both the C API header and the shared library files are available to
download from the releases page in the
[official repo](https://github.com/microsoft/onnxruntime). Download the archive
for the release you want to use, and extract it. The header file is located in
the "include" subdirectory, and the shared library will be located in the "lib"
subdirectory. (On Linux systems, you'll need the version of the .so with the
appended version numbers, e.g., `libonnxruntime.so.1.19.0`, and _not_ the
`libonnxruntime.so`, which is just a symbolic link.) The archive will contain
several other files containing C++ headers, debug symbols, and so on, but you
shouldn't need anything other than the single onnxruntime shared library and
`onnxruntime_c_api.h`. (The exception is if you're wanting to enable GPU
support, where you may need other shared-library files, such as
`execution_providers_cuda.dll` and `execution_providers_shared.dll` on Windows.)
Requirements
------------
To use this library, you'll need a version of Go with cgo support. If you are
not using an amd64 version of Windows or Linux (or if you want to provide your
own library for some other reason), you simply need to provide the correct path
to the shared library when initializing the wrapper. This is seen in the first
few lines of the following example.
Note that if you want to use CUDA, you'll need to be using a version of the
onnxruntime shared library with CUDA support, as well as be using a CUDA
version supported by the underlying version of your onnxruntime library. For
example, version 1.19.0 of the onnxruntime library only supports CUDA versions
12.x. See
[the onnxruntime CUDA support documentation](https://onnxruntime.ai/docs/execution-providers/CUDA-ExecutionProvider.html)
for more specifics.
Example Usage
-------------
The full documentation can be found at [pkg.go.dev](https://pkg.go.dev/github.com/yalue/onnxruntime_go).
Additionally, several example command-line applications complete with necessary
networks and data can be found in the
[`onnxruntime_go_examples` repository](https://github.com/yalue/onnxruntime_go_examples).
The following example illustrates how this library can be used to load and run
an ONNX network taking a single input tensor and producing a single output
tensor, both of which contain 32-bit floating point values. Note that error
handling is omitted; each of the functions returns an err value, which will be
non-nil in the case of failure.
```go
import (
"fmt"
ort "github.com/yalue/onnxruntime_go"
"os"
)
func main() {
// This line _may_ be optional; by default the library will try to load
// "onnxruntime.dll" on Windows, and "onnxruntime.so" on any other system.
// For stability, it is probably a good idea to always set this explicitly.
ort.SetSharedLibraryPath("path/to/onnxruntime.so")
err := ort.InitializeEnvironment()
if err != nil {
panic(err)
}
defer ort.DestroyEnvironment()
// For a slight performance boost and convenience when re-using existing
// tensors, this library expects the user to create all input and output
// tensors prior to creating the session. If this isn't ideal for your use
// case, see the DynamicAdvancedSession type in the documnentation, which
// allows input and output tensors to be specified when calling Run()
// rather than when initializing a session.
inputData := []float32{0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9}
inputShape := ort.NewShape(2, 5)
inputTensor, err := ort.NewTensor(inputShape, inputData)
defer inputTensor.Destroy()
// This hypothetical network maps a 2x5 input -> 2x3x4 output.
outputShape := ort.NewShape(2, 3, 4)
outputTensor, err := ort.NewEmptyTensor[float32](outputShape)
defer outputTensor.Destroy()
session, err := ort.NewAdvancedSession("path/to/network.onnx",
[]string{"Input 1 Name"}, []string{"Output 1 Name"},
[]ort.Value{inputTensor}, []ort.Value{outputTensor}, nil)
defer session.Destroy()
// Calling Run() will run the network, reading the current contents of the
// input tensors and modifying the contents of the output tensors.
err = session.Run()
// Get a slice view of the output tensor's data.
outputData := outputTensor.GetData()
// If you want to run the network on a different input, all you need to do
// is modify the input tensor data (available via inputTensor.GetData())
// and call Run() again.
// ...
}
```
Deprecated APIs
---------------
Older versions of this library used a typed `Session[T]` struct to keep track
of sessions. In retrospect, associating type parameters with Sessions was
unnecessary, and the `AdvancedSession` type, along with its associated APIs,
was added to rectify this mistake. For backwards compatibility, the old typed
`Session[T]` and `DynamicSession[T]` types are still included and unlikely to
be removed. However, they now delegate their functionality to
`AdvancedSession` internally. New code should always favor using
`AdvancedSession` directly.
Running Tests and System Compatibility for Testing
--------------------------------------------------
Navigate to this directory and run `go test -v`, or optionally
`go test -v -bench=.`. All tests should pass; tests relating to CUDA or other
accelerator support will be skipped on systems or onnxruntime builds that don't
support them.
Currently, this repository includes a copy of the onnxruntime shared libraries
for a few systems, including AMD64 windows, ARM64 Linux, and ARM64 darwin.
These should allow tests to pass on those systems without users needing to copy
additional libraries beyond cloning this repository. In the future, however,
this may change if support for more systems are added or removed.
You may want to use a different version of the `onnxruntime` shared library for
a couple reasons. In particular:
1. The included shared library copies do not include support for CUDA or other
accelerated execution providers, so CUDA-related tests will always be
skipped if you use the default libraries in this repo.
2. Many systems, including AMD64 and i386 Linux, and x86 osx, do not currently
have shared libraries included in `test_data/` in the first place. (I would
like to keep this directory, and the overall repo, smaller by keeping the
number of shared libraries small.)
If these or other reasons apply to you, the test code will check the
`ONNXRUNTIME_SHARED_LIBRARY_PATH` environment variable before attempting to
load a library from `test_data/`. So, if you are using one of these systems or
want accelerator-related tests to run, you should set the environment variable
to the path to the onnxruntime shared library. Afterwards, `go test -v` should
run and pass.
Training API Support
--------------------
This wrapper supports the onnxruntime training API on limited platforms. See
the `NewTrainingSession` and associated data types or functions to use it. So
far, the training API has only been tested on Linux, on `x86_64` architectures.
If you are not sure whether your platform or build of onnxruntime supports
training, you can call `onnxruntime_go.IsTrainingSupported()`, which will
return `true` if training is supported on your system.
+173
View File
@@ -0,0 +1,173 @@
package onnxruntime_go
// This file contains Session types that we maintain for compatibility
// purposes; the main onnxruntime_go.go file is dedicated to AdvancedSession
// now.
import (
"fmt"
"os"
)
// #include "onnxruntime_wrapper.h"
import "C"
// This type of session is for ONNX networks with the same input and output
// data types.
//
// NOTE: This type was written with a type parameter despite the fact that a
// type parameter is not necessary for any of its underlying implementation,
// which is a mistake in retrospect. It is preserved only for compatibility
// with older code, and new users should almost certainly be using an
// AdvancedSession instead.
//
// Using an AdvancedSession struct should be easier, and supports arbitrary
// combination of input and output tensor data types as well as more options.
type Session[T TensorData] struct {
// We now delegate all of the implementation to an AdvancedSession here.
s *AdvancedSession
}
// Similar to Session, but does not require the specification of the input
// and output shapes at session creation time, and allows for input and output
// tensors to have different types. This allows for fully dynamic input to the
// onnx model.
//
// NOTE: As with Session[T], new users should probably be using
// DynamicAdvancedSession in the future.
type DynamicSession[In TensorData, Out TensorData] struct {
s *DynamicAdvancedSession
}
// The same as NewSession, but takes a slice of bytes containing the .onnx
// network rather than a file path.
func NewSessionWithONNXData[T TensorData](onnxData []byte, inputNames,
outputNames []string, inputs, outputs []*Tensor[T]) (*Session[T], error) {
// Unfortunately, a slice of pointers that satisfy an interface don't count
// as a slice of interfaces (at least, as I write this), so we'll make the
// conversion here.
tmpInputs := make([]Value, len(inputs))
tmpOutputs := make([]Value, len(outputs))
for i, t := range inputs {
tmpInputs[i] = t
}
for i, t := range outputs {
tmpOutputs[i] = t
}
s, e := NewAdvancedSessionWithONNXData(onnxData, inputNames, outputNames,
tmpInputs, tmpOutputs, nil)
if e != nil {
return nil, e
}
return &Session[T]{
s: s,
}, nil
}
// Similar to NewSessionWithOnnxData, but for dynamic sessions.
func NewDynamicSessionWithONNXData[in TensorData, out TensorData](onnxData []byte,
inputNames, outputNames []string) (*DynamicSession[in, out], error) {
s, e := NewDynamicAdvancedSessionWithONNXData(onnxData, inputNames,
outputNames, nil)
if e != nil {
return nil, e
}
return &DynamicSession[in, out]{
s: s,
}, nil
}
// Loads the ONNX network at the given path, and initializes a Session
// instance. If this returns successfully, the caller must call Destroy() on
// the returned session when it is no longer needed. We require the user to
// provide the input and output tensors and names at this point, in order to
// not need to re-allocate them every time Run() is called. The user instead
// can just update or access the input/output tensor data after calling Run().
// The input and output tensors MUST outlive this session, and calling
// session.Destroy() will not destroy the input or output tensors.
func NewSession[T TensorData](onnxFilePath string, inputNames,
outputNames []string, inputs, outputs []*Tensor[T]) (*Session[T], error) {
fileContent, e := os.ReadFile(onnxFilePath)
if e != nil {
return nil, fmt.Errorf("Error reading %s: %w", onnxFilePath, e)
}
toReturn, e := NewSessionWithONNXData[T](fileContent, inputNames,
outputNames, inputs, outputs)
if e != nil {
return nil, fmt.Errorf("Error creating session from %s: %w",
onnxFilePath, e)
}
return toReturn, nil
}
// Same as NewSession, but for dynamic sessions.
func NewDynamicSession[in TensorData, out TensorData](onnxFilePath string,
inputNames, outputNames []string) (*DynamicSession[in, out], error) {
fileContent, e := os.ReadFile(onnxFilePath)
if e != nil {
return nil, fmt.Errorf("Error reading %s: %w", onnxFilePath, e)
}
toReturn, e := NewDynamicSessionWithONNXData[in, out](fileContent,
inputNames, outputNames)
if e != nil {
return nil, fmt.Errorf("Error creating session from %s: %w",
onnxFilePath, e)
}
return toReturn, nil
}
func (s *Session[_]) Destroy() error {
return s.s.Destroy()
}
func (s *DynamicSession[_, _]) Destroy() error {
return s.s.Destroy()
}
func (s *Session[T]) Run() error {
return s.s.Run()
}
// Unlike the non-dynamic equivalents, the DynamicSession's Run() function
// takes a list of input and output tensors rather than requiring the tensors
// to be specified at Session creation time. It is still the caller's
// responsibility to create and Destroy all tensors passed to this function.
func (s *DynamicSession[in, out]) Run(inputs []*Tensor[in],
outputs []*Tensor[out]) error {
if len(inputs) != len(s.s.s.inputNames) {
return fmt.Errorf("The session specified %d input names, but Run() "+
"was called with %d input tensors", len(s.s.s.inputNames),
len(inputs))
}
if len(outputs) != len(s.s.s.outputNames) {
return fmt.Errorf("The session specified %d output names, but Run() "+
"was called with %d output tensors", len(s.s.s.outputNames),
len(outputs))
}
inputValues := make([]*C.OrtValue, len(inputs))
for i, v := range inputs {
inputValues[i] = v.GetInternals().ortValue
}
outputValues := make([]*C.OrtValue, len(outputs))
for i, v := range outputs {
outputValues[i] = v.GetInternals().ortValue
}
status := C.RunOrtSession(s.s.s.ortSession, &inputValues[0],
&s.s.s.inputNames[0], C.int(len(inputs)), &outputValues[0],
&s.s.s.outputNames[0], C.int(len(outputs)))
if status != nil {
return fmt.Errorf("Error running network: %w", statusToError(status))
}
return nil
}
// This type alias is included to avoid breaking older code, where the inputs
// and outputs to session.Run() were ArbitraryTensors rather than Values.
type ArbitraryTensor = Value
// As with the ArbitraryTensor type, this type alias only exists to facilitate
// renaming an old type without breaking existing code.
type TensorInternalData = ValueInternalData
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+731
View File
@@ -0,0 +1,731 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
// This file contains the training c apis.
#pragma once
#include <stdbool.h>
#include "onnxruntime_c_api.h"
/** \page training_c_cpp_api Training C & C++ APIs
*
* Training C and C++ APIs are an extension of the \ref c_cpp_api "onnxruntime core C and C++ APIs" and should be used in conjunction with them.
*
* In order to train a model with onnxruntime, the following training artifacts must be generated:
* - The training onnx model
* - The checkpoint file
* - The optimizer onnx model
* - The eval onnx model model (optional)
*
* These training artifacts can be generated as part of an offline step using the python [utilities](https://github.com/microsoft/onnxruntime/blob/main/orttraining/orttraining/python/training/onnxblock/README.md) made available in the `onnxruntime-training` python package.
*
* After these artifacts have been generated, the C and C++ utilities listed in this documentation can be leveraged to perform training.
*
* If any problem is encountered, please create an [issue](https://github.com/microsoft/onnxruntime/issues/new) with your scenario and requirements, and we will be sure to respond and follow up on the request.
*
* <h1>Training C API</h1>
*
* ::OrtTrainingApi - Training C API functions.
*
* This C structure contains functions that enable users to perform training with onnxruntime.
*
* _Sample Code_:
*
* ```c
* #include <onnxruntime_training_api.h>
*
* OrtApi* g_ort_api = OrtGetApiBase()->GetApi(ORT_API_VERSION);
* OrtTrainingApi* g_ort_training_api = g_ort_api->GetTrainingApi(ORT_API_VERSION);
*
* OrtEnv* env = NULL;
* g_ort_api->CreateEnv(logging_level, logid, &env);
* OrtSessionOptions* session_options = NULL;
* g_ort_api->CreateSessionOptions(&session_options);
*
* OrtCheckpointState* state = NULL;
* g_ort_training_api->LoadCheckpoint(path_to_checkpoint, &state);
*
* OrtTrainingSession* training_session = NULL;
* g_ort_training_api->CreateTrainingSession(env, session_options, training_model_path,
* state, eval_model_path, optimizer_model_path,
* &training_session);
* // Training loop
* {
* g_ort_training_api->TrainStep(...);
* g_ort_training_api->OptimizerStep(...);
* g_ort_training_api->LazyResetGrad(...);
* }
*
* g_ort_training_api->ExportModelForInferencing(training_session, inference_model_path, ...);
* g_ort_training_api->SaveCheckpoint(state, path_to_checkpoint, false);
*
* g_ort_training_api->ReleaseTrainingSession(training_session);
* g_ort_training_api->ReleaseCheckpointState(state);
* ```
*
* > **Note**
* > The ::OrtCheckpointState contains the entire training state that the ::OrtTrainingSession uses. As a result, the training session must always have access to the state. That is to say, the ::OrtCheckpointState instance must outlive the lifetime of the ::OrtTrainingSession instance.
*
* <h1>Training C++ API</h1>
*
* @ref TrainingCpp - Training C++ API classes and functions.
*
* These C++ classes and functions enable users to perform training with onnxruntime.
*
* _Sample Code_:
*
* ```cc
* #include <onnxruntime_training_cxx_api.h>
*
* Ort::Env env;
* Ort::SessionOptions session_options;
*
* auto state = Ort::CheckpointState::LoadCheckpoint(path_to_checkpoint);
* auto training_session = Ort::TrainingSession(env, session_options, state, training_model_path,
* eval_model_path, optimizer_model_path);
*
* // Training Loop
* {
* training_session.TrainStep(...);
* training_session.OptimizerStep(...);
* training_session.LazyResetGrad(...);
* }
*
* training_session->ExportModelForInferencing(inference_model_path, ...);
* Ort::CheckpointState::SaveCheckpoint(state, path_to_checkpoint, false);
* ```
* > **Note**
* > The ::Ort::CheckpointState contains the entire training state that the ::Ort::TrainingSession uses. As a result, the training session must always have access to the state. That is to say, the ::Ort::CheckpointState instance must outlive the lifetime of the ::Ort::TrainingSession instance.
*/
/** @defgroup TrainingC Ort Training C API
* @{
*/
ORT_RUNTIME_CLASS(TrainingSession); // Type that enables performing training for the given user models.
ORT_RUNTIME_CLASS(CheckpointState); // Type that holds the training states for the training session.
/** \brief Type of property to be added to or returned from the ::OrtCheckpointState.
*/
typedef enum OrtPropertyType {
OrtIntProperty = 0,
OrtFloatProperty = 1,
OrtStringProperty = 2,
} OrtPropertyType;
/** \brief The Training C API that holds onnxruntime training function pointers
*
* All the Training C API functions are defined inside this structure as pointers to functions.
* Call OrtApi::GetTrainingApi to get a pointer to this struct.
*
* \nosubgrouping
*/
struct OrtTrainingApi {
/// \name Accessing The Training Session State
/// @{
/** \brief Load a checkpoint state from a file on disk into checkpoint_state.
*
* This function will parse a checkpoint file, pull relevant data and load the training
* state into the checkpoint_state. This checkpoint state can then be used to create the
* training session by invoking OrtTrainingApi::CreateTrainingSession. By doing so, the training
* session will resume training from the given checkpoint state.
* \note Note that the training session created with a checkpoint state uses this state to store the entire
* training state (including model parameters, its gradients, the optimizer states and the properties).
* As a result, it is required that the checkpoint state outlive the lifetime of the training session.
* \note Note that the checkpoint file can be either the complete checkpoint or the nominal checkpoint.
*
* \param[in] checkpoint_path Path to the checkpoint file
* \param[out] checkpoint_state Checkpoint state that contains the states of the training session.
*
* \snippet{doc} snippets.dox OrtStatus Return Value
*
*/
ORT_API2_STATUS(LoadCheckpoint, _In_ const ORTCHAR_T* checkpoint_path,
_Outptr_ OrtCheckpointState** checkpoint_state);
/** \brief Save the given state to a checkpoint file on disk.
*
* This function serializes the provided checkpoint state to a file on disk.
* This checkpoint can later be loaded by invoking OrtTrainingApi::LoadCheckpoint to resume
* training from this snapshot of the state.
*
* \param[in] checkpoint_state The checkpoint state to save.
* \param[in] checkpoint_path Path to the checkpoint file.
* \param[in] include_optimizer_state Flag to indicate whether to save the optimizer state or not.
*
* \snippet{doc} snippets.dox OrtStatus Return Value
*
*/
ORT_API2_STATUS(SaveCheckpoint, _In_ OrtCheckpointState* checkpoint_state, _In_ const ORTCHAR_T* checkpoint_path,
const bool include_optimizer_state);
/// @}
/// \name Implementing The Training Loop
/// @{
/** \brief Create a training session that can be used to begin or resume training.
*
* This function creates a training session based on the env and session options provided that can
* begin or resume training from a given checkpoint state for the given onnx models.
* The checkpoint state represents the parameters of the training session which will be moved
* to the device specified by the user through the session options (if necessary).
* The training session requires four training artifacts
* - The training onnx model
* - The evaluation onnx model (optional)
* - The optimizer onnx model
* - The checkpoint file
*
* These artifacts can be generated using the `onnxruntime-training` python [utility](https://github.com/microsoft/onnxruntime/blob/main/orttraining/orttraining/python/training/onnxblock/README.md).
*
* \param[in] env Environment to be used for the training session.
* \param[in] options Session options that the user can customize for this training session.
* \param[in] checkpoint_state Training states that the training session uses as a starting point for training.
* \param[in] train_model_path Model to be used to perform training.
* \param[in] eval_model_path Model to be used to perform evaluation.
* \param[in] optimizer_model_path Model to be used to perform gradient descent.
* \param[out] out Created training session.
*
* \snippet{doc} snippets.dox OrtStatus Return Value
*
*/
ORT_API2_STATUS(CreateTrainingSession, _In_ const OrtEnv* env, _In_ const OrtSessionOptions* options,
_Inout_ OrtCheckpointState* checkpoint_state, _In_ const ORTCHAR_T* train_model_path,
_In_ const ORTCHAR_T* eval_model_path, _In_ const ORTCHAR_T* optimizer_model_path,
_Outptr_result_maybenull_ OrtTrainingSession** out);
/** \brief Create a training session that can be used to begin or resume training.
* This api provides a way to load all the training artifacts from buffers instead of files.
*
* \param[in] env Environment to be used for the training session.
* \param[in] options Session options that the user can customize for this training session.
* \param[in] checkpoint_state Training states that the training session uses as a starting point for training.
* \param[in] train_model_data Buffer containing the model data to be used to perform training
* \param[in] train_data_length Length of the buffer containing train_model_data
* \param[in] eval_model_data Buffer containing the model data to be used to perform evaluation
* \param[in] eval_data_length Length of the buffer containing eval_model_data
* \param[in] optim_model_data Buffer containing the model data to be used to perform weight update
* \param[in] optim_data_length Length of the buffer containing optim_model_data
* \param[out] out Created training session.
*
*/
ORT_API2_STATUS(CreateTrainingSessionFromBuffer, _In_ const OrtEnv* env,
_In_ const OrtSessionOptions* options, _Inout_ OrtCheckpointState* checkpoint_state,
_In_ const void* train_model_data, size_t train_data_length,
_In_ const void* eval_model_data, size_t eval_data_length,
_In_ const void* optim_model_data, size_t optim_data_length,
_Outptr_result_maybenull_ OrtTrainingSession** out);
/// @}
/// \name Model IO Information
/// @{
/** \brief Retrieves the number of user outputs in the training model.
*
* This function returns the number of outputs of the training model so that the user can
* allocate space for the number of outputs when OrtTrainingApi::TrainStep is invoked.
*
* \param[in] sess The `this` pointer to the training session.
* \param[out] out Number of user outputs in the training model.
*
* \snippet{doc} snippets.dox OrtStatus Return Value
*
*/
ORT_API2_STATUS(TrainingSessionGetTrainingModelOutputCount, _In_ const OrtTrainingSession* sess, _Out_ size_t* out);
/** \brief Retrieves the number of user outputs in the eval model.
*
* This function returns the number of outputs of the eval model so that the user can
* allocate space for the number of outputs when OrtTrainingApi::EvalStep is invoked.
*
* \param[in] sess The `this` pointer to the training session.
* \param[out] out Number of user outputs in the eval model.
*
* \snippet{doc} snippets.dox OrtStatus Return Value
*
*/
ORT_API2_STATUS(TrainingSessionGetEvalModelOutputCount, _In_ const OrtTrainingSession* sess, _Out_ size_t* out);
/** \brief Retrieves the names of user outputs in the training model.
*
* This function returns the names of outputs of the training model that can be associated with the OrtValue(s)
* returned by the OrtTrainingApi::TrainStep function.
*
* \param[in] sess The `this` pointer to the training session.
* \param[in] index Index of the output name requested.
* \param[in] allocator Allocator to use to allocate the memory for the name.
* \param[out] output Name of the training model output at the given index.
*
* \snippet{doc} snippets.dox OrtStatus Return Value
*
*/
ORT_API2_STATUS(TrainingSessionGetTrainingModelOutputName, _In_ const OrtTrainingSession* sess, size_t index, _Inout_ OrtAllocator* allocator, _Outptr_ char** output);
/** \brief Retrieves the names of user outputs in the eval model.
*
* This function returns the names of outputs of the eval model that can be associated with the OrtValue(s) returned
* by the OrtTrainingApi::EvalStep function.
*
* \param[in] sess The `this` pointer to the training session.
* \param[in] index Index of the output name requested.
* \param[in] allocator Allocator to use to allocate the memory for the name.
* \param[out] output Name of the eval model output at the given index.
*
* \snippet{doc} snippets.dox OrtStatus Return Value
*
*/
ORT_API2_STATUS(TrainingSessionGetEvalModelOutputName, _In_ const OrtTrainingSession* sess, size_t index, _Inout_ OrtAllocator* allocator, _Outptr_ char** output);
/// @}
/// \name Implementing The Training Loop
/// @{
/** \brief Reset the gradients of all trainable parameters to zero lazily.
*
* This function sets the internal state of the training session such that the gradients of the trainable
* parameters in the OrtCheckpointState will be scheduled to be reset just before the new gradients are
* computed on the next invocation of the next OrtTrainingApi::TrainStep.
*
* \param[in] session The `this` pointer to the training session.
*
* \snippet{doc} snippets.dox OrtStatus Return Value
*
*/
ORT_API2_STATUS(LazyResetGrad, _Inout_ OrtTrainingSession* session);
/** \brief Computes the outputs of the training model and the gradients of the trainable parameters for the given inputs
*
* This function performs a training step that computes the outputs of the training model and the gradients
* of the trainable parameters for the given inputs. The train step is performed based on the training model
* that was provided to the training session.
* The OrtTrainingApi::TrainStep is equivalent of running forward propagation and backward propagation in a single
* step.
* The gradients computed are stored inside the training session state so they can be later consumed
* by the OrtTrainingApi::OptimizerStep function.
* The gradients can be lazily reset by invoking the OrtTrainingApi::LazyResetGrad function.
*
* \param[in] sess The `this` pointer to the training session.
* \param[in] run_options Run options for this training step.
* \param[in] inputs_len Number of user inputs to the training model.
* \param[in] inputs The user inputs to the training model.
* \param[in] outputs_len Number of user outputs expected from this training step.
* \param[out] outputs User outputs computed by train step.
*
* \snippet{doc} snippets.dox OrtStatus Return Value
*
*/
ORT_API2_STATUS(TrainStep, _Inout_ OrtTrainingSession* sess, _In_opt_ const OrtRunOptions* run_options,
_In_ size_t inputs_len, _In_reads_(inputs_len) const OrtValue* const* inputs,
_In_ size_t outputs_len, _Inout_updates_all_(outputs_len) OrtValue** outputs);
/** \brief Computes the outputs for the eval model for the given inputs
*
* This function performs an eval step that computes the outputs of the eval model for the given inputs.
* The eval step is performed based on the eval model that was provided to the training session.
*
* \param[in] sess The `this` pointer to the training session.
* \param[in] run_options Run options for this eval step.
* \param[in] inputs_len Number of user inputs to the eval model.
* \param[in] inputs The user inputs to the eval model.
* \param[in] outputs_len Number of user outputs expected from this eval step.
* \param[out] outputs User outputs computed by eval step.
*
* \snippet{doc} snippets.dox OrtStatus Return Value
*
*/
ORT_API2_STATUS(EvalStep, _In_ const OrtTrainingSession* sess, _In_opt_ const OrtRunOptions* run_options,
_In_ size_t inputs_len, _In_reads_(inputs_len) const OrtValue* const* inputs,
_In_ size_t outputs_len, _Inout_updates_all_(outputs_len) OrtValue** outputs);
/** \brief Sets the learning rate for this training session.
*
* This function allows users to set the learning rate for the training session. The current
* learning rate is maintained by the training session and can be overwritten by invoking
* this function with the desired learning rate. This function should not be used when a valid
* learning rate scheduler is registered. It should be used either to set the learning rate
* derived from a custom learning rate scheduler or to set a constant learning rate to be used
* throughout the training session.
* \note Please note that this function does not set the initial learning rate that may be needed
* by the predefined learning rate schedulers. To set the initial learning rate for learning
* rate schedulers, please look at the function OrtTrainingApi::RegisterLinearLRScheduler.
*
* \param[in] sess The `this` pointer to the training session.
* \param[in] learning_rate Desired learning rate to be set.
*
* \snippet{doc} snippets.dox OrtStatus Return Value
*
*/
ORT_API2_STATUS(SetLearningRate, _Inout_ OrtTrainingSession* sess, _In_ float learning_rate);
/** \brief Gets the current learning rate for this training session.
*
* This function allows users to get the learning rate for the training session. The current
* learning rate is maintained by the training session, and users can query it for the purpose
* of implementing their own learning rate schedulers.
*
* \param[in] sess The `this` pointer to the training session.
* \param[out] learning_rate Learning rate currently in use by the training session.
*
* \snippet{doc} snippets.dox OrtStatus Return Value
*
*/
ORT_API2_STATUS(GetLearningRate, _Inout_ OrtTrainingSession* sess, _Out_ float* learning_rate);
/** \brief Performs the weight updates for the trainable parameters using the optimizer model.
*
* This function performs the weight update step that updates the trainable parameters such that they
* take a step in the direction of their gradients (gradient descent). The optimizer step is performed
* based on the optimizer model that was provided to the training session.
* The updated parameters are stored inside the training state so that they can be used by the next
* OrtTrainingApi::TrainStep function call.
*
* \param[in] sess The `this` pointer to the training session.
* \param[in] run_options Run options for this optimizer step.
*
* \snippet{doc} snippets.dox OrtStatus Return Value
*
*/
ORT_API2_STATUS(OptimizerStep, _Inout_ OrtTrainingSession* sess,
_In_opt_ const OrtRunOptions* run_options);
/** \brief Registers a linear learning rate scheduler for the training session.
*
* Register a linear learning rate scheduler that decays the learning rate by linearly updated
* multiplicative factor from the initial learning rate set on the training session to 0. The decay
* is performed after the initial warm up phase where the learning rate is linearly incremented
* from 0 to the initial learning rate provided.
*
* \param[in] sess The `this` pointer to the training session.
* \param[in] warmup_step_count Warmup steps for LR warmup.
* \param[in] total_step_count Total step count.
* \param[in] initial_lr The initial learning rate to be used by the training session.
*
* \snippet{doc} snippets.dox OrtStatus Return Value
*
*/
ORT_API2_STATUS(RegisterLinearLRScheduler, _Inout_ OrtTrainingSession* sess, _In_ const int64_t warmup_step_count,
_In_ const int64_t total_step_count, _In_ const float initial_lr);
/** \brief Update the learning rate based on the registered learing rate scheduler.
*
* Takes a scheduler step that updates the learning rate that is being used by the training session.
* This function should typically be called before invoking the optimizer step for each round,
* or as determined necessary to update the learning rate being used by the training session.
* \note Please note that a valid predefined learning rate scheduler must be first registered to invoke this
* function.
*
* \param[in] sess The `this` pointer to the training session.
*
* \snippet{doc} snippets.dox OrtStatus Return Value
*
*/
ORT_API2_STATUS(SchedulerStep, _Inout_ OrtTrainingSession* sess);
/// @}
/// \name Accessing The Training Session State
/// @{
/** \brief Retrieves the size of all the parameters.
*
* Calculates the total number of primitive (datatype of the parameters) elements of all the parameters in the
* training state.
* When trainable_only argument is true, the size is calculated for trainable params only.
*
* \param[in] sess The `this` pointer to the training session.
* \param[out] out Size of all parameter elements.
* \param[in] trainable_only Whether to skip non-trainable parameters
*
* \snippet{doc} snippets.dox OrtStatus Return Value
*
*/
ORT_API2_STATUS(GetParametersSize, _Inout_ OrtTrainingSession* sess, _Out_ size_t* out, bool trainable_only);
/** \brief Copy all parameters to a contiguous buffer held by the argument parameters_buffer
*
* The parameters_buffer has to be of the size given by GetParametersSize api call,
* with matching setting for the argument trainable_only. All the target parameters must be of the same
* datatype. The OrtValue must be pre-allocated onto
* the desired device. This is a complementary function to OrtTrainingApi::CopyBufferToParameters.
* Parameter ordering is preserved.
* User is responsible for allocating and freeing the resources used by the parameters_buffer.
*
* \param[in] sess The `this` pointer to the training session.
* \param[in] trainable_only Whether to skip non-trainable parameters
* \param[out] parameters_buffer The pre-allocated OrtValue buffer to copy onto.
*
* \snippet{doc} snippets.dox OrtStatus Return Value
*
*/
ORT_API2_STATUS(CopyParametersToBuffer, _Inout_ OrtTrainingSession* sess,
_Inout_ OrtValue* parameters_buffer, bool trainable_only);
/** \brief Copy parameter values from the given contiguous buffer held by parameters_buffer to the training state
*
* The parameters_buffer argument has to be of the size given by OrtTrainingApi::GetParametersSize api call,
* with matching setting for trainable_only argument. All the target parameters must be of the same
* datatype. This is a complementary function to OrtTrainingApi::CopyParametersToBuffer
* and can be used to load updated buffer values onto the training state.
* Parameter ordering is preserved.
* User is responsible for allocating and freeing the resources used by the parameters_buffer.
* In case the training session was created with a nominal checkpoint, invoking this function is required
* to load the updated parameters onto the checkpoint to complete it.
*
* \param[in] sess The `this` pointer to the training session.
* \param[in] trainable_only Whether to skip non-trainable parameters
* \param[out] parameters_buffer The pre-allocated OrtValue buffer to copy from.
*
* \snippet{doc} snippets.dox OrtStatus Return Value
*
*/
ORT_API2_STATUS(CopyBufferToParameters, _Inout_ OrtTrainingSession* sess,
_Inout_ OrtValue* parameters_buffer, bool trainable_only);
/// @}
/// \name Release Training Resources
/// @{
/** \brief Frees up the memory used up by the training session.
*
* This function frees up any memory that was allocated in the training session. The training
* session can no longer be used after this call.
*
*/
ORT_CLASS_RELEASE(TrainingSession);
/** \brief Frees up the memory used up by the checkpoint state.
*
* This function frees up any memory that was allocated in the checkpoint state. The checkpoint
* state can no longer be used after this call.
* \note Note that the checkpoint state must be released only after the training session has been released.
*
*/
ORT_CLASS_RELEASE(CheckpointState);
/// @}
/// \name Prepare For Inferencing
/// @{
/** \brief Export a model that can be used for inferencing.
*
* If the training session was provided with an eval model, the training session can generate
* an inference model if it knows the inference graph outputs. The input inference graph outputs
* are used to prune the eval model so that the inference model's outputs align with the provided outputs.
* The exported model is saved at the path provided and can be used for inferencing with InferenceSession.
* \note Note that the function re-loads the eval model from the path provided to OrtTrainingApi::CreateTrainingSession
* and expects that this path still be valid.
*
* \param[in] sess The `this` pointer to the training session.
* \param[in] inference_model_path Path where the inference model should be serialized to.
* \param[in] graph_outputs_len Size of the graph output names array.
* \param[in] graph_output_names Names of the outputs that are needed in the inference model.
*
* \snippet{doc} snippets.dox OrtStatus Return Value
*
*/
ORT_API2_STATUS(ExportModelForInferencing, _Inout_ OrtTrainingSession* sess,
_In_ const ORTCHAR_T* inference_model_path, size_t graph_outputs_len,
_In_reads_(graph_outputs_len) const char* const* graph_output_names);
/// @}
/// \name Training Utilities
/// @{
/** \brief Sets the seed used for random number generation in Onnxruntime.
*
* Use this function to generate reproducible results. It should be noted that completely reproducible
* results are not guaranteed.
*
* \param[in] seed The seed to be set.
*
* \snippet{doc} snippets.dox OrtStatus Return Value
*
*/
ORT_API2_STATUS(SetSeed, _In_ const int64_t seed);
/// @}
/// \name Model IO Information
/// @{
/** \brief Retrieves the number of user inputs in the training model.
*
* This function returns the number of inputs of the training model so that the user can accordingly
* allocate the OrtValue(s) provided to the OrtTrainingApi::TrainStep function.
*
* \param[in] sess The `this` pointer to the training session.
* \param[out] out Number of user inputs in the training model.
*
* \snippet{doc} snippets.dox OrtStatus Return Value
*
*/
ORT_API2_STATUS(TrainingSessionGetTrainingModelInputCount, _In_ const OrtTrainingSession* sess, _Out_ size_t* out);
/** \brief Retrieves the number of user inputs in the eval model.
*
* This function returns the number of inputs of the eval model so that the user can accordingly
* allocate the OrtValue(s) provided to the OrtTrainingApi::EvalStep function.
*
* \param[in] sess The `this` pointer to the training session.
* \param[out] out Number of user inputs in the eval model.
*
* \snippet{doc} snippets.dox OrtStatus Return Value
*
*/
ORT_API2_STATUS(TrainingSessionGetEvalModelInputCount, _In_ const OrtTrainingSession* sess, _Out_ size_t* out);
/** \brief Retrieves the name of the user input at given index in the training model.
*
* This function returns the names of inputs of the training model that can be associated with the
* OrtValue(s) provided to the OrtTrainingApi::TrainStep function.
*
* \param[in] sess The `this` pointer to the training session.
* \param[in] index The index of the training model input name requested.
* \param[in] allocator The allocator to use to allocate the memory for the requested name.
* \param[out] output Name of the user input for the training model at the given index.
*
* \snippet{doc} snippets.dox OrtStatus Return Value
*
*/
ORT_API2_STATUS(TrainingSessionGetTrainingModelInputName, _In_ const OrtTrainingSession* sess, size_t index,
_In_ OrtAllocator* allocator, _Outptr_ char** output);
/** \brief Retrieves the name of the user input at given index in the eval model.
*
* This function returns the names of inputs of the eval model that can be associated with the OrtValue(s) provided
* to the OrtTrainingApi::EvalStep function.
*
* \param[in] sess The `this` pointer to the training session.
* \param[in] index The index of the eval model input name requested.
* \param[in] allocator The allocator to use to allocate the memory for the requested name.
* \param[out] output Name of the user input for the eval model at the given index.
*
* \snippet{doc} snippets.dox OrtStatus Return Value
*
*/
ORT_API2_STATUS(TrainingSessionGetEvalModelInputName, _In_ const OrtTrainingSession* sess, size_t index,
_In_ OrtAllocator* allocator, _Outptr_ char** output);
/// @}
/// \name Accessing The Training Session State
/// @{
/** \brief Adds or updates the given property to/in the checkpoint state.
*
* Runtime properties such as epoch, training step, best score, and others can be added to the checkpoint
* state by the user by calling this function with the corresponding property name and value.
* The given property name must be unique to be able to successfully add the property.
*
* \param[in] checkpoint_state The checkpoint state which should hold the property.
* \param[in] property_name Name of the property being added or updated.
* \param[in] property_type Type of the property associated with the given name.
* \param[in] property_value Property value associated with the given name.
*
* \snippet{doc} snippets.dox OrtStatus Return Value
*
*/
ORT_API2_STATUS(AddProperty, _Inout_ OrtCheckpointState* checkpoint_state,
_In_ const char* property_name, _In_ enum OrtPropertyType property_type,
_In_ void* property_value);
/** \brief Gets the property value associated with the given name from the checkpoint state.
*
* Gets the property value from an existing entry in the checkpoint state. The property must
* exist in the checkpoint state to be able to retrieve it successfully.
*
* \param[in] checkpoint_state The checkpoint state that is currently holding the property.
* \param[in] property_name Name of the property being retrieved.
* \param[in] allocator Allocator used to allocate the memory for the property_value.
* \param[out] property_type Type of the property associated with the given name.
* \param[out] property_value Property value associated with the given name.
*
* \snippet{doc} snippets.dox OrtStatus Return Value
*
*/
ORT_API2_STATUS(GetProperty, _In_ const OrtCheckpointState* checkpoint_state,
_In_ const char* property_name, _Inout_ OrtAllocator* allocator,
_Out_ enum OrtPropertyType* property_type, _Outptr_ void** property_value);
/// @}
/// \name Accessing The Training Session State
/// @{
/** \brief Load a checkpoint state from a buffer into checkpoint_state.
*
* This function will parse a checkpoint bytes buffer, pull relevant data and load the training
* state into the checkpoint_state. This checkpoint state can then be used to create the
* training session by invoking OrtTrainingApi::CreateTrainingSession. By doing so, the training
* session will resume training from the given checkpoint state.
* \note Note that the training session created with a checkpoint state uses this state to store the entire
* training state (including model parameters, its gradients, the optimizer states and the properties).
* As a result, it is required that the checkpoint state outlive the lifetime of the training session.
*
* \param[in] checkpoint_buffer Path to the checkpoint bytes buffer.
* \param[in] num_bytes Number of bytes in the checkpoint buffer.
* \param[out] checkpoint_state Checkpoint state that contains the states of the training session.
*
* \snippet{doc} snippets.dox OrtStatus Return Value
*
*/
ORT_API2_STATUS(LoadCheckpointFromBuffer, _In_ const void* checkpoint_buffer,
_In_ const size_t num_bytes, _Outptr_ OrtCheckpointState** checkpoint_state);
/** \brief Retrieves the type and shape information of the parameter associated with the given parameter name.
*
* This function retrieves the type and shape of the parameter associated with the given parameter name.
* The parameter must exist in the checkpoint state to be able to retrieve its type and shape information successfully.
*
* \param[in] checkpoint_state The checkpoint state.
* \param[in] parameter_name Name of the parameter being retrieved.
* \param[out] parameter_type_and_shape The type and shape of the parameter being retrieved.
*
* \snippet{doc} snippets.dox OrtStatus Return Value
*
*/
ORT_API2_STATUS(GetParameterTypeAndShape, _In_ const OrtCheckpointState* checkpoint_state,
_In_ const char* parameter_name, _Outptr_ OrtTensorTypeAndShapeInfo** parameter_type_and_shape);
/** \brief Updates the data associated with the model parameter in the checkpoint state for the given parameter name.
*
* This function updates a model parameter in the checkpoint state with the given parameter data.
* The training session must be already created with the checkpoint state that contains the parameter
* being updated. The given parameter is copied over to the registered device for the training session.
* The parameter must exist in the checkpoint state to be able to update it successfully.
*
* \param[in] checkpoint_state The checkpoint state.
* \param[in] parameter_name Name of the parameter being updated.
* \param[in] parameter The parameter data that should replace the existing parameter data.
*
* \snippet{doc} snippets.dox OrtStatus Return Value
*
*/
ORT_API2_STATUS(UpdateParameter, _Inout_ OrtCheckpointState* checkpoint_state,
_In_ const char* parameter_name, _In_ OrtValue* parameter);
/** \brief Gets the data associated with the model parameter from the checkpoint state for the given parameter name.
*
* This function retrieves the model parameter data from the checkpoint state for the given parameter name.
* The parameter is copied over and returned as an OrtValue. The training session must be already created
* with the checkpoint state that contains the parameter being retrieved.
* The parameter must exist in the checkpoint state to be able to retrieve it successfully.
*
* \param[in] checkpoint_state The checkpoint state.
* \param[in] parameter_name Name of the parameter being retrieved.
* \param[in] allocator Allocator used to allocate the memory for the parameter.
* \param[out] parameter The parameter data that is retrieved from the checkpoint state.
*
* \snippet{doc} snippets.dox OrtStatus Return Value
*
*/
ORT_API2_STATUS(GetParameter, _In_ const OrtCheckpointState* checkpoint_state,
_In_ const char* parameter_name, _Inout_ OrtAllocator* allocator,
_Outptr_ OrtValue** parameter);
/// @}
};
typedef struct OrtTrainingApi OrtTrainingApi;
/// @}
+639
View File
@@ -0,0 +1,639 @@
package onnxruntime_go
// #cgo CFLAGS: -O2 -g
//
// #include "onnxruntime_wrapper.h"
import "C"
import (
"fmt"
"os"
"path/filepath"
"unsafe"
)
var trainingNotSupportedError error = fmt.Errorf("training not supported by onnx library")
// Scalar is like a tensor but the underlying go slice is of length 1 and it has no dimension.
// It can be used to store e.g. the loss from a training cycle.
type Scalar[T TensorData] struct {
data []T
dataSize uintptr
ortValue *C.OrtValue
}
func (s *Scalar[T]) GetShape() Shape {
return nil
}
func (s *Scalar[T]) ZeroContents() {
C.memset(unsafe.Pointer(&s.data[0]), 0, C.size_t(s.dataSize))
}
func (s *Scalar[T]) Destroy() error {
C.ReleaseOrtValue(s.ortValue)
s.ortValue = nil
s.data = nil
s.dataSize = 0
return nil
}
// GetData returns the undelying data for the scalar.
// If you want to explicitly set the scalar's data, use Set.
func (t *Scalar[T]) GetData() T {
return t.data[0]
}
// Set allows to explicitly set the underlying value for the scalar.
func (t *Scalar[T]) Set(value T) {
t.data = []T{value}
}
func (t *Scalar[T]) DataType() C.ONNXTensorElementDataType {
return GetTensorElementDataType[T]()
}
func (t *Scalar[_]) GetInternals() *ValueInternalData {
return &ValueInternalData{
ortValue: t.ortValue,
}
}
func (t *Scalar[_]) GetONNXType() ONNXType {
return ONNXTypeTensor
}
// NewEmptyScalar creates a new scalar of type T.
func NewEmptyScalar[T TensorData]() (*Scalar[T], error) {
var data T
return NewScalar(data)
}
// NewScalar creates a new scalar of type T backed by a value of type T.
// Note that, differently from tensors, this is not a []T but just a value T.
func NewScalar[T TensorData](data T) (*Scalar[T], error) {
if !IsInitialized() {
return nil, NotInitializedError
}
dataSlice := []T{data}
var ortValue *C.OrtValue
dataType := GetTensorElementDataType[T]()
dataSize := unsafe.Sizeof(dataSlice[0]) * uintptr(1)
status := C.CreateOrtTensorWithShape(unsafe.Pointer(&dataSlice[0]),
C.size_t(dataSize), nil, C.int64_t(0), ortMemoryInfo, dataType, &ortValue)
if status != nil {
return nil, statusToError(status)
}
toReturn := Scalar[T]{
data: dataSlice,
dataSize: dataSize,
ortValue: ortValue,
}
return &toReturn, nil
}
// TraininSession is the type that wraps the C training session object.
type TrainingSession struct {
ortTrainingSession *C.OrtTrainingSession
ortCheckpointState *C.OrtCheckpointState
inputs []*C.OrtValue
outputs []*C.OrtValue
trainingModelPath *C.char
optimizerModelPath *C.char
evalModelPath *C.char
}
// ExportModel is used to export the final trained model to disk. It requires the path for
// the exported model as well as the names of the graph nodes to export.
// Note that currently the final model can only be exported if the session has been
// initialized with NewTrainingSession and the path to the eval model has been provided.
func (s *TrainingSession) ExportModel(path string, outputNames []string) error {
if s.evalModelPath == nil {
return fmt.Errorf("final model can only be exported if the eval model path is " +
"provided at session creation time (see NewTrainingSession)")
}
if path == "" {
return fmt.Errorf("path cannot be empty")
}
dir, _ := filepath.Split(path)
if _, err := os.Stat(dir); dir != "" && os.IsNotExist(err) {
return fmt.Errorf("directory %s does not exist", dir)
}
cOutputNames := make([]*C.char, len(outputNames))
for i, name := range outputNames {
cOutputNames[i] = C.CString(name)
}
cPath, err := createOrtCharString(path)
if err != nil {
return fmt.Errorf("Error converting export path to C string: %w", err)
}
outputLength := C.size_t(len(outputNames))
defer func() {
for i := range cOutputNames {
C.free(unsafe.Pointer(cOutputNames[i]))
}
C.free(unsafe.Pointer(cPath))
}()
status := C.ExportModel(s.ortTrainingSession, cPath, outputLength, &cOutputNames[0])
if status != nil {
return statusToError(status)
}
return nil
}
// SaveCheckpoint can be used to save the current checkpoint state at the specified path.
// This is useful to snapshot the training parameters to continue training later or on
// a different machine.
func (s *TrainingSession) SaveCheckpoint(path string, saveOptimizerState bool) error {
if path == "" {
return fmt.Errorf("path cannot be empty")
}
dir, _ := filepath.Split(path)
if _, err := os.Stat(dir); dir != "" && os.IsNotExist(err) {
return fmt.Errorf("directory %s does not exist", dir)
}
cPath, err := createOrtCharString(path)
if err != nil {
return fmt.Errorf("Error converting path to C string: %w", err)
}
var saveOptimizer int
if saveOptimizerState {
saveOptimizer = 1
}
defer func() {
C.free(unsafe.Pointer(cPath))
}()
status := C.SaveCheckpoint(s.ortCheckpointState, cPath, C.size_t(saveOptimizer))
if status != nil {
return statusToError(status)
}
return nil
}
// Destroy frees all the C memory associated to a training session.
func (s *TrainingSession) Destroy() error {
if s.ortTrainingSession != nil {
C.ReleaseOrtTrainingSession(s.ortTrainingSession)
s.ortTrainingSession = nil
}
// note: checkpoint MUST be released after session
if s.ortCheckpointState != nil {
C.ReleaseCheckpointState(s.ortCheckpointState)
s.ortCheckpointState = nil
}
C.free(unsafe.Pointer(s.trainingModelPath))
s.trainingModelPath = nil
C.free(unsafe.Pointer(s.evalModelPath))
s.evalModelPath = nil
C.free(unsafe.Pointer(s.optimizerModelPath))
s.optimizerModelPath = nil
s.inputs = nil
s.outputs = nil
return nil
}
// TrainStep performs the training step.
func (s *TrainingSession) TrainStep() error {
inputLength := C.size_t(len(s.inputs))
outputLength := C.size_t(len(s.outputs))
status := C.TrainStep(s.ortTrainingSession, inputLength, &s.inputs[0], outputLength, &s.outputs[0])
if status != nil {
return fmt.Errorf("error performing training step: %w", statusToError(status))
}
return nil
}
// TrainStep performs the optimizer step.
func (s *TrainingSession) OptimizerStep() error {
status := C.OptimizerStep(s.ortTrainingSession)
if status != nil {
return fmt.Errorf("error performing optimizer step: %w", statusToError(status))
}
return nil
}
// TrainStep performs the LazyResetGrad step.
func (s *TrainingSession) LazyResetGrad() error {
status := C.LazyResetGrad(s.ortTrainingSession)
if status != nil {
return fmt.Errorf("error performing lazyResetGrad step: %w", statusToError(status))
}
return nil
}
func getInputName(s *C.OrtTrainingSession, i int, model string) (string, error) {
var cName *C.char
var status *C.OrtStatus
switch model {
case "train":
status = C.TrainingSessionGetTrainingInputName(s, C.size_t(i), &cName)
case "eval":
status = C.TrainingSessionGetEvalInputName(s, C.size_t(i), &cName)
default:
return "", fmt.Errorf("%s model not recognized", model)
}
if status != nil {
return "", fmt.Errorf("error getting name: %w", statusToError(status))
}
name, e := convertORTString(cName)
if e != nil {
return "", fmt.Errorf("error converting C name to Go string: %w", e)
}
return name, nil
}
func getOutputName(s *C.OrtTrainingSession, i int, model string) (string, error) {
var cName *C.char
var status *C.OrtStatus
switch model {
case "train":
status = C.TrainingSessionGetTrainingOutputName(s, C.size_t(i), &cName)
case "eval":
status = C.TrainingSessionGetEvalOutputName(s, C.size_t(i), &cName)
default:
return "", fmt.Errorf("%s model not recognized", model)
}
if status != nil {
return "", fmt.Errorf("error getting name: %w", statusToError(status))
}
name, e := convertORTString(cName)
if e != nil {
return "", fmt.Errorf("error converting C name to Go string: %w", e)
}
return name, nil
}
type TrainingInputOutputNames struct {
TrainingInputNames []string
EvalInputNames []string
TrainingOutputNames []string
EvalOutputNames []string
}
// GetInputOutputNames returns the names of the training inputs and outputs
// for the training and validation models. Eval model is optional and can be empty
// string.
func GetInputOutputNames(checkpointStatePath string,
trainingModelPath string,
evalModelPath string) (*TrainingInputOutputNames, error) {
options, e := NewSessionOptions()
if e != nil {
return nil, fmt.Errorf("failed creating options with error: %v\n", e)
}
defer options.Destroy()
checkpointData, e := os.ReadFile(checkpointStatePath)
if e != nil {
return nil, fmt.Errorf("error reading %s: %w", checkpointStatePath, e)
}
trainingData, e := os.ReadFile(trainingModelPath)
if e != nil {
return nil, fmt.Errorf("error reading %s: %w", checkpointStatePath, e)
}
var evalData []byte
if evalModelPath != "" {
evalData, e = os.ReadFile(evalModelPath)
if e != nil {
return nil, fmt.Errorf("error reading %s: %w", evalModelPath, e)
}
}
// create checkpoint C object
ortCheckpointState, e := createCCheckpoint(checkpointData)
if e != nil {
return nil, fmt.Errorf("error creating C checkpointState: %w", e)
}
// create session C object
ortTrainingSession, e := createCTrainingSessionWithOnnxData(ortCheckpointState,
trainingData, evalData, nil, options)
if e != nil {
C.ReleaseCheckpointState(ortCheckpointState)
return nil, fmt.Errorf("error creating C training session: %w", e)
}
defer func() {
C.ReleaseOrtTrainingSession(ortTrainingSession)
C.ReleaseCheckpointState(ortCheckpointState)
}()
var inputCountTraining, inputCountEval C.size_t
status := C.TrainingSessionGetInputCount(ortTrainingSession, &inputCountTraining, &inputCountEval)
if status != nil {
return nil, statusToError(status)
}
var outputCountTraining, outputCountEval C.size_t
status = C.TrainingSessionGetOutputCount(ortTrainingSession, &outputCountTraining, &outputCountEval)
if status != nil {
return nil, statusToError(status)
}
trainInputNames := make([]string, inputCountTraining)
trainOutputNames := make([]string, outputCountTraining)
for i := 0; i < int(inputCountTraining); i++ {
name, err := getInputName(ortTrainingSession, i, "train")
if err != nil {
return nil, fmt.Errorf("error retrieving train input name: %w", err)
}
trainInputNames[i] = name
}
for i := 0; i < int(outputCountTraining); i++ {
name, err := getOutputName(ortTrainingSession, i, "train")
if err != nil {
return nil, fmt.Errorf("error retrieving train output name: %w", err)
}
trainOutputNames[i] = name
}
var evalInputNames []string
var evalOutputNames []string
if len(evalData) > 0 {
evalInputNames = make([]string, inputCountEval)
evalOutputNames = make([]string, outputCountEval)
for i := 0; i < int(inputCountEval); i++ {
name, err := getInputName(ortTrainingSession, i, "eval")
if err != nil {
return nil, fmt.Errorf("error retrieving eval input name: %w", err)
}
evalInputNames[i] = name
}
for i := 0; i < int(outputCountTraining); i++ {
name, err := getOutputName(ortTrainingSession, i, "eval")
if err != nil {
return nil, fmt.Errorf("error retrieving eval output name: %w", err)
}
evalOutputNames[i] = name
}
}
return &TrainingInputOutputNames{
TrainingInputNames: trainInputNames,
EvalInputNames: evalInputNames,
TrainingOutputNames: trainOutputNames,
EvalOutputNames: evalOutputNames,
}, nil
}
// IsTrainingSupported returns true if the training api is supported
// by the onnxruntime library.
func IsTrainingSupported() bool {
return C.IsTrainingApiSupported() != 0
}
func checkTraining() error {
if !IsInitialized() {
return NotInitializedError
}
if !IsTrainingSupported() {
return trainingNotSupportedError
}
return nil
}
func createCCheckpoint(onnxData []byte) (*C.OrtCheckpointState, error) {
if e := checkTraining(); e != nil {
return nil, e
}
if len(onnxData) == 0 {
return nil, fmt.Errorf("Missing checkpoint data")
}
var ortCheckpointState *C.OrtCheckpointState
status := C.CreateCheckpoint(unsafe.Pointer(&(onnxData[0])), C.size_t(len(onnxData)), &ortCheckpointState)
if status != nil {
return nil, statusToError(status)
}
return ortCheckpointState, nil
}
// createCTrainingSessionWithOnnxData creates a C session from byte data using buffers
func createCTrainingSessionWithOnnxData(checkpointState *C.OrtCheckpointState,
trainingData, evalData, optimizerData []byte,
options *SessionOptions) (*C.OrtTrainingSession, error) {
if e := checkTraining(); e != nil {
return nil, e
}
var ortTrainingSession *C.OrtTrainingSession
var ortSessionOptions *C.OrtSessionOptions
if options != nil {
ortSessionOptions = options.o
}
// eval model is optional
var evalDataPtr unsafe.Pointer
var evalDataSize C.size_t
if len(evalData) > 0 {
evalDataPtr = unsafe.Pointer(&(evalData[0]))
evalDataSize = C.size_t(len(evalData))
}
// optimizer model is also optional when e.g. getting input and output names
var optimizerDataPtr unsafe.Pointer
var optimizerDataSize C.size_t
if len(optimizerData) > 0 {
optimizerDataPtr = unsafe.Pointer(&(optimizerData[0]))
optimizerDataSize = C.size_t(len(optimizerData))
}
status := C.CreateTrainingSessionFromBuffer(
checkpointState,
unsafe.Pointer(&(trainingData[0])), C.size_t(len(trainingData)),
evalDataPtr, evalDataSize,
optimizerDataPtr, optimizerDataSize,
ortEnv, &ortTrainingSession, ortSessionOptions)
if status != nil {
return nil, statusToError(status)
}
return ortTrainingSession, nil
}
// createCTrainingSessionWithPaths creates a C session from paths
func createCtrainingSessionWithPaths(checkpointState *C.OrtCheckpointState,
trainingPath, evalPath, optimizerPath *C.char,
options *SessionOptions) (*C.OrtTrainingSession, error) {
if e := checkTraining(); e != nil {
return nil, e
}
var ortTrainingSession *C.OrtTrainingSession
var ortSessionOptions *C.OrtSessionOptions
if options != nil {
ortSessionOptions = options.o
}
status := C.CreateTrainingSessionFromPaths(checkpointState,
trainingPath, evalPath, optimizerPath, ortEnv, &ortTrainingSession, ortSessionOptions)
if status != nil {
return nil, statusToError(status)
}
return ortTrainingSession, nil
}
// NewTrainingSessionWithOnnxData is like NewTrainingSession, but it accepts
// bytes rather than paths to the training assets. Note that there does not
// seem to currently be a way to export the trained model from a session
// instantiated from bytes. If you wish to export the trained model, you should
// use NewTrainingSession instead.
func NewTrainingSessionWithOnnxData(checkpointData []byte,
trainingData []byte,
evalData []byte,
optimizerData []byte,
inputs, outputs []Value,
options *SessionOptions) (*TrainingSession, error) {
if err := checkTraining(); err != nil {
return nil, err
}
if err := validateInputOutputs(inputs, outputs); err != nil {
return nil, err
}
if len(trainingData) == 0 {
return nil, fmt.Errorf("training data has length zero.")
}
if len(optimizerData) == 0 {
return nil, fmt.Errorf("optimizer data has length zero.")
}
// create checkpoint C object
ortCheckpointState, e := createCCheckpoint(checkpointData)
if e != nil {
return nil, fmt.Errorf("error creating C checkpointState: %w", e)
}
// create session C object
ortTrainingSession, e := createCTrainingSessionWithOnnxData(ortCheckpointState,
trainingData, evalData, optimizerData, options)
if e != nil {
return nil, fmt.Errorf("error creating C training session: %w", e)
}
inputOrtTensors := make([]*C.OrtValue, len(inputs))
outputOrtTensors := make([]*C.OrtValue, len(outputs))
for i, v := range inputs {
inputOrtTensors[i] = v.GetInternals().ortValue
}
for i, v := range outputs {
outputOrtTensors[i] = v.GetInternals().ortValue
}
return &TrainingSession{
ortCheckpointState: ortCheckpointState,
ortTrainingSession: ortTrainingSession,
inputs: inputOrtTensors,
outputs: outputOrtTensors,
}, nil
}
func validateInputOutputs(inputs, outputs []Value) error {
if len(inputs) == 0 {
return fmt.Errorf("inputs must have length greater than zero")
}
if len(outputs) == 0 {
return fmt.Errorf("outputs must have length greater than zero")
}
return nil
}
// NewTrainingSession creates a new training session from paths stored on disk.
// evalModelPath is optional and can be the empty string. In case it is not
// provided, only the checkpoint state can be exported once training is complete
// (and not the final inference model).
func NewTrainingSession(checkpointStatePath string,
trainingModelPath string,
evalModelPath string,
optimizerModelPath string,
inputs, outputs []Value,
options *SessionOptions) (*TrainingSession, error) {
if err := checkTraining(); err != nil {
return nil, err
}
if err := validateInputOutputs(inputs, outputs); err != nil {
return nil, err
}
checkPointContent, e := os.ReadFile(checkpointStatePath)
if e != nil {
return nil, fmt.Errorf("reading checkpoint data failed: %s", e.Error())
}
// create checkpoint C object
ortCheckpointState, e := createCCheckpoint(checkPointContent)
if e != nil {
return nil, fmt.Errorf("error creating C checkpointState: %w", e)
}
// create session C object
if _, err := os.Stat(trainingModelPath); os.IsNotExist(err) {
return nil, fmt.Errorf("training model does not exist at path %s", trainingModelPath)
}
cTrainingPath, err := createOrtCharString(trainingModelPath)
if err != nil {
return nil, fmt.Errorf("Error converting training model path to C string: %w", err)
}
if _, err := os.Stat(optimizerModelPath); os.IsNotExist(err) {
return nil, fmt.Errorf("optimizer s does not exist at path %s", optimizerModelPath)
}
cOptimizerPath, err := createOrtCharString(optimizerModelPath)
if err != nil {
return nil, fmt.Errorf("Error converting optimizer path to C string: %w", err)
}
// eval is optional
var cEvalPath *C.char
if evalModelPath != "" {
if _, err := os.Stat(evalModelPath); os.IsNotExist(err) {
return nil, fmt.Errorf("eval model does not exist at path %s", evalModelPath)
}
cEvalPath, err = createOrtCharString(evalModelPath)
if err != nil {
return nil, fmt.Errorf("Error converting eval path to C string: %w", err)
}
} else {
cEvalPath = nil
}
ortTrainingSession, e := createCtrainingSessionWithPaths(ortCheckpointState,
cTrainingPath, cEvalPath, cOptimizerPath, options)
if e != nil {
return nil, fmt.Errorf("error creating C training session: %w", e)
}
inputOrtTensors := make([]*C.OrtValue, len(inputs))
outputOrtTensors := make([]*C.OrtValue, len(outputs))
for i, v := range inputs {
inputOrtTensors[i] = v.GetInternals().ortValue
}
for i, v := range outputs {
outputOrtTensors[i] = v.GetInternals().ortValue
}
return &TrainingSession{
ortCheckpointState: ortCheckpointState,
ortTrainingSession: ortTrainingSession,
inputs: inputOrtTensors,
outputs: outputOrtTensors,
evalModelPath: cEvalPath,
trainingModelPath: cTrainingPath,
optimizerModelPath: cOptimizerPath,
}, nil
}
+547
View File
@@ -0,0 +1,547 @@
#include "onnxruntime_wrapper.h"
static const OrtApi *ort_api = NULL;
static const char *ORT_VERSION = NULL;
static AppendCoreMLProviderFn append_coreml_provider_fn = NULL;
// The dml_provider_factory.h header for using DirectML is annoying to include
// here for a couple reasons:
// - It contains C++
// - It includes d3d12.h and DirectML.h, both of which may be hard to set up
// under mingw
// Fortunately, the basic AppendExecutionProvider_DML function from the
// OrtDmlApi struct does not rely on any of these things, but we still need the
// struct definition itself. Obviously, copying it here is not perfect, and
// we'll need to keep an eye on it to make sure it doesn't change between
// updates. Most importantly, we need to make sure that the one function we
// care about remains at the same place in the struct. Since it's first,
// hopefully it's unlikely to change.
typedef OrtStatus* (*AppendDirectMLProviderFn)(OrtSessionOptions*, int);
typedef struct {
AppendDirectMLProviderFn SessionOptionsAppendExecutionProvider_DML;
// All of these functions pointers should be irrelevant (and they depend on
// other definitions from dml_provider_factory.h), but I'll copy them here
// regardless as plain void*s. GetExecutionProviderApi shouldn't write to
// this struct anyway, as it only provides a const pointer to it.
void *SessionOptionsAppendExecutionProvider_DML1;
void *CreateGPUAllocationFromD3DResource;
void *FreeGPUAllocation;
void *GetD3D12ResourceFromAllocation;
void *SessionOptionsAppendExecutionProvider_DML2;
} DummyOrtDMLAPI;
int SetAPIFromBase(OrtApiBase *api_base) {
if (!api_base) return 1;
ort_api = api_base->GetApi(ORT_API_VERSION);
ORT_VERSION = api_base->GetVersionString();
if (!ort_api) return 2;
return 0;
}
const char *GetVersion() {
return ORT_VERSION;
}
void SetCoreMLProviderFunctionPointer(void *ptr) {
append_coreml_provider_fn = (AppendCoreMLProviderFn) ptr;
}
void ReleaseOrtStatus(OrtStatus *status) {
ort_api->ReleaseStatus(status);
}
OrtStatus *CreateOrtEnv(char *name, OrtEnv **env) {
return ort_api->CreateEnv(ORT_LOGGING_LEVEL_ERROR, name, env);
}
OrtStatus *DisableTelemetry(OrtEnv *env) {
return ort_api->DisableTelemetryEvents(env);
}
OrtStatus *EnableTelemetry(OrtEnv *env) {
return ort_api->EnableTelemetryEvents(env);
}
void ReleaseOrtEnv(OrtEnv *env) {
ort_api->ReleaseEnv(env);
}
OrtStatus *CreateOrtMemoryInfo(OrtMemoryInfo **mem_info) {
return ort_api->CreateCpuMemoryInfo(OrtArenaAllocator, OrtMemTypeDefault,
mem_info);
}
void ReleaseOrtMemoryInfo(OrtMemoryInfo *info) {
ort_api->ReleaseMemoryInfo(info);
}
const char *GetErrorMessage(OrtStatus *status) {
if (!status) return "No error (NULL status)";
return ort_api->GetErrorMessage(status);
}
OrtStatus *CreateSessionOptions(OrtSessionOptions **o) {
return ort_api->CreateSessionOptions(o);
}
void ReleaseSessionOptions(OrtSessionOptions *o) {
ort_api->ReleaseSessionOptions(o);
}
OrtStatus *SetIntraOpNumThreads(OrtSessionOptions *o, int n) {
return ort_api->SetIntraOpNumThreads(o, n);
}
OrtStatus *SetInterOpNumThreads(OrtSessionOptions *o, int n) {
return ort_api->SetInterOpNumThreads(o, n);
}
OrtStatus *SetCpuMemArena(OrtSessionOptions *o, int use_arena){
if (use_arena)
return ort_api->EnableCpuMemArena(o);
return ort_api->DisableCpuMemArena(o);
}
OrtStatus *SetMemPattern(OrtSessionOptions *o, int use_mem_pattern){
if (use_mem_pattern)
return ort_api->EnableMemPattern(o);
return ort_api->DisableMemPattern(o);
}
OrtStatus *AppendExecutionProviderCUDAV2(OrtSessionOptions *o,
OrtCUDAProviderOptionsV2 *cuda_options) {
return ort_api->SessionOptionsAppendExecutionProvider_CUDA_V2(o,
cuda_options);
}
OrtStatus *CreateCUDAProviderOptions(OrtCUDAProviderOptionsV2 **o) {
return ort_api->CreateCUDAProviderOptions(o);
}
void ReleaseCUDAProviderOptions(OrtCUDAProviderOptionsV2 *o) {
ort_api->ReleaseCUDAProviderOptions(o);
}
OrtStatus *UpdateCUDAProviderOptions(OrtCUDAProviderOptionsV2 *o,
const char **keys, const char **values, int num_keys) {
return ort_api->UpdateCUDAProviderOptions(o, keys, values, num_keys);
}
OrtStatus *CreateTensorRTProviderOptions(OrtTensorRTProviderOptionsV2 **o) {
return ort_api->CreateTensorRTProviderOptions(o);
}
void ReleaseTensorRTProviderOptions(OrtTensorRTProviderOptionsV2 *o) {
ort_api->ReleaseTensorRTProviderOptions(o);
}
OrtStatus *UpdateTensorRTProviderOptions(OrtTensorRTProviderOptionsV2 *o,
const char **keys, const char **values, int num_keys) {
return ort_api->UpdateTensorRTProviderOptions(o, keys, values, num_keys);
}
OrtStatus *AppendExecutionProviderTensorRTV2(OrtSessionOptions *o,
OrtTensorRTProviderOptionsV2 *tensor_rt_options) {
return ort_api->SessionOptionsAppendExecutionProvider_TensorRT_V2(o,
tensor_rt_options);
}
OrtStatus *AppendExecutionProviderCoreML(OrtSessionOptions *o,
uint32_t flags) {
if (!append_coreml_provider_fn) {
return ort_api->CreateStatus(ORT_NOT_IMPLEMENTED, "Your platform or "
"onnxruntime library does not support CoreML");
}
return append_coreml_provider_fn(o, flags);
}
OrtStatus *AppendExecutionProviderDirectML(OrtSessionOptions *o,
int device_id) {
DummyOrtDMLAPI *dml_api = NULL;
OrtStatus *status = NULL;
status = ort_api->GetExecutionProviderApi("DML", ORT_API_VERSION,
(const void **) (&dml_api));
if (status) return status;
status = dml_api->SessionOptionsAppendExecutionProvider_DML(o, device_id);
return status;
}
OrtStatus *AppendExecutionProviderOpenVINOV2(OrtSessionOptions *o,
const char **keys, const char **values, int num_keys) {
return ort_api->SessionOptionsAppendExecutionProvider_OpenVINO_V2(o, keys,
values, num_keys);
}
OrtStatus *CreateSession(void *model_data, size_t model_data_length,
OrtEnv *env, OrtSession **out, OrtSessionOptions *options) {
OrtStatus *status = NULL;
int default_options = 0;
if (!options) {
default_options = 1;
status = ort_api->CreateSessionOptions(&options);
if (status) return status;
}
status = ort_api->CreateSessionFromArray(env, model_data, model_data_length,
options, out);
if (default_options) {
// If we created a default, empty, options struct, we don't need to keep it
// after creating the session.
ort_api->ReleaseSessionOptions(options);
}
return status;
}
OrtStatus *CreateSessionFromFile(char *model_path, OrtEnv *env,
OrtSession **out, OrtSessionOptions *options) {
// Nearly identical to CreateSession, except invokes ort_api->CreateSession
// rather than ort_api->CreateSessionFromArray.
OrtStatus *status = NULL;
int default_options = 0;
if (!options) {
default_options = 1;
status = ort_api->CreateSessionOptions(&options);
if (status) return status;
}
status = ort_api->CreateSession(env, (const ORTCHAR_T*) model_path, options,
out);
if (default_options) ort_api->ReleaseSessionOptions(options);
return status;
}
OrtStatus *RunOrtSession(OrtSession *session,
OrtValue **inputs, char **input_names, int input_count,
OrtValue **outputs, char **output_names, int output_count) {
OrtStatus *status = NULL;
status = ort_api->Run(session, NULL, (const char* const*) input_names,
(const OrtValue* const*) inputs, input_count,
(const char* const*) output_names, output_count, outputs);
return status;
}
void ReleaseOrtSession(OrtSession *session) {
ort_api->ReleaseSession(session);
}
OrtStatus *SessionGetInputCount(OrtSession *session, size_t *result) {
return ort_api->SessionGetInputCount(session, result);
}
OrtStatus *SessionGetOutputCount(OrtSession *session, size_t *result) {
return ort_api->SessionGetOutputCount(session, result);
}
void ReleaseOrtValue(OrtValue *value) {
ort_api->ReleaseValue(value);
}
OrtStatus *CreateOrtTensorWithShape(void *data, size_t data_size,
int64_t *shape, int64_t shape_size, OrtMemoryInfo *mem_info,
ONNXTensorElementDataType dtype, OrtValue **out) {
OrtStatus *status = NULL;
status = ort_api->CreateTensorWithDataAsOrtValue(mem_info, data, data_size,
shape, shape_size, dtype, out);
return status;
}
OrtStatus *GetTensorTypeAndShape(const OrtValue *value, OrtTensorTypeAndShapeInfo **out) {
return ort_api->GetTensorTypeAndShape(value, out);
}
OrtStatus *GetDimensionsCount(const OrtTensorTypeAndShapeInfo *info, size_t *out) {
return ort_api->GetDimensionsCount(info, out);
}
OrtStatus *GetDimensions(const OrtTensorTypeAndShapeInfo *info, int64_t *dim_values, size_t dim_values_length) {
return ort_api->GetDimensions(info, dim_values, dim_values_length);
}
OrtStatus *GetTensorElementType(const OrtTensorTypeAndShapeInfo *info, enum ONNXTensorElementDataType *out) {
return ort_api->GetTensorElementType(info, out);
}
void ReleaseTensorTypeAndShapeInfo(OrtTensorTypeAndShapeInfo *input) {
ort_api->ReleaseTensorTypeAndShapeInfo(input);
}
OrtStatus *GetTensorMutableData(OrtValue *value, void **out) {
return ort_api->GetTensorMutableData(value, out);
}
OrtStatus *SessionGetInputName(OrtSession *session, size_t i, char **name) {
OrtAllocator *allocator = NULL;
OrtStatus *status = NULL;
status = ort_api->GetAllocatorWithDefaultOptions(&allocator);
if (status) return status;
return ort_api->SessionGetInputName(session, i, allocator, name);
}
OrtStatus *SessionGetOutputName(OrtSession *session, size_t i, char **name) {
OrtAllocator *allocator = NULL;
OrtStatus *status = NULL;
status = ort_api->GetAllocatorWithDefaultOptions(&allocator);
if (status) return status;
return ort_api->SessionGetOutputName(session, i, allocator, name);
}
OrtStatus *FreeWithDefaultORTAllocator(void *to_free) {
OrtAllocator *allocator = NULL;
OrtStatus *status = NULL;
status = ort_api->GetAllocatorWithDefaultOptions(&allocator);
if (status) return status;
return ort_api->AllocatorFree(allocator, to_free);
}
OrtStatus *SessionGetInputTypeInfo(OrtSession *session, size_t i,
OrtTypeInfo **out) {
return ort_api->SessionGetInputTypeInfo(session, i, out);
}
OrtStatus *SessionGetOutputTypeInfo(OrtSession *session, size_t i,
OrtTypeInfo **out) {
return ort_api->SessionGetOutputTypeInfo(session, i, out);
}
void ReleaseTypeInfo(OrtTypeInfo *o) {
ort_api->ReleaseTypeInfo(o);
}
OrtStatus *GetONNXTypeFromTypeInfo(OrtTypeInfo *info, enum ONNXType *out) {
return ort_api->GetOnnxTypeFromTypeInfo(info, out);
}
OrtStatus *CastTypeInfoToTensorInfo(OrtTypeInfo *type_info,
OrtTensorTypeAndShapeInfo **out) {
return ort_api->CastTypeInfoToTensorInfo(type_info,
(const OrtTensorTypeAndShapeInfo **) out);
}
OrtStatus *SessionGetModelMetadata(OrtSession *s, OrtModelMetadata **m) {
return ort_api->SessionGetModelMetadata(s, m);
}
void ReleaseModelMetadata(OrtModelMetadata *m) {
return ort_api->ReleaseModelMetadata(m);
}
OrtStatus *ModelMetadataGetProducerName(OrtModelMetadata *m, char **name) {
OrtAllocator *allocator = NULL;
OrtStatus *status = NULL;
status = ort_api->GetAllocatorWithDefaultOptions(&allocator);
if (status) return status;
return ort_api->ModelMetadataGetProducerName(m, allocator, name);
}
OrtStatus *ModelMetadataGetGraphName(OrtModelMetadata *m, char **name) {
OrtAllocator *allocator = NULL;
OrtStatus *status = NULL;
status = ort_api->GetAllocatorWithDefaultOptions(&allocator);
if (status) return status;
return ort_api->ModelMetadataGetGraphName(m, allocator, name);
}
OrtStatus *ModelMetadataGetDomain(OrtModelMetadata *m, char **domain) {
OrtAllocator *allocator = NULL;
OrtStatus *status = NULL;
status = ort_api->GetAllocatorWithDefaultOptions(&allocator);
if (status) return status;
return ort_api->ModelMetadataGetDomain(m, allocator, domain);
}
OrtStatus *ModelMetadataGetDescription(OrtModelMetadata *m, char **desc) {
OrtAllocator *allocator = NULL;
OrtStatus *status = NULL;
status = ort_api->GetAllocatorWithDefaultOptions(&allocator);
if (status) return status;
return ort_api->ModelMetadataGetDescription(m, allocator, desc);
}
OrtStatus *ModelMetadataLookupCustomMetadataMap(OrtModelMetadata *m, char *key,
char **value) {
OrtAllocator *allocator = NULL;
OrtStatus *status = NULL;
status = ort_api->GetAllocatorWithDefaultOptions(&allocator);
if (status) return status;
return ort_api->ModelMetadataLookupCustomMetadataMap(m, allocator, key,
value);
}
OrtStatus *ModelMetadataGetCustomMetadataMapKeys(OrtModelMetadata *m,
char ***keys, int64_t *num_keys) {
OrtAllocator *allocator = NULL;
OrtStatus *status = NULL;
status = ort_api->GetAllocatorWithDefaultOptions(&allocator);
if (status) return status;
return ort_api->ModelMetadataGetCustomMetadataMapKeys(m, allocator, keys,
num_keys);
}
OrtStatus *ModelMetadataGetVersion(OrtModelMetadata *m, int64_t *version) {
return ort_api->ModelMetadataGetVersion(m, version);
}
OrtStatus *GetValue(OrtValue *container, int index, OrtValue **dst) {
OrtAllocator *allocator = NULL;
OrtStatus *status = NULL;
status = ort_api->GetAllocatorWithDefaultOptions(&allocator);
if (status) return status;
return ort_api->GetValue(container, index, allocator, dst);
}
OrtStatus *GetValueType(OrtValue *v, enum ONNXType *out) {
return ort_api->GetValueType(v, out);
}
OrtStatus *GetValueCount(OrtValue *v, size_t *out) {
return ort_api->GetValueCount(v, out);
}
OrtStatus *CreateOrtValue(OrtValue **in, size_t num_values,
enum ONNXType value_type, OrtValue **out) {
return ort_api->CreateValue((const OrtValue* const*) in, num_values,
value_type, out);
}
// TRAINING API WRAPPER
static const OrtTrainingApi *ort_training_api = NULL;
void SetTrainingApi() {
ort_training_api = ort_api->GetTrainingApi(ORT_API_VERSION);
}
int IsTrainingApiSupported() {
return ort_training_api != NULL;
}
OrtStatus *CreateCheckpoint(void *checkpoint_data, size_t checkpoint_data_length, OrtCheckpointState **out) {
OrtStatus *status = NULL;
status = ort_training_api->LoadCheckpointFromBuffer(checkpoint_data, checkpoint_data_length, out);
return status;
}
OrtStatus *CreateTrainingSessionFromBuffer(OrtCheckpointState *checkpoint_state,
void *training_model_data, size_t training_model_data_length,
void *eval_model_data, size_t eval_model_data_length,
void *optim_model_data, size_t optim_model_data_length,
OrtEnv *env, OrtTrainingSession **out, OrtSessionOptions *options) {
OrtStatus *status = NULL;
int default_options = 0;
if (!options) {
default_options = 1;
status = ort_api->CreateSessionOptions(&options);
if (status) return status;
}
status = ort_training_api->CreateTrainingSessionFromBuffer(env, options, checkpoint_state,
training_model_data, training_model_data_length, eval_model_data, eval_model_data_length,
optim_model_data, optim_model_data_length, out);
if (default_options) {
ort_api->ReleaseSessionOptions(options);
}
return status;
}
OrtStatus *CreateTrainingSessionFromPaths(OrtCheckpointState *checkpoint_state,
char *training_model_path, char *eval_model_path, char *optim_model_path,
OrtEnv *env, OrtTrainingSession **out, OrtSessionOptions *options) {
OrtStatus *status = NULL;
int default_options = 0;
if (!options) {
default_options = 1;
status = ort_api->CreateSessionOptions(&options);
if (status) return status;
}
status = ort_training_api->CreateTrainingSession(env, options, checkpoint_state,
training_model_path, eval_model_path, optim_model_path, out);
if (default_options) {
ort_api->ReleaseSessionOptions(options);
}
return status;
}
OrtStatus *TrainingSessionGetInputCount(OrtTrainingSession *training_session, size_t *result_training, size_t *result_eval) {
OrtStatus *status = NULL;
status = ort_training_api->TrainingSessionGetTrainingModelInputCount(training_session, result_training);
if (status) return status;
status = ort_training_api->TrainingSessionGetEvalModelInputCount(training_session, result_eval);
return status;
}
OrtStatus *TrainingSessionGetOutputCount(OrtTrainingSession *training_session, size_t *result_training, size_t *result_eval) {
OrtStatus *status = NULL;
status = ort_training_api->TrainingSessionGetTrainingModelOutputCount(training_session, result_training);
if (status) return status;
status = ort_training_api->TrainingSessionGetEvalModelOutputCount(training_session, result_eval);
return status;
}
OrtStatus *TrainingSessionGetTrainingInputName(OrtTrainingSession *training_session, size_t i, char **name) {
OrtAllocator *allocator = NULL;
OrtStatus *status = NULL;
status = ort_api->GetAllocatorWithDefaultOptions(&allocator);
if (status) return status;
return ort_training_api->TrainingSessionGetTrainingModelInputName(training_session, i, allocator, name);
}
OrtStatus *TrainingSessionGetTrainingOutputName(OrtTrainingSession *training_session, size_t i, char **name) {
OrtAllocator *allocator = NULL;
OrtStatus *status = NULL;
status = ort_api->GetAllocatorWithDefaultOptions(&allocator);
if (status) return status;
return ort_training_api->TrainingSessionGetTrainingModelOutputName(training_session, i, allocator, name);
}
OrtStatus *TrainingSessionGetEvalInputName(OrtTrainingSession *training_session, size_t i, char **name) {
OrtAllocator *allocator = NULL;
OrtStatus *status = NULL;
status = ort_api->GetAllocatorWithDefaultOptions(&allocator);
if (status) return status;
return ort_training_api->TrainingSessionGetEvalModelInputName(training_session, i, allocator, name);
}
OrtStatus *TrainingSessionGetEvalOutputName(OrtTrainingSession *training_session, size_t i, char **name) {
OrtAllocator *allocator = NULL;
OrtStatus *status = NULL;
status = ort_api->GetAllocatorWithDefaultOptions(&allocator);
if (status) return status;
return ort_training_api->TrainingSessionGetEvalModelOutputName(training_session, i, allocator, name);
}
OrtStatus *TrainStep(OrtTrainingSession *training_session, size_t inputs_len, OrtValue **inputs, size_t output_len, OrtValue **outputs) {
OrtStatus *status = NULL;
status = ort_training_api->TrainStep(training_session, NULL, inputs_len, (const OrtValue* const*) inputs, output_len, outputs);
return status;
}
OrtStatus *OptimizerStep(OrtTrainingSession *training_session) {
OrtStatus *status = NULL;
status = ort_training_api->OptimizerStep(training_session, NULL);
return status;
}
OrtStatus *LazyResetGrad(OrtTrainingSession *training_session) {
OrtStatus *status = NULL;
status = ort_training_api->LazyResetGrad(training_session);
return status;
}
OrtStatus *SaveCheckpoint(OrtCheckpointState *checkpoint, char *path, size_t include_optimizer) {
OrtStatus *status = NULL;
status = ort_training_api->SaveCheckpoint(checkpoint, path, include_optimizer);
return status;
}
OrtStatus *ExportModel(OrtTrainingSession *training_session, char *path, size_t outputs_len, char **output_names) {
OrtStatus *status = NULL;
status = ort_training_api->ExportModelForInferencing(training_session, path, outputs_len, (const char* const*) output_names);
return status;
}
void ReleaseOrtTrainingSession(OrtTrainingSession *session) {
ort_training_api->ReleaseTrainingSession(session);
}
void ReleaseCheckpointState(OrtCheckpointState *checkpoint) {
ort_training_api->ReleaseCheckpointState(checkpoint);
}
+329
View File
@@ -0,0 +1,329 @@
#ifndef ONNXRUNTIME_WRAPPER_H
#define ONNXRUNTIME_WRAPPER_H
// We want to always use the unix-like onnxruntime C APIs, even on Windows, so
// we need to undefine _WIN32 before including onnxruntime_c_api.h. However,
// this requires a careful song-and-dance.
// First, include these common headers, as they get transitively included by
// onnxruntime_c_api.h. We need to include them ourselves, first, so that the
// preprocessor will skip them while _WIN32 is undefined.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Next, we actually include the header.
#undef _WIN32
#include "onnxruntime_c_api.h"
#include "onnxruntime_training_c_api.h"
// ... However, mingw will complain if _WIN32 is *not* defined! So redefine it.
#define _WIN32
#ifdef __cplusplus
extern "C" {
#endif
// Used for the OrtSessionOptionsAppendExecutionProvider_CoreML function
// pointer on supported systems. Must match the signature in
// coreml_provider_factory.h provided along with the onnxruntime releases for
// Apple platforms.
typedef OrtStatus* (*AppendCoreMLProviderFn)(OrtSessionOptions*, uint32_t);
// Takes a pointer to the api_base struct in order to obtain the OrtApi
// pointer. Intended to be called from Go. Returns nonzero on error.
int SetAPIFromBase(OrtApiBase *api_base);
// Get the version of the Onnxruntime library for logging.
const char *GetVersion();
// OrtSessionOptionsAppendExecutionProvider_CoreML is exported directly from
// the Apple .dylib, so we call this function on Apple platforms to set the
// function pointer to the correct address. On other platforms, the function
// pointer should remain NULL.
void SetCoreMLProviderFunctionPointer(void *ptr);
// Wraps ort_api->ReleaseStatus(status)
void ReleaseOrtStatus(OrtStatus *status);
// Wraps calling ort_api->CreateEnv. Returns a non-NULL status on error.
OrtStatus *CreateOrtEnv(char *name, OrtEnv **env);
// Wraps ort_api->DisableTelemetryEvents. Returns a non-NULL status on error.
OrtStatus *DisableTelemetry(OrtEnv *env);
// Wraps ort_api->EnableTelemetryEvents. Returns a non-NULL status on error.
OrtStatus *EnableTelemetry(OrtEnv *env);
// Wraps ort_api->ReleaseEnv
void ReleaseOrtEnv(OrtEnv *env);
// Wraps ort_api->CreateCpuMemoryInfo with some basic, default settings.
OrtStatus *CreateOrtMemoryInfo(OrtMemoryInfo **mem_info);
// Wraps ort_api->ReleaseMemoryInfo
void ReleaseOrtMemoryInfo(OrtMemoryInfo *info);
// Returns the message associated with the given ORT status.
const char *GetErrorMessage(OrtStatus *status);
// Wraps ort_api->CreateSessionOptions
OrtStatus *CreateSessionOptions(OrtSessionOptions **o);
// Wraps ort_api->ReleaseSessionOptions
void ReleaseSessionOptions(OrtSessionOptions *o);
// Wraps ort_api->SetIntraOpNumThreads
OrtStatus *SetIntraOpNumThreads(OrtSessionOptions *o, int n);
// Wraps ort_api->SetInterOpNumThreads
OrtStatus *SetInterOpNumThreads(OrtSessionOptions *o, int n);
// Wraps ort_api->EnableCpuMemArena & ort_api->DisableCpuMemArena
OrtStatus *SetCpuMemArena(OrtSessionOptions *o, int use_arena);
// Wraps ort_api->EnableMemPattern & ort_api->DisableMemPattern
OrtStatus *SetMemPattern(OrtSessionOptions *o, int use_mem_pattern);
// Wraps ort_api->SessionOptionsAppendExecutionProvider_CUDA_V2
OrtStatus *AppendExecutionProviderCUDAV2(OrtSessionOptions *o,
OrtCUDAProviderOptionsV2 *cuda_options);
// Wraps ort_api->CreateCUDAProviderOptions
OrtStatus *CreateCUDAProviderOptions(OrtCUDAProviderOptionsV2 **o);
// Wraps ort_api->ReleaseCUDAProviderOptions
void ReleaseCUDAProviderOptions(OrtCUDAProviderOptionsV2 *o);
// Wraps ort_api->UpdateCUDAProviderOptions
OrtStatus *UpdateCUDAProviderOptions(OrtCUDAProviderOptionsV2 *o,
const char **keys, const char **values, int num_keys);
// Wraps ort_api->CreateTensorRTProviderOptions
OrtStatus *CreateTensorRTProviderOptions(OrtTensorRTProviderOptionsV2 **o);
// Wraps ort_api->ReleaseTensorRTProviderOptions
void ReleaseTensorRTProviderOptions(OrtTensorRTProviderOptionsV2 *o);
// Wraps ort_api->UpdateTensorRTProviderOptions
OrtStatus *UpdateTensorRTProviderOptions(OrtTensorRTProviderOptionsV2 *o,
const char **keys, const char **values, int num_keys);
// Wraps ort_api->SessionOptionsAppendExecutionProvider_TensorRT_V2
OrtStatus *AppendExecutionProviderTensorRTV2(OrtSessionOptions *o,
OrtTensorRTProviderOptionsV2 *tensor_rt_options);
// Wraps OrtSessionOptionsAppendExecutionProvider_CoreML, exported from the
// dylib on Apple devices. Safely returns a non-NULL status on other platforms.
OrtStatus *AppendExecutionProviderCoreML(OrtSessionOptions *o,
uint32_t flags);
// Wraps getting the OrtDmlApi struct and calling
// dml_api->SessionOptionsAppendExecutionProvider_DML.
OrtStatus *AppendExecutionProviderDirectML(OrtSessionOptions *o,
int device_id);
// Wraps ort_api->AppendExecutionProvider_OpenVINO_V2
OrtStatus *AppendExecutionProviderOpenVINOV2(OrtSessionOptions *o,
const char **keys, const char **values, int num_keys);
// Creates an ORT session using the given model. The given options pointer may
// be NULL; if it is, then we'll use default options.
OrtStatus *CreateSession(void *model_data, size_t model_data_length,
OrtEnv *env, OrtSession **out, OrtSessionOptions *options);
// Like the CreateSession function, but takes a path to a model rather than a
// buffer containing it.
OrtStatus *CreateSessionFromFile(char *model_path, OrtEnv *env,
OrtSession **out, OrtSessionOptions *options);
// Runs an ORT session with the given input and output tensors, along with
// their names. In our use case, outputs must NOT be NULL.
OrtStatus *RunOrtSession(OrtSession *session,
OrtValue **inputs, char **input_names, int input_count,
OrtValue **outputs, char **output_names, int output_count);
// Wraps ort_api->ReleaseSession
void ReleaseOrtSession(OrtSession *session);
// Wraps ort_api->SessionGetInputCount.
OrtStatus *SessionGetInputCount(OrtSession *session, size_t *result);
// Wraps ort_api->SessionGetOutputCount.
OrtStatus *SessionGetOutputCount(OrtSession *session, size_t *result);
// Used to free OrtValue instances, such as tensors.
void ReleaseOrtValue(OrtValue *value);
// Creates an OrtValue tensor with the given shape, and backed by the user-
// supplied data buffer.
OrtStatus *CreateOrtTensorWithShape(void *data, size_t data_size,
int64_t *shape, int64_t shape_size, OrtMemoryInfo *mem_info,
ONNXTensorElementDataType dtype, OrtValue **out);
// Wraps ort_api->GetTensorTypeAndShape
OrtStatus *GetTensorTypeAndShape(const OrtValue *value, OrtTensorTypeAndShapeInfo **out);
// Wraps ort_api->GetDimensionsCount
OrtStatus *GetDimensionsCount(const OrtTensorTypeAndShapeInfo *info, size_t *out);
// Wraps ort_api->GetDimensions
OrtStatus *GetDimensions(const OrtTensorTypeAndShapeInfo *info, int64_t *dim_values, size_t dim_values_length);
// Wraps ort_api->GetTensorElementType
OrtStatus *GetTensorElementType(const OrtTensorTypeAndShapeInfo *info, enum ONNXTensorElementDataType *out);
// Wraps ort_api->ReleaseTensorTypeAndShapeInfo
void ReleaseTensorTypeAndShapeInfo(OrtTensorTypeAndShapeInfo *input);
// Wraps ort_api->GetTensorMutableData
OrtStatus *GetTensorMutableData(OrtValue *value, void **out);
// Wraps ort_api->SessionGetInputName, using the default allocator.
OrtStatus *SessionGetInputName(OrtSession *session, size_t i, char **name);
// Wraps ort_api->SessionGetOutputName, using the default allocator.
OrtStatus *SessionGetOutputName(OrtSession *session, size_t i, char **name);
// Frees anything that was allocated using the default ORT allocator.
OrtStatus *FreeWithDefaultORTAllocator(void *to_free);
// Wraps ort_api->SessionGetInputTypeInfo.
OrtStatus *SessionGetInputTypeInfo(OrtSession *session, size_t i,
OrtTypeInfo **out);
// Wraps ort_api->SessionGetOutputTypeInfo.
OrtStatus *SessionGetOutputTypeInfo(OrtSession *session, size_t i,
OrtTypeInfo **out);
// If the type_info is for a tensor, sets out to the a pointer to the tensor's
// NameAndTypeInfo. Do _not_ free the out pointer; it will be freed when
// type_info is released.
//
// Wraps ort_api->CastTypeInfoToTensorInfo.
OrtStatus *CastTypeInfoToTensorInfo(OrtTypeInfo *type_info,
OrtTensorTypeAndShapeInfo **out);
// Wraps ort_api->GetOnnxTypeFromTypeInfo.
OrtStatus *GetONNXTypeFromTypeInfo(OrtTypeInfo *info, enum ONNXType *out);
// Wraps ort_api->FreeTypeInfo.
void ReleaseTypeInfo(OrtTypeInfo *o);
// Wraps ort_spi->SessionGetModelMetadata.
OrtStatus *SessionGetModelMetadata(OrtSession *s, OrtModelMetadata **out);
// Wraps ort_api->ReleaseModelMetadata.
void ReleaseModelMetadata(OrtModelMetadata *m);
// Wraps ort_api->ModelMetadataGetProducerName, using the default allocator.
OrtStatus *ModelMetadataGetProducerName(OrtModelMetadata *m, char **name);
// Wraps ort_api->ModelMetadataGetGraphName, using the default allocator.
OrtStatus *ModelMetadataGetGraphName(OrtModelMetadata *m, char **name);
// Wraps ort_api->ModelMetadataGetDomain, using the default allocator.
OrtStatus *ModelMetadataGetDomain(OrtModelMetadata *m, char **domain);
// Wraps ort_api->ModelMetadataGetDescription, using the default allocator.
OrtStatus *ModelMetadataGetDescription(OrtModelMetadata *m, char **desc);
// Wraps ort_api->ModelMetadataLookupCustomMetadataMap, using the default
// allocator.
OrtStatus *ModelMetadataLookupCustomMetadataMap(OrtModelMetadata *m, char *key,
char **value);
// Wraps ort_api->ModelMetadataGetCustomMetadataMapKeys, using the default
// allocator.
OrtStatus *ModelMetadataGetCustomMetadataMapKeys(OrtModelMetadata *m,
char ***keys, int64_t *num_keys);
// Wraps ort_api->ModelMetadataGetVersion.
OrtStatus *ModelMetadataGetVersion(OrtModelMetadata *m, int64_t *version);
// Wraps ort_api->GetValue. Uses the default allocator.
OrtStatus *GetValue(OrtValue *container, int index, OrtValue **dst);
// Wraps ort_api->GetValueType.
OrtStatus *GetValueType(OrtValue *v, enum ONNXType *out);
// Wraps ort_api->GetValueCount.
OrtStatus *GetValueCount(OrtValue *v, size_t *out);
// Wraps ort_api->CreateValue to create a map or a sequence.
OrtStatus *CreateOrtValue(OrtValue **in, size_t num_values,
enum ONNXType value_type, OrtValue **out);
// TRAINING API WRAPPER
void SetTrainingApi();
// Checks if training api is supported.
int IsTrainingApiSupported();
// Wraps ort_training_api->CreateSessionFromBuffer.
// Creates and ORT checkpoint from the checkpoint data.
OrtStatus *CreateCheckpoint(void *checkpoint_data,
size_t checkpoint_data_length, OrtCheckpointState **out);
// Wraps ort_training_api->CreateTrainingSessionFromBuffer. Creates an ORT
// training session using the given models and checkpoint. The given options
// pointer may be NULL; if it is, then we'll use default options.
OrtStatus *CreateTrainingSessionFromBuffer(OrtCheckpointState *checkpoint_state,
void *training_model_data, size_t training_model_data_length,
void *eval_model_data, size_t eval_model_data_length,
void *optim_model_data, size_t optim_model_data_length,
OrtEnv *env, OrtTrainingSession **out, OrtSessionOptions *options);
// Wraps ort_training_api->CreateTrainingSession.
// Currently this is the only way to create a training session that is able to
// export the final trained model to disk.
OrtStatus *CreateTrainingSessionFromPaths(OrtCheckpointState *checkpoint_state,
char *training_model_path, char *eval_model_path, char *optim_model_path,
OrtEnv *env, OrtTrainingSession **out, OrtSessionOptions *options);
// Wraps ort_training_api->TrainingSessionGetTrainingModelInputCount
// and ort_training_api->TrainingSessionGetEvalgModelInputCount.
OrtStatus *TrainingSessionGetInputCount(OrtTrainingSession *training_session, size_t *result_training, size_t *result_eval);
// Wraps ort_training_api->TrainingSessionGetTrainingModelOutputCounet
// and ort_training_api->TrainingSessionGetEvalgModelOutputCount.
OrtStatus *TrainingSessionGetOutputCount(OrtTrainingSession *training_session, size_t *result_training, size_t *result_eval);
// Wraps ort_training_api->TrainingSessionGetTrainingModelInputName.
OrtStatus *TrainingSessionGetTrainingInputName(OrtTrainingSession *training_session, size_t i, char **name);
// Wraps ort_training_api->TrainingSessionGetEvalModelInputName.
OrtStatus *TrainingSessionGetEvalInputName(OrtTrainingSession *training_session, size_t i, char **name);
// Wraps ort_training_api->TrainingSessionGetTrainingModelOutputName.
OrtStatus *TrainingSessionGetTrainingOutputName(OrtTrainingSession *training_session, size_t i, char **name);
// Wraps ort_training_api->TrainingSessionGetEvalModelOutputName.
OrtStatus *TrainingSessionGetEvalOutputName(OrtTrainingSession *training_session, size_t i, char **name);
// Wraps ort_training_api->TrainStep.
OrtStatus *TrainStep(OrtTrainingSession *training_session, size_t inputs_len, OrtValue **inputs, size_t output_len, OrtValue **outputs);
// Wraps ort_training_api->OptimizerStep.
OrtStatus *OptimizerStep(OrtTrainingSession *training_session);
// Wraps ort_training_api->LazyResetGrad.
OrtStatus *LazyResetGrad(OrtTrainingSession *training_session);
// Wraps ort_training_api->SaveCheckpoint.
OrtStatus *SaveCheckpoint(OrtCheckpointState *checkpoint, char *path, size_t include_optimizer);
// Wraps ort_training_api->ExportModel.
OrtStatus *ExportModel(OrtTrainingSession *training_session, char *path, size_t outputs_len, char **output_names);
// Wraps ort_training_api->ReleaseTrainingSession.
void ReleaseOrtTrainingSession(OrtTrainingSession *session);
// Wraps ort_training_api->ReleaseCheckpointState.
void ReleaseCheckpointState(OrtCheckpointState *checkpoint);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // ONNXRUNTIME_WRAPPER_H
+109
View File
@@ -0,0 +1,109 @@
//go:build !windows
package onnxruntime_go
import (
"fmt"
"runtime"
"unsafe"
)
/*
#cgo LDFLAGS: -ldl
#include <dlfcn.h>
#include "onnxruntime_wrapper.h"
typedef OrtApiBase* (*GetOrtApiBaseFunction)(void);
// Since Go can't call C function pointers directly, we just use this helper
// when calling GetApiBase
OrtApiBase *CallGetAPIBaseFunction(void *fn) {
OrtApiBase *to_return = ((GetOrtApiBaseFunction) fn)();
return to_return;
}
*/
import "C"
// This file includes the code for loading the onnxruntime and setting up the
// environment on non-Windows systems. For now, it has been tested on Linux and
// arm64 OSX.
// This will contain the handle to the onnxruntime shared library if it has
// been loaded successfully.
var libraryHandle unsafe.Pointer
func platformCleanup() error {
v, e := C.dlclose(libraryHandle)
if v != 0 {
return fmt.Errorf("Error closing the library: %w", e)
}
return nil
}
// Should only be called on Apple systems; looks up the CoreML provider
// function which should only be exported on apple onnxruntime dylib files.
func setAppendCoreMLFunctionPointer(libraryHandle unsafe.Pointer) error {
// This function name must match the name in coreml_provider_factory.h,
// which is provided in the onnxruntime release's include/ directory on for
// Apple platforms.
fnName := "OrtSessionOptionsAppendExecutionProvider_CoreML"
cFunctionName := C.CString(fnName)
defer C.free(unsafe.Pointer(cFunctionName))
appendCoreMLProviderProc := C.dlsym(libraryHandle, cFunctionName)
if appendCoreMLProviderProc == nil {
msg := C.GoString(C.dlerror())
return fmt.Errorf("Error looking up %s: %s", fnName, msg)
}
C.SetCoreMLProviderFunctionPointer(appendCoreMLProviderProc)
return nil
}
func platformInitializeEnvironment() error {
if onnxSharedLibraryPath == "" {
onnxSharedLibraryPath = "onnxruntime.so"
}
cName := C.CString(onnxSharedLibraryPath)
defer C.free(unsafe.Pointer(cName))
handle := C.dlopen(cName, C.RTLD_LAZY)
if handle == nil {
msg := C.GoString(C.dlerror())
return fmt.Errorf("Error loading ONNX shared library \"%s\": %s",
onnxSharedLibraryPath, msg)
}
cFunctionName := C.CString("OrtGetApiBase")
defer C.free(unsafe.Pointer(cFunctionName))
getAPIBaseProc := C.dlsym(handle, cFunctionName)
if getAPIBaseProc == nil {
C.dlclose(handle)
msg := C.GoString(C.dlerror())
return fmt.Errorf("Error looking up OrtGetApiBase in \"%s\": %s",
onnxSharedLibraryPath, msg)
}
ortAPIBase := C.CallGetAPIBaseFunction(getAPIBaseProc)
tmp := C.SetAPIFromBase((*C.OrtApiBase)(unsafe.Pointer(ortAPIBase)))
if tmp != 0 {
C.dlclose(handle)
return fmt.Errorf("Error setting ORT API base: %d", tmp)
}
if (runtime.GOOS == "darwin") || (runtime.GOOS == "ios") {
setAppendCoreMLFunctionPointer(handle)
// We'll silently ignore potential errors returned by
// setAppendCoreMLFunctionPointer (for now at least). Even though we're
// on Apple hardware, it's possible that the user will have compiled
// the onnxruntime library from source without CoreML support.
// A failure here will only leave the coreml function pointer as NULL
// in our C code, which will be detected and result in an error at
// runtime.
}
libraryHandle = handle
return nil
}
// Converts the given path to an ORTCHAR_T string, pointed to by a *C.char. The
// returned string must be freed using C.free when no longer needed. This
// wrapper is used for source compatibility with onnxruntime API functions
// requiring paths, which must be UTF-16 on Windows but UTF-8 elsewhere.
func createOrtCharString(str string) (*C.char, error) {
return C.CString(str), nil
}
+107
View File
@@ -0,0 +1,107 @@
//go:build windows
package onnxruntime_go
// This file includes the Windows-specific code for loading the onnxruntime
// library and setting up the environment.
import (
"fmt"
"syscall"
"unicode/utf16"
"unicode/utf8"
"unsafe"
)
// #include "onnxruntime_wrapper.h"
import "C"
// This will contain the handle to the onnxruntime dll if it has been loaded
// successfully.
var libraryHandle syscall.Handle
func platformCleanup() error {
e := syscall.FreeLibrary(libraryHandle)
libraryHandle = 0
return e
}
func platformInitializeEnvironment() error {
if onnxSharedLibraryPath == "" {
onnxSharedLibraryPath = "onnxruntime.dll"
}
handle, e := syscall.LoadLibrary(onnxSharedLibraryPath)
if e != nil {
return fmt.Errorf("Error loading ONNX shared library \"%s\": %w",
onnxSharedLibraryPath, e)
}
getApiBaseProc, e := syscall.GetProcAddress(handle, "OrtGetApiBase")
if e != nil {
syscall.FreeLibrary(handle)
return fmt.Errorf("Error finding OrtGetApiBase function in %s: %w",
onnxSharedLibraryPath, e)
}
ortApiBase, _, e := syscall.SyscallN(uintptr(getApiBaseProc), 0)
if ortApiBase == 0 {
syscall.FreeLibrary(handle)
if e != nil {
return fmt.Errorf("Error calling OrtGetApiBase: %w", e)
} else {
return fmt.Errorf("Error calling OrtGetApiBase")
}
}
tmp := C.SetAPIFromBase((*C.OrtApiBase)(unsafe.Pointer(ortApiBase)))
if tmp != 0 {
syscall.FreeLibrary(handle)
return fmt.Errorf("Error setting ORT API base: %d", tmp)
}
// we do not initialize the training API on windows (see setup_env.go)
// because currently we cannot support the conversion from UTF-8 to wide
// character. See https://github.com/yalue/onnxruntime_go/pull/56.
libraryHandle = handle
return nil
}
// Converts the given string to a UTF-16 string, pointed to by a raw
// *C.char. Note that we actually keep ORTCHAR_T defined to char even
// on Windows, so do _not_ index into this string from Cgo code and expect to
// get correct characters! Instead, this should only be used to obtain pointers
// that are passed to onnxruntime windows DLL functions expecting ORTCHAR_T*
// args. This is required because we undefine _WIN32 for cgo compatibility when
// including onnxruntime_c_api.h, but still interact with a DLL that was
// compiled assuming _WIN32 was defined.
//
// The pointer returned by this function must still be freed using C.free when
// no longer needed. This will return an error if the given string contains
// non-UTF8 characters.
func createOrtCharString(str string) (*C.char, error) {
src := []uint8(str)
// Assumed common case: the utf16 buffer contains one uint16 per utf8 byte
// plus one more for the required null terminator in the C buffer.
dst := make([]uint16, 0, len(src)+1)
// Convert UTF-8 to UTF-16 by reading each subsequent rune from src and
// appending it as UTF-16 to dst.
for len(src) > 0 {
r, size := utf8.DecodeRune(src)
if r == utf8.RuneError {
return nil, fmt.Errorf("Invalid UTF-8 rune found in \"%s\"", str)
}
src = src[size:]
dst = utf16.AppendRune(dst, r)
}
// Make sure dst contains the null terminator. Additionally this will cause
// us to return an empty string if the original string was empty.
dst = append(dst, 0)
// Finally, we need to copy dst into a C array for compatibility with
// C.CString.
toReturn := C.calloc(C.size_t(len(dst)), 2)
if toReturn == nil {
return nil, fmt.Errorf("Error allocating buffer for the utf16 string")
}
C.memcpy(toReturn, unsafe.Pointer(&(dst[0])), C.size_t(len(dst))*2)
return (*C.char)(toReturn), nil
}
+54
View File
@@ -0,0 +1,54 @@
package onnxruntime_go
// This file contains definitions for the generic tensor data types we support.
// #include "onnxruntime_wrapper.h"
import "C"
import (
"reflect"
)
type FloatData interface {
~float32 | ~float64
}
type IntData interface {
~int8 | ~uint8 | ~int16 | ~uint16 | ~int32 | ~uint32 | ~int64 | ~uint64
}
// This is used as a type constraint for the generic Tensor type.
type TensorData interface {
FloatData | IntData
}
// Returns the ONNX enum value used to indicate TensorData type T.
func GetTensorElementDataType[T TensorData]() C.ONNXTensorElementDataType {
// Sadly, we can't do type assertions to get underlying types, so we need
// to use reflect here instead.
var v T
kind := reflect.ValueOf(v).Kind()
switch kind {
case reflect.Float64:
return C.ONNX_TENSOR_ELEMENT_DATA_TYPE_DOUBLE
case reflect.Float32:
return C.ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT
case reflect.Int8:
return C.ONNX_TENSOR_ELEMENT_DATA_TYPE_INT8
case reflect.Uint8:
return C.ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT8
case reflect.Int16:
return C.ONNX_TENSOR_ELEMENT_DATA_TYPE_INT16
case reflect.Uint16:
return C.ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT16
case reflect.Int32:
return C.ONNX_TENSOR_ELEMENT_DATA_TYPE_INT32
case reflect.Uint32:
return C.ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT32
case reflect.Int64:
return C.ONNX_TENSOR_ELEMENT_DATA_TYPE_INT64
case reflect.Uint64:
return C.ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT64
}
return C.ONNX_TENSOR_ELEMENT_DATA_TYPE_UNDEFINED
}