mirror of
https://github.com/saymrwulf/onnxruntime.git
synced 2026-07-21 19:18:55 +00:00
### Description Merge main to WindowsAI ### Motivation and Context <!-- - Why is this change required? What problem does it solve? - If it fixes an open issue, please link to the issue here. --> --------- Signed-off-by: Nash <george.nash@intel.com> Signed-off-by: Yiming Hu <yiming.hu@amd.com> Signed-off-by: Liqun Fu <liqfu@microsoft.com> Co-authored-by: Kaz Nishimura <kazssym@linuxfront.com> Co-authored-by: Tianlei Wu <tlwu@microsoft.com> Co-authored-by: Nat Kershaw (MSFT) <nakersha@microsoft.com> Co-authored-by: Yulong Wang <7679871+fs-eire@users.noreply.github.com> Co-authored-by: Changming Sun <chasun@microsoft.com> Co-authored-by: zesongw <zesong.wang@intel.com> Co-authored-by: Yi Zhang <zhanyi@microsoft.com> Co-authored-by: Dmitri Smirnov <yuslepukhin@users.noreply.github.com> Co-authored-by: Yifan Li <109183385+yf711@users.noreply.github.com> Co-authored-by: simonjub <78098752+simonjub@users.noreply.github.com> Co-authored-by: PeixuanZuo <94887879+PeixuanZuo@users.noreply.github.com> Co-authored-by: Adrian Lizarraga <adlizarraga@microsoft.com> Co-authored-by: Edward Chen <18449977+edgchen1@users.noreply.github.com> Co-authored-by: Arthur Islamov <arthur@islamov.ai> Co-authored-by: Jambay Kinley <jambaykinley@microsoft.com> Co-authored-by: Justin Chu <justinchuby@users.noreply.github.com> Co-authored-by: Wei-Sheng Chin <wschin@outlook.com> Co-authored-by: Bowen Bao <bowbao@microsoft.com> Co-authored-by: Hariharan Seshadri <shariharan91@gmail.com> Co-authored-by: Numfor Tiapo <numsmt2@gmail.com> Co-authored-by: Vincent Wang <wangwchpku@outlook.com> Co-authored-by: Pranav Sharma <prs@microsoft.com> Co-authored-by: George Nash <george.nash@intel.com> Co-authored-by: Abhishek Jindal <abjindal@microsoft.com> Co-authored-by: pengwa <pengwa@microsoft.com> Co-authored-by: Yiming Hu <woinck@users.noreply.github.com> Co-authored-by: Jiajia Qin <jiajia.qin@intel.com> Co-authored-by: Lukas Berbuer <36054362+lukasberbuer@users.noreply.github.com> Co-authored-by: Wanming Lin <wanming.lin@intel.com> Co-authored-by: Xavier Dupré <xadupre@users.noreply.github.com> Co-authored-by: aimilefth <60664743+aimilefth@users.noreply.github.com> Co-authored-by: Baiju Meswani <bmeswani@microsoft.com> Co-authored-by: Adam Pocock <adam.pocock@oracle.com> Co-authored-by: Chi Lo <54722500+chilo-ms@users.noreply.github.com> Co-authored-by: RandySheriffH <48490400+RandySheriffH@users.noreply.github.com> Co-authored-by: Randy Shuai <rashuai@microsoft.com> Co-authored-by: Vadym Stupakov <vadim.stupakov@gmail.com> Co-authored-by: Jian Chen <cjian@microsoft.com> Co-authored-by: Brian Lambert <98757707+brian-pieces@users.noreply.github.com> Co-authored-by: Nicolò Lucchesi <nicolo.lucchesi@gmail.com> Co-authored-by: liqun Fu <liqfu@microsoft.com> Co-authored-by: trajep <trajepl@gmail.com> Co-authored-by: Scott McKay <skottmckay@gmail.com> Co-authored-by: Mustafa Ateş Uzun <mustafauzun0@gmail.com> Co-authored-by: MistEO <mistereo@hotmail.com> Co-authored-by: satyajandhyala <satya.k.jandhyala@gmail.com> Co-authored-by: shaahji <96227573+shaahji@users.noreply.github.com> Co-authored-by: Rachel Guo <35738743+YUNQIUGUO@users.noreply.github.com> Co-authored-by: rachguo <rachguo@rachguos-Mini.attlocal.net> Co-authored-by: Caroline Zhu <wolfivyaura@gmail.com> Co-authored-by: Caroline Zhu <carolinezhu@microsoft.com> Co-authored-by: Guenther Schmuelling <guschmue@microsoft.com> Co-authored-by: xhcao <xinghua.cao@intel.com> Co-authored-by: Ella Charlaix <80481427+echarlaix@users.noreply.github.com> Co-authored-by: Xu Xing <xing.xu@intel.com> Co-authored-by: Hector Li <hecli@microsoft.com> Co-authored-by: Ye Wang <52801275+wangyems@users.noreply.github.com> Co-authored-by: Your Name <you@example.com> Co-authored-by: Benedikt Hilmes <benedikt.hilmes@rwth-aachen.de> Co-authored-by: rachguo <rachguo@rachguos-Mac-mini.local> Co-authored-by: George Wu <jywu@microsoft.com> Co-authored-by: JiCheng <wejoncy@163.com> Co-authored-by: Sheil Kumar <smk2007@gmail.com> Co-authored-by: Sheil Kumar <sheilk@microsoft.com> Co-authored-by: cloudhan <guangyunhan@microsoft.com> Co-authored-by: kyoshisuki <143475866+kyoshisuki@users.noreply.github.com> Co-authored-by: aciddelgado <139922440+aciddelgado@users.noreply.github.com> Co-authored-by: tlwu@microsoft.com <tlwu@a100.crj0ad2y1kku1j4yxl4sj10o4e.gx.internal.cloudapp.net> Co-authored-by: Maximilian Müller <44298237+gedoensmax@users.noreply.github.com> Co-authored-by: Tang, Cheng <souptc@gmail.com> Co-authored-by: Cheng Tang <chenta@microsoft.com@orttrainingdev9.d32nl1ml4oruzj4qz3bqlggovf.px.internal.cloudapp.net> Co-authored-by: Cheng Tang <chenta@microsoft.com> Co-authored-by: Jeff Daily <jeff.daily@amd.com> Co-authored-by: cloudhan <cloudhan@outlook.com> Co-authored-by: Yufeng Li <liyufeng1987@gmail.com> Co-authored-by: Zhang Lei <zhang.huanning@hotmail.com> Co-authored-by: Dwayne Robinson <fdwr@hotmail.com> Co-authored-by: Zhipeng Han <zhipeng.han@outlook.com> Co-authored-by: Thiago Crepaldi <thiago.crepaldi@microsoft.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Patrice Vignola <vignola.patrice@gmail.com> Co-authored-by: kunal-vaishnavi <115581922+kunal-vaishnavi@users.noreply.github.com> Co-authored-by: snadampal <87143774+snadampal@users.noreply.github.com> Co-authored-by: Sumit Agarwal <sumitagarwal330@gmail.com> Co-authored-by: Ashwini Khade <askhade@microsoft.com> Co-authored-by: Yang Gu <yang.gu@intel.com> Co-authored-by: Cheng Tang <chenta@a100.crj0ad2y1kku1j4yxl4sj10o4e.gx.internal.cloudapp.net> Co-authored-by: mindest <30493312+mindest@users.noreply.github.com> Co-authored-by: Scott McKay <Scott.McKay@microsoft.com> Co-authored-by: Xavier Dupre <xadupre@microsoft.com@orttrainingdev9.d32nl1ml4oruzj4qz3bqlggovf.px.internal.cloudapp.net> Co-authored-by: guyang3532 <62738430+guyang3532@users.noreply.github.com> Co-authored-by: Carson M <carson@pyke.io> Co-authored-by: sophies927 <107952697+sophies927@users.noreply.github.com>
621 lines
30 KiB
C#
621 lines
30 KiB
C#
using Microsoft.ML.OnnxRuntime.Tensors;
|
|
using System;
|
|
using System.Buffers;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using Xunit;
|
|
|
|
namespace Microsoft.ML.OnnxRuntime.Tests
|
|
{
|
|
// Copy of the class that is internal in the main package
|
|
public class DisposableListTest<T> : List<T>, IDisposableReadOnlyCollection<T>
|
|
where T : IDisposable
|
|
{
|
|
public DisposableListTest()
|
|
{ }
|
|
|
|
public DisposableListTest(IEnumerable<T> enumerable) : base(enumerable)
|
|
{ }
|
|
|
|
public DisposableListTest(int count)
|
|
: base(count)
|
|
{ }
|
|
|
|
#region IDisposable Support
|
|
private bool disposedValue = false; // To detect redundant calls
|
|
|
|
protected virtual void Dispose(bool disposing)
|
|
{
|
|
if (!disposedValue)
|
|
{
|
|
if (disposing)
|
|
{
|
|
// Dispose in the reverse order.
|
|
// Objects should typically be destroyed/disposed
|
|
// in the reverse order of its creation
|
|
// especially if the objects created later refer to the
|
|
// objects created earlier. For homogeneous collections of objects
|
|
// it would not matter.
|
|
for (int i = this.Count - 1; i >= 0; --i)
|
|
{
|
|
this[i]?.Dispose();
|
|
}
|
|
this.Clear();
|
|
}
|
|
|
|
disposedValue = true;
|
|
}
|
|
}
|
|
|
|
// This code added to correctly implement the disposable pattern.
|
|
public void Dispose()
|
|
{
|
|
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
|
|
Dispose(true);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
#endregion
|
|
}
|
|
|
|
internal struct DisposableTestPair<TValue> : IDisposable
|
|
where TValue : IDisposable
|
|
{
|
|
public string Key;
|
|
public TValue Value;
|
|
public DisposableTestPair(string key, TValue value)
|
|
{
|
|
Key = key;
|
|
Value = value;
|
|
}
|
|
public void Dispose()
|
|
{
|
|
Value?.Dispose();
|
|
}
|
|
}
|
|
|
|
internal static class TestDataLoader
|
|
{
|
|
internal static byte[] LoadModelFromEmbeddedResource(string path)
|
|
{
|
|
var assembly = typeof(TestDataLoader).Assembly;
|
|
byte[] model = null;
|
|
|
|
var resourceName = assembly.GetManifestResourceNames().Single(p => p.EndsWith("." + path));
|
|
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
|
|
{
|
|
using (MemoryStream memoryStream = new MemoryStream())
|
|
{
|
|
stream.CopyTo(memoryStream);
|
|
model = memoryStream.ToArray();
|
|
}
|
|
}
|
|
|
|
return model;
|
|
}
|
|
|
|
internal static float[] LoadTensorFromEmbeddedResource(string path)
|
|
{
|
|
var tensorData = new List<float>();
|
|
var assembly = typeof(TestDataLoader).Assembly;
|
|
|
|
var resourceName = assembly.GetManifestResourceNames().Single(p => p.EndsWith("." + path));
|
|
using (StreamReader inputFile = new StreamReader(assembly.GetManifestResourceStream(resourceName)))
|
|
{
|
|
inputFile.ReadLine(); // skip the input name
|
|
string[] dataStr = inputFile.ReadLine().Split(new char[] { ',', '[', ']' }, StringSplitOptions.RemoveEmptyEntries);
|
|
for (int i = 0; i < dataStr.Length; i++)
|
|
{
|
|
tensorData.Add(Single.Parse(dataStr[i]));
|
|
}
|
|
}
|
|
|
|
return tensorData.ToArray();
|
|
}
|
|
|
|
static NamedOnnxValue LoadTensorPb(Onnx.TensorProto tensor, string nodeName, NodeMetadata nodeMeta)
|
|
{
|
|
if (nodeMeta.OnnxValueType != OnnxValueType.ONNX_TYPE_TENSOR)
|
|
{
|
|
throw new InvalidDataException($"Metadata for: '{nodeName}' has a type: '{nodeMeta.OnnxValueType}'" +
|
|
$" but loading as tensor: '{tensor.Name}'");
|
|
}
|
|
|
|
var protoDt = (Tensors.TensorElementType)tensor.DataType;
|
|
var metaElementType = nodeMeta.ElementDataType;
|
|
if (!((protoDt == metaElementType) ||
|
|
(protoDt == TensorElementType.UInt16 &&
|
|
(metaElementType == TensorElementType.BFloat16 || metaElementType == TensorElementType.Float16))))
|
|
throw new InvalidDataException($"For node: '{nodeName}' metadata expects: '{metaElementType}' but loaded loaded tensor type: '{protoDt}'");
|
|
|
|
// Tensors within Sequences may have no dimensions as the standard allows
|
|
// different dimensions for each tensor element of the sequence
|
|
if (nodeMeta.Dimensions.Length > 0 && nodeMeta.Dimensions.Length != tensor.Dims.Count)
|
|
{
|
|
throw new InvalidDataException($"node: '{nodeName}' nodeMeta.Dim.Length: {nodeMeta.Dimensions.Length} " +
|
|
$"is expected to be equal to tensor.Dims.Count {tensor.Dims.Count}");
|
|
}
|
|
|
|
var intDims = new int[tensor.Dims.Count];
|
|
for (int i = 0; i < tensor.Dims.Count; i++)
|
|
{
|
|
intDims[i] = (int)tensor.Dims[i];
|
|
}
|
|
|
|
for (int i = 0; i < nodeMeta.Dimensions.Length; i++)
|
|
{
|
|
if ((nodeMeta.Dimensions[i] != -1) && (nodeMeta.Dimensions[i] != tensor.Dims[i]))
|
|
throw new InvalidDataException($"Node: '{nodeName}' dimension at idx {i} is {nodeMeta.Dimensions}[{i}] " +
|
|
$"is expected to either be -1 or {tensor.Dims[i]}");
|
|
}
|
|
|
|
// element type for Float16 and BFloat16 in the loaded tensor would always be uint16, so
|
|
// we want to use element type from metadata
|
|
if (protoDt == TensorElementType.String)
|
|
return CreateNamedOnnxValueFromStringTensor(tensor.StringData, nodeName, intDims);
|
|
|
|
return CreateNamedOnnxValueFromTensorRawData(nodeName, tensor.RawData.Span, metaElementType, intDims);
|
|
}
|
|
|
|
internal static NamedOnnxValue CreateNamedOnnxValueFromTensorRawData(string nodeName, ReadOnlySpan<byte> rawData,
|
|
TensorElementType elementType, int[] intDims)
|
|
{
|
|
switch (elementType)
|
|
{
|
|
case TensorElementType.Float:
|
|
return CreateNamedOnnxValueFromRawData<float>(nodeName, rawData, intDims);
|
|
case TensorElementType.Double:
|
|
return CreateNamedOnnxValueFromRawData<double>(nodeName, rawData, intDims);
|
|
case TensorElementType.Int32:
|
|
return CreateNamedOnnxValueFromRawData<int>(nodeName, rawData, intDims);
|
|
case TensorElementType.UInt32:
|
|
return CreateNamedOnnxValueFromRawData<uint>(nodeName, rawData, intDims);
|
|
case TensorElementType.Int16:
|
|
return CreateNamedOnnxValueFromRawData<short>(nodeName, rawData, intDims);
|
|
case TensorElementType.UInt16:
|
|
return CreateNamedOnnxValueFromRawData<ushort>(nodeName, rawData, intDims);
|
|
case TensorElementType.Int64:
|
|
return CreateNamedOnnxValueFromRawData<long>(nodeName, rawData, intDims);
|
|
case TensorElementType.UInt64:
|
|
return CreateNamedOnnxValueFromRawData<ulong>(nodeName, rawData, intDims);
|
|
case TensorElementType.UInt8:
|
|
return CreateNamedOnnxValueFromRawData<byte>(nodeName, rawData, intDims);
|
|
case TensorElementType.Int8:
|
|
return CreateNamedOnnxValueFromRawData<sbyte>(nodeName, rawData, intDims);
|
|
case TensorElementType.Bool:
|
|
return CreateNamedOnnxValueFromRawData<bool>(nodeName, rawData, intDims);
|
|
case TensorElementType.Float16:
|
|
return CreateNamedOnnxValueFromRawData<Float16>(nodeName, rawData, intDims);
|
|
case TensorElementType.BFloat16:
|
|
return CreateNamedOnnxValueFromRawData<BFloat16>(nodeName, rawData, intDims);
|
|
case TensorElementType.String:
|
|
throw new ArgumentException("For string tensors of type use: CreateNamedOnnxValueFromStringTensor.");
|
|
default:
|
|
throw new NotImplementedException($"Tensors of type: {elementType} not currently supported by this function");
|
|
}
|
|
}
|
|
|
|
internal static NamedOnnxValue LoadTensorFromEmbeddedResourcePb(string path, string nodeName, NodeMetadata nodeMeta)
|
|
{
|
|
Onnx.TensorProto tensor = null;
|
|
|
|
var assembly = typeof(TestDataLoader).Assembly;
|
|
|
|
using (Stream stream = assembly.GetManifestResourceStream($"{assembly.GetName().Name}.TestData.{path}"))
|
|
{
|
|
tensor = Onnx.TensorProto.Parser.ParseFrom(stream);
|
|
}
|
|
|
|
return LoadTensorPb(tensor, nodeName, nodeMeta);
|
|
}
|
|
|
|
internal static NamedOnnxValue LoadOnnxValueFromFilePb(string fullFilename, string nodeName, NodeMetadata nodeMeta)
|
|
{
|
|
// No sparse tensor support yet
|
|
// Set buffer size to 4MB
|
|
const int readBufferSize = 4194304;
|
|
using (var file = new FileStream(fullFilename, FileMode.Open, FileAccess.Read, FileShare.Read, readBufferSize))
|
|
{
|
|
switch (nodeMeta.OnnxValueType)
|
|
{
|
|
case OnnxValueType.ONNX_TYPE_TENSOR:
|
|
{
|
|
var tensor = Onnx.TensorProto.Parser.ParseFrom(file);
|
|
return LoadTensorPb(tensor, nodeName, nodeMeta);
|
|
}
|
|
case OnnxValueType.ONNX_TYPE_SEQUENCE:
|
|
{
|
|
var sequence = Onnx.SequenceProto.Parser.ParseFrom(file);
|
|
return CreateNamedOnnxValueFromSequence(sequence, nodeName, nodeMeta);
|
|
}
|
|
case OnnxValueType.ONNX_TYPE_MAP:
|
|
{
|
|
throw new NotImplementedException(
|
|
"Map test data format requires clarification: https://github.com/onnx/onnx/issues/5072");
|
|
}
|
|
|
|
case OnnxValueType.ONNX_TYPE_OPTIONAL:
|
|
{
|
|
var opt = Onnx.OptionalProto.Parser.ParseFrom(file);
|
|
return CreateNamedOnnxValueFromOptional(opt, nodeName, nodeMeta);
|
|
}
|
|
default:
|
|
throw new NotImplementedException($"Unable to load value type: {nodeMeta.OnnxValueType} not implemented");
|
|
}
|
|
}
|
|
}
|
|
|
|
internal static DisposableTestPair<OrtValue> LoadOrtValueFromFilePb(string fullFilename, string nodeName, NodeMetadata nodeMeta)
|
|
{
|
|
// No sparse tensor support yet
|
|
// Set buffer size to 4MB
|
|
const int readBufferSize = 4194304;
|
|
using (var file = new FileStream(fullFilename, FileMode.Open, FileAccess.Read, FileShare.Read, readBufferSize))
|
|
{
|
|
switch (nodeMeta.OnnxValueType)
|
|
{
|
|
case OnnxValueType.ONNX_TYPE_TENSOR:
|
|
{
|
|
var tensor = Onnx.TensorProto.Parser.ParseFrom(file);
|
|
return new DisposableTestPair<OrtValue>(nodeName, LoadOrValueTensorPb(tensor, nodeName, nodeMeta));
|
|
}
|
|
case OnnxValueType.ONNX_TYPE_SEQUENCE:
|
|
{
|
|
var sequence = Onnx.SequenceProto.Parser.ParseFrom(file);
|
|
return new DisposableTestPair<OrtValue>(nodeName, CreateOrtValueFromSequence(sequence, nodeName, nodeMeta));
|
|
}
|
|
case OnnxValueType.ONNX_TYPE_MAP:
|
|
{
|
|
throw new NotImplementedException(
|
|
"Map test data format requires clarification: https://github.com/onnx/onnx/issues/5072");
|
|
}
|
|
|
|
case OnnxValueType.ONNX_TYPE_OPTIONAL:
|
|
{
|
|
var opt = Onnx.OptionalProto.Parser.ParseFrom(file);
|
|
return new DisposableTestPair<OrtValue>(nodeName, CreateOrtValueFromOptional(opt, nodeName, nodeMeta));
|
|
}
|
|
default:
|
|
throw new NotImplementedException($"Unable to load value type: {nodeMeta.OnnxValueType} not implemented");
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void SequenceCheckMatchOnnxType(string nodeName, SequenceMetadata meta,
|
|
OnnxValueType onnxType)
|
|
{
|
|
if (meta.ElementMeta.OnnxValueType == onnxType)
|
|
return;
|
|
|
|
throw new InvalidDataException($"Sequence node: '{nodeName}' " +
|
|
$"has element type: '{onnxType}'" +
|
|
$" expected: '{meta.ElementMeta.OnnxValueType}'");
|
|
}
|
|
|
|
private static string MakeSequenceElementName(string nodeName, string seqName, int seqNum)
|
|
{
|
|
if (seqName.Length > 0)
|
|
return $"seq.{nodeName}.data.{seqName}.{seqNum}";
|
|
else
|
|
return $"seq.{nodeName}.data._.{seqNum}";
|
|
}
|
|
|
|
internal static NamedOnnxValue CreateNamedOnnxValueFromSequence(Onnx.SequenceProto sequence, string nodeName, NodeMetadata nodeMeta)
|
|
{
|
|
var sequenceMeta = nodeMeta.AsSequenceMetadata();
|
|
var elemMeta = sequenceMeta.ElementMeta;
|
|
|
|
int seqNum = 0;
|
|
var seqElemType = (Onnx.SequenceProto.Types.DataType)sequence.ElemType;
|
|
switch (seqElemType)
|
|
{
|
|
case Onnx.SequenceProto.Types.DataType.Tensor:
|
|
{
|
|
SequenceCheckMatchOnnxType(nodeName, sequenceMeta, OnnxValueType.ONNX_TYPE_TENSOR);
|
|
var sequenceOfTensors = new List<NamedOnnxValue>(sequence.TensorValues.Count);
|
|
foreach (var tensor in sequence.TensorValues)
|
|
{
|
|
var elemName = MakeSequenceElementName(nodeName, sequence.Name, seqNum++);
|
|
var namedOnnxValue = LoadTensorPb(tensor, elemName, elemMeta);
|
|
sequenceOfTensors.Add(namedOnnxValue);
|
|
}
|
|
return NamedOnnxValue.CreateFromSequence(nodeName, sequenceOfTensors);
|
|
}
|
|
case Onnx.SequenceProto.Types.DataType.Sequence:
|
|
{
|
|
SequenceCheckMatchOnnxType(nodeName, sequenceMeta, OnnxValueType.ONNX_TYPE_SEQUENCE);
|
|
var seqOfSequences = new List<NamedOnnxValue>(sequence.SequenceValues.Count);
|
|
foreach (var s in sequence.SequenceValues)
|
|
{
|
|
var elemName = MakeSequenceElementName(nodeName, sequence.Name, seqNum++);
|
|
seqOfSequences.Add(CreateNamedOnnxValueFromSequence(s, elemName, elemMeta));
|
|
}
|
|
return NamedOnnxValue.CreateFromSequence(nodeName, seqOfSequences);
|
|
}
|
|
case Onnx.SequenceProto.Types.DataType.Map:
|
|
{
|
|
SequenceCheckMatchOnnxType(nodeName, sequenceMeta, OnnxValueType.ONNX_TYPE_MAP);
|
|
var seqOfMaps = new List<NamedOnnxValue>(sequence.MapValues.Count);
|
|
foreach (var m in sequence.MapValues)
|
|
{
|
|
var elemName = MakeSequenceElementName(nodeName, sequence.Name, seqNum++);
|
|
seqOfMaps.Add(CreateNamedOnnxValueFromMap(m, elemName, elemMeta));
|
|
}
|
|
return NamedOnnxValue.CreateFromSequence(nodeName, seqOfMaps);
|
|
}
|
|
case Onnx.SequenceProto.Types.DataType.Optional:
|
|
{
|
|
SequenceCheckMatchOnnxType(nodeName, sequenceMeta, OnnxValueType.ONNX_TYPE_OPTIONAL);
|
|
var seqOfOpts = new List<NamedOnnxValue>(sequence.OptionalValues.Count);
|
|
foreach (var opt in sequence.OptionalValues)
|
|
{
|
|
var elemName = MakeSequenceElementName(nodeName, sequence.Name, seqNum++);
|
|
seqOfOpts.Add(CreateNamedOnnxValueFromOptional(opt, elemName, elemMeta));
|
|
}
|
|
return NamedOnnxValue.CreateFromSequence(nodeName, seqOfOpts);
|
|
}
|
|
default:
|
|
throw new NotImplementedException($"Sequence test data loading does not support element type: " +
|
|
$"'{seqElemType}'");
|
|
}
|
|
}
|
|
internal static NamedOnnxValue CreateNamedOnnxValueFromMap(Onnx.MapProto map, string nodeName, NodeMetadata nodeMetadata)
|
|
{
|
|
// See GH issue https://github.com/onnx/onnx/issues/5072
|
|
throw new NotImplementedException($"Loading map node: '{nodeName}' not implemented yet");
|
|
}
|
|
|
|
internal static NamedOnnxValue CreateNamedOnnxValueFromOptional(Onnx.OptionalProto optional, string nodeName, NodeMetadata nodeMetadata)
|
|
{
|
|
var meta = nodeMetadata.AsOptionalMetadata().ElementMeta;
|
|
switch ((Onnx.OptionalProto.Types.DataType)optional.ElemType)
|
|
{
|
|
case Onnx.OptionalProto.Types.DataType.Tensor:
|
|
{
|
|
var tensor = optional.TensorValue;
|
|
return LoadTensorPb(tensor, nodeName, meta);
|
|
}
|
|
case Onnx.OptionalProto.Types.DataType.Sequence:
|
|
{
|
|
var sequence = optional.SequenceValue;
|
|
return CreateNamedOnnxValueFromSequence(sequence, nodeName, meta);
|
|
}
|
|
case Onnx.OptionalProto.Types.DataType.Map:
|
|
{
|
|
var map = optional.MapValue;
|
|
return CreateNamedOnnxValueFromMap(map, nodeName, meta);
|
|
}
|
|
case Onnx.OptionalProto.Types.DataType.Optional:
|
|
throw new NotImplementedException($"Unable to load '{nodeName}' optional contained within optional");
|
|
default:
|
|
// Test data contains OptionalProto with the contained element type undefined.
|
|
// the premise is, if the element is not fed as an input, we should not care
|
|
// what Onnx type it is. However, we do not need to support AFAIK such inputs
|
|
// since the value for them could never be supplied.
|
|
throw new NotImplementedException($"Unable to load '{nodeName}' optional element type of: {(Onnx.OptionalProto.Types.DataType)optional.ElemType} type");
|
|
}
|
|
}
|
|
|
|
internal static NamedOnnxValue CreateNamedOnnxValueFromRawData<T>(string name, ReadOnlySpan<byte> rawData,
|
|
int[] dimensions)
|
|
where T : struct
|
|
{
|
|
var typedSrcSpan = MemoryMarshal.Cast<byte, T>(rawData);
|
|
var dt = new DenseTensor<T>(typedSrcSpan.ToArray(), dimensions);
|
|
return NamedOnnxValue.CreateFromTensor<T>(name, dt);
|
|
}
|
|
|
|
static OrtValue LoadOrValueTensorPb(Onnx.TensorProto tensor, string nodeName, NodeMetadata nodeMeta)
|
|
{
|
|
if (nodeMeta.OnnxValueType != OnnxValueType.ONNX_TYPE_TENSOR)
|
|
{
|
|
throw new InvalidDataException($"Metadata for: '{nodeName}' has a type: '{nodeMeta.OnnxValueType}'" +
|
|
$" but loading as tensor: {tensor.Name}");
|
|
}
|
|
|
|
var protoDt = (Tensors.TensorElementType)tensor.DataType;
|
|
var metaElementType = nodeMeta.ElementDataType;
|
|
if (!((protoDt == metaElementType) ||
|
|
(protoDt == TensorElementType.UInt16 &&
|
|
(metaElementType == TensorElementType.BFloat16 || metaElementType == TensorElementType.Float16))))
|
|
throw new InvalidDataException($"For node: '{nodeName}' metadata expects: '{metaElementType}' but loaded loaded tensor type: '{protoDt}'");
|
|
|
|
// Tensors within Sequences may have no dimensions as the standard allows
|
|
// different dimensions for each tensor element of the sequence
|
|
if (nodeMeta.Dimensions.Length > 0 && nodeMeta.Dimensions.Length != tensor.Dims.Count)
|
|
{
|
|
throw new InvalidDataException($"node: '{nodeName}' nodeMeta.Dim.Length: {nodeMeta.Dimensions.Length} " +
|
|
$"is expected to be equal to tensor.Dims.Count {tensor.Dims.Count}");
|
|
}
|
|
|
|
var shape = tensor.Dims.ToArray();
|
|
|
|
for (int i = 0; i < nodeMeta.Dimensions.Length; i++)
|
|
{
|
|
if ((nodeMeta.Dimensions[i] != -1) && (nodeMeta.Dimensions[i] != shape[i]))
|
|
throw new InvalidDataException($"Node: '{nodeName}' dimension at idx {i} is {nodeMeta.Dimensions}[{i}] " +
|
|
$"is expected to either be -1 or {shape[i]}");
|
|
}
|
|
|
|
// element type for Float16 and BFloat16 in the loaded tensor would always be uint16, so
|
|
// we want to use element type from metadata
|
|
if (protoDt == TensorElementType.String)
|
|
return CreateOrtValueFromStringTensor(tensor.StringData, shape);
|
|
|
|
return CreateOrtValueFromRawData(OrtAllocator.DefaultInstance, tensor.RawData.Span, metaElementType, shape);
|
|
}
|
|
|
|
internal static OrtValue CreateOrtValueFromSequence(Onnx.SequenceProto sequence, string nodeName, NodeMetadata nodeMeta)
|
|
{
|
|
var sequenceMeta = nodeMeta.AsSequenceMetadata();
|
|
var elemMeta = sequenceMeta.ElementMeta;
|
|
|
|
int seqNum = 0;
|
|
var seqElemType = (Onnx.SequenceProto.Types.DataType)sequence.ElemType;
|
|
switch (seqElemType)
|
|
{
|
|
case Onnx.SequenceProto.Types.DataType.Tensor:
|
|
{
|
|
SequenceCheckMatchOnnxType(nodeName, sequenceMeta, OnnxValueType.ONNX_TYPE_TENSOR);
|
|
using DisposableListTest<OrtValue> sequenceOfTensors = new(sequence.TensorValues.Count);
|
|
foreach (var tensor in sequence.TensorValues)
|
|
{
|
|
var element = LoadOrValueTensorPb(tensor, sequence.Name, elemMeta);
|
|
sequenceOfTensors.Add(element);
|
|
}
|
|
// Will take possession of ortValues in the sequence and will clear this container
|
|
return OrtValue.CreateSequence(sequenceOfTensors);
|
|
}
|
|
case Onnx.SequenceProto.Types.DataType.Sequence: // Sequence of sequences
|
|
{
|
|
SequenceCheckMatchOnnxType(nodeName, sequenceMeta, OnnxValueType.ONNX_TYPE_SEQUENCE);
|
|
using DisposableListTest<OrtValue> seqOfSequences = new(sequence.TensorValues.Count);
|
|
foreach (var s in sequence.SequenceValues)
|
|
{
|
|
var elemName = MakeSequenceElementName(nodeName, sequence.Name, seqNum++);
|
|
var ortValue = CreateOrtValueFromSequence(s, elemName, elemMeta);
|
|
seqOfSequences.Add(ortValue);
|
|
}
|
|
return OrtValue.CreateSequence(seqOfSequences);
|
|
}
|
|
case Onnx.SequenceProto.Types.DataType.Map:
|
|
{
|
|
throw new NotImplementedException(
|
|
"Test data format for maps is under investigation");
|
|
}
|
|
case Onnx.SequenceProto.Types.DataType.Optional:
|
|
{
|
|
SequenceCheckMatchOnnxType(nodeName, sequenceMeta, OnnxValueType.ONNX_TYPE_OPTIONAL);
|
|
using DisposableListTest<OrtValue> seqOfSequences = new(sequence.TensorValues.Count);
|
|
foreach (var opt in sequence.OptionalValues)
|
|
{
|
|
var elemName = MakeSequenceElementName(nodeName, sequence.Name, seqNum++);
|
|
var ortValue = CreateOrtValueFromOptional(opt, elemName, elemMeta);
|
|
seqOfSequences.Add(ortValue);
|
|
}
|
|
return OrtValue.CreateSequence(seqOfSequences);
|
|
}
|
|
default:
|
|
throw new NotImplementedException($"Sequence test data loading does not support element type: " +
|
|
$"'{seqElemType}'");
|
|
}
|
|
}
|
|
|
|
internal static OrtValue CreateOrtValueFromOptional(Onnx.OptionalProto optional, string nodeName, NodeMetadata nodeMetadata)
|
|
{
|
|
var meta = nodeMetadata.AsOptionalMetadata().ElementMeta;
|
|
switch ((Onnx.OptionalProto.Types.DataType)optional.ElemType)
|
|
{
|
|
case Onnx.OptionalProto.Types.DataType.Tensor:
|
|
{
|
|
var tensor = optional.TensorValue;
|
|
return LoadOrValueTensorPb(tensor, nodeName, meta);
|
|
}
|
|
case Onnx.OptionalProto.Types.DataType.Sequence:
|
|
{
|
|
var sequence = optional.SequenceValue;
|
|
return CreateOrtValueFromSequence(sequence, nodeName, meta);
|
|
}
|
|
case Onnx.OptionalProto.Types.DataType.Map:
|
|
{
|
|
throw new NotImplementedException(
|
|
"Test data format for maps is under investigation");
|
|
}
|
|
case Onnx.OptionalProto.Types.DataType.Optional:
|
|
throw new NotImplementedException($"Unable to load '{nodeName}' optional contained within optional");
|
|
default:
|
|
// Test data contains OptionalProto with the contained element type undefined.
|
|
// the premise is, if the element is not fed as an input, we should not care
|
|
// what Onnx type it is. However, we do not need to support AFAIK such inputs
|
|
// since the value for them could never be supplied.
|
|
throw new NotImplementedException($"Unable to load '{nodeName}' optional element type of: {(Onnx.OptionalProto.Types.DataType)optional.ElemType} type");
|
|
}
|
|
}
|
|
|
|
internal static OrtValue CreateOrtValueFromRawData(OrtAllocator allocator, ReadOnlySpan<byte> rawData, TensorElementType elementType, long[] shape)
|
|
{
|
|
Debug.Assert(elementType != TensorElementType.String, "Does not support strings");
|
|
var typeInfo = TensorBase.GetElementTypeInfo(elementType);
|
|
Assert.NotNull(typeInfo);
|
|
|
|
// ArrayUtilities not accessible in all builds
|
|
var shapeSize = ShapeUtils.GetSizeForShape(shape);
|
|
var inferredSize = rawData.Length / typeInfo.TypeSize;
|
|
Assert.Equal(shapeSize, inferredSize);
|
|
Assert.Equal(0, rawData.Length % typeInfo.TypeSize);
|
|
|
|
var ortValue = OrtValue.CreateAllocatedTensorValue(allocator, elementType, shape);
|
|
try
|
|
{
|
|
// The endianess data in protobuf is little endian.
|
|
// We simply copy raw memory into the tensor raw data.
|
|
var span = ortValue.GetTensorMutableRawData();
|
|
Assert.Equal(rawData.Length, span.Length);
|
|
rawData.CopyTo(span);
|
|
return ortValue;
|
|
}
|
|
catch (Exception)
|
|
{
|
|
ortValue.Dispose();
|
|
throw;
|
|
}
|
|
}
|
|
|
|
internal static NamedOnnxValue CreateNamedOnnxValueFromStringTensor(IList<Google.Protobuf.ByteString> strings,
|
|
string nodeName, int[] dimensions)
|
|
{
|
|
string[] strArray = new string[strings.Count];
|
|
for (int i = 0; i < strings.Count; ++i)
|
|
{
|
|
#if NET6_0_OR_GREATER
|
|
strArray[i] = Encoding.UTF8.GetString(strings[i].Span);
|
|
#else
|
|
strArray[i] = Encoding.UTF8.GetString(strings[i].ToByteArray());
|
|
#endif
|
|
}
|
|
|
|
var dt = new DenseTensor<string>(strArray, dimensions);
|
|
return NamedOnnxValue.CreateFromTensor<string>(nodeName, dt);
|
|
}
|
|
internal static OrtValue CreateOrtValueFromStringTensor(IList<Google.Protobuf.ByteString> strings,
|
|
long[] shape)
|
|
{
|
|
var ortValue = OrtValue.CreateTensorWithEmptyStrings(OrtAllocator.DefaultInstance, shape);
|
|
try
|
|
{
|
|
for (int i = 0; i < strings.Count; ++i)
|
|
{
|
|
ortValue.StringTensorSetElementAt(strings[i].Span, i);
|
|
}
|
|
return ortValue;
|
|
}
|
|
catch (Exception)
|
|
{
|
|
ortValue.Dispose();
|
|
throw;
|
|
}
|
|
}
|
|
|
|
internal static float[] LoadTensorFromFile(string filename, bool skipheader = true)
|
|
{
|
|
var tensorData = new List<float>();
|
|
|
|
// read data from file
|
|
using (var inputFile = new System.IO.StreamReader(filename))
|
|
{
|
|
if (skipheader)
|
|
inputFile.ReadLine(); // skip the input name
|
|
string[] dataStr = inputFile.ReadLine().Split(new char[] { ',', '[', ']', ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
|
for (int i = 0; i < dataStr.Length; i++)
|
|
{
|
|
tensorData.Add(Single.Parse(dataStr[i]));
|
|
}
|
|
}
|
|
|
|
return tensorData.ToArray();
|
|
}
|
|
}
|
|
}
|