diff --git a/src/Microsoft.ML.Data/Prediction/Calibrator.cs b/src/Microsoft.ML.Data/Prediction/Calibrator.cs index 1328bb006a..895bf92273 100644 --- a/src/Microsoft.ML.Data/Prediction/Calibrator.cs +++ b/src/Microsoft.ML.Data/Prediction/Calibrator.cs @@ -1437,13 +1437,13 @@ public bool SaveAsOnnx(OnnxContext ctx, string[] scoreProbablityColumnNames, str string opType = "Affine"; string linearOutput = ctx.AddIntermediateVariable(null, "linearOutput", true); var node = ctx.CreateNode(opType, new[] { scoreProbablityColumnNames[0] }, - new[] { linearOutput }, ctx.GetNodeName(opType), "ai.onnx"); + new[] { linearOutput }, ctx.GetNodeName(opType), ""); node.AddAttribute("alpha", ParamA * -1); node.AddAttribute("beta", -0.0000001); opType = "Sigmoid"; node = ctx.CreateNode(opType, new[] { linearOutput }, - new[] { scoreProbablityColumnNames[1] }, ctx.GetNodeName(opType), "ai.onnx"); + new[] { scoreProbablityColumnNames[1] }, ctx.GetNodeName(opType), ""); return true; } diff --git a/src/Microsoft.ML.Data/Transforms/ConcatTransform.cs b/src/Microsoft.ML.Data/Transforms/ConcatTransform.cs index 725c005d31..c12dd8fad5 100644 --- a/src/Microsoft.ML.Data/Transforms/ConcatTransform.cs +++ b/src/Microsoft.ML.Data/Transforms/ConcatTransform.cs @@ -723,7 +723,6 @@ public void SaveAsOnnx(OnnxContext ctx) var node = ctx.CreateNode(opType, inputList.Select(t => t.Key), new[] { ctx.AddIntermediateVariable(outColType, outName) }, ctx.GetNodeName(opType)); - node.AddAttribute("inputList", inputList.Select(x => x.Key)); node.AddAttribute("inputdimensions", inputList.Select(x => x.Value)); } } diff --git a/src/Microsoft.ML.Data/Transforms/TermTransform.cs b/src/Microsoft.ML.Data/Transforms/TermTransform.cs index 7591179588..23440af133 100644 --- a/src/Microsoft.ML.Data/Transforms/TermTransform.cs +++ b/src/Microsoft.ML.Data/Transforms/TermTransform.cs @@ -719,7 +719,10 @@ protected override bool SaveAsOnnxCore(OnnxContext ctx, int iinfo, ColInfo info, var node = ctx.CreateNode(opType, srcVariableName, dstVariableName, ctx.GetNodeName(opType)); node.AddAttribute("classes_strings", terms.DenseValues()); node.AddAttribute("default_int64", -1); - node.AddAttribute("default_string", DvText.Empty); + //default_string needs to be an empty string but there is a BUG in Lotus that + //throws a validation error when default_string is empty. As a work around, set + //default_string to a space. + node.AddAttribute("default_string", " "); return true; } diff --git a/src/Microsoft.ML.Onnx/OnnxUtils.cs b/src/Microsoft.ML.Onnx/OnnxUtils.cs index be5a833643..9605226846 100644 --- a/src/Microsoft.ML.Onnx/OnnxUtils.cs +++ b/src/Microsoft.ML.Onnx/OnnxUtils.cs @@ -252,10 +252,10 @@ public static ModelProto MakeModel(List nodes, string producerName, s model.Domain = domain; model.ProducerName = producerName; model.ProducerVersion = producerVersion; - model.IrVersion = (long)UniversalModelFormat.Onnx.Version.IrVersion; + model.IrVersion = (long)Version.IrVersion; model.ModelVersion = modelVersion; model.OpsetImport.Add(new OperatorSetIdProto() { Domain = "ai.onnx.ml", Version = 1 }); - model.OpsetImport.Add(new OperatorSetIdProto() { Domain = "ai.onnx", Version = 6 }); + model.OpsetImport.Add(new OperatorSetIdProto() { Domain = "", Version = 7 }); model.Graph = new GraphProto(); var graph = model.Graph; graph.Node.Add(nodes); diff --git a/src/Microsoft.ML.StandardLearners/Standard/LinearPredictor.cs b/src/Microsoft.ML.StandardLearners/Standard/LinearPredictor.cs index 3fc63060e8..8f81324768 100644 --- a/src/Microsoft.ML.StandardLearners/Standard/LinearPredictor.cs +++ b/src/Microsoft.ML.StandardLearners/Standard/LinearPredictor.cs @@ -238,10 +238,10 @@ public bool SaveAsOnnx(OnnxContext ctx, string[] outputs, string featureColumn) string opType = "LinearRegressor"; var node = ctx.CreateNode(opType, new[] { featureColumn }, outputs, ctx.GetNodeName(opType)); // Selection of logit or probit output transform. enum {'NONE', 'LOGIT', 'PROBIT} - node.AddAttribute("post_transform", 0); + node.AddAttribute("post_transform", "NONE"); node.AddAttribute("targets", 1); node.AddAttribute("coefficients", Weight.DenseValues()); - node.AddAttribute("intercepts", Bias); + node.AddAttribute("intercepts", new float[] { Bias }); return true; } diff --git a/src/Microsoft.ML.StandardLearners/Standard/LogisticRegression/MulticlassLogisticRegression.cs b/src/Microsoft.ML.StandardLearners/Standard/LogisticRegression/MulticlassLogisticRegression.cs index 5f7712843f..4fa0f1a38e 100644 --- a/src/Microsoft.ML.StandardLearners/Standard/LogisticRegression/MulticlassLogisticRegression.cs +++ b/src/Microsoft.ML.StandardLearners/Standard/LogisticRegression/MulticlassLogisticRegression.cs @@ -845,12 +845,12 @@ public bool SaveAsOnnx(OnnxContext ctx, string[] outputs, string featureColumn) string opType = "LinearClassifier"; var node = ctx.CreateNode(opType, new[] { featureColumn }, outputs, ctx.GetNodeName(opType)); - // Selection of logit or probit output transform. enum {'NONE', 'LOGIT', 'PROBIT} - node.AddAttribute("post_transform", 0); + // Selection of logit or probit output transform. enum {'NONE', 'SOFTMAX', 'LOGISTIC', 'SOFTMAX_ZERO', 'PROBIT} + node.AddAttribute("post_transform", "NONE"); node.AddAttribute("multi_class", true); node.AddAttribute("coefficients", _weights.SelectMany(w => w.DenseValues())); node.AddAttribute("intercepts", _biases); - node.AddAttribute("classlabels_strings", _labelNames); + node.AddAttribute("classlabels_ints", Enumerable.Range(0, _numClasses).Select(x => (long)x)); return true; } diff --git a/src/Microsoft.ML.Transforms/NAReplaceTransform.cs b/src/Microsoft.ML.Transforms/NAReplaceTransform.cs index 30384780e9..35e6517814 100644 --- a/src/Microsoft.ML.Transforms/NAReplaceTransform.cs +++ b/src/Microsoft.ML.Transforms/NAReplaceTransform.cs @@ -633,13 +633,13 @@ protected override bool SaveAsOnnxCore(OnnxContext ctx, int iinfo, ColInfo info, node.AddAttribute("replaced_value_float", Single.NaN); if (!Infos[iinfo].TypeSrc.IsVector) - node.AddAttribute("imputed_value_float", Enumerable.Repeat((float)_repValues[iinfo], 1)); + node.AddAttribute("imputed_value_floats", Enumerable.Repeat((float)_repValues[iinfo], 1)); else { if (_repIsDefault[iinfo] != null) node.AddAttribute("imputed_value_floats", (float[])_repValues[iinfo]); else - node.AddAttribute("imputed_value_float", Enumerable.Repeat((float)_repValues[iinfo], 1)); + node.AddAttribute("imputed_value_floats", Enumerable.Repeat((float)_repValues[iinfo], 1)); } return true; diff --git a/test/BaselineOutput/Common/Onnx/BinaryClassification/BreastCancer/BinaryClassificationFastTreeSaveModelToOnnxTest.json b/test/BaselineOutput/Common/Onnx/BinaryClassification/BreastCancer/BinaryClassificationFastTreeSaveModelToOnnxTest.json new file mode 100644 index 0000000000..920aa1d728 --- /dev/null +++ b/test/BaselineOutput/Common/Onnx/BinaryClassification/BreastCancer/BinaryClassificationFastTreeSaveModelToOnnxTest.json @@ -0,0 +1,542 @@ +{ + "irVersion": "3", + "producerName": "ML.NET", + "producerVersion": "##VERSION##", + "domain": "Onnx", + "graph": { + "node": [ + { + "input": [ + "F1" + ], + "output": [ + "F10" + ], + "name": "Imputer", + "opType": "Imputer", + "attribute": [ + { + "name": "replaced_value_float", + "f": "NaN", + "type": "FLOAT" + }, + { + "name": "imputed_value_floats", + "floats": [ + 0 + ], + "type": "FLOATS" + } + ], + "domain": "ai.onnx.ml" + }, + { + "input": [ + "F10" + ], + "output": [ + "F11" + ], + "name": "Scaler", + "opType": "Scaler", + "attribute": [ + { + "name": "offset", + "floats": [ + 0 + ], + "type": "FLOATS" + }, + { + "name": "scale", + "floats": [ + 0.1 + ], + "type": "FLOATS" + } + ], + "domain": "ai.onnx.ml" + }, + { + "input": [ + "F2" + ], + "output": [ + "F20" + ], + "name": "LabelEncoder", + "opType": "LabelEncoder", + "attribute": [ + { + "name": "classes_strings", + "strings": [ + "NA==", + "MQ==", + "OA==", + "MTA=", + "Mg==", + "Mw==", + "Nw==", + "NQ==", + "Ng==", + "OQ==" + ], + "type": "STRINGS" + }, + { + "name": "default_int64", + "i": "-1", + "type": "INT" + }, + { + "name": "default_string", + "s": "IA==", + "type": "STRING" + } + ], + "domain": "ai.onnx.ml" + }, + { + "input": [ + "F20" + ], + "output": [ + "F21" + ], + "name": "OneHotEncoder", + "opType": "OneHotEncoder", + "attribute": [ + { + "name": "cats_int64s", + "ints": [ + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "10" + ], + "type": "INTS" + }, + { + "name": "zeros", + "i": "1", + "type": "INT" + } + ], + "domain": "ai.onnx.ml" + }, + { + "input": [ + "F11", + "F21" + ], + "output": [ + "Features" + ], + "name": "FeatureVectorizer", + "opType": "FeatureVectorizer", + "attribute": [ + { + "name": "inputdimensions", + "ints": [ + "1", + "10" + ], + "type": "INTS" + } + ], + "domain": "ai.onnx.ml" + }, + { + "input": [ + "Features" + ], + "output": [ + "Score" + ], + "name": "TreeEnsembleRegressor", + "opType": "TreeEnsembleRegressor", + "attribute": [ + { + "name": "post_transform", + "s": "Tk9ORQ==", + "type": "STRING" + }, + { + "name": "n_targets", + "i": "1", + "type": "INT" + }, + { + "name": "base_values", + "floats": [ + 0 + ], + "type": "FLOATS" + }, + { + "name": "aggregate_function", + "s": "U1VN", + "type": "STRING" + }, + { + "name": "nodes_treeids", + "ints": [ + "0", + "0", + "0" + ], + "type": "INTS" + }, + { + "name": "nodes_nodeids", + "ints": [ + "0", + "1", + "2" + ], + "type": "INTS" + }, + { + "name": "nodes_featureids", + "ints": [ + "2", + "0", + "0" + ], + "type": "INTS" + }, + { + "name": "nodes_modes", + "strings": [ + "QlJBTkNIX0xFUQ==", + "TEVBRg==", + "TEVBRg==" + ], + "type": "STRINGS" + }, + { + "name": "nodes_values", + "floats": [ + 0.5, + 0, + 0 + ], + "type": "FLOATS" + }, + { + "name": "nodes_truenodeids", + "ints": [ + "1", + "0", + "0" + ], + "type": "INTS" + }, + { + "name": "nodes_falsenodeids", + "ints": [ + "2", + "0", + "0" + ], + "type": "INTS" + }, + { + "name": "nodes_missing_value_tracks_true", + "ints": [ + "0", + "0", + "0" + ], + "type": "INTS" + }, + { + "name": "target_treeids", + "ints": [ + "0", + "0" + ], + "type": "INTS" + }, + { + "name": "target_nodeids", + "ints": [ + "1", + "2" + ], + "type": "INTS" + }, + { + "name": "target_ids", + "ints": [ + "0", + "0" + ], + "type": "INTS" + }, + { + "name": "target_weights", + "floats": [ + 0.504761934, + -0.979112267 + ], + "type": "FLOATS" + } + ], + "domain": "ai.onnx.ml" + }, + { + "input": [ + "Score" + ], + "output": [ + "linearOutput" + ], + "name": "Affine", + "opType": "Affine", + "attribute": [ + { + "name": "alpha", + "f": 0.4, + "type": "FLOAT" + }, + { + "name": "beta", + "f": -1E-07, + "type": "FLOAT" + } + ] + }, + { + "input": [ + "linearOutput" + ], + "output": [ + "Probability" + ], + "name": "Sigmoid", + "opType": "Sigmoid" + }, + { + "input": [ + "Probability" + ], + "output": [ + "PredictedLabel" + ], + "name": "Binarizer", + "opType": "Binarizer", + "attribute": [ + { + "name": "threshold", + "f": 0.5, + "type": "FLOAT" + } + ], + "domain": "ai.onnx.ml" + } + ], + "name": "BinaryClassificationFastTreeSaveModelToOnnxTest", + "input": [ + { + "name": "F1", + "type": { + "tensorType": { + "elemType": "FLOAT", + "shape": { + "dim": [ + { + "dimValue": "1" + }, + { + "dimValue": "1" + } + ] + } + } + } + }, + { + "name": "F2", + "type": { + "tensorType": { + "elemType": "STRING", + "shape": { + "dim": [ + { + "dimValue": "1" + }, + { + "dimValue": "1" + } + ] + } + } + } + } + ], + "output": [ + { + "name": "PredictedLabel", + "type": { + "tensorType": { + "elemType": "FLOAT", + "shape": { + "dim": [ + { + "dimValue": "1" + }, + { + "dimValue": "1" + } + ] + } + } + } + }, + { + "name": "Score", + "type": { + "tensorType": { + "elemType": "FLOAT", + "shape": { + "dim": [ + { + "dimValue": "1" + }, + { + "dimValue": "1" + } + ] + } + } + } + }, + { + "name": "Probability", + "type": { + "tensorType": { + "elemType": "FLOAT", + "shape": { + "dim": [ + { + "dimValue": "1" + }, + { + "dimValue": "1" + } + ] + } + } + } + } + ], + "valueInfo": [ + { + "name": "F10", + "type": { + "tensorType": { + "elemType": "FLOAT", + "shape": { + "dim": [ + { + "dimValue": "1" + }, + { + "dimValue": "1" + } + ] + } + } + } + }, + { + "name": "F11", + "type": { + "tensorType": { + "elemType": "FLOAT", + "shape": { + "dim": [ + { + "dimValue": "1" + }, + { + "dimValue": "1" + } + ] + } + } + } + }, + { + "name": "F20", + "type": { + "tensorType": { + "elemType": "INT64", + "shape": { + "dim": [ + { + "dimValue": "1" + }, + { + "dimValue": "1" + } + ] + } + } + } + }, + { + "name": "F21", + "type": { + "tensorType": { + "elemType": "FLOAT", + "shape": { + "dim": [ + { + "dimValue": "1" + }, + { + "dimValue": "10" + } + ] + } + } + } + }, + { + "name": "Features", + "type": { + "tensorType": { + "elemType": "FLOAT", + "shape": { + "dim": [ + { + "dimValue": "1" + }, + { + "dimValue": "11" + } + ] + } + } + } + } + ] + }, + "opsetImport": [ + { + "domain": "ai.onnx.ml", + "version": "1" + }, + { + "version": "7" + } + ] +} \ No newline at end of file diff --git a/test/BaselineOutput/Common/Onnx/BinaryClassification/BreastCancer/BinaryClassificationLRSaveModelToOnnxTest.json b/test/BaselineOutput/Common/Onnx/BinaryClassification/BreastCancer/BinaryClassificationLRSaveModelToOnnxTest.json new file mode 100644 index 0000000000..92d9816c37 --- /dev/null +++ b/test/BaselineOutput/Common/Onnx/BinaryClassification/BreastCancer/BinaryClassificationLRSaveModelToOnnxTest.json @@ -0,0 +1,254 @@ +{ + "irVersion": "3", + "producerName": "ML.NET", + "producerVersion": "##VERSION##", + "domain": "Onnx", + "graph": { + "node": [ + { + "input": [ + "Features" + ], + "output": [ + "Features0" + ], + "name": "Scaler", + "opType": "Scaler", + "attribute": [ + { + "name": "offset", + "floats": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "type": "FLOATS" + }, + { + "name": "scale", + "floats": [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ], + "type": "FLOATS" + } + ], + "domain": "ai.onnx.ml" + }, + { + "input": [ + "Features0" + ], + "output": [ + "Score" + ], + "name": "LinearRegressor", + "opType": "LinearRegressor", + "attribute": [ + { + "name": "post_transform", + "s": "Tk9ORQ==", + "type": "STRING" + }, + { + "name": "targets", + "i": "1", + "type": "INT" + }, + { + "name": "coefficients", + "floats": [ + 2.6596148, + 1.67937, + 1.94177353, + 1.42409551, + 0.852847636, + 2.93048549, + 1.74959826, + 1.58030283, + 0.5948697 + ], + "type": "FLOATS" + }, + { + "name": "intercepts", + "floats": [ + -6.183617 + ], + "type": "FLOATS" + } + ], + "domain": "ai.onnx.ml" + }, + { + "input": [ + "Score" + ], + "output": [ + "linearOutput" + ], + "name": "Affine", + "opType": "Affine", + "attribute": [ + { + "name": "alpha", + "f": 1, + "type": "FLOAT" + }, + { + "name": "beta", + "f": -1E-07, + "type": "FLOAT" + } + ] + }, + { + "input": [ + "linearOutput" + ], + "output": [ + "Probability" + ], + "name": "Sigmoid", + "opType": "Sigmoid" + }, + { + "input": [ + "Probability" + ], + "output": [ + "PredictedLabel" + ], + "name": "Binarizer", + "opType": "Binarizer", + "attribute": [ + { + "name": "threshold", + "f": 0.5, + "type": "FLOAT" + } + ], + "domain": "ai.onnx.ml" + } + ], + "name": "BinaryClassificationLRSaveModelToOnnxTest", + "input": [ + { + "name": "Features", + "type": { + "tensorType": { + "elemType": "FLOAT", + "shape": { + "dim": [ + { + "dimValue": "1" + }, + { + "dimValue": "9" + } + ] + } + } + } + } + ], + "output": [ + { + "name": "PredictedLabel", + "type": { + "tensorType": { + "elemType": "FLOAT", + "shape": { + "dim": [ + { + "dimValue": "1" + }, + { + "dimValue": "1" + } + ] + } + } + } + }, + { + "name": "Score", + "type": { + "tensorType": { + "elemType": "FLOAT", + "shape": { + "dim": [ + { + "dimValue": "1" + }, + { + "dimValue": "1" + } + ] + } + } + } + }, + { + "name": "Probability", + "type": { + "tensorType": { + "elemType": "FLOAT", + "shape": { + "dim": [ + { + "dimValue": "1" + }, + { + "dimValue": "1" + } + ] + } + } + } + } + ], + "valueInfo": [ + { + "name": "Features0", + "type": { + "tensorType": { + "elemType": "FLOAT", + "shape": { + "dim": [ + { + "dimValue": "1" + }, + { + "dimValue": "9" + } + ] + } + } + } + } + ] + }, + "opsetImport": [ + { + "domain": "ai.onnx.ml", + "version": "1" + }, + { + "version": "7" + } + ] +} \ No newline at end of file diff --git a/test/BaselineOutput/Common/Onnx/BinaryClassification/BreastCancer/BinaryClassificationLightGBMSaveModelToOnnxTest.json b/test/BaselineOutput/Common/Onnx/BinaryClassification/BreastCancer/BinaryClassificationLightGBMSaveModelToOnnxTest.json new file mode 100644 index 0000000000..3989a39903 --- /dev/null +++ b/test/BaselineOutput/Common/Onnx/BinaryClassification/BreastCancer/BinaryClassificationLightGBMSaveModelToOnnxTest.json @@ -0,0 +1,285 @@ +{ + "irVersion": "3", + "producerName": "ML.NET", + "producerVersion": "##VERSION##", + "domain": "Onnx", + "graph": { + "node": [ + { + "input": [ + "Features" + ], + "output": [ + "Score" + ], + "name": "TreeEnsembleRegressor", + "opType": "TreeEnsembleRegressor", + "attribute": [ + { + "name": "post_transform", + "s": "Tk9ORQ==", + "type": "STRING" + }, + { + "name": "n_targets", + "i": "1", + "type": "INT" + }, + { + "name": "base_values", + "floats": [ + 0 + ], + "type": "FLOATS" + }, + { + "name": "aggregate_function", + "s": "U1VN", + "type": "STRING" + }, + { + "name": "nodes_treeids", + "ints": [ + "0", + "0", + "0" + ], + "type": "INTS" + }, + { + "name": "nodes_nodeids", + "ints": [ + "0", + "1", + "2" + ], + "type": "INTS" + }, + { + "name": "nodes_featureids", + "ints": [ + "1", + "0", + "0" + ], + "type": "INTS" + }, + { + "name": "nodes_modes", + "strings": [ + "QlJBTkNIX0xFUQ==", + "TEVBRg==", + "TEVBRg==" + ], + "type": "STRINGS" + }, + { + "name": "nodes_values", + "floats": [ + 2.5, + 0, + 0 + ], + "type": "FLOATS" + }, + { + "name": "nodes_truenodeids", + "ints": [ + "1", + "0", + "0" + ], + "type": "INTS" + }, + { + "name": "nodes_falsenodeids", + "ints": [ + "2", + "0", + "0" + ], + "type": "INTS" + }, + { + "name": "nodes_missing_value_tracks_true", + "ints": [ + "0", + "0", + "0" + ], + "type": "INTS" + }, + { + "name": "target_treeids", + "ints": [ + "0", + "0" + ], + "type": "INTS" + }, + { + "name": "target_nodeids", + "ints": [ + "1", + "2" + ], + "type": "INTS" + }, + { + "name": "target_ids", + "ints": [ + "0", + "0" + ], + "type": "INTS" + }, + { + "name": "target_weights", + "floats": [ + -1.799208, + -0.34535858 + ], + "type": "FLOATS" + } + ], + "domain": "ai.onnx.ml" + }, + { + "input": [ + "Score" + ], + "output": [ + "linearOutput" + ], + "name": "Affine", + "opType": "Affine", + "attribute": [ + { + "name": "alpha", + "f": 0.5, + "type": "FLOAT" + }, + { + "name": "beta", + "f": -1E-07, + "type": "FLOAT" + } + ] + }, + { + "input": [ + "linearOutput" + ], + "output": [ + "Probability" + ], + "name": "Sigmoid", + "opType": "Sigmoid" + }, + { + "input": [ + "Probability" + ], + "output": [ + "PredictedLabel" + ], + "name": "Binarizer", + "opType": "Binarizer", + "attribute": [ + { + "name": "threshold", + "f": 0.5, + "type": "FLOAT" + } + ], + "domain": "ai.onnx.ml" + } + ], + "name": "BinaryClassificationLightGBMSaveModelToOnnxTest", + "input": [ + { + "name": "Features", + "type": { + "tensorType": { + "elemType": "FLOAT", + "shape": { + "dim": [ + { + "dimValue": "1" + }, + { + "dimValue": "9" + } + ] + } + } + } + } + ], + "output": [ + { + "name": "PredictedLabel", + "type": { + "tensorType": { + "elemType": "FLOAT", + "shape": { + "dim": [ + { + "dimValue": "1" + }, + { + "dimValue": "1" + } + ] + } + } + } + }, + { + "name": "Score", + "type": { + "tensorType": { + "elemType": "FLOAT", + "shape": { + "dim": [ + { + "dimValue": "1" + }, + { + "dimValue": "1" + } + ] + } + } + } + }, + { + "name": "Probability", + "type": { + "tensorType": { + "elemType": "FLOAT", + "shape": { + "dim": [ + { + "dimValue": "1" + }, + { + "dimValue": "1" + } + ] + } + } + } + } + ] + }, + "opsetImport": [ + { + "domain": "ai.onnx.ml", + "version": "1" + }, + { + "version": "7" + } + ] +} \ No newline at end of file diff --git a/test/BaselineOutput/Common/Onnx/BinaryClassification/BreastCancer/SaveModelToOnnxTest.json b/test/BaselineOutput/Common/Onnx/BinaryClassification/BreastCancer/SaveModelToOnnxTest.json deleted file mode 100644 index b2d611b784..0000000000 --- a/test/BaselineOutput/Common/Onnx/BinaryClassification/BreastCancer/SaveModelToOnnxTest.json +++ /dev/null @@ -1,716 +0,0 @@ -{ - "irVersion": "3", - "producerName": "ML.NET", - "producerVersion": "##VERSION##", - "domain": "Onnx", - "graph": { - "node": [ - { - "input": [ - "Features" - ], - "output": [ - "Score" - ], - "name": "TreeEnsembleRegressor", - "opType": "TreeEnsembleRegressor", - "attribute": [ - { - "name": "post_transform", - "s": "Tk9ORQ==", - "type": "STRING" - }, - { - "name": "n_targets", - "i": "1", - "type": "INT" - }, - { - "name": "base_values", - "floats": [ - 0 - ], - "type": "FLOATS" - }, - { - "name": "aggregate_function", - "s": "U1VN", - "type": "STRING" - }, - { - "name": "nodes_treeids", - "ints": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "2", - "2", - "2", - "2", - "2", - "2", - "2", - "2", - "2", - "3", - "3", - "3", - "3", - "3", - "3", - "3", - "3", - "3", - "4", - "4", - "4", - "4", - "4", - "4", - "4", - "4", - "4" - ], - "type": "INTS" - }, - { - "name": "nodes_nodeids", - "ints": [ - "0", - "1", - "2", - "3", - "4", - "5", - "6", - "7", - "8", - "0", - "1", - "2", - "3", - "4", - "5", - "6", - "7", - "8", - "0", - "1", - "2", - "3", - "4", - "5", - "6", - "7", - "8", - "0", - "1", - "2", - "3", - "4", - "5", - "6", - "7", - "8", - "0", - "1", - "2", - "3", - "4", - "5", - "6", - "7", - "8" - ], - "type": "INTS" - }, - { - "name": "nodes_featureids", - "ints": [ - "1", - "2", - "5", - "0", - "0", - "0", - "0", - "0", - "0", - "1", - "5", - "2", - "0", - "0", - "0", - "0", - "0", - "0", - "1", - "5", - "2", - "0", - "0", - "0", - "0", - "0", - "0", - "2", - "6", - "5", - "1", - "0", - "0", - "0", - "0", - "0", - "1", - "5", - "0", - "1", - "0", - "0", - "0", - "0", - "0" - ], - "type": "INTS" - }, - { - "name": "nodes_modes", - "strings": [ - "QlJBTkNIX0xFUQ==", - "QlJBTkNIX0xFUQ==", - "QlJBTkNIX0xFUQ==", - "QlJBTkNIX0xFUQ==", - "TEVBRg==", - "TEVBRg==", - "TEVBRg==", - "TEVBRg==", - "TEVBRg==", - "QlJBTkNIX0xFUQ==", - "QlJBTkNIX0xFUQ==", - "QlJBTkNIX0xFUQ==", - "QlJBTkNIX0xFUQ==", - "TEVBRg==", - "TEVBRg==", - "TEVBRg==", - "TEVBRg==", - "TEVBRg==", - "QlJBTkNIX0xFUQ==", - "QlJBTkNIX0xFUQ==", - "QlJBTkNIX0xFUQ==", - "QlJBTkNIX0xFUQ==", - "TEVBRg==", - "TEVBRg==", - "TEVBRg==", - "TEVBRg==", - "TEVBRg==", - "QlJBTkNIX0xFUQ==", - "QlJBTkNIX0xFUQ==", - "QlJBTkNIX0xFUQ==", - "QlJBTkNIX0xFUQ==", - "TEVBRg==", - "TEVBRg==", - "TEVBRg==", - "TEVBRg==", - "TEVBRg==", - "QlJBTkNIX0xFUQ==", - "QlJBTkNIX0xFUQ==", - "QlJBTkNIX0xFUQ==", - "QlJBTkNIX0xFUQ==", - "TEVBRg==", - "TEVBRg==", - "TEVBRg==", - "TEVBRg==", - "TEVBRg==" - ], - "type": "STRINGS" - }, - { - "name": "nodes_values", - "floats": [ - 2.5, - 2.5, - 5.5, - 5.5, - 0, - 0, - 0, - 0, - 0, - 3.5, - 3.5, - 1.5, - 3.5, - 0, - 0, - 0, - 0, - 0, - 3.5, - 2.5, - 2.5, - 5.5, - 0, - 0, - 0, - 0, - 0, - 3.5, - 3.5, - 2.5, - 4.5, - 0, - 0, - 0, - 0, - 0, - 2.5, - 1.5, - 6.5, - 5.5, - 0, - 0, - 0, - 0, - 0 - ], - "type": "FLOATS" - }, - { - "name": "nodes_truenodeids", - "ints": [ - "2", - "3", - "4", - "5", - "0", - "0", - "0", - "0", - "0", - "1", - "4", - "3", - "6", - "0", - "0", - "0", - "0", - "0", - "1", - "4", - "3", - "6", - "0", - "0", - "0", - "0", - "0", - "1", - "4", - "6", - "5", - "0", - "0", - "0", - "0", - "0", - "2", - "3", - "4", - "5", - "0", - "0", - "0", - "0", - "0" - ], - "type": "INTS" - }, - { - "name": "nodes_falsenodeids", - "ints": [ - "1", - "6", - "7", - "8", - "0", - "0", - "0", - "0", - "0", - "5", - "2", - "7", - "8", - "0", - "0", - "0", - "0", - "0", - "5", - "2", - "7", - "8", - "0", - "0", - "0", - "0", - "0", - "3", - "2", - "7", - "8", - "0", - "0", - "0", - "0", - "0", - "1", - "6", - "7", - "8", - "0", - "0", - "0", - "0", - "0" - ], - "type": "INTS" - }, - { - "name": "nodes_missing_value_tracks_true", - "ints": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ], - "type": "INTS" - }, - { - "name": "target_treeids", - "ints": [ - "0", - "0", - "0", - "0", - "0", - "1", - "1", - "1", - "1", - "1", - "2", - "2", - "2", - "2", - "2", - "3", - "3", - "3", - "3", - "3", - "4", - "4", - "4", - "4", - "4" - ], - "type": "INTS" - }, - { - "name": "target_nodeids", - "ints": [ - "4", - "5", - "6", - "7", - "8", - "4", - "5", - "6", - "7", - "8", - "4", - "5", - "6", - "7", - "8", - "4", - "5", - "6", - "7", - "8", - "4", - "5", - "6", - "7", - "8" - ], - "type": "INTS" - }, - { - "name": "target_ids", - "ints": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ], - "type": "INTS" - }, - { - "name": "target_weights", - "floats": [ - -0.9755501, - -0.8947368, - 0.8347107, - 0.75, - 1, - -0.8157646, - 0.75331986, - -0.8725711, - 0.6149202, - 1.05311215, - -0.739901066, - 0.65209645, - -0.618561566, - 0.6576947, - 0.7696665, - -0.663163662, - 0.229835972, - -0.49779135, - 0.670133352, - 0.660453737, - -0.620322645, - -0.634804964, - 0.513690054, - 0.650555968, - 0.6567067 - ], - "type": "FLOATS" - } - ], - "domain": "ai.onnx.ml" - }, - { - "input": [ - "Score" - ], - "output": [ - "linearOutput" - ], - "name": "Affine", - "opType": "Affine", - "attribute": [ - { - "name": "alpha", - "f": 0.4, - "type": "FLOAT" - }, - { - "name": "beta", - "f": -1E-07, - "type": "FLOAT" - } - ], - "domain": "ai.onnx" - }, - { - "input": [ - "linearOutput" - ], - "output": [ - "Probability" - ], - "name": "Sigmoid", - "opType": "Sigmoid", - "domain": "ai.onnx" - }, - { - "input": [ - "Probability" - ], - "output": [ - "PredictedLabel" - ], - "name": "Binarizer", - "opType": "Binarizer", - "attribute": [ - { - "name": "threshold", - "f": 0.5, - "type": "FLOAT" - } - ], - "domain": "ai.onnx.ml" - } - ], - "name": "SaveModelToOnnxTest", - "input": [ - { - "name": "Features", - "type": { - "tensorType": { - "elemType": "FLOAT", - "shape": { - "dim": [ - { - "dimValue": "1" - }, - { - "dimValue": "9" - } - ] - } - } - } - } - ], - "output": [ - { - "name": "PredictedLabel", - "type": { - "tensorType": { - "elemType": "FLOAT", - "shape": { - "dim": [ - { - "dimValue": "1" - }, - { - "dimValue": "1" - } - ] - } - } - } - }, - { - "name": "Score", - "type": { - "tensorType": { - "elemType": "FLOAT", - "shape": { - "dim": [ - { - "dimValue": "1" - }, - { - "dimValue": "1" - } - ] - } - } - } - }, - { - "name": "Probability", - "type": { - "tensorType": { - "elemType": "FLOAT", - "shape": { - "dim": [ - { - "dimValue": "1" - }, - { - "dimValue": "1" - } - ] - } - } - } - } - ] - }, - "opsetImport": [ - { - "domain": "ai.onnx.ml", - "version": "1" - }, - { - "domain": "ai.onnx", - "version": "6" - } - ] -} \ No newline at end of file diff --git a/test/BaselineOutput/Common/Onnx/MultiClassClassification/BreastCancer/MultiClassificationLRSaveModelToOnnxTest.json b/test/BaselineOutput/Common/Onnx/MultiClassClassification/BreastCancer/MultiClassificationLRSaveModelToOnnxTest.json new file mode 100644 index 0000000000..b21412a2bb --- /dev/null +++ b/test/BaselineOutput/Common/Onnx/MultiClassClassification/BreastCancer/MultiClassificationLRSaveModelToOnnxTest.json @@ -0,0 +1,205 @@ +{ + "irVersion": "3", + "producerName": "ML.NET", + "producerVersion": "##VERSION##", + "domain": "Onnx", + "graph": { + "node": [ + { + "input": [ + "Features" + ], + "output": [ + "Features0" + ], + "name": "Scaler", + "opType": "Scaler", + "attribute": [ + { + "name": "offset", + "floats": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "type": "FLOATS" + }, + { + "name": "scale", + "floats": [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ], + "type": "FLOATS" + } + ], + "domain": "ai.onnx.ml" + }, + { + "input": [ + "Features0" + ], + "output": [ + "PredictedLabel", + "Score" + ], + "name": "LinearClassifier", + "opType": "LinearClassifier", + "attribute": [ + { + "name": "post_transform", + "s": "Tk9ORQ==", + "type": "STRING" + }, + { + "name": "multi_class", + "i": "1", + "type": "INT" + }, + { + "name": "coefficients", + "floats": [ + -1.58059466, + -0.82541883, + -1.05039084, + -0.792811334, + -0.385914773, + -1.59029973, + -1.01633251, + -0.8349969, + -0.3322066, + 1.58059633, + 0.8254174, + 1.05039155, + 0.7928113, + 0.385914057, + 1.59029937, + 1.01633251, + 0.8349978, + 0.332206637 + ], + "type": "FLOATS" + }, + { + "name": "intercepts", + "floats": [ + 3.36230779, + -3.36230469 + ], + "type": "FLOATS" + }, + { + "name": "classlabels_ints", + "ints": [ + "0", + "1" + ], + "type": "INTS" + } + ], + "domain": "ai.onnx.ml" + } + ], + "name": "MultiClassificationLRSaveModelToOnnxTest", + "input": [ + { + "name": "Features", + "type": { + "tensorType": { + "elemType": "FLOAT", + "shape": { + "dim": [ + { + "dimValue": "1" + }, + { + "dimValue": "9" + } + ] + } + } + } + } + ], + "output": [ + { + "name": "PredictedLabel", + "type": { + "tensorType": { + "elemType": "INT64", + "shape": { + "dim": [ + { + "dimValue": "1" + }, + { + "dimValue": "1" + } + ] + } + } + } + }, + { + "name": "Score", + "type": { + "tensorType": { + "elemType": "FLOAT", + "shape": { + "dim": [ + { + "dimValue": "1" + }, + { + "dimValue": "2" + } + ] + } + } + } + } + ], + "valueInfo": [ + { + "name": "Features0", + "type": { + "tensorType": { + "elemType": "FLOAT", + "shape": { + "dim": [ + { + "dimValue": "1" + }, + { + "dimValue": "9" + } + ] + } + } + } + } + ] + }, + "opsetImport": [ + { + "domain": "ai.onnx.ml", + "version": "1" + }, + { + "version": "7" + } + ] +} \ No newline at end of file diff --git a/test/Microsoft.ML.Tests/OnnxTests.cs b/test/Microsoft.ML.Tests/OnnxTests.cs index f4428242be..d9c05ddcc1 100644 --- a/test/Microsoft.ML.Tests/OnnxTests.cs +++ b/test/Microsoft.ML.Tests/OnnxTests.cs @@ -8,6 +8,7 @@ using Microsoft.ML.Runtime.Data; using Microsoft.ML.Runtime.RunTests; using Microsoft.ML.Trainers; +using Microsoft.ML.Transforms; using System.IO; using System.Text.RegularExpressions; using Xunit; @@ -25,6 +26,14 @@ public class BreastCancerData { public float Label; + public float F1; + public DvText F2; + } + + public class BreastCancerDataAllColumns + { + public float Label; + [VectorType(9)] public float[] Features; } @@ -35,8 +44,86 @@ public class BreastCancerPrediction public DvBool Cancerous; } + public class BreastCancerMCPrediction + { + [ColumnName("Score")] + public float[] Scores; + } + + [Fact] + public void BinaryClassificationFastTreeSaveModelToOnnxTest() + { + string dataPath = GetDataPath(@"breast-cancer.txt"); + var pipeline = new LearningPipeline(); + + pipeline.Add(new Data.TextLoader(dataPath) + { + Arguments = new TextLoaderArguments + { + Separator = new[] { '\t' }, + HasHeader = true, + Column = new[] + { + new TextLoaderColumn() + { + Name = "Label", + Source = new [] { new TextLoaderRange(0) }, + Type = Data.DataKind.Num + }, + + new TextLoaderColumn() + { + Name = "F1", + Source = new [] { new TextLoaderRange(1, 1) }, + Type = Data.DataKind.Num + }, + + new TextLoaderColumn() + { + Name = "F2", + Source = new [] { new TextLoaderRange(2, 2) }, + Type = Data.DataKind.TX + } + } + } + }); + + pipeline.Add(new MissingValueSubstitutor("F1")); + pipeline.Add(new MinMaxNormalizer("F1")); + pipeline.Add(new CategoricalOneHotVectorizer("F2")); + pipeline.Add(new ColumnConcatenator("Features", "F1", "F2")); + pipeline.Add(new FastTreeBinaryClassifier() { NumLeaves = 2, NumTrees = 1, MinDocumentsInLeafs = 2 }); + + var model = pipeline.Train(); + var subDir = Path.Combine("..", "..", "BaselineOutput", "Common", "Onnx", "BinaryClassification", "BreastCancer"); + var onnxPath = GetOutputPath(subDir, "BinaryClassificationFastTreeSaveModelToOnnxTest.onnx"); + DeleteOutputPath(onnxPath); + + var onnxAsJsonPath = GetOutputPath(subDir, "BinaryClassificationFastTreeSaveModelToOnnxTest.json"); + DeleteOutputPath(onnxAsJsonPath); + + OnnxConverter converter = new OnnxConverter() + { + InputsToDrop = new[] { "Label" }, + OutputsToDrop = new[] { "Label", "F1", "F2", "Features" }, + Onnx = onnxPath, + Json = onnxAsJsonPath, + Domain = "Onnx" + }; + + converter.Convert(model); + + // Strip the version. + var fileText = File.ReadAllText(onnxAsJsonPath); + fileText = Regex.Replace(fileText, "\"producerVersion\": \"([^\"]+)\"", "\"producerVersion\": \"##VERSION##\""); + File.WriteAllText(onnxAsJsonPath, fileText); + + CheckEquality(subDir, "BinaryClassificationFastTreeSaveModelToOnnxTest.json"); + Done(); + } + [Fact] - public void BinaryClassificationSaveModelToOnnxTest() + public void BinaryClassificationLightGBMSaveModelToOnnxTest() { string dataPath = GetDataPath(@"breast-cancer.txt"); var pipeline = new LearningPipeline(); @@ -66,14 +153,137 @@ public void BinaryClassificationSaveModelToOnnxTest() } }); - pipeline.Add(new FastTreeBinaryClassifier() { NumLeaves = 5, NumTrees = 5, MinDocumentsInLeafs = 2 }); + pipeline.Add(new LightGbmBinaryClassifier() { NumLeaves = 2, NumBoostRound = 1, MinDataPerLeaf = 2 }); - var model = pipeline.Train(); + var model = pipeline.Train(); + var subDir = Path.Combine("..", "..", "BaselineOutput", "Common", "Onnx", "BinaryClassification", "BreastCancer"); + var onnxPath = GetOutputPath(subDir, "BinaryClassificationLightGBMSaveModelToOnnxTest.onnx"); + DeleteOutputPath(onnxPath); + + var onnxAsJsonPath = GetOutputPath(subDir, "BinaryClassificationLightGBMSaveModelToOnnxTest.json"); + DeleteOutputPath(onnxAsJsonPath); + + OnnxConverter converter = new OnnxConverter() + { + InputsToDrop = new[] { "Label" }, + OutputsToDrop = new[] { "Label", "Features" }, + Onnx = onnxPath, + Json = onnxAsJsonPath, + Domain = "Onnx" + }; + + converter.Convert(model); + + // Strip the version. + var fileText = File.ReadAllText(onnxAsJsonPath); + fileText = Regex.Replace(fileText, "\"producerVersion\": \"([^\"]+)\"", "\"producerVersion\": \"##VERSION##\""); + File.WriteAllText(onnxAsJsonPath, fileText); + + CheckEquality(subDir, "BinaryClassificationLightGBMSaveModelToOnnxTest.json"); + Done(); + } + + [Fact] + public void BinaryClassificationLRSaveModelToOnnxTest() + { + string dataPath = GetDataPath(@"breast-cancer.txt"); + var pipeline = new LearningPipeline(); + + pipeline.Add(new Data.TextLoader(dataPath) + { + Arguments = new TextLoaderArguments + { + Separator = new[] { '\t' }, + HasHeader = true, + Column = new[] + { + new TextLoaderColumn() + { + Name = "Label", + Source = new [] { new TextLoaderRange(0) }, + Type = Data.DataKind.Num + }, + + new TextLoaderColumn() + { + Name = "Features", + Source = new [] { new TextLoaderRange(1, 9) }, + Type = Data.DataKind.Num + } + } + } + }); + + pipeline.Add(new LogisticRegressionBinaryClassifier() { UseThreads = false }); + + var model = pipeline.Train(); var subDir = Path.Combine("..", "..", "BaselineOutput", "Common", "Onnx", "BinaryClassification", "BreastCancer"); - var onnxPath = GetOutputPath(subDir, "SaveModelToOnnxTest.pb"); + var onnxPath = GetOutputPath(subDir, "BinaryClassificationLRSaveModelToOnnxTest.onnx"); + DeleteOutputPath(onnxPath); + + var onnxAsJsonPath = GetOutputPath(subDir, "BinaryClassificationLRSaveModelToOnnxTest.json"); + DeleteOutputPath(onnxAsJsonPath); + + OnnxConverter converter = new OnnxConverter() + { + InputsToDrop = new[] { "Label" }, + OutputsToDrop = new[] { "Label", "Features" }, + Onnx = onnxPath, + Json = onnxAsJsonPath, + Domain = "Onnx" + }; + + converter.Convert(model); + + // Strip the version. + var fileText = File.ReadAllText(onnxAsJsonPath); + fileText = Regex.Replace(fileText, "\"producerVersion\": \"([^\"]+)\"", "\"producerVersion\": \"##VERSION##\""); + File.WriteAllText(onnxAsJsonPath, fileText); + + CheckEquality(subDir, "BinaryClassificationLRSaveModelToOnnxTest.json"); + Done(); + } + + [Fact] + public void MultiClassificationLRSaveModelToOnnxTest() + { + string dataPath = GetDataPath(@"breast-cancer.txt"); + var pipeline = new LearningPipeline(); + + pipeline.Add(new Data.TextLoader(dataPath) + { + Arguments = new TextLoaderArguments + { + Separator = new[] { '\t' }, + HasHeader = true, + Column = new[] + { + new TextLoaderColumn() + { + Name = "Label", + Source = new [] { new TextLoaderRange(0) }, + Type = Data.DataKind.Num + }, + + new TextLoaderColumn() + { + Name = "Features", + Source = new [] { new TextLoaderRange(1, 9) }, + Type = Data.DataKind.Num + } + } + } + }); + + pipeline.Add(new Dictionarizer("Label")); + pipeline.Add(new LogisticRegressionClassifier() { UseThreads = false }); + + var model = pipeline.Train(); + var subDir = Path.Combine("..", "..", "BaselineOutput", "Common", "Onnx", "MultiClassClassification", "BreastCancer"); + var onnxPath = GetOutputPath(subDir, "MultiClassificationLRSaveModelToOnnxTest.onnx"); DeleteOutputPath(onnxPath); - var onnxAsJsonPath = GetOutputPath(subDir, "SaveModelToOnnxTest.json"); + var onnxAsJsonPath = GetOutputPath(subDir, "MultiClassificationLRSaveModelToOnnxTest.json"); DeleteOutputPath(onnxAsJsonPath); OnnxConverter converter = new OnnxConverter() @@ -92,8 +302,9 @@ public void BinaryClassificationSaveModelToOnnxTest() fileText = Regex.Replace(fileText, "\"producerVersion\": \"([^\"]+)\"", "\"producerVersion\": \"##VERSION##\""); File.WriteAllText(onnxAsJsonPath, fileText); - CheckEquality(subDir, "SaveModelToOnnxTest.json"); + CheckEquality(subDir, "MultiClassificationLRSaveModelToOnnxTest.json"); Done(); } + } }