diff --git a/src/Microsoft.ML.AutoML/TrainerExtensions/RecommendationTrainerExtensions.cs b/src/Microsoft.ML.AutoML/TrainerExtensions/RecommendationTrainerExtensions.cs index 032790d96e..fa59a84594 100644 --- a/src/Microsoft.ML.AutoML/TrainerExtensions/RecommendationTrainerExtensions.cs +++ b/src/Microsoft.ML.AutoML/TrainerExtensions/RecommendationTrainerExtensions.cs @@ -18,6 +18,7 @@ public ITrainerEsitmator CreateInstance(MLContext mlContext, IEnumerable str.Name != _settings.LabelName).Select((str) => str.Name).ToList(); + var sampleResult = Utils.GenerateSampleData(_settings.TrainDataset, _columnInferenceResult); PredictProgram = new CSharpCodeFile() { File = new PredictProgram() @@ -86,14 +89,13 @@ public AzureAttachConsoleAppCodeGenerator(Pipeline pipeline, ColumnInferenceResu TaskType = _settings.MlTask.ToString(), LabelName = _settings.LabelName, Namespace = _nameSpaceValue, - TestDataPath = _settings.TestDataset, - TrainDataPath = _settings.TrainDataset, AllowQuoting = _columnInferenceResult.TextLoaderOptions.AllowQuoting, AllowSparse = _columnInferenceResult.TextLoaderOptions.AllowSparse, HasHeader = _columnInferenceResult.TextLoaderOptions.HasHeader, Separator = _columnInferenceResult.TextLoaderOptions.Separators.FirstOrDefault(), Target = _settings.Target, Features = featuresList, + SampleData = sampleResult, }.TransformText(), Name = "Program.cs", }; diff --git a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/CodeGenerator.cs b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/CodeGenerator.cs index e8f488985e..796bdd6e44 100644 --- a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/CodeGenerator.cs +++ b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/CodeGenerator.cs @@ -434,19 +434,19 @@ private string GeneratePredictProgramCSFileContent(string namespaceValue) { var columns = _columnInferenceResult.TextLoaderOptions.Columns; var featuresList = columns.Where((str) => str.Name != _settings.LabelName).Select((str) => str.Name).ToList(); + var sampleData = Utils.GenerateSampleData(_settings.TrainDataset, _columnInferenceResult); PredictProgram predictProgram = new PredictProgram() { TaskType = _settings.MlTask.ToString(), LabelName = _settings.LabelName, Namespace = namespaceValue, - TestDataPath = _settings.TestDataset, - TrainDataPath = _settings.TrainDataset, HasHeader = _columnInferenceResult.TextLoaderOptions.HasHeader, Separator = _columnInferenceResult.TextLoaderOptions.Separators.FirstOrDefault(), AllowQuoting = _columnInferenceResult.TextLoaderOptions.AllowQuoting, AllowSparse = _columnInferenceResult.TextLoaderOptions.AllowSparse, Features = featuresList, Target = _settings.Target, + SampleData = sampleData, }; return predictProgram.TransformText(); } diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProgram.cs b/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProgram.cs index 640f0cb201..93aa8a68e8 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProgram.cs +++ b/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProgram.cs @@ -32,32 +32,29 @@ public virtual string TransformText() } else if(Target == CSharp.GenerateTarget.ModelBuilder){ MB_Annotation(); } - this.Write("\r\nusing System;\r\nusing System.IO;\r\nusing System.Linq;\r\nusing Microsoft.ML;\r\nusing" + - " "); + this.Write("\r\nusing System;\r\nusing "); this.Write(this.ToStringHelper.ToStringWithCulture(Namespace)); this.Write(".Model;\r\n\r\nnamespace "); this.Write(this.ToStringHelper.ToStringWithCulture(Namespace)); - this.Write(".ConsoleApp\r\n{\r\n class Program\r\n {\r\n //Dataset to use for prediction" + - "s \r\n"); -if(string.IsNullOrEmpty(TestDataPath)){ - this.Write(" private const string DATA_FILEPATH = @\""); - this.Write(this.ToStringHelper.ToStringWithCulture(TrainDataPath)); - this.Write("\";\r\n"); - } else{ - this.Write(" private const string DATA_FILEPATH = @\""); - this.Write(this.ToStringHelper.ToStringWithCulture(TestDataPath)); - this.Write("\";\r\n"); - } - this.Write(@" - static void Main(string[] args) - { - // Create single instance of sample data from first line of dataset for model input - ModelInput sampleData = CreateSingleDataSample(DATA_FILEPATH); - - // Make a single prediction on the sample data and print results - var predictionResult = ConsumeModel.Predict(sampleData); - - Console.WriteLine(""Using model to make single prediction -- Comparing actual "); + this.Write(".ConsoleApp\r\n{\r\n class Program\r\n {\r\n static void Main(string[] args)" + + "\r\n {\r\n // Create single instance of sample data from first lin" + + "e of dataset for model input\r\n"); + if(SampleData != null) { + this.Write(" ModelInput sampleData = new ModelInput()\r\n {\r\n"); + foreach(var kv in SampleData){ + this.Write(" "); + this.Write(this.ToStringHelper.ToStringWithCulture(kv.Key)); + this.Write("="); + this.Write(this.ToStringHelper.ToStringWithCulture(kv.Value)); + this.Write(",\r\n"); +} + this.Write(" };\r\n"); +}else{ + this.Write(" ModelInput sampleData = new ModelInput();\r\n"); +} + this.Write("\r\n\t\t\t// Make a single prediction on the sample data and print results\r\n\t\t\tvar pre" + + "dictionResult = ConsumeModel.Predict(sampleData);\r\n\r\n\t\t\tConsole.WriteLine(\"Using" + + " model to make single prediction -- Comparing actual "); this.Write(this.ToStringHelper.ToStringWithCulture(Utils.Normalize(LabelName))); this.Write(" with predicted "); this.Write(this.ToStringHelper.ToStringWithCulture(Utils.Normalize(LabelName))); @@ -70,81 +67,35 @@ static void Main(string[] args) this.Write("}\");\r\n"); } if("BinaryClassification".Equals(TaskType) ){ - this.Write("\t\t\tConsole.WriteLine($\"\\n\\nActual "); - this.Write(this.ToStringHelper.ToStringWithCulture(Utils.Normalize(LabelName))); - this.Write(": {sampleData."); - this.Write(this.ToStringHelper.ToStringWithCulture(Utils.Normalize(LabelName))); - this.Write("} \\nPredicted "); + this.Write("\t\t\tConsole.WriteLine($\"\\n\\nPredicted "); this.Write(this.ToStringHelper.ToStringWithCulture(Utils.Normalize(LabelName))); this.Write(": {predictionResult.Prediction}\\n\\n\");\r\n"); } else if("Regression".Equals(TaskType) || "Recommendation".Equals(TaskType)){ - this.Write("\t\t\tConsole.WriteLine($\"\\n\\nActual "); - this.Write(this.ToStringHelper.ToStringWithCulture(Utils.Normalize(LabelName))); - this.Write(": {sampleData."); - this.Write(this.ToStringHelper.ToStringWithCulture(Utils.Normalize(LabelName))); - this.Write("} \\nPredicted "); + this.Write("\t\t\tConsole.WriteLine($\"\\n\\nPredicted "); this.Write(this.ToStringHelper.ToStringWithCulture(Utils.Normalize(LabelName))); this.Write(": {predictionResult.Score}\\n\\n\");\r\n"); } else if("MulticlassClassification".Equals(TaskType)){ - this.Write("\t\t\tConsole.WriteLine($\"\\n\\nActual "); - this.Write(this.ToStringHelper.ToStringWithCulture(Utils.Normalize(LabelName))); - this.Write(": {sampleData."); - this.Write(this.ToStringHelper.ToStringWithCulture(Utils.Normalize(LabelName))); - this.Write("} \\nPredicted "); + this.Write("\t\t\tConsole.WriteLine($\"\\n\\nPredicted "); this.Write(this.ToStringHelper.ToStringWithCulture(Utils.Normalize(LabelName))); this.Write(" value {predictionResult.Prediction} \\nPredicted "); this.Write(this.ToStringHelper.ToStringWithCulture(Utils.Normalize(LabelName))); this.Write(" scores: [{String.Join(\",\", predictionResult.Score)}]\\n\\n\");\r\n"); } - this.Write(@" Console.WriteLine(""=============== End of process, hit any key to finish ===============""); - Console.ReadKey(); - } - - // Change this code to create your own sample data - #region CreateSingleDataSample - // Method to load single row of dataset to try a single prediction - private static ModelInput CreateSingleDataSample(string dataFilePath) - { - // Create MLContext - MLContext mlContext = new MLContext(); - - // Load dataset - IDataView dataView = mlContext.Data.LoadFromTextFile( - path: dataFilePath, - hasHeader : "); - this.Write(this.ToStringHelper.ToStringWithCulture(HasHeader.ToString().ToLowerInvariant())); - this.Write(",\r\n separatorChar : \'"); - this.Write(this.ToStringHelper.ToStringWithCulture(Regex.Escape(Separator.ToString()))); - this.Write("\',\r\n allowQuoting : "); - this.Write(this.ToStringHelper.ToStringWithCulture(AllowQuoting.ToString().ToLowerInvariant())); - this.Write(",\r\n allowSparse: "); - this.Write(this.ToStringHelper.ToStringWithCulture(AllowSparse.ToString().ToLowerInvariant())); - this.Write(@"); - - // Use first line of dataset as model input - // You can replace this with new test data (hardcoded or from end-user application) - ModelInput sampleForPrediction = mlContext.Data.CreateEnumerable(dataView, false) - .First(); - return sampleForPrediction; - } - #endregion - } -} -"); + this.Write(" Console.WriteLine(\"=============== End of process, hit any key to fin" + + "ish ===============\");\r\n Console.ReadKey();\r\n }\r\n }\r\n}\r\n"); return this.GenerationEnvironment.ToString(); } public string TaskType {get;set;} public string Namespace {get;set;} public string LabelName {get;set;} -public string TestDataPath {get;set;} -public string TrainDataPath {get;set;} public char Separator {get;set;} public bool AllowQuoting {get;set;} public bool AllowSparse {get;set;} public bool HasHeader {get;set;} public IList Features {get;set;} internal CSharp.GenerateTarget Target {get;set;} +public IDictionary SampleData {get;set;} void CLI_Annotation() diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProgram.tt b/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProgram.tt index 5d01aaf401..5338f2f0ed 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProgram.tt +++ b/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProgram.tt @@ -12,26 +12,25 @@ <# } #> using System; -using System.IO; -using System.Linq; -using Microsoft.ML; using <#= Namespace #>.Model; namespace <#= Namespace #>.ConsoleApp { class Program { - //Dataset to use for predictions -<#if(string.IsNullOrEmpty(TestDataPath)){ #> - private const string DATA_FILEPATH = @"<#= TrainDataPath #>"; -<# } else{ #> - private const string DATA_FILEPATH = @"<#= TestDataPath #>"; -<# } #> - static void Main(string[] args) { - // Create single instance of sample data from first line of dataset for model input - ModelInput sampleData = CreateSingleDataSample(DATA_FILEPATH); + // Create single instance of sample data from first line of dataset for model input +<# if(SampleData != null) {#> + ModelInput sampleData = new ModelInput() + { +<# foreach(var kv in SampleData){ #> + <#= kv.Key #>=<#= kv.Value #>, +<#}#> + }; +<#}else{#> + ModelInput sampleData = new ModelInput(); +<#}#> // Make a single prediction on the sample data and print results var predictionResult = ConsumeModel.Predict(sampleData); @@ -41,51 +40,26 @@ namespace <#= Namespace #>.ConsoleApp Console.WriteLine($"<#= label #>: {sampleData.<#= Utils.Normalize(label) #>}"); <#}#> <#if("BinaryClassification".Equals(TaskType) ){ #> - Console.WriteLine($"\n\nActual <#= Utils.Normalize(LabelName) #>: {sampleData.<#= Utils.Normalize(LabelName) #>} \nPredicted <#= Utils.Normalize(LabelName) #>: {predictionResult.Prediction}\n\n"); + Console.WriteLine($"\n\nPredicted <#= Utils.Normalize(LabelName) #>: {predictionResult.Prediction}\n\n"); <#} else if("Regression".Equals(TaskType) || "Recommendation".Equals(TaskType)){#> - Console.WriteLine($"\n\nActual <#= Utils.Normalize(LabelName) #>: {sampleData.<#= Utils.Normalize(LabelName) #>} \nPredicted <#= Utils.Normalize(LabelName) #>: {predictionResult.Score}\n\n"); + Console.WriteLine($"\n\nPredicted <#= Utils.Normalize(LabelName) #>: {predictionResult.Score}\n\n"); <#} else if("MulticlassClassification".Equals(TaskType)){#> - Console.WriteLine($"\n\nActual <#= Utils.Normalize(LabelName) #>: {sampleData.<#= Utils.Normalize(LabelName) #>} \nPredicted <#= Utils.Normalize(LabelName) #> value {predictionResult.Prediction} \nPredicted <#= Utils.Normalize(LabelName) #> scores: [{String.Join(",", predictionResult.Score)}]\n\n"); + Console.WriteLine($"\n\nPredicted <#= Utils.Normalize(LabelName) #> value {predictionResult.Prediction} \nPredicted <#= Utils.Normalize(LabelName) #> scores: [{String.Join(",", predictionResult.Score)}]\n\n"); <#} #> Console.WriteLine("=============== End of process, hit any key to finish ==============="); Console.ReadKey(); } - - // Change this code to create your own sample data - #region CreateSingleDataSample - // Method to load single row of dataset to try a single prediction - private static ModelInput CreateSingleDataSample(string dataFilePath) - { - // Create MLContext - MLContext mlContext = new MLContext(); - - // Load dataset - IDataView dataView = mlContext.Data.LoadFromTextFile( - path: dataFilePath, - hasHeader : <#= HasHeader.ToString().ToLowerInvariant() #>, - separatorChar : '<#= Regex.Escape(Separator.ToString()) #>', - allowQuoting : <#= AllowQuoting.ToString().ToLowerInvariant() #>, - allowSparse: <#= AllowSparse.ToString().ToLowerInvariant() #>); - - // Use first line of dataset as model input - // You can replace this with new test data (hardcoded or from end-user application) - ModelInput sampleForPrediction = mlContext.Data.CreateEnumerable(dataView, false) - .First(); - return sampleForPrediction; - } - #endregion } } <#+ public string TaskType {get;set;} public string Namespace {get;set;} public string LabelName {get;set;} -public string TestDataPath {get;set;} -public string TrainDataPath {get;set;} public char Separator {get;set;} public bool AllowQuoting {get;set;} public bool AllowSparse {get;set;} public bool HasHeader {get;set;} public IList Features {get;set;} internal CSharp.GenerateTarget Target {get;set;} +public IDictionary SampleData {get;set;} #> diff --git a/src/Microsoft.ML.CodeGenerator/Utils.cs b/src/Microsoft.ML.CodeGenerator/Utils.cs index 0eb75c8585..552f44fac4 100644 --- a/src/Microsoft.ML.CodeGenerator/Utils.cs +++ b/src/Microsoft.ML.CodeGenerator/Utils.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Reflection; using System.Text; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; @@ -23,13 +24,96 @@ internal static string Sanitize(string name) return string.Join("", name.Select(x => Char.IsLetterOrDigit(x) ? x : '_')); } + internal static IDictionary GenerateSampleData(string inputFile, ColumnInferenceResults columnInference) + { + try + { + var mlContext = new MLContext(); + var textLoader = mlContext.Data.CreateTextLoader(columnInference.TextLoaderOptions); + var trainData = textLoader.Load(inputFile); + return Utils.GenerateSampleData(trainData, columnInference); + } + catch (Exception) + { + return null; + } + } + + internal static IDictionary GenerateSampleData(IDataView dataView, ColumnInferenceResults columnInference) + { + var featureColumns = dataView.Schema.AsEnumerable().Where(col => col.Name != columnInference.ColumnInformation.LabelColumnName && !columnInference.ColumnInformation.IgnoredColumnNames.Contains(col.Name)); + var rowCursor = dataView.GetRowCursor(featureColumns); + + var sampleData = featureColumns.Select(column => new { key = Utils.Normalize(column.Name), val = "null" }).ToDictionary(x => x.key, x => x.val); + if (rowCursor.MoveNext()) + { + var getGetGetterMethod = typeof(Utils).GetMethod(nameof(Utils.GetValueFromColumn), BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); + + foreach (var column in featureColumns) + { + var getGeneraicGetGetterMethod = getGetGetterMethod.MakeGenericMethod(column.Type.RawType); + string val = getGeneraicGetGetterMethod.Invoke(null, new object[] { rowCursor, column }) as string; + sampleData[Utils.Normalize(column.Name)] = val; + } + } + + return sampleData; + } + + internal static string GetValueFromColumn(DataViewRowCursor rowCursor, DataViewSchema.Column column) + { + T val = default; + var getter = rowCursor.GetGetter(column); + getter(ref val); + + // wrap string in quotes + if (typeof(T) == typeof(ReadOnlyMemory)) + { + return $"@\"{val.ToString().Replace("\"", "\\\"")}\""; + } + + if (val is null) + { + return "\"null\""; + } + + if (val is float) + { + var f = val as float?; + if (Single.IsNaN(f.GetValueOrDefault())) + { + return "Single.NaN"; + } + + if (Single.IsPositiveInfinity(f.GetValueOrDefault())) + { + return "Single.PositiveInfinity"; + } + + if (Single.IsNegativeInfinity(f.GetValueOrDefault())) + { + return "Single.NegativeInfinity"; + } + + return f?.ToString() + "F"; + } + + if (val is bool) + { + var f = val as bool?; + return f.GetValueOrDefault() ? "true" : "false"; + } + + return val.ToString(); + } + internal static string Normalize(string input) { //check if first character is int if (!string.IsNullOrEmpty(input) && int.TryParse(input.Substring(0, 1), out int val)) { - input = "Col" + input; - return input; + input = "_" + input; + return Normalize(input); } switch (input) { diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureCodeGeneratorTest.Program.cs.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureCodeGeneratorTest.Program.cs.approved.txt index 0e3110cc17..54f234df1f 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureCodeGeneratorTest.Program.cs.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureCodeGeneratorTest.Program.cs.approved.txt @@ -1,22 +1,16 @@ // This file was auto-generated by ML.NET Model Builder. using System; -using System.IO; -using System.Linq; -using Microsoft.ML; using Test.Model; namespace Test.ConsoleApp { class Program { - //Dataset to use for predictions - private const string DATA_FILEPATH = @"\path\to\file"; - static void Main(string[] args) { // Create single instance of sample data from first line of dataset for model input - ModelInput sampleData = CreateSingleDataSample(DATA_FILEPATH); + ModelInput sampleData = new ModelInput(); // Make a single prediction on the sample data and print results var predictionResult = ConsumeModel.Predict(sampleData); @@ -36,33 +30,9 @@ namespace Test.ConsoleApp Console.WriteLine($"Capital_loss: {sampleData.Capital_loss}"); Console.WriteLine($"Hours_per_week: {sampleData.Hours_per_week}"); Console.WriteLine($"Native_country: {sampleData.Native_country}"); - Console.WriteLine($"\n\nActual Label: {sampleData.Label} \nPredicted Label value {predictionResult.Prediction} \nPredicted Label scores: [{String.Join(",", predictionResult.Score)}]\n\n"); + Console.WriteLine($"\n\nPredicted Label value {predictionResult.Prediction} \nPredicted Label scores: [{String.Join(",", predictionResult.Score)}]\n\n"); Console.WriteLine("=============== End of process, hit any key to finish ==============="); Console.ReadKey(); } - - // Change this code to create your own sample data - #region CreateSingleDataSample - // Method to load single row of dataset to try a single prediction - private static ModelInput CreateSingleDataSample(string dataFilePath) - { - // Create MLContext - MLContext mlContext = new MLContext(); - - // Load dataset - IDataView dataView = mlContext.Data.LoadFromTextFile( - path: dataFilePath, - hasHeader: true, - separatorChar: ',', - allowQuoting: true, - allowSparse: true); - - // Use first line of dataset as model input - // You can replace this with new test data (hardcoded or from end-user application) - ModelInput sampleForPrediction = mlContext.Data.CreateEnumerable(dataView, false) - .First(); - return sampleForPrediction; - } - #endregion } } diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureImageCodeGeneratorTest.Program.cs.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureImageCodeGeneratorTest.Program.cs.approved.txt index de7b111934..9164e5e151 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureImageCodeGeneratorTest.Program.cs.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureImageCodeGeneratorTest.Program.cs.approved.txt @@ -1,55 +1,25 @@ // This file was auto-generated by ML.NET Model Builder. using System; -using System.IO; -using System.Linq; -using Microsoft.ML; using CodeGenTest.Model; namespace CodeGenTest.ConsoleApp { class Program { - //Dataset to use for predictions - private const string DATA_FILEPATH = @"/path/to/dataset"; - static void Main(string[] args) { // Create single instance of sample data from first line of dataset for model input - ModelInput sampleData = CreateSingleDataSample(DATA_FILEPATH); + ModelInput sampleData = new ModelInput(); // Make a single prediction on the sample data and print results var predictionResult = ConsumeModel.Predict(sampleData); Console.WriteLine("Using model to make single prediction -- Comparing actual Label with predicted Label from sample data...\n\n"); Console.WriteLine($"ImageSource: {sampleData.ImageSource}"); - Console.WriteLine($"\n\nActual Label: {sampleData.Label} \nPredicted Label value {predictionResult.Prediction} \nPredicted Label scores: [{String.Join(",", predictionResult.Score)}]\n\n"); + Console.WriteLine($"\n\nPredicted Label value {predictionResult.Prediction} \nPredicted Label scores: [{String.Join(",", predictionResult.Score)}]\n\n"); Console.WriteLine("=============== End of process, hit any key to finish ==============="); Console.ReadKey(); } - - // Change this code to create your own sample data - #region CreateSingleDataSample - // Method to load single row of dataset to try a single prediction - private static ModelInput CreateSingleDataSample(string dataFilePath) - { - // Create MLContext - MLContext mlContext = new MLContext(); - - // Load dataset - IDataView dataView = mlContext.Data.LoadFromTextFile( - path: dataFilePath, - hasHeader: true, - separatorChar: '\t', - allowQuoting: true, - allowSparse: true); - - // Use first line of dataset as model input - // You can replace this with new test data (hardcoded or from end-user application) - ModelInput sampleForPrediction = mlContext.Data.CreateEnumerable(dataView, false) - .First(); - return sampleForPrediction; - } - #endregion } } diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.ConsoleAppProgramCSFileContentTest.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.ConsoleAppProgramCSFileContentTest.approved.txt index 994e835f0d..5155d5cf2a 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.ConsoleAppProgramCSFileContentTest.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.ConsoleAppProgramCSFileContentTest.approved.txt @@ -5,22 +5,16 @@ //***************************************************************************************** using System; -using System.IO; -using System.Linq; -using Microsoft.ML; using TestNamespace.Model; namespace TestNamespace.ConsoleApp { class Program { - //Dataset to use for predictions - private const string DATA_FILEPATH = @"x:\dummypath\dummy_test.csv"; - static void Main(string[] args) { // Create single instance of sample data from first line of dataset for model input - ModelInput sampleData = CreateSingleDataSample(DATA_FILEPATH); + ModelInput sampleData = new ModelInput(); // Make a single prediction on the sample data and print results var predictionResult = ConsumeModel.Predict(sampleData); @@ -31,33 +25,9 @@ namespace TestNamespace.ConsoleApp Console.WriteLine($"col3: {sampleData.Col3}"); Console.WriteLine($"col4: {sampleData.Col4}"); Console.WriteLine($"col5: {sampleData.Col5}"); - Console.WriteLine($"\n\nActual Label: {sampleData.Label} \nPredicted Label: {predictionResult.Prediction}\n\n"); + Console.WriteLine($"\n\nPredicted Label: {predictionResult.Prediction}\n\n"); Console.WriteLine("=============== End of process, hit any key to finish ==============="); Console.ReadKey(); } - - // Change this code to create your own sample data - #region CreateSingleDataSample - // Method to load single row of dataset to try a single prediction - private static ModelInput CreateSingleDataSample(string dataFilePath) - { - // Create MLContext - MLContext mlContext = new MLContext(); - - // Load dataset - IDataView dataView = mlContext.Data.LoadFromTextFile( - path: dataFilePath, - hasHeader: true, - separatorChar: ',', - allowQuoting: true, - allowSparse: true); - - // Use first line of dataset as model input - // You can replace this with new test data (hardcoded or from end-user application) - ModelInput sampleForPrediction = mlContext.Data.CreateEnumerable(dataView, false) - .First(); - return sampleForPrediction; - } - #endregion } } diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.PredictionProgramCSFileContentTest.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.PredictionProgramCSFileContentTest.approved.txt index 994e835f0d..5155d5cf2a 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.PredictionProgramCSFileContentTest.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.PredictionProgramCSFileContentTest.approved.txt @@ -5,22 +5,16 @@ //***************************************************************************************** using System; -using System.IO; -using System.Linq; -using Microsoft.ML; using TestNamespace.Model; namespace TestNamespace.ConsoleApp { class Program { - //Dataset to use for predictions - private const string DATA_FILEPATH = @"x:\dummypath\dummy_test.csv"; - static void Main(string[] args) { // Create single instance of sample data from first line of dataset for model input - ModelInput sampleData = CreateSingleDataSample(DATA_FILEPATH); + ModelInput sampleData = new ModelInput(); // Make a single prediction on the sample data and print results var predictionResult = ConsumeModel.Predict(sampleData); @@ -31,33 +25,9 @@ namespace TestNamespace.ConsoleApp Console.WriteLine($"col3: {sampleData.Col3}"); Console.WriteLine($"col4: {sampleData.Col4}"); Console.WriteLine($"col5: {sampleData.Col5}"); - Console.WriteLine($"\n\nActual Label: {sampleData.Label} \nPredicted Label: {predictionResult.Prediction}\n\n"); + Console.WriteLine($"\n\nPredicted Label: {predictionResult.Prediction}\n\n"); Console.WriteLine("=============== End of process, hit any key to finish ==============="); Console.ReadKey(); } - - // Change this code to create your own sample data - #region CreateSingleDataSample - // Method to load single row of dataset to try a single prediction - private static ModelInput CreateSingleDataSample(string dataFilePath) - { - // Create MLContext - MLContext mlContext = new MLContext(); - - // Load dataset - IDataView dataView = mlContext.Data.LoadFromTextFile( - path: dataFilePath, - hasHeader: true, - separatorChar: ',', - allowQuoting: true, - allowSparse: true); - - // Use first line of dataset as model input - // You can replace this with new test data (hardcoded or from end-user application) - ModelInput sampleForPrediction = mlContext.Data.CreateEnumerable(dataView, false) - .First(); - return sampleForPrediction; - } - #endregion } } diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.Recommendation_GenerateConsoleAppProjectContents_VerifyPredictProgram.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.Recommendation_GenerateConsoleAppProjectContents_VerifyPredictProgram.approved.txt index e1c883ff06..58246e6e84 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.Recommendation_GenerateConsoleAppProjectContents_VerifyPredictProgram.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.Recommendation_GenerateConsoleAppProjectContents_VerifyPredictProgram.approved.txt @@ -5,22 +5,16 @@ //***************************************************************************************** using System; -using System.IO; -using System.Linq; -using Microsoft.ML; using TestNamespace.Model; namespace TestNamespace.ConsoleApp { class Program { - //Dataset to use for predictions - private const string DATA_FILEPATH = @"x:\dummypath\dummy_test.csv"; - static void Main(string[] args) { // Create single instance of sample data from first line of dataset for model input - ModelInput sampleData = CreateSingleDataSample(DATA_FILEPATH); + ModelInput sampleData = new ModelInput(); // Make a single prediction on the sample data and print results var predictionResult = ConsumeModel.Predict(sampleData); @@ -28,33 +22,9 @@ namespace TestNamespace.ConsoleApp Console.WriteLine("Using model to make single prediction -- Comparing actual Label with predicted Label from sample data...\n\n"); Console.WriteLine($"userId: {sampleData.UserId}"); Console.WriteLine($"movieId: {sampleData.MovieId}"); - Console.WriteLine($"\n\nActual Label: {sampleData.Label} \nPredicted Label: {predictionResult.Score}\n\n"); + Console.WriteLine($"\n\nPredicted Label: {predictionResult.Score}\n\n"); Console.WriteLine("=============== End of process, hit any key to finish ==============="); Console.ReadKey(); } - - // Change this code to create your own sample data - #region CreateSingleDataSample - // Method to load single row of dataset to try a single prediction - private static ModelInput CreateSingleDataSample(string dataFilePath) - { - // Create MLContext - MLContext mlContext = new MLContext(); - - // Load dataset - IDataView dataView = mlContext.Data.LoadFromTextFile( - path: dataFilePath, - hasHeader: true, - separatorChar: ',', - allowQuoting: true, - allowSparse: true); - - // Use first line of dataset as model input - // You can replace this with new test data (hardcoded or from end-user application) - ModelInput sampleForPrediction = mlContext.Data.CreateEnumerable(dataView, false) - .First(); - return sampleForPrediction; - } - #endregion } } diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/TemplateTest.TestPredictProgram_WithSampleData.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/TemplateTest.TestPredictProgram_WithSampleData.approved.txt new file mode 100644 index 0000000000..2fd922ee54 --- /dev/null +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/TemplateTest.TestPredictProgram_WithSampleData.approved.txt @@ -0,0 +1,28 @@ +// This file was auto-generated by ML.NET Model Builder. + +using System; +using Namespace.Model; + +namespace Namespace.ConsoleApp +{ + class Program + { + static void Main(string[] args) + { + // Create single instance of sample data from first line of dataset for model input + ModelInput sampleData = new ModelInput() + { + key1="key1", + key2="key2", + key3="key\"3", + }; + + // Make a single prediction on the sample data and print results + var predictionResult = ConsumeModel.Predict(sampleData); + + Console.WriteLine("Using model to make single prediction -- Comparing actual LabelName with predicted LabelName from sample data...\n\n"); + Console.WriteLine("=============== End of process, hit any key to finish ==============="); + Console.ReadKey(); + } + } +} diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/TemplateTest.cs b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/TemplateTest.cs new file mode 100644 index 0000000000..d4bb4ef9ca --- /dev/null +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/TemplateTest.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Text; +using ApprovalTests; +using ApprovalTests.Reporters; +using Microsoft.ML.CodeGenerator.Templates.Console; +using Microsoft.ML.TestFramework; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.ML.CodeGenerator.Tests +{ + [UseReporter(typeof(DiffReporter))] + public class TemplateTest : BaseTestClass + { + public TemplateTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + [UseReporter(typeof(DiffReporter))] + [MethodImpl(MethodImplOptions.NoInlining)] + public void TestPredictProgram_WithSampleData() + { + var predictProgram = new PredictProgram() + { + SampleData = new Dictionary() + { + { "key1", "\"key1\"" }, + { "key2", "\"key2\"" }, + { "key3", "\"key\\\"3\"" }, + }, + TaskType = "null", + Features = new List(), + Namespace = "Namespace", + LabelName = "LabelName", + Separator = ',' + }; + Approvals.Verify(predictProgram.TransformText()); + } + } +} diff --git a/test/Microsoft.ML.CodeGenerator.Tests/CodeGenTests.cs b/test/Microsoft.ML.CodeGenerator.Tests/CodeGenTests.cs index 190d2872ee..ef85fd5a6b 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/CodeGenTests.cs +++ b/test/Microsoft.ML.CodeGenerator.Tests/CodeGenTests.cs @@ -139,16 +139,6 @@ public void TrainerComplexParameterTest() Assert.Equal(expectedUsings, actual.Item2[0]); } - [Fact] - public void NormalizeTest() - { - var testStrArray = new string[] { "Abc Abc", "abc ABC" }; - var expectedStrArray = new string[] { "Abc_Abc", "Abc_ABC" }; - for (int i = 0; i != expectedStrArray.Count(); ++i) - { - var actualStr = Microsoft.ML.CodeGenerator.Utilities.Utils.Normalize(testStrArray[i]); - Assert.Equal(expectedStrArray[i], actualStr); - } - } + } } diff --git a/test/Microsoft.ML.CodeGenerator.Tests/Microsoft.ML.CodeGenerator.Tests.csproj b/test/Microsoft.ML.CodeGenerator.Tests/Microsoft.ML.CodeGenerator.Tests.csproj index bd4611427b..84df70b8a9 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/Microsoft.ML.CodeGenerator.Tests.csproj +++ b/test/Microsoft.ML.CodeGenerator.Tests/Microsoft.ML.CodeGenerator.Tests.csproj @@ -7,6 +7,7 @@ + diff --git a/test/Microsoft.ML.CodeGenerator.Tests/UtilTest.cs b/test/Microsoft.ML.CodeGenerator.Tests/UtilTest.cs new file mode 100644 index 0000000000..401ae4b10b --- /dev/null +++ b/test/Microsoft.ML.CodeGenerator.Tests/UtilTest.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.ML; +using Microsoft.ML.AutoML; +using Microsoft.ML.CodeGenerator.Utilities; +using Microsoft.ML.Data; +using Microsoft.ML.TestFramework; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.ML.CodeGenerator.Tests +{ + class TestClass + { + [LoadColumn(0)] + public string Label { get; set; } + + [LoadColumn(1)] + public string STR { get; set; } + + [LoadColumn(2)] + public string PATH { get; set; } + + [LoadColumn(3)] + public int INT { get; set; } + + [LoadColumn(4)] + public Double DOUBLE { get; set; } + + [LoadColumn(5)] + public float FLOAT { get; set; } + + [LoadColumn(6)] + public string TrickySTR { get; set; } + + [LoadColumn(7)] + public float SingleNan { get; set; } + + [LoadColumn(8)] + public float SinglePositiveInfinity { get; set; } + + [LoadColumn(9)] + public float SingleNegativeInfinity { get; set; } + + [LoadColumn(10)] + public string EmptyString { get; set; } + + [LoadColumn(11)] + public bool One { get; set; } + + [LoadColumn(12)] + public bool T { get; set; } + } + + public class UtilTest : BaseTestClass + { + public UtilTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public async Task TestGenerateSampleDataAsync() + { + var filePath = "sample.txt"; + using (var file = new StreamWriter(filePath)) + { + await file.WriteLineAsync("Label,STR,PATH,INT,DOUBLE,FLOAT,TrickySTR,SingleNan,SinglePositiveInfinity,SingleNegativeInfinity,EmptyString,One,T"); + await file.WriteLineAsync("label1,feature1,/path/to/file,2,1.2,1.223E+10,ab\"\';@#$%^&-++==,NaN,Infinity,-Infinity,,1,T"); + await file.FlushAsync(); + file.Close(); + var context = new MLContext(); + var dataView = context.Data.LoadFromTextFile(filePath,separatorChar:',', hasHeader: true); + var columnInference = new ColumnInferenceResults() + { + ColumnInformation = new ColumnInformation() + { + LabelColumnName = "Label" + } + }; + var sampleData = Utils.GenerateSampleData(dataView, columnInference); + Assert.Equal("@\"feature1\"", sampleData["STR"]); + Assert.Equal("@\"/path/to/file\"", sampleData["PATH"]); + Assert.Equal("2", sampleData["INT"]); + Assert.Equal("1.2", sampleData["DOUBLE"]); + Assert.Equal("1.223E+10F", sampleData["FLOAT"]); + Assert.Equal("@\"ab\\\"\';@#$%^&-++==\"", sampleData["TrickySTR"]); + Assert.Equal($"Single.NaN", sampleData["SingleNan"]); + Assert.Equal($"Single.PositiveInfinity", sampleData["SinglePositiveInfinity"]); + Assert.Equal($"Single.NegativeInfinity", sampleData["SingleNegativeInfinity"]); + Assert.Equal("@\"\"", sampleData["EmptyString"]); + Assert.Equal($"true", sampleData["One"]); + Assert.Equal($"true", sampleData["T"]); + } + } + + [Fact] + public void NormalizeTest() + { + var testStrArray = new string[] { "Abc Abc", "abc ABC", "12", "12.3", "1AB .C"}; + var expectedStrArray = new string[] { "Abc_Abc", "Abc_ABC", "_12", "_12_3", "_1AB__C" }; + for (int i = 0; i != expectedStrArray.Count(); ++i) + { + var actualStr = Microsoft.ML.CodeGenerator.Utilities.Utils.Normalize(testStrArray[i]); + Assert.Equal(expectedStrArray[i], actualStr); + } + } + } +}