コンテンツにスキップ

C#

.NET CLIでNugetパッケージをインストールする

Section titled “.NET CLIでNugetパッケージをインストールする”
Terminal window
dotnet add package Microsoft.ML.OnnxRuntime --version 1.16.0
dotnet add package System.Numerics.Tensors --version 0.1.0
using Microsoft.ML.OnnxRuntime;
using System.Numerics.Tensors;

これは、SciKit Learnで作成されたNLPモデルの推論にC#でORTを使用するAzure Functionの例です。

public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log, ExecutionContext context)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string review = req.Query["review"];
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
review ??= data.review;
Debug.Assert(!string.IsNullOrEmpty(review), "Expecting a string with a content");
// 推論セッションを作成するためのモデルへのパスを取得します。
const string modelPath = "./model.onnx";
// モデルパスからInferenceSessionを作成します。
// セッションの作成と読み込みはリクエストごとにコストがかかります。
// これらはキャッシュしたほうが良いです
using var session = new InferenceSession(modelPath);
// 入力テンソルを作成します(NLPの例)
using var inputOrtValue = OrtValue.CreateTensorWithEmptyStrings(OrtAllocator.DefaultInstance, new long[] { 1, 1 });
inputOrtValue.StringTensorSetElementAt(review, 0);
// セッションの入力データを作成します。この場合、すべての出力を要求します。
var inputs = new Dictionary<string, OrtValue>
{
{ "input", inputOrtValue }
};
using var runOptions = new RunOptions();
// 出力としてマップのシーケンスを取得しています。シーケンスの最初の要素(マップ)に関心があります。
// その結果はマップのシーケンスであり、そこから最初のマップのみが必要です。
using var outputs = session.Run(runOptions, inputs, session.OutputNames);
Debug.Assert(outputs.Count > 0, "Expecting some output");
// マップのシーケンスである最後の出力が必要です
var lastOutput = outputs[outputs.Count - 1];
// 出力タイプをチェックするためのオプションのコード
{
var outputTypeInfo = lastOutput.GetTypeInfo();
Debug.Assert(outputTypeInfo.OnnxType == OnnxValueType.ONNX_TYPE_SEQUENCE, "Expecting a sequence");
var sequenceTypeInfo = outputTypeInfo.SequenceTypeInfo;
Debug.Assert(sequenceTypeInfo.ElementType.OnnxType == OnnxValueType.ONNX_TYPE_MAP, "Expecting a sequence of maps");
}
var elementsNum = lastOutput.GetValueCount();
Debug.Assert(elementsNum > 0, "Expecting a non empty sequence");
// シーケンス内の最初のマップを取得します
using var firstMap = lastOutput.GetValue(0, OrtAllocator.DefaultInstance);
// チェック用のオプションのコード
{
// マップには常にキーと値の2つの要素があります
// これは文字列から浮動小数点数へのマップであると想定しています
var mapTypeInfo = firstMap.GetTypeInfo().MapTypeInfo;
Debug.Assert(mapTypeInfo.KeyType == TensorElementType.String, "Expecting keys to be strings");
Debug.Assert(mapTypeInfo.ValueType.OnnxType == OnnxValueType.ONNX_TYPE_TENSOR, "Values are in the tensor");
Debug.Assert(mapTypeInfo.ValueType.TensorTypeAndShapeInfo.ElementDataType == TensorElementType.Float, "Result map value is float");
}
var inferenceResult = new Dictionary<string, float>();
// ビジターを使用してマップのキーと値を読み取ります
// ここでは、キーと値は対応するエントリの同じ数で表されます
// string -> float
firstMap.ProcessMap((keys, values) => {
// ネイティブバッファに直接アクセスします
var valuesSpan = values.GetTensorDataAsSpan<float>();
var entryCount = (int)keys.GetTensorTypeAndShape().ElementCount;
inferenceResult.EnsureCapacity(entryCount);
for (int i = 0; i < entryCount; ++i)
{
inferenceResult.Add(keys.GetStringElement(i), valuesSpan[i]);
}
}, OrtAllocator.DefaultInstance);
// 推論結果をjsonとして返します。
return new JsonResult(inferenceResult);
}

入力/出力テンソルバッファを再利用する

Section titled “入力/出力テンソルバッファを再利用する”

シナリオによっては、入力/出力テンソルを再利用したい場合があります。これは、2つのモデルを連鎖させたい場合(つまり、一方の出力を他方の入力として供給する)、または複数の推論実行中に推論速度を高速化したい場合によく発生します。

連鎖:モデルAの出力をモデルBの入力として供給する

Section titled “連鎖:モデルAの出力をモデルBの入力として供給する”
using Microsoft.ML.OnnxRuntime.Tensors;
using Microsoft.ML.OnnxRuntime;
namespace Samples
{
class FeedModelAToModelB
{
static void Program()
{
const string modelAPath = "./modelA.onnx";
const string modelBPath = "./modelB.onnx";
using InferenceSession session1 = new InferenceSession(modelAPath);
using InferenceSession session2 = new InferenceSession(modelBPath);
// 説明のみ
float[] inputData = { 1, 2, 3, 4 };
long[] inputShape = { 1, 4 };
using var inputOrtValue = OrtValue.CreateTensorValueFromMemory(inputData, inputShape);
// セッションの入力データを作成します。この場合、すべての出力を要求します。
var inputs1 = new Dictionary<string, OrtValue>
{
{ "input", inputOrtValue }
};
using var runOptions = new RunOptions();
// session1の推論
using (var outputs1 = session1.Run(runOptions, inputs1, session1.OutputNames))
{
// 中間値を取得します
var outputToFeed = outputs1.First();
// ONNX値の名前を変更します
// session2の入力リストを作成します
var inputs2 = new Dictionary<string, OrtValue>
{
{ "inputNameForModelB", outputToFeed }
};
// session2の推論
using (var results = session2.Run(runOptions, inputs2, session2.OutputNames))
{
// 結果を操作します
}
}
}
}
}

固定サイズの入力と出力を持つ複数の推論実行

Section titled “固定サイズの入力と出力を持つ複数の推論実行”

モデルに固定サイズの入力と数値テンソルの出力がある場合、 OrtValueとそのAPIを使用して推論速度を高速化し、データ転送を最小限に抑えます。 OrtValueクラスを使用すると、入力テンソルと出力テンソルの基になるバッファを再利用できます。 マネージドバッファをピン留めし、推論に使用します。また、出力のネイティブバッファへの直接アクセスも提供します。 出力用にOrtValueを事前に割り当てたり、既存のバッファの上に作成したりすることもできます。 これにより、実行時間全体で時間が顕著になる小さなモデルに有益なオーバーヘッドを回避できます。

OrtValueクラスは、Onnruntime C# APIの他の多くのクラスと同様にIDisposableであることに注意してください。 メモリリークを回避するために、マネージドバッファのピン留めを解除するか、ネイティブバッファを解放するために、適切に破棄する必要があります。

GPUパッケージを使用している場合は、InferenceSessionを作成するときに適切なSessionOptionsを使用するだけです。

int gpuDeviceId = 0; // 実行するGPUデバイスID
using var gpuSessionOptoins = SessionOptions.MakeSessionOptionWithCudaProvider(gpuDeviceId);
using var session = new InferenceSession("model.onnx", gpuSessionOptoins);

ONNXランタイムは、.NET標準プラットフォームのいずれかでONNXモデルの推論を実行するためのC# .NETバインディングを提供します。

サポートされているバージョン

Section titled “サポートされているバージョン”

.NET standard 1.1

アーティファクト説明サポートされているプラットフォーム
Microsoft.ML.OnnxRuntimeCPU (リリース)Windows, Linux, Mac, X64, X86 (Windowsのみ), ARM64 (Windowsのみ)…詳細: 互換性
Microsoft.ML.OnnxRuntime.GpuGPU - CUDA (リリース)Windows, Linux, Mac, X64…詳細: 互換性
Microsoft.ML.OnnxRuntime.DirectMLGPU - DirectML (リリース)Windows 10 1709+
onnxruntimeCPU, GPU (開発版), CPU (オンデバイストレーニング)リリース版と同じ
Microsoft.ML.OnnxRuntime.TrainingCPU オンデバイストレーニング (リリース)Windows, Linux, Mac, X64, X86 (Windowsのみ), ARM64 (Windowsのみ)…詳細: 互換性

C# APIリファレンス

チュートリアル: 基本 - C#を参照してください。