-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Create API for extracting information about the nodes in a TensorFlow model #862
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
0e1e2fb
0476b47
f632417
78b0ae4
9669fbe
3702031
1f6a931
478b020
8a9e2c4
bc9fbc1
7ad8de9
b94987d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,8 +4,6 @@ | |
|
|
||
| using System; | ||
| using System.Runtime.InteropServices; | ||
| using System.Text; | ||
| using System.Globalization; | ||
| using System.Linq; | ||
|
|
||
| // We use this TF_Xxx as the native "TF_Xxx *" as those are opaque | ||
|
|
@@ -24,9 +22,9 @@ | |
| using TF_DeviceList = System.IntPtr; | ||
|
|
||
| using size_t = System.UIntPtr; | ||
| using System.Numerics; | ||
| using System.Collections.Generic; | ||
| using System.Linq.Expressions; | ||
| using Microsoft.ML.Runtime.Internal.Utilities; | ||
| using Microsoft.ML.Runtime.Data; | ||
|
|
||
| #pragma warning disable MSML_GeneralName | ||
| #pragma warning disable MSML_PrivateFieldName | ||
|
|
@@ -700,6 +698,24 @@ public override string ToString() | |
| IntPtr len; | ||
| return TF_GraphDebugString(Handle, out len); | ||
| } | ||
|
|
||
| [DllImport(NativeBinding.TensorFlowLibrary)] | ||
| internal static extern string TF_OperationName(TF_Operation oper); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rather than directly exposing the TF C-API we should consider bringing back the TF# defined API (TFOperation class) on top of it. See zeahmed@b2a8016#diff-ec7ea5716f3c05f773d3e1507b4f486aL729, zeahmed@b2a8016#diff-ec7ea5716f3c05f773d3e1507b4f486aL748. #Resolved |
||
|
|
||
| [DllImport(NativeBinding.TensorFlowLibrary)] | ||
| internal static extern string TF_OperationOpType(TF_Operation oper); | ||
|
|
||
| [DllImport(NativeBinding.TensorFlowLibrary)] | ||
| internal static extern int TF_OperationNumOutputs(TF_Operation oper); | ||
|
|
||
| [DllImport(NativeBinding.TensorFlowLibrary)] | ||
| internal static extern TFDataType TF_OperationOutputType(TFOutput oper_out); | ||
|
|
||
| [DllImport(NativeBinding.TensorFlowLibrary)] | ||
| internal static extern int TF_OperationNumInputs(TF_Operation oper); | ||
|
|
||
| [DllImport(NativeBinding.TensorFlowLibrary)] | ||
| internal static unsafe extern TF_Operation TF_GraphNextOperation(TF_Graph graph, long* pos); | ||
| } | ||
|
|
||
| /// <summary> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,9 +3,16 @@ | |
| // See the LICENSE file in the project root for more information. | ||
|
|
||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.IO; | ||
| using System.Linq; | ||
| using System.Runtime.InteropServices; | ||
| using Microsoft.ML.Runtime; | ||
| using Microsoft.ML.Runtime.Data; | ||
| using Microsoft.ML.Runtime.ImageAnalytics.EntryPoints; | ||
| using Microsoft.ML.Runtime.Internal.Utilities; | ||
|
|
||
| using TF_Operation = System.IntPtr; | ||
|
|
||
| namespace Microsoft.ML.Transforms.TensorFlow | ||
| { | ||
|
|
@@ -22,7 +29,54 @@ public static void Initialize() | |
| ImageAnalytics.Initialize(); | ||
| } | ||
|
|
||
| private static unsafe ISchema GetModelSchema(IExceptionContext ectx, TFGraph graph) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
just curious, why it's unsafe? #Closed
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for pointing it out, it doesn't need to be. In a previous iteration I was directly running the while loop over the graph that is now in the Graph class, so it was unsafe. In reply to: 218204434 [](ancestors = 218204434) |
||
| { | ||
| long pos = 0; | ||
| TF_Operation oper; | ||
| var res = new List<KeyValuePair<string, ColumnType>>(); | ||
| while ((oper = TFGraph.TF_GraphNextOperation(graph.handle, &pos)) != IntPtr.Zero) | ||
| { | ||
| var name = TFGraph.TF_OperationName(oper); | ||
| var type = TFGraph.TF_OperationOpType(oper); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Its not being used anywhere. #Closed |
||
| var numOutputs = TFGraph.TF_OperationNumOutputs(oper); | ||
| if (numOutputs != 1) | ||
| continue; | ||
|
|
||
| var numInputs = TFGraph.TF_OperationNumInputs(oper); | ||
| if (numInputs == 0) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
What does
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The input has OpType "Placeholder". There are other nodes with numInputs==0, which have OpType "Const". I am not sure what they do but I think we don't want them in our output schema. What do you think? In reply to: 216508337 [](ancestors = 216508337)
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What do you think if such nodes are not filter? Do you foresee adverse effect of doing this or not doing this? In reply to: 216758114 [](ancestors = 216758114,216508337)
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added the nodes with 0 inputs to the schema as well. In reply to: 216817673 [](ancestors = 216817673,216758114,216508337) |
||
| continue; | ||
|
|
||
| var tfType = TFGraph.TF_OperationOutputType(new TFOutput(graph[name])); | ||
| var mlType = Tf2MlNetTypeOrNull(tfType); | ||
| if (mlType == null) | ||
| continue; | ||
|
|
||
| var shape = graph.GetTensorShape(new TFOutput(graph[name])); | ||
| var shapeArray = shape.ToIntArray(); | ||
| var columnType = Utils.Size(shapeArray) > 0 && shapeArray.Skip(1).All(x => x > 0) ? | ||
| new VectorType(mlType, shapeArray[0] > 0 ? shapeArray : shapeArray.Skip(1).ToArray()) | ||
| : new VectorType(mlType); | ||
| res.Add(new KeyValuePair<string, ColumnType>(name, columnType)); | ||
| } | ||
| return new SimpleSchema(ectx, res.ToArray()); | ||
| } | ||
|
|
||
| public static ISchema GetModelSchema(IExceptionContext ectx, string modelFile) | ||
| { | ||
| var bytes = File.ReadAllBytes(modelFile); | ||
| var session = LoadTFSession(ectx, bytes, modelFile); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
What about models that are not frozen? I assume it will be there once @abgoswam changes are there, right? #Closed
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. I assume that LoadTFSession will take care of the logic to decide which kind of model to load. In reply to: 216509938 [](ancestors = 216509938) |
||
| return GetModelSchema(ectx, session.Graph); | ||
| } | ||
|
|
||
| internal static PrimitiveType Tf2MlNetType(TFDataType type) | ||
| { | ||
| var mlNetType = Tf2MlNetTypeOrNull(type); | ||
| if (mlNetType == null) | ||
| throw new NotSupportedException("TensorFlow type not supported."); | ||
| return mlNetType; | ||
| } | ||
|
|
||
| private static PrimitiveType Tf2MlNetTypeOrNull(TFDataType type) | ||
| { | ||
| switch (type) | ||
| { | ||
|
|
@@ -35,8 +89,27 @@ internal static PrimitiveType Tf2MlNetType(TFDataType type) | |
| case TFDataType.UInt64: | ||
| return NumberType.U8; | ||
| default: | ||
| throw new NotSupportedException("TensorFlow type not supported."); | ||
| return null; | ||
| } | ||
| } | ||
|
|
||
| internal static TFSession LoadTFSession(IExceptionContext ectx, byte[] modelBytes, string modelArg) | ||
| { | ||
| var graph = new TFGraph(); | ||
| try | ||
| { | ||
| graph.Import(modelBytes, ""); | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| if (!string.IsNullOrEmpty(modelArg)) | ||
| throw ectx.Except($"TensorFlow exception triggered while loading model from '{modelArg}'"); | ||
| #pragma warning disable MSML_NoMessagesForLoadContext | ||
| throw ectx.ExceptDecode(ex, "Tensorflow exception triggered while loading model."); | ||
| #pragma warning restore MSML_NoMessagesForLoadContext | ||
|
|
||
| } | ||
| return new TFSession(graph); | ||
| } | ||
|
|
||
| internal static unsafe void FetchData<T>(IntPtr data, T[] result) | ||
|
|
@@ -57,6 +130,10 @@ internal static bool IsTypeSupported(TFDataType tfoutput) | |
| { | ||
| case TFDataType.Float: | ||
| case TFDataType.Double: | ||
| case TFDataType.UInt8: | ||
| case TFDataType.UInt16: | ||
| case TFDataType.UInt32: | ||
| case TFDataType.UInt64: | ||
| return true; | ||
| default: | ||
| return false; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -158,22 +158,6 @@ public static IDataTransform Create(IHostEnvironment env, ModelLoadContext ctx, | |
| public static IRowMapper Create(IHostEnvironment env, ModelLoadContext ctx, ISchema inputSchema) | ||
| => Create(env, ctx).MakeRowMapper(inputSchema); | ||
|
|
||
| private TFSession LoadTFSession(byte[] modelBytes) | ||
| { | ||
| var graph = new TFGraph(); | ||
| try | ||
| { | ||
| graph.Import(modelBytes, ""); | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| #pragma warning disable MSML_NoMessagesForLoadContext | ||
| throw _host.ExceptDecode(ex, "Tensorflow exception triggered while loading model."); | ||
| #pragma warning restore MSML_NoMessagesForLoadContext | ||
| } | ||
| return new TFSession(graph); | ||
| } | ||
|
|
||
| private static byte[] CheckFileAndRead(IHostEnvironment env, string modelFile) | ||
| { | ||
| env.CheckNonWhiteSpace(modelFile, nameof(modelFile)); | ||
|
|
@@ -182,16 +166,16 @@ private static byte[] CheckFileAndRead(IHostEnvironment env, string modelFile) | |
| } | ||
|
|
||
| public TensorFlowTransform(IHostEnvironment env, string modelFile, string[] inputs, string[] outputs) : | ||
| this(env, CheckFileAndRead(env, modelFile), inputs, outputs) | ||
| this(env, CheckFileAndRead(env, modelFile), inputs, outputs, modelFile) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
is it model file or model args? #Closed |
||
| { | ||
| } | ||
|
|
||
| private TensorFlowTransform(IHostEnvironment env, byte[] modelBytes, string[] inputs, string[] outputs) | ||
| private TensorFlowTransform(IHostEnvironment env, byte[] modelBytes, string[] inputs, string[] outputs, string modelFile = null) | ||
| { | ||
| Contracts.CheckValue(env, nameof(env)); | ||
| _host = env.Register(nameof(RegistrationName)); | ||
| _host.CheckValue(modelBytes, nameof(modelBytes)); | ||
| Session = LoadTFSession(modelBytes); | ||
| Session = TensorFlowUtils.LoadTFSession(_host, modelBytes, modelFile); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Abhishek is working on brining another way to load session, so I think it's better to extend CheckFileAndRead function and force it to return you Session instead of byte array. So private constructor would just accept session. #Closed |
||
| foreach (var input in inputs) | ||
| { | ||
| _host.CheckNonWhiteSpace(input, nameof(inputs)); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,6 +8,7 @@ | |
| using Microsoft.ML.Runtime.ImageAnalytics; | ||
| using Microsoft.ML.Runtime.LightGBM; | ||
| using Microsoft.ML.Transforms; | ||
| using Microsoft.ML.Transforms.TensorFlow; | ||
| using System.Collections.Generic; | ||
| using System.IO; | ||
| using Xunit; | ||
|
|
@@ -70,6 +71,16 @@ public void TensorFlowTransformMatrixMultiplicationTest() | |
| } | ||
| } | ||
|
|
||
| [Fact] | ||
| public void TensorFlowListLayersMnistConv() | ||
| { | ||
| var model_location = "mnist_model/frozen_saved_model.pb"; | ||
| using (var env = new TlcEnvironment(seed: 1, conc: 1)) | ||
| { | ||
| var schema = TensorFlowUtils.GetModelSchema(env, model_location); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It would be nice to have schema checked against the actual model information. It seems like |
||
| } | ||
| } | ||
|
|
||
| [Fact] | ||
| public void TensorFlowTransformMNISTConvTest() | ||
| { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are these using statements actually necessary? I'm missing the additions that actually used them. #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for pointing it out. I added the new API in this file, but ended up moving it to TensorflowUtils.
In reply to: 216493797 [](ancestors = 216493797)