Skip to content

Commit

Permalink
Add Info and SetDim commands, expand Clean (#5)
Browse files Browse the repository at this point in the history
Incl. miscellaneous extension methods.
  • Loading branch information
nietras authored Mar 20, 2021
1 parent 7f25451 commit 9dc908d
Show file tree
Hide file tree
Showing 44 changed files with 1,473 additions and 211 deletions.
6 changes: 6 additions & 0 deletions OnnxSharp.sln
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "root", "root", "{604A7FA2-1
rename.ps1 = rename.ps1
restore.ps1 = restore.ps1
test.ps1 = test.ps1
update-tool-from-build.ps1 = update-tool-from-build.ps1
update.ps1 = update.ps1
EndProjectSection
EndProject
Expand All @@ -38,6 +39,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OnnxSharp.Test", "src\OnnxS
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnet-onnx", "src\dotnet-onnx\dotnet-onnx.csproj", "{DA6F8267-24F1-4104-AB36-10C97B899A8E}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "github-workflows", "github-workflows", "{1EA6AA06-C436-482C-A14C-1AC414D5552F}"
ProjectSection(SolutionItems) = preProject
.github\workflows\dotnet.yml = .github\workflows\dotnet.yml
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down
62 changes: 47 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,64 @@
![Build and test](https://github.com/nietras/OnnxSharp/workflows/.NET/badge.svg)
[![NuGet](https://img.shields.io/nuget/v/OnnxSharp)](https://www.nuget.org/packages/OnnxSharp/)
[![Downloads](https://img.shields.io/nuget/dt/OnnxSharp)](https://www.nuget.org/packages/OnnxSharp/)
[![Stars](https://img.shields.io/github/stars/nietras/OnnxSharp)](https://github.com/nietras/OnnxSharp/stargazers)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE.md)

# OnnxSharp
ONNX format parsing and manipulation in C#.
|What |Links and Status|
|---------------|------|
|`OnnxSharp` |[![NuGet](https://img.shields.io/nuget/v/OnnxSharp)](https://www.nuget.org/packages/OnnxSharp/) [![Downloads](https://img.shields.io/nuget/dt/OnnxSharp)](https://www.nuget.org/packages/OnnxSharp/) |
|`dotnet-onnx`|[![NuGet](https://img.shields.io/nuget/v/dotnet-onnx)](https://www.nuget.org/packages/dotnet-onnx/) [![Downloads](https://img.shields.io/nuget/dt/dotnet-onnx)](https://www.nuget.org/packages/dotnet-onnx/) |

# Status
Pretty much just:
# `OnnxSharp` library and `dotnet-onnx` tool
ONNX format parsing and manipulation in C# and with command line .NET tool.

# Quick Guide
Install latest version of .NET:
* PowerShell (Windows): [https://dot.net/v1/dotnet-install.ps1](https://dot.net/v1/dotnet-install.ps1)
* Bash (Linux/macOS): [https://dot.net/v1/dotnet-install.sh](https://dot.net/v1/dotnet-install.sh)

#### Code
|What |How |
|--------------|---------------------------------------------------|
|Install |`dotnet add PROJECT.csproj package OnnxSharp`|
|Parse |`var model = ModelProto.Parser.ParseFromFile("mnist-8.onnx");`|
|Info |`var info = model.Graph.Info();`|
|Clean |`model.Graph.Clean();`|
|SetDim |`model.Graph.SetDim();`|
|Write |`model.WriteToFile("mnist-8-clean-dynamic.onnx");`|

#### Tool
|What |How |
|--------------|----------------------------|
|Install |`dotnet tool install dotnet-onnx -g`|
|Info |`dotnet onnx info mnist-8.onnx`|
|Info |`dotnet onnx info mnist-8.onnx`|
|Clean |`dotnet onnx clean mnist-8.onnx mnist-8-clean.onnx`|
|SetDim |`dotnet onnx setdim mnist-8.onnx mnist-8-setdim.onnx`|

# Source Code
Base functionality is based on:
```
.\protoc.exe .\onnx.proto3 --csharp_out=OnnxSharp
```
Everything else written in beautiful C# 9.0 as extensions to this.

# Example Code
```csharp
using System.IO;
using Google.Protobuf;
using Onnx;

// Examples see https://github.com/onnx/models
var onnxFilePath = @"mnist-8.onnx";
var onnxInputFilePath = @"mnist-8.onnx";

using var fileStream = File.OpenRead(onnxFilePath);

var model = Onnx.ModelProto.Parser.ParseFrom(fileStream);
var model = ModelProto.Parser.ParseFromFile(onnxInputFilePath);

var graph = model.Graph;
var inputs = graph.Input;
var valueInfos = graph.ValueInfo;
var outputs = graph.Output;
// Clean graph e.g. remove initializers from inputs that may prevent constant folding
graph.Clean();
// Set dimension in graph to enable dynamic batch size during inference
graph.SetDim(dimIndex: 0, DimParamOrValue.New("N"));
// Get summarized info about the graph
var info = graph.Info();

System.Console.WriteLine(info);

model.WriteToFile(@"mnist-8-clean-dynamic-batch-size.onnx");
```
4 changes: 2 additions & 2 deletions src/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
<Authors>nietras</Authors>
<Copyright>Copyright © nietras 2021</Copyright>
<NeutralLanguage>en</NeutralLanguage>
<AssemblyVersion>0.1.0.0</AssemblyVersion>
<FileVersion>0.1.1</FileVersion>
<AssemblyVersion>0.2.0.0</AssemblyVersion>
<FileVersion>0.2.0</FileVersion>
<InformationalVersion>$(FileVersion)</InformationalVersion>
<PackageVersion>$(InformationalVersion)</PackageVersion>

Expand Down
36 changes: 24 additions & 12 deletions src/OnnxSharp.Test/AssemblyResourceLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,23 @@ namespace OnnxSharp.Test
{
public static class AssemblyResourceLoader
{
public static readonly string ResourceNamespace = typeof(AssemblyResourceLoader).Assembly.GetName().Name;
public static readonly string ResourceNamespace =
typeof(AssemblyResourceLoader).Assembly.GetName().Name;
public const string ResourceNamePrefix = "";

public static string[] GetStringArray(string resourceName)
public static byte[] GetBytes(string resourceName)
{
return GetString(resourceName).Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
using (var stream = GetStream(resourceName))
using (var memoryStream = new MemoryStream())
{
stream.CopyTo(memoryStream);
return memoryStream.ToArray();
}
}

public static string[] GetLines(string resourceName) => GetString(resourceName)
.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);

public static string GetString(string resourceName)
{
using (var stream = GetStream(resourceName))
Expand All @@ -24,25 +33,27 @@ public static string GetString(string resourceName)
}
}

public static string GetFullResourceName(string resourceName)
{
return ResourceNamePrefix + resourceName;
}
public static string GetFullResourceName(string resourceName) =>
ResourceNamePrefix + resourceName;

public static string FindResourceName(Func<string, bool> filter)
{
var names = FindResourceNames(filter);

if (names.Length == 0)
{
throw new ArgumentException("Could not find any resource. The desired file might not have been defined as Embedded Resource.");
throw new ArgumentException("Could not find any resource. " +
"The desired file might not have been defined as Embedded Resource.");
}
else if (names.Length != 1)
{
throw new ArgumentException($"Ambiguous name, cannot identify resource - found {names.Length} possible candidates.");
throw new ArgumentException($"Ambiguous name, cannot identify resource - " +
$"found {names.Length} possible candidates.");
}
else
{
return names[0];
}

return names.Single();
}

public static string[] FindResourceNames(Func<string, bool> filter)
Expand All @@ -66,7 +77,8 @@ public static Stream GetStream(string resourceName)
var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(fullResourceName);
if (stream == null)
{
throw new ArgumentException($"Could not find resource '{resourceName}'. The desired file might not have been defined as Embedded Resource.");
throw new ArgumentException($"Could not find resource '{resourceName}'. " +
$"The desired file might not have been defined as Embedded Resource.");
}
return stream;
}
Expand Down
171 changes: 171 additions & 0 deletions src/OnnxSharp.Test/GraphExtensionsTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
using System;
using System.IO;
using Google.Protobuf;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Onnx;

namespace OnnxSharp.Test
{
[TestClass]
public class GraphExtensionsTest
{
readonly Func<Stream> m_createStream = () => AssemblyResourceLoader.GetStream("mnist-8.onnx");

[TestMethod]
public void ParseFrom()
{
// Act
var model = ModelProto.Parser.ParseFrom(m_createStream);

// Assert
var graph = model.Graph;
// 9 inputs since includes initializers
Assert.AreEqual(9, graph.Input.Count);
Assert.AreEqual(1, graph.Output.Count);
}

[TestMethod]
public void Info()
{
// Arrange
var model = ModelProto.Parser.ParseFrom(m_createStream);

// Act
var actual = model.Graph.Info();

// Assert
var expected = ExpectedInfo;
Assert.AreEqual(expected, actual);
}

[TestMethod]
public void Clean()
{
// Arrange
var model = ModelProto.Parser.ParseFrom(m_createStream);

// Act
model.Graph.Clean();

// Assert
var graph = model.Graph;
Assert.AreEqual(1, graph.Input.Count);
Assert.AreEqual(1, graph.Output.Count);
var expectedName = $"mnist-8-expected-{nameof(Clean)}.onnx";
AssertModelBytesEqualToEmbeddedExpected(model, expectedName);
}

[TestMethod]
public void RemoveInitializersFromInputs()
{
// Arrange
var model = ModelProto.Parser.ParseFrom(m_createStream);

// Act
model.Graph.RemoveInitializersFromInputs();

// Assert
var graph = model.Graph;
Assert.AreEqual(1, graph.Input.Count);
Assert.AreEqual(1, graph.Output.Count);
var expectedName = $"mnist-8-expected-{nameof(RemoveInitializersFromInputs)}.onnx";
AssertModelBytesEqualToEmbeddedExpected(model, expectedName);
}

[TestMethod]
public void RemoveUnnecessaryInitializerReshapes()
{
// Arrange
var model = ModelProto.Parser.ParseFrom(m_createStream);

// Act
model.Graph.RemoveUnnecessaryInitializerReshapes();

// Assert
var graph = model.Graph;
Assert.AreEqual(8, graph.Input.Count);
Assert.AreEqual(1, graph.Output.Count);
var expectedName = $"mnist-8-expected-{nameof(RemoveUnnecessaryInitializerReshapes)}.onnx";
AssertModelBytesEqualToEmbeddedExpected(model, expectedName);
}

[TestMethod]
public void SetDim()
{
// Arrange
var model = ModelProto.Parser.ParseFrom(m_createStream);

// Act
model.Graph.SetDim(dimIndex: 0, DimParamOrValue.New("N"));

// Assert
var graph = model.Graph;
Assert.AreEqual(9, graph.Input.Count);
Assert.AreEqual(1, graph.Output.Count);
var expectedName = $"mnist-8-expected-{nameof(SetDim)}.onnx";
AssertModelBytesEqualToEmbeddedExpected(model, expectedName);
}

static void AssertModelBytesEqualToEmbeddedExpected(ModelProto model, string expectedName)
{
var actualBytes = model.ToByteArray();
//model.WriteToFile(expectedName);
var expectedBytes = AssemblyResourceLoader.GetBytes(expectedName);
CollectionAssert.AreEqual(expectedBytes, actualBytes);
}

const string ExpectedInfo = @"## Inputs without Initializer
### Tensors
|Name |Type |ElemType|Shape |SizeInFile|
|:-----|:---------|:-------|--------:|---------:|
|Input3|TensorType|Float |1x1x28x28| 32|
## Outputs
### Tensors
|Name |Type |ElemType|Shape|SizeInFile|
|:---------------|:---------|:-------|----:|---------:|
|Plus214_Output_0|TensorType|Float | 1x10| 34|
## Inputs with Initializer
### Tensors
|Name |Type |ElemType|Shape |SizeInFile|
|:---------------------------------|:---------|:-------|--------:|---------:|
|Parameter5 |TensorType|Float | 8x1x5x5| 36|
|Parameter6 |TensorType|Float | 8x1x1| 32|
|Parameter87 |TensorType|Float | 16x8x5x5| 37|
|Parameter88 |TensorType|Float | 16x1x1| 33|
|Pooling160_Output_0_reshape0_shape|TensorType|Int64 | 2| 48|
|Parameter193 |TensorType|Float |16x4x4x10| 38|
|Parameter193_reshape1_shape |TensorType|Int64 | 2| 41|
|Parameter194 |TensorType|Float | 1x10| 30|
## Initializers (Parameters etc.)
|Name |DataType|Dims |Π(Dims)|[v0,v1..vN] | (Min,Mean,Max) |SizeInFile|
|:---------------------------------|:-------|--------:|------:|-----------------------------------:|---------:|
|Parameter193 |Float |16x4x4x10| 2560|(-7.595E-001,-1.779E-003,1.186E+000)| 10265|
|Parameter87 |Float | 16x8x5x5| 3200|(-5.089E-001,-3.028E-002,5.647E-001)| 12824|
|Parameter5 |Float | 8x1x5x5| 200|(-9.727E-001,-7.360E-003,1.019E+000)| 823|
|Parameter6 |Float | 8x1x1| 8|(-4.338E-001,-1.023E-001,9.164E-002)| 53|
|Parameter88 |Float | 16x1x1| 16|(-4.147E-001,-1.554E-001,1.328E-002)| 86|
|Pooling160_Output_0_reshape0_shape|Int64 | 2| 2| [1,256]| 46|
|Parameter193_reshape1_shape |Int64 | 2| 2| [256,10]| 39|
|Parameter194 |Float | 1x10| 10|(-1.264E-001,-4.777E-006,1.402E-001)| 62|
## Value Infos
### Tensors
|Name |Type |ElemType|Shape |SizeInFile|
|:---------------------------|:---------|:-------|---------:|---------:|
|Parameter193_reshape1 |TensorType|Float | 256x10| 40|
|Convolution28_Output_0 |TensorType|Float | 1x8x28x28| 48|
|Plus30_Output_0 |TensorType|Float | 1x8x28x28| 41|
|ReLU32_Output_0 |TensorType|Float | 1x8x28x28| 41|
|Pooling66_Output_0 |TensorType|Float | 1x8x14x14| 44|
|Convolution110_Output_0 |TensorType|Float |1x16x14x14| 49|
|Plus112_Output_0 |TensorType|Float |1x16x14x14| 42|
|ReLU114_Output_0 |TensorType|Float |1x16x14x14| 42|
|Pooling160_Output_0 |TensorType|Float | 1x16x4x4| 45|
|Pooling160_Output_0_reshape0|TensorType|Float | 1x256| 47|
|Times212_Output_0 |TensorType|Float | 1x10| 35|
";
}
}
41 changes: 0 additions & 41 deletions src/OnnxSharp.Test/MnistTest.cs

This file was deleted.

Loading

0 comments on commit 9dc908d

Please sign in to comment.