Description
### System information
- Win 10, 1903
- .NET 4.7.2
### Issue
remark: I do have a functional workaround solution, but it requires quite some nasty additional code and probably is a bit slower than optimal.
The main point of critique here concerns the OnnxTransform class.
What did you do?
Tried to do inference with an ONNX-model for sequence labeling with two variable input axes (batch and sequence).
The whole input (3 dimensions: batch, sequence, features) were put into one big float[]. Still not sure whether this is really the correct way. All attempts to use multidimensional input fields failed.
What happened?
As stated in source code (OnnxTransform.MakeRowMapper(...), see below), dimensions of variable input axes were set to 1, resulting in incorrect batches and sequences of size 1 each, regardless of specified DataView.
There is no explicit hint leading to the dimensionality reduction, however. The problem is documented, but that's in a private method. You only get an exception when feeding your whole sequence (as stated as one big float[]), because due to the dimension reduction the set up model expects length 1 * 1 * FeatureLength, and the input vector gets is large for that. At least that exception tells you that the product of the dimensions is relevant.
The workaround consists of setting up the "model" for each sequence by using the ApplyOnnxModel-overload with the input size dictionary "shapesDictionary". The documentation for that overload method is a bit cryptic, though (see below). It does allow to override the reduction to 1 of the variable axes (and only for those!) by using the values in the dictionary, but I learned that from the source code, not from the documentation.
The DataView has to be reset for each sequence, too, of course, to contain the specific sequence length.
Took unnecessarily long to find the source of the problem and the workaround solution.
What did you expect?
Best case: Full support for variable input dimensions, which should extend to the DataView and the model.
2nd best: Even for my chosen workaround of adjusting everything for each sequence to contain specific sequence length, the model-row-mapper should infer the correct dimensions instead of reducing them to 1 (there IS a todo comment for that). Fortunately, the time for the resets still make the workaround kind of usable. (although at batch size 1, so far, for more padding will probably be necessary, the effect of which remains to be checked)
At least: Output some kind of hint that the reduction takes place, at least in the public documentation for ApplyOnnxModel(...) or with further information in the exception when the sizes don't fit.
Source code / logs
From OnnxTransform.cs:
private protected override IRowMapper MakeRowMapper(DataViewSchema inputSchema) => new Mapper(this, inputSchema);
/// <summary>
/// This design assumes that all unknown dimensions are 1s. It also convert scalar shape [] in ONNX to [1].
/// [TODO] We should infer the unknown shape from input data instead of forcing them to be 1.
/// </summary>
private static IEnumerable<int> AdjustDimensions(OnnxShape shape)
{
if (shape.Count > 0)
{
return shape.Select(x => (x <= 0) ? 1 : x);
}
return new[] { 1 };
}
From OnnxCatalog.ApplyOnnxModel(...) not too helpful documentation:
/// <param name="shapeDictionary">ONNX shape should be used to over those loaded from <paramref name="modelFile"/>.</param>
suggestion:
/// <param name="shapeDictionary">Variable dimension sizes as stated in the loaded <paramref name="modelFile"/> are replaced by dimension sizes from this dictionary. All dimensions, also nonvariable, have to be given, though. For keys use names as stated in the ONNX model, e.g. "input"</param>