-
Notifications
You must be signed in to change notification settings - Fork 48
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
Synchronously validate input operands/activations #572
Comments
If we wanted to go the "define connect" route, it might be something like:
Some "connect" phrases currently reference the MLOperand, and some reference the platform operand instead; referencing the MLOperands consistently would simplify things. |
We'll want something similar for MLActivations, i.e. either an explicit step or by defining steps for "register it as activation". |
When "connect" references the platform operand, such as I suppose here it means a process of calling the native platform API to "connect" the platform operand to platform operator as input (or output). Actually, I think it should be a sub-step of "2. Make a request to the underlying platform to:".
Agreed. If we go this route, I think we should also define the "operator" concept within the spec. For a WebNN graph, MLOperands are edges, they need to be connected to nodes (operators), to construct a graph. |
I think it might be useful to think of these validation steps in tiers:
Currently this is specified as validate MLOperand and includes checks like that
These steps should also be run (synchronously) for each
This is where we finally talk to the platform API:
I propose that (1) and (2) should be run (synchronously) for each The
Or maybe: The
Ideally (though there might be some quirks which make this not feasible) none of the Thoughts? |
FYI, we asked around for precedent for labeling blocks of substeps. https://html.spec.whatwg.org/multipage/webappapis.html#update-the-rendering has some examples (italics before a clause), and our handy spec experts are not opposed to doing so if it improves readability. I like the idea of the "validate" and "connect" blocks, maybe "calculate output shape" as a block as well. |
+1 |
Building on the conversation above, here's a sketch of something concrete. If this looks like a decent starting point I can put up a PR for detailed nitpicks.
Programming Model / OverviewReplace bits about operands with the following:
Insert the following near the build() description:
MLActivation interfaceUpdate to slot description:
MLOperand interfaceUpdate internal slot description:
And delete [[operand]] Standard MLGraphBuilder Operand-vending Method StructureThe whatever(input, options) method steps are:
|
This looks great to me! There are a couple of things I notice about this framework which we should follow up on elsewhere:
|
Thanks @inexorabletash ! The proposal looks great to me.
Could you please elaborate a bit how operator internal concept is defined? Is it an internal interface definition for a specific operation? For example, something like Chromium prototype's |
As sketched above, it's just describing the abstract concept of an operator. Like algorithms and internal slots, these spec concept objects are useful for defining behavior, but aren't script visible so only matching the observable behavior is required. But they very often do map 1:1 with implementations. That Conv2d example from the Chromium prototype impl is what this would map to. |
As discussed in webmachinelearning#572: - Define an "operator" concept, with inputs, outputs, activations. - Defer "platform operator" and "platform operand" to build. - Standardize the graph connection steps across builder methods. - Simplify activation, input and constant steps. Not covered in this change: - Erroring if input's [[builder]] doesn't match `this` - Build algorithm - covered by webmachinelearning#448 and webmachinelearning#457 - gru() is missing steps to populate output. Added "Issue" note. - Introducing "Validate arguments" section for each method. - Introducing "Calculate output shape" section for each method. For webmachinelearning#549 and webmachinelearning#572.
* Content: Define operand concept, simplify graph connection steps As discussed in #572: - Define an "operator" concept, with inputs, outputs, activations. - Defer "platform operator" and "platform operand" to build. - Standardize the graph connection steps across builder methods. - Simplify activation, input and constant steps. Not covered in this change: - Erroring if input's [[builder]] doesn't match `this` - Build algorithm - covered by #448 and #457 - gru() is missing steps to populate output. Added "Issue" note. - Introducing "Validate arguments" section for each method. - Introducing "Calculate output shape" section for each method. For #549 and #572. * Trivial fixes - missing spaces, wrong operation name * lstm(): Fix output2 shape calculation In tip-of-tree, the "desc" MLOperandDescriptor is populated, used, then modified and conditionally used again (if `returnSequence` is true) when creating "output2". This was broken in previous commits in this branch. Restore the intent, using a separate "desc2" MLOperandDescriptor only conditionally populated and then conditionally used. * Update index.bs Co-authored-by: Ningxin Hu <[email protected]> * Initial review feedback: * Add other operand inputs * PreLU -> PReLU * Define split() output shapes calculations * Introduce definition for computational graph with inputs and constants * Revise Programming Model section --------- Co-authored-by: Ningxin Hu <[email protected]>
Concrete remaining work following #591
And then for these:
... in practice, these end up being empty for many ops, and very intertwined for others; i.e. it's hard to separate argument validation from output shape calculation. Is this actually valuable? Opinions (and PRs) welcome! |
Previously the spec had a "validate `MLOperand`" helper that (1) ensured the operand was from the passed `MLGraphBuilder` and (2) that the operand was internally consistent, and this was called during (3) `build()` and (4) only `concat()` among the vending methods. - (1) is needed but can be done when the `MLOperand` is created, giving better feedback, so (3) isn't needed. - (2) is not needed - `MLOperands` are immutable so they can't be created in a bad state. - (4) should be expanded to all `MLOperand` creations that take input `MLOperand`s. This renames the helper, ensures it is called by every `MLOperand` vending method that takes `MLOperand` inputs, and drops it from `build()` (although that will probably collide with PR webmachinelearning#603 which should land first). Similar validation is added for `MLActivation`s. For webmachinelearning#572
* Synchronously validate input operands/validations Previously the spec had a "validate `MLOperand`" helper that (1) ensured the operand was from the passed `MLGraphBuilder` and (2) that the operand was internally consistent, and this was called during (3) `build()` and (4) only `concat()` among the vending methods. - (1) is needed but can be done when the `MLOperand` is created, giving better feedback, so (3) isn't needed. - (2) is not needed - `MLOperands` are immutable so they can't be created in a bad state. - (4) should be expanded to all `MLOperand` creations that take input `MLOperand`s. This renames the helper, ensures it is called by every `MLOperand` vending method that takes `MLOperand` inputs, and drops it from `build()` (although that will probably collide with PR #603 which should land first). Similar validation is added for `MLActivation`s. For #572 * Apply suggestions from code review Missed a few "if it exists" clauses Co-authored-by: Ningxin Hu <[email protected]> --------- Co-authored-by: Ningxin Hu <[email protected]>
Places that create ops use an inconsistent mixed of simple phrases, camelCase, Title Case, ACRONYMS, and "quoted strings". The most common was camelCase, but the wording can be weird, and the bulk-defined binary/unary/logical/pooling/reduction ops and activations use a "quotedCamelCase", so I went with that. See webmachinelearning#591 (comment) for the most commentary. Resolves webmachinelearning#572
For the "remaining work", namely:
My suggestion is: add as needed, e.g. when a method's steps could be broken up to improve readability. But adding to all methods would just be clutter. |
When calling a builder method on MLGraphBuilder that takes an MLOperand or MLActivation as an input (i.e. anything but input() and constant()), there is no check that the input is from the same builder.
The only place in the spec that does this is validate MLOperand steps that take an operand and builder.
This algorithm is only called in:
@a-sully raised this in #552 and it was previously mentioned (but without specifics) in #234
Should failure here be synchronous? This is implied by the behavior of concat() and the open ended "If any of the following sub-steps fail, ..." text in the builder methods. I don't see a reason for async failure (except in build itself, since you can't synchronously fail a method that returns a promise) so I'd strongly prefer synchronous failure. (If async, the build() should be augmented to traverse the whole graph.)
Assuming we want synchronous failure we could spec this in a few ways:
Either way, concat() needs to be aligned with the other methods (fix to pass this.[[builder]] or remove redundant steps)
This also raises the question of what exception type it should be:
Additionally... is there anything other than "same builder" that's not currently validated, but that should be?
The text was updated successfully, but these errors were encountered: