-
Notifications
You must be signed in to change notification settings - Fork 1.9k
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
Isolate ONNX implementations in separate DLL #462
Conversation
@@ -87,7 +86,7 @@ private static VersionInfo GetVersionInfo() | |||
/// Returns the underlying data view of the composite loader. | |||
/// This can be used to programmatically explore the chain of transforms that's inside the composite loader. | |||
/// </summary> | |||
internal IDataView View { get { return _view; } } | |||
public IDataView View { get; } |
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.
public [](start = 8, length = 6)
Not sure about this one..
Build OSX10.13 Release #Closed |
@dotnet-bot test OSX10.13 Release |
{ | ||
void AddAttribute(string argName, double value); | ||
void AddAttribute(string argName, IEnumerable<double> value); | ||
void AddAttribute(string argName, IEnumerable<float> value); |
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.
IEnumerable value); [](start = 42, length = 26)
IEnumerable value); [](start = 42, length = 26)
so everyone has a pair of T value and IEnumerable method, but float has only IEnumerable
I don't know is it wrong or not, but it just stands out. #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.
Yes. I think it's because double
param will work for both float
and double
, but IEnumerable<double>
will only work for IEnumerable<double>
, not also IEnumerable<float>
. #Resolved
void AddAttribute(string argName, IEnumerable<DvText> value); | ||
void AddAttribute(string argName, IEnumerable<string> value); | ||
void AddAttribute(string argName, string value); | ||
void AddAttribute(string argName, bool value); |
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.
My "OCD" wants them to be formatted in either all T value methods first, and then all IEnumerable values methods, or as couples of T value method + IEnumerable value.
#Resolved
Sure, it is actually pretty simple once you know how. You can duplicate/emulate #392, which adds the
|
/// | ||
/// | ||
/// </summary> | ||
public interface IOnnxContext |
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.
Would it be better to make an abstract class instead? That way we can add more members to this type in the future without having to make an IOnnxContext2
type.
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.
Maybe, maybe. Though in this case, given that the point is that I want none of its implementation to be in here, I'm not certain that I see the benefit of a base class. Is there some article I could read that expresses this principle?
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.
I think what I'd like to do is lock down this interface/base-class. Is there some way I can do that? Make a base class that no one can easily descend from, except some sort of "approved" implementing assembly? (In this case the onnx project, and also possibly some sort of test assembly.)
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.
https://msdn.microsoft.com/en-us/library/ms229013(v=vs.100).aspx
Do favor defining classes over interfaces.
In later versions of your library, you can safely add new members to classes; you cannot add members to interfaces without breaking existing code.
Do use abstract (MustInherit in Visual Basic) classes instead of interfaces to decouple the contract from implementations.
Also, this isn't official msdn, but it appears to be copied from the book
Is there some way I can do that? Make a base class that no one can easily descend from, except some sort of "approved" implementing assembly?
You can't do it with an interface, as anyone can implement the interface. But you can do it with an abstract class by making the constructor internal
.
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.
I'm not sure I get the point about interfaces vs. base classes. If the class would only have abstract methods (as these would), I'm not sure I see how the problem is any better with base classes. And to the extent that you can "solve" it by introducing implementations of methods you introduce the fragile base class problem.
internal
from what I understand works only for restricting access to the same assembly. This certainly doesn't cover this case, since the entire point is that the implementation should be in a different assembly. Anything based on signed DLLs, perhaps?
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.
If the class would only have abstract methods (as these would), I'm not sure I see how the problem is any better with base classes.
The point is, you can add members (properties, non-abstract methods) to a class without being a breaking change. New methods can be virtual, and your implementation class override them. And the abstract class can no-op, throw, return a default value, etc.
Once you ship an interface, you can never add new members to it.
Now in this case, if you restrict the class so that ONLY you can inherit from it (like using an internal
constructor), then you can also add abstract methods to the base class in future versions. Because you know that no one else can inherit from it.
internal from what I understand works only for restricting access to the same assembly. This certainly doesn't cover this case, since the entire point is that the implementation should be in a different assembly.
You can use InternalsVisibleTo
to get around this, if you want.
/// That method creates a with inputs and outputs, but this object can modify the node further | ||
/// by adding attributes (in ONNX parlance, attributes are more or less constant parameterizations). | ||
/// </summary> | ||
public interface IOnnxNode |
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.
Similar question as IOnnxContext
. Would it be better if this was an abstract class?
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.
Maybe?
0677f4f
to
23f62cf
Compare
All right, I think I did it correctly, let me know. But I'm not certain. How can I test it out? So if I just run I guess I've never tried to build the nugets directly from here, not sure if there's some other script other than In reply to: 401915564 [](ancestors = 401915564) |
|
Ah nice. I feel like I should have known that. Yes seems to work. Thanks Eric! In reply to: 401961587 [](ancestors = 401961587) |
@dotnet-bot test OSX10.13 Release |
f25025a
to
4851898
Compare
|
||
<PropertyGroup> | ||
<TargetFramework>netstandard2.0</TargetFramework> | ||
<PackageDescription>ML.NET component for Exporting ONNX Models</PackageDescription> |
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.
Do we envision using this package/library for more than just exporting an ONNX model? For example, if I want to load/execute an ONNX model, would that be through a separate library?
(nit) does Exporting
need to be capitalized?
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.
Do we envision using this package/library for more than just exporting an ONNX model? For example, if I want to load/execute an ONNX model, would that be through a separate library?
I'm not certain. If I had to guess, this hypothetical execution would involve linking to some native library in some fashion (Lotus?). I don't see much opportunity for code reuse in that case of course, so that suggests a different library.
does Exporting need to be capitalized?
No.
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.
Sounds good. We can adjust the package description later, if we add other functionality.
/// <param name="name">The name of the operator, which ought to be something returned from <see cref="OnnxContext.GetNodeName(string)"/></param> | ||
/// <param name="domain">The domain of the ONNX operator, if non-default</param> | ||
/// <returns>A node added to the in-progress ONNX graph, that attributes can be set on</returns> | ||
public static OnnxNode CreateNode(this OnnxContext ctx, |
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.
Do we need this to be an extension method anymore? Can't it just be a regular method on OnnxContext
?
…implementation to separate DLL.
Remove needless delegate
Forgot one dependency in Tests -> Onnx
* Improve ML.NET version handling to actually rely on the assembly vs. a constant string we have to change. * Tighten checks of arguments to the context implementation.
4851898
to
9429d88
Compare
/// given to a component, all other components up to that component have already attempted to express themselves in | ||
/// this context, with their outputs possibly available in the ONNX graph. | ||
/// | ||
/// |
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.
you probably don't need this lines.
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.
} | ||
} | ||
|
||
string columnName = _columnNameMap.Single(kvp => string.Compare(kvp.Value, variableName) == 0).Key; |
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.
string.Compare(kvp.Value, variableName) == 0 [](start = 61, length = 44)
curious, why you prefer string.Compare to just '==' #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.
I don't, not quite sure what happened here. I'm fairly certain I wouldn't have written that...
In reply to: 199965797 [](ancestors = 199965797)
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.
@dotnet-bot test Linux Debug |
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.
Abstraction of ONNX exporting to interfaces, and isolation of actual implementation to separate DLL. Creation of a new NuGet to isolate Protobuf dependency.
Fixes #162.
Previously, the ONNX infrastructure and implementations (including refs to protobuf) were in a central DLL. This gave us a dependency on a separate somewhat large project (protobuf), that was only of interest to people saving ONNX models.
By having the components save themselves through abstract classes rather than actual instantiable classes (
OnnxContext
became an abstract class with a hiddenOnnxContextImpl
implementation,NodeProto
andOnnxUtils
became theOnnxNode
abstract class with, likewise, a hidden implementation), there is no need for any "direct" dependency on protobuf.All implementation classes become internal classes of the
Microsoft.ML.Onnx
project. (This was previously calledMicrosoft.ML.UniversalFormat
due to historical reasons that no longer make sense.) The only public classes in that project are the entry-points and commands insideSaveOnnxCommand.cs
, which instantiate actual implementors of those interfaces, then pass to ONNX savable components.Also, I opportunistically improved documentation on those public interfaces (though even with docs the interfaces would scarcely make sense to someone unfamiliar with ONNX), and improved the code.