Add internal slots to MLOperand and MLActivation#337
Add internal slots to MLOperand and MLActivation#337zolkis wants to merge 17 commits intowebmachinelearning:mainfrom
Conversation
Signed-off-by: Zoltan Kis <[email protected]>
…to operand-activation-improvements
Signed-off-by: Zoltan Kis <[email protected]>
Signed-off-by: Zoltan Kis <[email protected]>
|
@huningxin PTAL :) |
index.bs
Outdated
| : <dfn>\[[tensorData]]</dfn> of type {{ArrayBufferView}} | ||
| :: | ||
| The {{MLOperand}}'s data buffer (only for constant operands) | ||
| : <dfn>\[[activation]]</dfn> of type {{MLActivation}} |
There was a problem hiding this comment.
MLOperand may not need to associate with an MLActivation. It may need a slot for the operator (e.g., platform object represents the node within the platform graph) that produces this MLOperand.
There was a problem hiding this comment.
Yes, I agree this was meant as an internal slot for the operator. But currently MLOperator is MLActivation. I think we might need both. I expressed my doubts in #335
I would need both to move on. MLOperand is connected with operations, which currently overlap with activations (see #335). We need to clarify these relationships before moving on to algorithms. I have several algorithms in the work that depend on this. |
Signed-off-by: Zoltan Kis <[email protected]>
|
Experimentally added constructors for |
index.bs
Outdated
| 1. Let |kind| be the second argument. | ||
| 1. If |kind| is not an {{MLOperandKind}}, throw a "{{TypeError}}" {{DOMException}} and abort these steps. | ||
| 1. Set [=this=]'s {{MLOperand/[[kind]]}} to |kind|. | ||
| 1. Let |type| be the third argument. |
There was a problem hiding this comment.
Should |type| be passed? Or just use {{MLOperandDescriptor/type}} instead?
index.bs
Outdated
| 1. If |dimensions| is not an array of positive numbers, return `false`; | ||
| 1. If |dimensions|.length is 0, return `false`. | ||
| 1. If |dimensions|.length is too large to be supported by the implementation, return `false`. | ||
| 1. If any element of |dimensions| is too large to be supported by the implementation, return `false`. |
There was a problem hiding this comment.
Should the data type of element be validated, to be unsigned long? The value 0 (0 size dimension) should also be invalid.
There was a problem hiding this comment.
I don't know, I guess all integer type values should be accepted (the first step checks that), the question is range.
There was a problem hiding this comment.
The range should be [1, ULONG_MAX].
There was a problem hiding this comment.
I think that may be implementation dependent. What we can explicitly check is that it's a positive ulong, but the implementation may have other constraints that it should be able to tell the API user if the specified dimension is not supported for whatever reason.
index.bs
Outdated
| 1. If |type| is not equal to |bufferView|'s [=element type=], throw a "{{DataError}}" {{DOMException}} and abort these steps. | ||
| 1. If the [=byte length=] of |desc| is not supported by the implementation, throw a "{{DataError}}" {{DOMException}} and abort these steps. | ||
| 1. If the [=byte length=] of |desc| is not equal with |bufferView|.\[[ByteLength]], throw a "{{DataError}}" {{DOMException}} and abort these steps. | ||
| 1. Set [=this=]'s {{MLOperand/[[tensorData]]}} to |bufferView|. |
There was a problem hiding this comment.
Should MLOperand have corresponding platform object? Should the kind, type, dimensions and values be used to create the platform object?
There was a problem hiding this comment.
Made a phrasing for that, let's check.
There was a problem hiding this comment.
Because the next step is to register this to underlying platform, I am not sure whether MLOperand needs to keep a reference to |bufferView|. Any use cases?
There was a problem hiding this comment.
The use case is for that step when registering to the underlying platform IIUC.
index.bs
Outdated
| 1. Otherwise, of |kind| is `"output"`: | ||
| 1. Let |dimensions| be the fourth argument. | ||
| 1. If the [=check dimensions=] steps given |type| and |dimensions| return `false`, throw a "{{DataError}}" {{DOMException}} and abort these steps. | ||
| 1. Let |activation| be the fifth argument. |
There was a problem hiding this comment.
I don't think |activation| should be used here. |activation| is only used as activation function that may be fused into an operator. Instead, could the platform object representing an operator be used? The operator produces this MLOperand.
There was a problem hiding this comment.
I agree, but it would be nice to have a definition for an operator. An obscure internal object exposed in an internal slot doesn't sound like a clear definition. :)
I wonder if defining MLOperator and also keeping MLActivation would work better? since now we have only MLActivation defined in the spec but MLOperator is used in the implementation.
In that sense, MLOperator would be similarly empty object like MLGraph currently is. Of course Occam tells that if we can do without MLOperator in the spec, we should. But then we need a clear "private" definition of an operator object (for internal use).
index.bs
Outdated
| 1. If |desc| is not an {{MLOperandDescriptor}}, throw a "{{TypeError}}" {{DOMException}} and abort these steps. | ||
| 1. If |type| is not equal to |desc|'s {{MLOperandDescriptor/type}}, throw a "{{DataError}}" {{DOMException}} and abort these steps. | ||
| 1. If the [=byte length=] of |desc| is not supported by the implementation, throw a "{{DataError}}" {{DOMException}} and abort these steps. | ||
| 1. Set [=this=]'s {{MLOperand/[[dimensions]]}} to |desc|.{{MLOperandDescriptor/dimensions}}. |
There was a problem hiding this comment.
Ditto, may need to create the platform object representing this "input" MLOperand.
… private, add operator internal slot, improve steps Signed-off-by: Zoltan Kis <[email protected]>
|
I addressed the comments the following way:
The goal of the PR is to establish a start, that can be used from other (future) algorithms. We can improve on these also in future PRs. |
Signed-off-by: Zoltan Kis <[email protected]>
|
Added clarification notes to MLOperand and MLActivation. |
…and use the prose as if webmachinelearning#337 was merged. Signed-off-by: Zoltan Kis <[email protected]>
index.bs
Outdated
| 1. If |type| is not equal to |bufferView|'s [=element type=], throw a "{{DataError}}" {{DOMException}} and abort these steps. | ||
| 1. If the [=byte length=] of |desc| is not supported by the implementation, throw a "{{DataError}}" {{DOMException}} and abort these steps. | ||
| 1. If the [=byte length=] of |desc| is not equal with |bufferView|.\[[ByteLength]], throw a "{{DataError}}" {{DOMException}} and abort these steps. | ||
| 1. Set [=this=]'s {{MLOperand/[[tensorData]]}} to |bufferView|. |
There was a problem hiding this comment.
Because the next step is to register this to underlying platform, I am not sure whether MLOperand needs to keep a reference to |bufferView|. Any use cases?
index.bs
Outdated
| 1. Set [=this=]'s {{MLOperand/[[tensorData]]}} to |bufferView|. | ||
| 1. Otherwise, of |kind| is `"input"`: | ||
| 1. Let |name| be the fourth argument. | ||
| 1. Make a request to the underlying platform to register [=this=] as a constant. |
There was a problem hiding this comment.
Should we make a request to "platform graph builder"? Should the "platform graph builder" be an internal slot of MLGraphBuilder |builder|?
There was a problem hiding this comment.
For text "as a constant", should we map it to the common computational graph concept?
Let's say MLOperands are actually the "edges" within the graph, while the operators are "nodes". For a node, the input edges represent the input operands of this operator, the output edges represent the output operands of this operator. There could be three types of edge: 1) input edge that only points to a node ("to" node) without the "from" node. It represent the graph input; 2) intermediate edge that connects two nodes, "from" node and "to" node; 3) output edge that only has a "from" a node without a "to" node.
For WebNN MLOperand kind,
- "input" MLOperand should map to input edge of the computational graph.
- "constant" MLOperand should also map to input edge, bound with static data.
- "output" MLOperand is always has the "from" node. It may be two possible types of edge:
- When it is consumed by another operator, say used as an input operand for an operator build method,
builder.relu(x), it turns to an intermediate edge. - When it is passed to
build(MLNamedOperands outputs)method in theoutputsargument, it turns to the output edge of the computational graph.
- When it is consumed by another operator, say used as an input operand for an operator build method,
It might be helpful to refer to a platform graph API, such as DirectML DML_GRAPH_DESC.
There was a problem hiding this comment.
Besides "register", I suppose the MLOperand should keep the platform operand object (or the edge of platform computational graph) created by platform graph builder as an internal slot. This platform object should be passed to the platform graph builder when creating an operator (or the node of the platform computational graph).
There was a problem hiding this comment.
Thanks Ningxin, looks like this uncovered a lot of things :).
I can do all these, but it is hard to formally use here a graph concept that is defined later (by implementations).
I agree we should define the elements of the structure in the spec, by internal slots (the edge position you described above) and also reference examples like DirectML as you suggested.
We can/should define algorithms for what it means to "register as a constant" or as an output, associating an internal slot for them.
These "create MLOperand" and "create MLActivation" are anyway meta-algorithms, used internally in the spec, so anything build by them is a temporary step, will be completed when these are referenced from the methods of MLGraphBuilder. So it is there many of the things described in your later comments would happen.
All in all, I think we need a revamp of the programming model, MLGraphBuilder etc. Should I do that in this PR?
There was a problem hiding this comment.
All in all, I think we need a revamp of the programming model, MLGraphBuilder etc. Should I do that in this PR?
@zolkis yes, the smaller the PRs the easier to review and discuss. I'm not sure if stacked PRs work here but that could be one solution.
There was a problem hiding this comment.
I have experimented how these slots would play out.
I ended up making minimal changes for this, removing the unneeded slots, but did not introduce new slots for platform objects. Will do that later if needed - at the moment they are not needed and can be referred to in prose.
index.bs
Outdated
| 1. Set [=this=]'s {{MLOperand/[[dimensions]]}} to |desc|.{{MLOperandDescriptor/dimensions}}. | ||
| 1. Make a request to the underlying platform to register [=this=] as an input. | ||
| 1. If that fails, throw a "{{TypeError}}" {{DOMException}} and abort these steps. | ||
| 1. Otherwise, of |kind| is `"output"`: |
There was a problem hiding this comment.
An "output" operand should be created from one of the operator build methods. I think the "platform operator" (or the node of the platform computational graph) that produces this operand should be passed as the next argument and it should be kept in the [[operator]] internal slot.
index.bs
Outdated
| : <dfn>\[[builder]]</dfn> of type {{MLGraphBuilder}} | ||
| :: | ||
| The {{MLOperand}}'s associated builder object. | ||
| : <dfn>\[[operator]]</dfn> of type [=function=] |
There was a problem hiding this comment.
I am not familiar with [=function=]. Does it mean a JavaScript function? Would operator be the underlying platform operator object (or the node of the platform computational graph)?
There was a problem hiding this comment.
I think I meant the latter and this needs some rework. For denoting a platform object ('s reference), the internal slot will be a generic object (that can be a function as well).
index.bs
Outdated
| : <dfn>\[[name]]</dfn> of type [=string=] | ||
| :: | ||
| The {{MLOperand}}'s name (only for input operands). | ||
| : <dfn>\[[tensorData]]</dfn> of type {{ArrayBufferView}} |
There was a problem hiding this comment.
Is [[tensorData]] needed to be an internal slot? I suppose the values of ArrayBufferViews should be passed to platform graph builder when "register" the constant. What's the use case of it?
There was a problem hiding this comment.
Well, that is the use case for it, AFAICT. The "create MLOperand" steps are continued in the builder methods, but they might need the slot for tensor data. But if something is not needed (no use case), will be removed.
index.bs
Outdated
| 1. If |dimensions| is not an array of positive numbers, return `false`; | ||
| 1. If |dimensions|.length is 0, return `false`. | ||
| 1. If |dimensions|.length is too large to be supported by the implementation, return `false`. | ||
| 1. If any element of |dimensions| is too large to be supported by the implementation, return `false`. |
There was a problem hiding this comment.
The range should be [1, ULONG_MAX].
index.bs
Outdated
| Objects implementing the {{MLActivation}} interface represent activation function types. | ||
|
|
||
| <script type=idl> | ||
| typedef object MLActivationOptions; |
There was a problem hiding this comment.
Should the type MLActivationOptions be an union of all supported options type, say
typedef (MLClampOptions or MLLeakyReluOptions or ...) MLActivationOptions;
There was a problem hiding this comment.
Yes, it's the union of all supported options.
Should we explicitly enumerate all of them in a typedef (that would cover a gap in the Web IDL), or should we use a generic dictionary and specify in prose the current meaning? (that would not change the Web IDL, but would clarify the prose).
I do see value in both approaches.
@anssiko do you have a preference?
There was a problem hiding this comment.
@zolkis in general if we can define something in Web IDL we should prefer that way over prose. Web IDL is machine-readable and allows for bindings code generation and testing.
There was a problem hiding this comment.
SGTM. It's a bit unusual to have such long unions, currently 12 of them, but we can easily revert any time, in the case an external reviewer finds that awkward.
There was a problem hiding this comment.
Filed #363 to track this, if we want to.
For now I will go with the explicit union then.
There was a problem hiding this comment.
I don't think you can have a WebIDL union of dictionaries, since dictionaries aren't distinguishable and union members must be distinguishable.
There was a problem hiding this comment.
Thanks Dom for that reminder. Looks like we are going with option 1, i.e. generic dictionary + specific spec prose in algorithms.
There was a problem hiding this comment.
Why do we need to define a new type called MLActivationOptions? An MLActivation is already an abstract interface that can sufficiently represent all available activation types (that are available for operator fusions) e.g. the clamp activation as an MLActivation interface is already created wtih the specific MLClampOptions accordingly.
Also a union of all relevant activation options is unnecessary since the use case for each will only always require just one concrete activation option type e.g. MLClampOptions for clamp activation, etc.
There was a problem hiding this comment.
Chai, are you saying that instead of a union of MLActivationOptions we should define a separate type for each respective activation option?
I sympathize with that, and removing the generic object typedef - it doesn't add much value indeed. It is like specifying function interface with generic object parameters.
There was a problem hiding this comment.
Removed MLActivationOptions.
|
@huningxin Next, I will do input() and constant() and will also attempt the build() steps, at least a start, to see how it plays out with MLOperand and MLActivation slots. But that needs this PR merged. |
Signed-off-by: Zoltan Kis <[email protected]>
13fd3c5 to
37e02db
Compare
|
I am preparing the PR for input() and constant(), and that enables simplifying this PR. Moving it back to WiP. |
…or aligning with builder's upcoming input(), constant() and MLGraph steps/improvements. Signed-off-by: Zoltan Kis <[email protected]>
|
Quite a lot of changes, but now it works with the upcoming builder/input() and constant() steps. |
Signed-off-by: Zoltan Kis <[email protected]>
|
Added a "copy MLOperand" and "validate MLOperand" steps since needed in other algorithms. |
Signed-off-by: Zoltan Kis <[email protected]>
Signed-off-by: Zoltan Kis <[email protected]>
…arning#337 Signed-off-by: Zoltan Kis <[email protected]>
Signed-off-by: Zoltan Kis <[email protected]>
…OperandKind typedef from the IDL Signed-off-by: Zoltan Kis <[email protected]>
index.bs
Outdated
| Objects implementing the {{MLActivation}} interface represent activation function types. | ||
|
|
||
| <script type=idl> | ||
| typedef object MLActivationOptions; |
There was a problem hiding this comment.
Why do we need to define a new type called MLActivationOptions? An MLActivation is already an abstract interface that can sufficiently represent all available activation types (that are available for operator fusions) e.g. the clamp activation as an MLActivation interface is already created wtih the specific MLClampOptions accordingly.
Also a union of all relevant activation options is unnecessary since the use case for each will only always require just one concrete activation option type e.g. MLClampOptions for clamp activation, etc.
…rences. Signed-off-by: Zoltan Kis <[email protected]>
Signed-off-by: Zoltan Kis <[email protected]>
Signed-off-by: Zoltan Kis <[email protected]>
Signed-off-by: Zoltan Kis <[email protected]>
Squashed the following commits: Enclose arg + return descriptions to separate from normative algorithms Change manual link to autolink Use the 'get a copy' reference for buffer source Factor our validating bufferview with descriptor Fix initialization typo Add reference to the platform operand object MLOperand/[[operand]] Lift dependency changes from webmachinelearning#337 Fix typos, remove unneeded dependencies from webmachinelearning#337 Add dependency for buffer validation from webmachinelearning#329 Fix the scalar algorithm / comments Discern between scalar and tensor constant, fix typos Fix review comments. Add stylistic boxes for algorithm steps, informal steps, internal slots Fix typo. Remove stylistic boxes for now. Address reviews comments. Remove MLOperandKind typedef from the IDL. Move note to text. Make the check on kind an assert. Remove the MLOperand/[[kind]] internal slot and creation parameter. Remove back quotes from title Signed-off-by: Zoltan Kis <[email protected]>
Squashed the following commits:
Enclose arg + return descriptions to separate from normative algorithms
Change manual link to autolink
Use the 'get a copy' reference for buffer source
Factor our validating bufferview with descriptor
Fix initialization typo
Add reference to the platform operand object MLOperand/[[operand]]
Lift dependency changes from webmachinelearning#337
Fix typos, remove unneeded dependencies from webmachinelearning#337
Add dependency for buffer validation from webmachinelearning#329
Fix the scalar algorithm / comments
Discern between scalar and tensor constant, fix typos
Fix review comments. Add stylistic boxes for algorithm steps, informal steps, internal slots
Fix typo. Remove stylistic boxes for now.
Address reviews comments. Remove MLOperandKind typedef from the IDL.
Move note to text. Make the check on kind an assert.
Remove the MLOperand/[[kind]] internal slot and creation parameter.
Remove back quotes from title
Signed-off-by: Zoltan Kis <[email protected]>
Squashed the following commits:
Enclose arg + return descriptions to separate from normative algorithms
Change manual link to autolink
Use the 'get a copy' reference for buffer source
Factor our validating bufferview with descriptor
Fix initialization typo
Add reference to the platform operand object MLOperand/[[operand]]
Lift dependency changes from webmachinelearning#337
Fix typos, remove unneeded dependencies from webmachinelearning#337
Add dependency for buffer validation from webmachinelearning#329
Fix the scalar algorithm / comments
Discern between scalar and tensor constant, fix typos
Fix review comments. Add stylistic boxes for algorithm steps, informal steps, internal slots
Fix typo. Remove stylistic boxes for now.
Address reviews comments. Remove MLOperandKind typedef from the IDL.
Move note to text. Make the check on kind an assert.
Remove the MLOperand/[[kind]] internal slot and creation parameter.
Remove back quotes from title
Signed-off-by: Zoltan Kis <[email protected]>
Squashed the following commits:
Enclose arg + return descriptions to separate from normative algorithms
Change manual link to autolink
Use the 'get a copy' reference for buffer source
Factor our validating bufferview with descriptor
Fix initialization typo
Add reference to the platform operand object MLOperand/[[operand]]
Lift dependency changes from #337
Fix typos, remove unneeded dependencies from #337
Add dependency for buffer validation from #329
Fix the scalar algorithm / comments
Discern between scalar and tensor constant, fix typos
Fix review comments. Add stylistic boxes for algorithm steps, informal steps, internal slots
Fix typo. Remove stylistic boxes for now.
Address reviews comments. Remove MLOperandKind typedef from the IDL.
Move note to text. Make the check on kind an assert.
Remove the MLOperand/[[kind]] internal slot and creation parameter.
Remove back quotes from title
Signed-off-by: Zoltan Kis <[email protected]>
|
Marked as a draft because some parts have been lifted to merged PRs, and some other parts could be used by other PRs, in the pace they will be needed. |
|
Moved to #400 with new target branch. |
Fix #336
Discussion about content preferred there.
Discussion about spec/syntax preferred here.
Preview | Diff