From 3af04ad089b51fc705b8c92a5224ef9cb13dbed3 Mon Sep 17 00:00:00 2001 From: Aishwarya Bhandare Date: Tue, 8 Oct 2019 13:52:43 -0700 Subject: [PATCH 01/15] Add MaybeDownloadFile(), where check to redownload existing file if file is not of expected size. --- src/Microsoft.ML.Dnn/DnnUtils.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/Microsoft.ML.Dnn/DnnUtils.cs b/src/Microsoft.ML.Dnn/DnnUtils.cs index 1e9f2f7ef8..595a5cc1ff 100644 --- a/src/Microsoft.ML.Dnn/DnnUtils.cs +++ b/src/Microsoft.ML.Dnn/DnnUtils.cs @@ -92,6 +92,24 @@ internal static Session LoadTFSession(IExceptionContext ectx, byte[] modelBytes, } return new Session(graph); } + internal static void MaybeDownloadFile(Uri address, string fileName) + { + using (WebClient client = new WebClient()) + { + if (File.Exists(fileName)) + { + client.OpenRead(address); + var totalSizeInBytes = Convert.ToInt64(client.ResponseHeaders["Content-Length"]); + var currentSize = new FileInfo(fileName).Length; + + //If current file size is not equal to expected file size, re-download file + if (currentSize != totalSizeInBytes) + client.DownloadFile(new Uri($"{address}"), fileName); + } + else + client.DownloadFile(new Uri($"{address}"), fileName); + } + } internal static Graph LoadMetaGraph(string path) { From 22c414a7a210895d0b000f12e5617bf70b138cbe Mon Sep 17 00:00:00 2001 From: Aishwarya Bhandare Date: Wed, 9 Oct 2019 16:32:31 -0700 Subject: [PATCH 02/15] Changed WebClient to HttpClient, renamed function to DownloadIfNeeded. --- src/Microsoft.ML.Dnn/DnnUtils.cs | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/Microsoft.ML.Dnn/DnnUtils.cs b/src/Microsoft.ML.Dnn/DnnUtils.cs index 595a5cc1ff..7556a34d3d 100644 --- a/src/Microsoft.ML.Dnn/DnnUtils.cs +++ b/src/Microsoft.ML.Dnn/DnnUtils.cs @@ -8,8 +8,10 @@ using System.IO.Compression; using System.Linq; using System.Net; +using System.Net.Http; using System.Security.AccessControl; using System.Security.Principal; +using System.Threading.Tasks; using Microsoft.ML.Data; using Microsoft.ML.Runtime; using Tensorflow; @@ -92,22 +94,32 @@ internal static Session LoadTFSession(IExceptionContext ectx, byte[] modelBytes, } return new Session(graph); } - internal static void MaybeDownloadFile(Uri address, string fileName) + internal static async void DownloadIfNeededAsync(Uri address, string fileName) { - using (WebClient client = new WebClient()) + using (HttpClient client = new HttpClient()) { if (File.Exists(fileName)) { - client.OpenRead(address); - var totalSizeInBytes = Convert.ToInt64(client.ResponseHeaders["Content-Length"]); + var headerResponse = client.GetAsync(address,HttpCompletionOption.ResponseHeadersRead).Result; + var totalSizeInBytes = headerResponse.Content.Headers.ContentLength; var currentSize = new FileInfo(fileName).Length; //If current file size is not equal to expected file size, re-download file if (currentSize != totalSizeInBytes) - client.DownloadFile(new Uri($"{address}"), fileName); + { + var response = client.GetAsync(address).Result; + using FileStream fileStream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None); + using Stream contentStream = response.Content.ReadAsStreamAsync().Result; + await contentStream.CopyToAsync(fileStream); + } } else - client.DownloadFile(new Uri($"{address}"), fileName); + { + var response = client.GetAsync(address).Result; + using FileStream fileStream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None); + using Stream contentStream = response.Content.ReadAsStreamAsync().Result; + await contentStream.CopyToAsync(fileStream); + } } } From 728dc922874ac9d8552840c128067fbf16ab0eb1 Mon Sep 17 00:00:00 2001 From: Aishwarya Bhandare Date: Thu, 10 Oct 2019 13:00:17 -0700 Subject: [PATCH 03/15] Added unit test. --- src/Microsoft.ML.Dnn/DnnUtils.cs | 1 + .../TensorflowTests.cs | 84 +++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/src/Microsoft.ML.Dnn/DnnUtils.cs b/src/Microsoft.ML.Dnn/DnnUtils.cs index 7556a34d3d..fb5b5fe846 100644 --- a/src/Microsoft.ML.Dnn/DnnUtils.cs +++ b/src/Microsoft.ML.Dnn/DnnUtils.cs @@ -107,6 +107,7 @@ internal static async void DownloadIfNeededAsync(Uri address, string fileName) //If current file size is not equal to expected file size, re-download file if (currentSize != totalSizeInBytes) { + File.Delete(fileName); var response = client.GetAsync(address).Result; using FileStream fileStream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None); using Stream contentStream = response.Content.ReadAsStreamAsync().Result; diff --git a/test/Microsoft.ML.Tests/ScenariosWithDirectInstantiation/TensorflowTests.cs b/test/Microsoft.ML.Tests/ScenariosWithDirectInstantiation/TensorflowTests.cs index d767c79c41..24de06dbcb 100644 --- a/test/Microsoft.ML.Tests/ScenariosWithDirectInstantiation/TensorflowTests.cs +++ b/test/Microsoft.ML.Tests/ScenariosWithDirectInstantiation/TensorflowTests.cs @@ -1755,6 +1755,90 @@ public void TensorFlowImageClassificationEarlyStoppingDecreasing() Assert.InRange(lastEpoch, 1, 49); } + [TensorFlowFact] + public void TensorflowRedownloadModelFile() + { + string assetsRelativePath = @"assets"; + string assetsPath = GetAbsolutePath(assetsRelativePath); + string imagesDownloadFolderPath = Path.Combine(assetsPath, "inputs", + "images"); + + //Download the image set and unzip + string finalImagesFolderName = DownloadImageSet( + imagesDownloadFolderPath); + + string fullImagesetFolderPath = Path.Combine( + imagesDownloadFolderPath, finalImagesFolderName); + + MLContext mlContext = new MLContext(seed: 1); + + //Load all the original images info + IEnumerable images = LoadImagesFromDirectory( + folder: fullImagesetFolderPath, useFolderNameAsLabel: true).Take(20); + + IDataView shuffledFullImagesDataset = mlContext.Data.ShuffleRows( + mlContext.Data.LoadFromEnumerable(images), seed: 1); + + shuffledFullImagesDataset = mlContext.Transforms.Conversion + .MapValueToKey("Label") + .Fit(shuffledFullImagesDataset) + .Transform(shuffledFullImagesDataset); + + // Split the data 80:10 into train and test sets, train and evaluate. + TrainTestData trainTestData = mlContext.Data.TrainTestSplit( + shuffledFullImagesDataset, testFraction: 0.2, seed: 1); + + IDataView trainDataset = trainTestData.TrainSet; + IDataView testDataset = trainTestData.TestSet; + var validationSet = mlContext.Transforms.LoadImages("Image", fullImagesetFolderPath, false, "ImagePath") // false indicates we want the image as a VBuffer + .Fit(testDataset) + .Transform(testDataset); + + //If model file exists, delete it + if(File.Exists(@"resnet_v2_101_299.meta")) + File.Delete(@"resnet_v2_101_299.meta"); + + //Create empty file with same name as model file to + //simulate incomplete model file scenario. + using (File.Create(@"resnet_v2_101_299.meta")) { } + + //Create pipeline and run + var pipeline = mlContext.Transforms.LoadImages("Image", fullImagesetFolderPath, false, "ImagePath") // false indicates we want the image as a VBuffer + .Append(mlContext.Model.ImageClassification( + "Image", "Label", + // Just by changing/selecting InceptionV3 here instead of + // ResnetV2101 you can try a different architecture/pre-trained + // model. + arch: ImageClassificationEstimator.Architecture.ResnetV2101, + epoch: 1, + batchSize: 10, + learningRate: 0.01f, + metricsCallback: (metrics) => Console.WriteLine(metrics), + testOnTrainSet: false, + disableEarlyStopping: true, + reuseTrainSetBottleneckCachedValues: true, + reuseValidationSetBottleneckCachedValues: true) + .Append(mlContext.Transforms.Conversion.MapKeyToValue(outputColumnName: "PredictedLabel", inputColumnName: "PredictedLabel"))); + + var trainedModel = pipeline.Fit(trainDataset); + + mlContext.Model.Save(trainedModel, shuffledFullImagesDataset.Schema, + "model.zip"); + + ITransformer loadedModel; + DataViewSchema schema; + using (var file = File.OpenRead("model.zip")) + loadedModel = mlContext.Model.Load(file, out schema); + + // Testing EvaluateModel: group testing on test dataset + IDataView predictions = trainedModel.Transform(testDataset); + var metrics = mlContext.MulticlassClassification.Evaluate(predictions); + + File.Delete(@"resnet_v2_101_299.meta"); + + // Accuracy should be returned, indicating training loop ran successfully. + Assert.InRange(metrics.MicroAccuracy, 0.1, 1); + } public static IEnumerable LoadImagesFromDirectory(string folder, bool useFolderNameAsLabel = true) { From 6aea270e7c063cc0f760a137d5aa054c23b29fff Mon Sep 17 00:00:00 2001 From: Aishwarya Bhandare Date: Thu, 10 Oct 2019 13:05:20 -0700 Subject: [PATCH 04/15] Fixed newline after test --- .../ScenariosWithDirectInstantiation/TensorflowTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/test/Microsoft.ML.Tests/ScenariosWithDirectInstantiation/TensorflowTests.cs b/test/Microsoft.ML.Tests/ScenariosWithDirectInstantiation/TensorflowTests.cs index 24de06dbcb..60eb2269a3 100644 --- a/test/Microsoft.ML.Tests/ScenariosWithDirectInstantiation/TensorflowTests.cs +++ b/test/Microsoft.ML.Tests/ScenariosWithDirectInstantiation/TensorflowTests.cs @@ -1839,6 +1839,7 @@ public void TensorflowRedownloadModelFile() // Accuracy should be returned, indicating training loop ran successfully. Assert.InRange(metrics.MicroAccuracy, 0.1, 1); } + public static IEnumerable LoadImagesFromDirectory(string folder, bool useFolderNameAsLabel = true) { From 95e37eb9aad54e781ca4636f835ccb043c358684 Mon Sep 17 00:00:00 2001 From: Aishwarya Bhandare Date: Fri, 11 Oct 2019 14:40:56 -0700 Subject: [PATCH 05/15] Removed asynchronous copy --- src/Microsoft.ML.Dnn/DnnUtils.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.ML.Dnn/DnnUtils.cs b/src/Microsoft.ML.Dnn/DnnUtils.cs index fb5b5fe846..c42aee5de0 100644 --- a/src/Microsoft.ML.Dnn/DnnUtils.cs +++ b/src/Microsoft.ML.Dnn/DnnUtils.cs @@ -94,7 +94,7 @@ internal static Session LoadTFSession(IExceptionContext ectx, byte[] modelBytes, } return new Session(graph); } - internal static async void DownloadIfNeededAsync(Uri address, string fileName) + internal static void DownloadIfNeeded(Uri address, string fileName) { using (HttpClient client = new HttpClient()) { @@ -111,7 +111,7 @@ internal static async void DownloadIfNeededAsync(Uri address, string fileName) var response = client.GetAsync(address).Result; using FileStream fileStream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None); using Stream contentStream = response.Content.ReadAsStreamAsync().Result; - await contentStream.CopyToAsync(fileStream); + contentStream.CopyTo(fileStream); } } else @@ -119,7 +119,7 @@ internal static async void DownloadIfNeededAsync(Uri address, string fileName) var response = client.GetAsync(address).Result; using FileStream fileStream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None); using Stream contentStream = response.Content.ReadAsStreamAsync().Result; - await contentStream.CopyToAsync(fileStream); + contentStream.CopyTo(fileStream); } } } From c59067a65c517361420be7b48752368b0bfcb874 Mon Sep 17 00:00:00 2001 From: Aishwarya Bhandare Date: Mon, 14 Oct 2019 13:50:25 -0700 Subject: [PATCH 06/15] added test for InceptionV3, fixed formatting. --- src/Microsoft.ML.Dnn/DnnUtils.cs | 8 ++-- .../Attributes/TensorflowTheoryAttribute.cs | 2 +- .../TensorflowTests.cs | 39 +++++++++++++------ 3 files changed, 32 insertions(+), 17 deletions(-) diff --git a/src/Microsoft.ML.Dnn/DnnUtils.cs b/src/Microsoft.ML.Dnn/DnnUtils.cs index c42aee5de0..d2f70afe27 100644 --- a/src/Microsoft.ML.Dnn/DnnUtils.cs +++ b/src/Microsoft.ML.Dnn/DnnUtils.cs @@ -110,16 +110,16 @@ internal static void DownloadIfNeeded(Uri address, string fileName) File.Delete(fileName); var response = client.GetAsync(address).Result; using FileStream fileStream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None); - using Stream contentStream = response.Content.ReadAsStreamAsync().Result; - contentStream.CopyTo(fileStream); + using Stream contentStream = response.Content.ReadAsStreamAsync().Result; + contentStream.CopyTo(fileStream); } } else { var response = client.GetAsync(address).Result; using FileStream fileStream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None); - using Stream contentStream = response.Content.ReadAsStreamAsync().Result; - contentStream.CopyTo(fileStream); + using Stream contentStream = response.Content.ReadAsStreamAsync().Result; + contentStream.CopyTo(fileStream); } } } diff --git a/test/Microsoft.ML.TestFramework/Attributes/TensorflowTheoryAttribute.cs b/test/Microsoft.ML.TestFramework/Attributes/TensorflowTheoryAttribute.cs index 24b73a56f5..55d4dc0632 100644 --- a/test/Microsoft.ML.TestFramework/Attributes/TensorflowTheoryAttribute.cs +++ b/test/Microsoft.ML.TestFramework/Attributes/TensorflowTheoryAttribute.cs @@ -20,4 +20,4 @@ protected override bool IsEnvironmentSupported() return Environment.Is64BitProcess; } } -} \ No newline at end of file +} diff --git a/test/Microsoft.ML.Tests/ScenariosWithDirectInstantiation/TensorflowTests.cs b/test/Microsoft.ML.Tests/ScenariosWithDirectInstantiation/TensorflowTests.cs index 60eb2269a3..79bedf2f34 100644 --- a/test/Microsoft.ML.Tests/ScenariosWithDirectInstantiation/TensorflowTests.cs +++ b/test/Microsoft.ML.Tests/ScenariosWithDirectInstantiation/TensorflowTests.cs @@ -1755,8 +1755,10 @@ public void TensorFlowImageClassificationEarlyStoppingDecreasing() Assert.InRange(lastEpoch, 1, 49); } - [TensorFlowFact] - public void TensorflowRedownloadModelFile() + [TensorFlowTheory] + [InlineData(ImageClassificationEstimator.Architecture.ResnetV2101)] + [InlineData(ImageClassificationEstimator.Architecture.InceptionV3)] + public void TensorflowRedownloadModelFile(ImageClassificationEstimator.Architecture arch) { string assetsRelativePath = @"assets"; string assetsPath = GetAbsolutePath(assetsRelativePath); @@ -1774,7 +1776,7 @@ public void TensorflowRedownloadModelFile() //Load all the original images info IEnumerable images = LoadImagesFromDirectory( - folder: fullImagesetFolderPath, useFolderNameAsLabel: true).Take(20); + folder: fullImagesetFolderPath, useFolderNameAsLabel: true); IDataView shuffledFullImagesDataset = mlContext.Data.ShuffleRows( mlContext.Data.LoadFromEnumerable(images), seed: 1); @@ -1794,13 +1796,28 @@ public void TensorflowRedownloadModelFile() .Fit(testDataset) .Transform(testDataset); - //If model file exists, delete it - if(File.Exists(@"resnet_v2_101_299.meta")) - File.Delete(@"resnet_v2_101_299.meta"); - - //Create empty file with same name as model file to + //If model file exists, delete it and create empty file + //with the same name as model file to //simulate incomplete model file scenario. - using (File.Create(@"resnet_v2_101_299.meta")) { } + if (arch == ImageClassificationEstimator.Architecture.ResnetV2101) + { + + if (File.Exists(@"resnet_v2_101_299.meta")) + File.Delete(@"resnet_v2_101_299.meta"); + + using (File.Create(@"resnet_v2_101_299.meta")) { } + } + else if(arch == ImageClassificationEstimator.Architecture.InceptionV3) + { + if (File.Exists(@"tfhub_modules.zip")) + File.Delete(@"tfhub_modules.zip"); + if (Directory.Exists(@"tfhub_modules")) + Directory.Delete(@"tfhub_modules", true); + if (File.Exists(@"InceptionV3.meta")) + File.Delete(@"InceptionV3.meta"); + + using (File.Create(@"InceptionV3.meta")) { } + } //Create pipeline and run var pipeline = mlContext.Transforms.LoadImages("Image", fullImagesetFolderPath, false, "ImagePath") // false indicates we want the image as a VBuffer @@ -1809,7 +1826,7 @@ public void TensorflowRedownloadModelFile() // Just by changing/selecting InceptionV3 here instead of // ResnetV2101 you can try a different architecture/pre-trained // model. - arch: ImageClassificationEstimator.Architecture.ResnetV2101, + arch: arch, epoch: 1, batchSize: 10, learningRate: 0.01f, @@ -1834,8 +1851,6 @@ public void TensorflowRedownloadModelFile() IDataView predictions = trainedModel.Transform(testDataset); var metrics = mlContext.MulticlassClassification.Evaluate(predictions); - File.Delete(@"resnet_v2_101_299.meta"); - // Accuracy should be returned, indicating training loop ran successfully. Assert.InRange(metrics.MicroAccuracy, 0.1, 1); } From a9163e44e0a57941c475c6673df0fa7a949951e4 Mon Sep 17 00:00:00 2001 From: Aishwarya Bhandare Date: Wed, 23 Oct 2019 15:33:26 -0700 Subject: [PATCH 07/15] Modify to call DownloadIfNeeded --- src/Microsoft.ML.Dnn/DnnUtils.cs | 59 ++++++++++++-------------------- 1 file changed, 21 insertions(+), 38 deletions(-) diff --git a/src/Microsoft.ML.Dnn/DnnUtils.cs b/src/Microsoft.ML.Dnn/DnnUtils.cs index d2f70afe27..f819caa970 100644 --- a/src/Microsoft.ML.Dnn/DnnUtils.cs +++ b/src/Microsoft.ML.Dnn/DnnUtils.cs @@ -96,32 +96,30 @@ internal static Session LoadTFSession(IExceptionContext ectx, byte[] modelBytes, } internal static void DownloadIfNeeded(Uri address, string fileName) { - using (HttpClient client = new HttpClient()) + using HttpClient client = new HttpClient(); + if (File.Exists(fileName)) { - if (File.Exists(fileName)) - { - var headerResponse = client.GetAsync(address,HttpCompletionOption.ResponseHeadersRead).Result; - var totalSizeInBytes = headerResponse.Content.Headers.ContentLength; - var currentSize = new FileInfo(fileName).Length; + var headerResponse = client.GetAsync(address, HttpCompletionOption.ResponseHeadersRead).Result; + var totalSizeInBytes = headerResponse.Content.Headers.ContentLength; + var currentSize = new FileInfo(fileName).Length; - //If current file size is not equal to expected file size, re-download file - if (currentSize != totalSizeInBytes) - { - File.Delete(fileName); - var response = client.GetAsync(address).Result; - using FileStream fileStream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None); - using Stream contentStream = response.Content.ReadAsStreamAsync().Result; - contentStream.CopyTo(fileStream); - } - } - else + //If current file size is not equal to expected file size, re-download file + if (currentSize != totalSizeInBytes) { + File.Delete(fileName); var response = client.GetAsync(address).Result; using FileStream fileStream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None); using Stream contentStream = response.Content.ReadAsStreamAsync().Result; contentStream.CopyTo(fileStream); } } + else + { + var response = client.GetAsync(address).Result; + using FileStream fileStream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None); + using Stream contentStream = response.Content.ReadAsStreamAsync().Result; + contentStream.CopyTo(fileStream); + } } internal static Graph LoadMetaGraph(string path) @@ -303,43 +301,28 @@ internal static DnnModel LoadDnnModel(IHostEnvironment env, Architecture arch, b if (arch == Architecture.InceptionV3) { var baseGitPath = @"https://raw.githubusercontent.com/SciSharp/TensorFlow.NET/master/graph/InceptionV3.meta"; - using (WebClient client = new WebClient()) - { - client.DownloadFile(new Uri($"{baseGitPath}"), @"InceptionV3.meta"); - } + DownloadIfNeeded(new Uri($"{baseGitPath}"), @"InceptionV3.meta"); baseGitPath = @"https://github.com/SciSharp/TensorFlow.NET/raw/master/data/tfhub_modules.zip"; - using (WebClient client = new WebClient()) - { - client.DownloadFile(new Uri($"{baseGitPath}"), @"tfhub_modules.zip"); + DownloadIfNeeded(new Uri($"{baseGitPath}"), @"tfhub_modules.zip"); + if (!Directory.Exists(@"tfhub_modules")) ZipFile.ExtractToDirectory(Path.Combine(Directory.GetCurrentDirectory(), @"tfhub_modules.zip"), @"tfhub_modules"); - } } else if (arch == Architecture.ResnetV2101) { var baseGitPath = @"https://aka.ms/mlnet-resources/image/ResNet101Tensorflow/resnet_v2_101_299.meta"; - using (WebClient client = new WebClient()) - { - client.DownloadFile(new Uri($"{baseGitPath}"), @"resnet_v2_101_299.meta"); - } + DownloadIfNeeded(new Uri($"{baseGitPath}"), @"resnet_v2_101_299.meta"); } else if (arch == Architecture.MobilenetV2) { var baseGitPath = @"https://tlcresources.blob.core.windows.net/image/MobileNetV2TensorFlow/mobilenet_v2.meta"; - using (WebClient client = new WebClient()) - { - client.DownloadFile(new Uri($"{baseGitPath}"), @"mobilenet_v2.meta"); - } + DownloadIfNeeded(new Uri($"{baseGitPath}"), @"mobilenet_v2.meta"); } else if (arch == Architecture.ResnetV250) { var baseGitPath = @"https://tlcresources.blob.core.windows.net/image/ResNetV250TensorFlow/resnet_v2_50_299.meta"; - using (WebClient client = new WebClient()) - { - client.DownloadFile(new Uri($"{baseGitPath}"), @"resnet_v2_50_299.meta"); - } + DownloadIfNeeded(new Uri($"{baseGitPath}"), @"resnet_v2_50_299.meta"); } - } return new DnnModel(GetSession(env, modelPath, metaGraph), modelPath); From 090346c6ce19dea073af4439c50199f4c435bd46 Mon Sep 17 00:00:00 2001 From: Aishwarya Bhandare Date: Wed, 23 Oct 2019 16:10:24 -0700 Subject: [PATCH 08/15] fixed unit test, minor formatting --- src/Microsoft.ML.Dnn/DnnUtils.cs | 52 +++++++++---------- .../Attributes/TensorflowTheoryAttribute.cs | 2 +- .../TensorflowTests.cs | 27 +++++----- 3 files changed, 38 insertions(+), 43 deletions(-) diff --git a/src/Microsoft.ML.Dnn/DnnUtils.cs b/src/Microsoft.ML.Dnn/DnnUtils.cs index f819caa970..b6014afd5c 100644 --- a/src/Microsoft.ML.Dnn/DnnUtils.cs +++ b/src/Microsoft.ML.Dnn/DnnUtils.cs @@ -7,11 +7,9 @@ using System.IO; using System.IO.Compression; using System.Linq; -using System.Net; using System.Net.Http; using System.Security.AccessControl; using System.Security.Principal; -using System.Threading.Tasks; using Microsoft.ML.Data; using Microsoft.ML.Runtime; using Tensorflow; @@ -94,6 +92,7 @@ internal static Session LoadTFSession(IExceptionContext ectx, byte[] modelBytes, } return new Session(graph); } + internal static void DownloadIfNeeded(Uri address, string fileName) { using HttpClient client = new HttpClient(); @@ -295,34 +294,31 @@ internal static DnnModel LoadDnnModel(IHostEnvironment env, string modelPath, bo internal static DnnModel LoadDnnModel(IHostEnvironment env, Architecture arch, bool metaGraph = false) { - var modelPath = ModelLocation[arch]; - if (!File.Exists(modelPath)) + var modelPath = ImageClassificationEstimator.ModelLocation[arch]; + if (arch == ImageClassificationEstimator.Architecture.InceptionV3) { - if (arch == Architecture.InceptionV3) - { - var baseGitPath = @"https://raw.githubusercontent.com/SciSharp/TensorFlow.NET/master/graph/InceptionV3.meta"; - DownloadIfNeeded(new Uri($"{baseGitPath}"), @"InceptionV3.meta"); + var baseGitPath = @"https://raw.githubusercontent.com/SciSharp/TensorFlow.NET/master/graph/InceptionV3.meta"; + DownloadIfNeeded(new Uri($"{baseGitPath}"), @"InceptionV3.meta"); - baseGitPath = @"https://github.com/SciSharp/TensorFlow.NET/raw/master/data/tfhub_modules.zip"; - DownloadIfNeeded(new Uri($"{baseGitPath}"), @"tfhub_modules.zip"); - if (!Directory.Exists(@"tfhub_modules")) - ZipFile.ExtractToDirectory(Path.Combine(Directory.GetCurrentDirectory(), @"tfhub_modules.zip"), @"tfhub_modules"); - } - else if (arch == Architecture.ResnetV2101) - { - var baseGitPath = @"https://aka.ms/mlnet-resources/image/ResNet101Tensorflow/resnet_v2_101_299.meta"; - DownloadIfNeeded(new Uri($"{baseGitPath}"), @"resnet_v2_101_299.meta"); - } - else if (arch == Architecture.MobilenetV2) - { - var baseGitPath = @"https://tlcresources.blob.core.windows.net/image/MobileNetV2TensorFlow/mobilenet_v2.meta"; - DownloadIfNeeded(new Uri($"{baseGitPath}"), @"mobilenet_v2.meta"); - } - else if (arch == Architecture.ResnetV250) - { - var baseGitPath = @"https://tlcresources.blob.core.windows.net/image/ResNetV250TensorFlow/resnet_v2_50_299.meta"; - DownloadIfNeeded(new Uri($"{baseGitPath}"), @"resnet_v2_50_299.meta"); - } + baseGitPath = @"https://github.com/SciSharp/TensorFlow.NET/raw/master/data/tfhub_modules.zip"; + DownloadIfNeeded(new Uri($"{baseGitPath}"), @"tfhub_modules.zip"); + if (!Directory.Exists(@"tfhub_modules")) + ZipFile.ExtractToDirectory(Path.Combine(Directory.GetCurrentDirectory(), @"tfhub_modules.zip"), @"tfhub_modules"); + } + else if (arch == ImageClassificationEstimator.Architecture.ResnetV2101) + { + var baseGitPath = @"https://aka.ms/mlnet-resources/image/ResNet101Tensorflow/resnet_v2_101_299.meta"; + DownloadIfNeeded(new Uri($"{baseGitPath}"), @"resnet_v2_101_299.meta"); + } + else if (arch == ImageClassificationEstimator.Architecture.MobilenetV2) + { + var baseGitPath = @"https://tlcresources.blob.core.windows.net/image/MobileNetV2TensorFlow/mobilenet_v2.meta"; + DownloadIfNeeded(new Uri($"{baseGitPath}"), @"mobilenet_v2.meta"); + } + else if (arch == ImageClassificationEstimator.Architecture.ResnetV250) + { + var baseGitPath = @"https://tlcresources.blob.core.windows.net/image/ResNetV250TensorFlow/resnet_v2_50_299.meta"; + DownloadIfNeeded(new Uri($"{baseGitPath}"), @"resnet_v2_50_299.meta"); } return new DnnModel(GetSession(env, modelPath, metaGraph), modelPath); diff --git a/test/Microsoft.ML.TestFramework/Attributes/TensorflowTheoryAttribute.cs b/test/Microsoft.ML.TestFramework/Attributes/TensorflowTheoryAttribute.cs index 55d4dc0632..24b73a56f5 100644 --- a/test/Microsoft.ML.TestFramework/Attributes/TensorflowTheoryAttribute.cs +++ b/test/Microsoft.ML.TestFramework/Attributes/TensorflowTheoryAttribute.cs @@ -20,4 +20,4 @@ protected override bool IsEnvironmentSupported() return Environment.Is64BitProcess; } } -} +} \ No newline at end of file diff --git a/test/Microsoft.ML.Tests/ScenariosWithDirectInstantiation/TensorflowTests.cs b/test/Microsoft.ML.Tests/ScenariosWithDirectInstantiation/TensorflowTests.cs index 79bedf2f34..620fc365e3 100644 --- a/test/Microsoft.ML.Tests/ScenariosWithDirectInstantiation/TensorflowTests.cs +++ b/test/Microsoft.ML.Tests/ScenariosWithDirectInstantiation/TensorflowTests.cs @@ -1819,22 +1819,21 @@ public void TensorflowRedownloadModelFile(ImageClassificationEstimator.Architect using (File.Create(@"InceptionV3.meta")) { } } + var options = new ImageClassificationEstimator.Options() + { + FeaturesColumnName = "Image", + LabelColumnName = "Label", + Arch = arch, + Epoch = 1, + BatchSize = 10, + MetricsCallback = (metrics) => Console.WriteLine(metrics), + TestOnTrainSet = false, + DisableEarlyStopping = true + }; + //Create pipeline and run var pipeline = mlContext.Transforms.LoadImages("Image", fullImagesetFolderPath, false, "ImagePath") // false indicates we want the image as a VBuffer - .Append(mlContext.Model.ImageClassification( - "Image", "Label", - // Just by changing/selecting InceptionV3 here instead of - // ResnetV2101 you can try a different architecture/pre-trained - // model. - arch: arch, - epoch: 1, - batchSize: 10, - learningRate: 0.01f, - metricsCallback: (metrics) => Console.WriteLine(metrics), - testOnTrainSet: false, - disableEarlyStopping: true, - reuseTrainSetBottleneckCachedValues: true, - reuseValidationSetBottleneckCachedValues: true) + .Append(mlContext.Model.ImageClassification(options) .Append(mlContext.Transforms.Conversion.MapKeyToValue(outputColumnName: "PredictedLabel", inputColumnName: "PredictedLabel"))); var trainedModel = pipeline.Fit(trainDataset); From 34ba676c374241b1bb99f3ae104cabbe636d7e77 Mon Sep 17 00:00:00 2001 From: Aishwarya Bhandare Date: Fri, 25 Oct 2019 17:04:02 -0700 Subject: [PATCH 09/15] fix test and change after rebase --- src/Microsoft.ML.Dnn/DnnUtils.cs | 10 +++++----- .../TensorflowTests.cs | 18 +++++++++--------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Microsoft.ML.Dnn/DnnUtils.cs b/src/Microsoft.ML.Dnn/DnnUtils.cs index b6014afd5c..3d02ceea67 100644 --- a/src/Microsoft.ML.Dnn/DnnUtils.cs +++ b/src/Microsoft.ML.Dnn/DnnUtils.cs @@ -294,8 +294,8 @@ internal static DnnModel LoadDnnModel(IHostEnvironment env, string modelPath, bo internal static DnnModel LoadDnnModel(IHostEnvironment env, Architecture arch, bool metaGraph = false) { - var modelPath = ImageClassificationEstimator.ModelLocation[arch]; - if (arch == ImageClassificationEstimator.Architecture.InceptionV3) + var modelPath = ImageClassificationTrainer.ModelLocation[arch]; + if (arch == ImageClassificationTrainer.Architecture.InceptionV3) { var baseGitPath = @"https://raw.githubusercontent.com/SciSharp/TensorFlow.NET/master/graph/InceptionV3.meta"; DownloadIfNeeded(new Uri($"{baseGitPath}"), @"InceptionV3.meta"); @@ -305,17 +305,17 @@ internal static DnnModel LoadDnnModel(IHostEnvironment env, Architecture arch, b if (!Directory.Exists(@"tfhub_modules")) ZipFile.ExtractToDirectory(Path.Combine(Directory.GetCurrentDirectory(), @"tfhub_modules.zip"), @"tfhub_modules"); } - else if (arch == ImageClassificationEstimator.Architecture.ResnetV2101) + else if (arch == ImageClassificationTrainer.Architecture.ResnetV2101) { var baseGitPath = @"https://aka.ms/mlnet-resources/image/ResNet101Tensorflow/resnet_v2_101_299.meta"; DownloadIfNeeded(new Uri($"{baseGitPath}"), @"resnet_v2_101_299.meta"); } - else if (arch == ImageClassificationEstimator.Architecture.MobilenetV2) + else if (arch == ImageClassificationTrainer.Architecture.MobilenetV2) { var baseGitPath = @"https://tlcresources.blob.core.windows.net/image/MobileNetV2TensorFlow/mobilenet_v2.meta"; DownloadIfNeeded(new Uri($"{baseGitPath}"), @"mobilenet_v2.meta"); } - else if (arch == ImageClassificationEstimator.Architecture.ResnetV250) + else if (arch == ImageClassificationTrainer.Architecture.ResnetV250) { var baseGitPath = @"https://tlcresources.blob.core.windows.net/image/ResNetV250TensorFlow/resnet_v2_50_299.meta"; DownloadIfNeeded(new Uri($"{baseGitPath}"), @"resnet_v2_50_299.meta"); diff --git a/test/Microsoft.ML.Tests/ScenariosWithDirectInstantiation/TensorflowTests.cs b/test/Microsoft.ML.Tests/ScenariosWithDirectInstantiation/TensorflowTests.cs index 620fc365e3..86cc1b6bac 100644 --- a/test/Microsoft.ML.Tests/ScenariosWithDirectInstantiation/TensorflowTests.cs +++ b/test/Microsoft.ML.Tests/ScenariosWithDirectInstantiation/TensorflowTests.cs @@ -1756,9 +1756,9 @@ public void TensorFlowImageClassificationEarlyStoppingDecreasing() } [TensorFlowTheory] - [InlineData(ImageClassificationEstimator.Architecture.ResnetV2101)] - [InlineData(ImageClassificationEstimator.Architecture.InceptionV3)] - public void TensorflowRedownloadModelFile(ImageClassificationEstimator.Architecture arch) + [InlineData(ImageClassificationTrainer.Architecture.ResnetV2101)] + [InlineData(ImageClassificationTrainer.Architecture.InceptionV3)] + public void TensorflowRedownloadModelFile(ImageClassificationTrainer.Architecture arch) { string assetsRelativePath = @"assets"; string assetsPath = GetAbsolutePath(assetsRelativePath); @@ -1799,7 +1799,7 @@ public void TensorflowRedownloadModelFile(ImageClassificationEstimator.Architect //If model file exists, delete it and create empty file //with the same name as model file to //simulate incomplete model file scenario. - if (arch == ImageClassificationEstimator.Architecture.ResnetV2101) + if (arch == ImageClassificationTrainer.Architecture.ResnetV2101) { if (File.Exists(@"resnet_v2_101_299.meta")) @@ -1807,7 +1807,7 @@ public void TensorflowRedownloadModelFile(ImageClassificationEstimator.Architect using (File.Create(@"resnet_v2_101_299.meta")) { } } - else if(arch == ImageClassificationEstimator.Architecture.InceptionV3) + else if(arch == ImageClassificationTrainer.Architecture.InceptionV3) { if (File.Exists(@"tfhub_modules.zip")) File.Delete(@"tfhub_modules.zip"); @@ -1819,21 +1819,21 @@ public void TensorflowRedownloadModelFile(ImageClassificationEstimator.Architect using (File.Create(@"InceptionV3.meta")) { } } - var options = new ImageClassificationEstimator.Options() + var options = new ImageClassificationTrainer.Options() { - FeaturesColumnName = "Image", + FeatureColumnName = "Image", LabelColumnName = "Label", Arch = arch, Epoch = 1, BatchSize = 10, MetricsCallback = (metrics) => Console.WriteLine(metrics), TestOnTrainSet = false, - DisableEarlyStopping = true + EarlyStoppingCriteria = null }; //Create pipeline and run var pipeline = mlContext.Transforms.LoadImages("Image", fullImagesetFolderPath, false, "ImagePath") // false indicates we want the image as a VBuffer - .Append(mlContext.Model.ImageClassification(options) + .Append(mlContext.MulticlassClassification.Trainers.ImageClassification(options) .Append(mlContext.Transforms.Conversion.MapKeyToValue(outputColumnName: "PredictedLabel", inputColumnName: "PredictedLabel"))); var trainedModel = pipeline.Fit(trainDataset); From 81dba7380ccc74be5924ed3ce4603279071615c2 Mon Sep 17 00:00:00 2001 From: Zeeshan Siddiqui Date: Sun, 27 Oct 2019 16:54:37 -0700 Subject: [PATCH 10/15] sync to master and use LoadRawImagesBytes instead of LoadImages. --- .../ScenariosWithDirectInstantiation/TensorflowTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Microsoft.ML.Tests/ScenariosWithDirectInstantiation/TensorflowTests.cs b/test/Microsoft.ML.Tests/ScenariosWithDirectInstantiation/TensorflowTests.cs index 86cc1b6bac..95483496c1 100644 --- a/test/Microsoft.ML.Tests/ScenariosWithDirectInstantiation/TensorflowTests.cs +++ b/test/Microsoft.ML.Tests/ScenariosWithDirectInstantiation/TensorflowTests.cs @@ -1792,7 +1792,7 @@ public void TensorflowRedownloadModelFile(ImageClassificationTrainer.Architectur IDataView trainDataset = trainTestData.TrainSet; IDataView testDataset = trainTestData.TestSet; - var validationSet = mlContext.Transforms.LoadImages("Image", fullImagesetFolderPath, false, "ImagePath") // false indicates we want the image as a VBuffer + var validationSet = mlContext.Transforms.LoadRawImageBytes("Image", fullImagesetFolderPath, "ImagePath") // false indicates we want the image as a VBuffer .Fit(testDataset) .Transform(testDataset); @@ -1832,7 +1832,7 @@ public void TensorflowRedownloadModelFile(ImageClassificationTrainer.Architectur }; //Create pipeline and run - var pipeline = mlContext.Transforms.LoadImages("Image", fullImagesetFolderPath, false, "ImagePath") // false indicates we want the image as a VBuffer + var pipeline = mlContext.Transforms.LoadRawImageBytes("Image", fullImagesetFolderPath, "ImagePath") // false indicates we want the image as a VBuffer .Append(mlContext.MulticlassClassification.Trainers.ImageClassification(options) .Append(mlContext.Transforms.Conversion.MapKeyToValue(outputColumnName: "PredictedLabel", inputColumnName: "PredictedLabel"))); From 33a9d6e8c2b9df48f6b1c5ac141bfa4318ba37ef Mon Sep 17 00:00:00 2001 From: Zeeshan Siddiqui Date: Sun, 27 Oct 2019 17:33:54 -0700 Subject: [PATCH 11/15] Dispose HttpClient object and wait for task for finish. --- src/Microsoft.ML.Dnn/DnnUtils.cs | 54 +++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/src/Microsoft.ML.Dnn/DnnUtils.cs b/src/Microsoft.ML.Dnn/DnnUtils.cs index 3d02ceea67..8dbfe823f0 100644 --- a/src/Microsoft.ML.Dnn/DnnUtils.cs +++ b/src/Microsoft.ML.Dnn/DnnUtils.cs @@ -95,30 +95,46 @@ internal static Session LoadTFSession(IExceptionContext ectx, byte[] modelBytes, internal static void DownloadIfNeeded(Uri address, string fileName) { - using HttpClient client = new HttpClient(); - if (File.Exists(fileName)) + using (var client = new HttpClient()) { - var headerResponse = client.GetAsync(address, HttpCompletionOption.ResponseHeadersRead).Result; - var totalSizeInBytes = headerResponse.Content.Headers.ContentLength; - var currentSize = new FileInfo(fileName).Length; - - //If current file size is not equal to expected file size, re-download file - if (currentSize != totalSizeInBytes) + if (File.Exists(fileName)) + { + var task = client.GetAsync(address, HttpCompletionOption.ResponseHeadersRead); + task.Wait(); + var headerResponse = task.Result; + var totalSizeInBytes = headerResponse.Content.Headers.ContentLength; + var currentSize = new FileInfo(fileName).Length; + + //If current file size is not equal to expected file size, re-download file + if (currentSize != totalSizeInBytes) + { + File.Delete(fileName); + task = client.GetAsync(address); + task.Wait(); + var response = task.Result; + var contentTask = response.Content.ReadAsStreamAsync(); + contentTask.Wait(); + using FileStream fileStream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None); + using Stream contentStream = contentTask.Result; + { + contentStream.CopyTo(fileStream); + } + } + } + else { - File.Delete(fileName); - var response = client.GetAsync(address).Result; + var task = client.GetAsync(address); + task.Wait(); + var response = task.Result; + var contentTask = response.Content.ReadAsStreamAsync(); + contentTask.Wait(); using FileStream fileStream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None); - using Stream contentStream = response.Content.ReadAsStreamAsync().Result; - contentStream.CopyTo(fileStream); + using Stream contentStream = contentTask.Result; + { + contentStream.CopyTo(fileStream); + } } } - else - { - var response = client.GetAsync(address).Result; - using FileStream fileStream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None); - using Stream contentStream = response.Content.ReadAsStreamAsync().Result; - contentStream.CopyTo(fileStream); - } } internal static Graph LoadMetaGraph(string path) From 406ef4993e30e812f7a0235f1966dace5d4559e7 Mon Sep 17 00:00:00 2001 From: Zeeshan Siddiqui Date: Sun, 27 Oct 2019 17:40:13 -0700 Subject: [PATCH 12/15] clean up. --- src/Microsoft.ML.Dnn/DnnUtils.cs | 33 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/src/Microsoft.ML.Dnn/DnnUtils.cs b/src/Microsoft.ML.Dnn/DnnUtils.cs index 8dbfe823f0..2f2010161d 100644 --- a/src/Microsoft.ML.Dnn/DnnUtils.cs +++ b/src/Microsoft.ML.Dnn/DnnUtils.cs @@ -10,6 +10,7 @@ using System.Net.Http; using System.Security.AccessControl; using System.Security.Principal; +using System.Threading.Tasks; using Microsoft.ML.Data; using Microsoft.ML.Runtime; using Tensorflow; @@ -93,15 +94,13 @@ internal static Session LoadTFSession(IExceptionContext ectx, byte[] modelBytes, return new Session(graph); } - internal static void DownloadIfNeeded(Uri address, string fileName) + internal static async Task DownloadIfNeeded(Uri address, string fileName) { using (var client = new HttpClient()) { if (File.Exists(fileName)) { - var task = client.GetAsync(address, HttpCompletionOption.ResponseHeadersRead); - task.Wait(); - var headerResponse = task.Result; + var headerResponse = await client.GetAsync(address, HttpCompletionOption.ResponseHeadersRead); var totalSizeInBytes = headerResponse.Content.Headers.ContentLength; var currentSize = new FileInfo(fileName).Length; @@ -109,13 +108,9 @@ internal static void DownloadIfNeeded(Uri address, string fileName) if (currentSize != totalSizeInBytes) { File.Delete(fileName); - task = client.GetAsync(address); - task.Wait(); - var response = task.Result; - var contentTask = response.Content.ReadAsStreamAsync(); - contentTask.Wait(); + var response = await client.GetAsync(address); using FileStream fileStream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None); - using Stream contentStream = contentTask.Result; + using Stream contentStream = await response.Content.ReadAsStreamAsync(); { contentStream.CopyTo(fileStream); } @@ -123,13 +118,9 @@ internal static void DownloadIfNeeded(Uri address, string fileName) } else { - var task = client.GetAsync(address); - task.Wait(); - var response = task.Result; - var contentTask = response.Content.ReadAsStreamAsync(); - contentTask.Wait(); + var response = await client.GetAsync(address); using FileStream fileStream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None); - using Stream contentStream = contentTask.Result; + using Stream contentStream = await response.Content.ReadAsStreamAsync(); { contentStream.CopyTo(fileStream); } @@ -314,27 +305,27 @@ internal static DnnModel LoadDnnModel(IHostEnvironment env, Architecture arch, b if (arch == ImageClassificationTrainer.Architecture.InceptionV3) { var baseGitPath = @"https://raw.githubusercontent.com/SciSharp/TensorFlow.NET/master/graph/InceptionV3.meta"; - DownloadIfNeeded(new Uri($"{baseGitPath}"), @"InceptionV3.meta"); + DownloadIfNeeded(new Uri($"{baseGitPath}"), @"InceptionV3.meta").Wait(); baseGitPath = @"https://github.com/SciSharp/TensorFlow.NET/raw/master/data/tfhub_modules.zip"; - DownloadIfNeeded(new Uri($"{baseGitPath}"), @"tfhub_modules.zip"); + DownloadIfNeeded(new Uri($"{baseGitPath}"), @"tfhub_modules.zip").Wait(); if (!Directory.Exists(@"tfhub_modules")) ZipFile.ExtractToDirectory(Path.Combine(Directory.GetCurrentDirectory(), @"tfhub_modules.zip"), @"tfhub_modules"); } else if (arch == ImageClassificationTrainer.Architecture.ResnetV2101) { var baseGitPath = @"https://aka.ms/mlnet-resources/image/ResNet101Tensorflow/resnet_v2_101_299.meta"; - DownloadIfNeeded(new Uri($"{baseGitPath}"), @"resnet_v2_101_299.meta"); + DownloadIfNeeded(new Uri($"{baseGitPath}"), @"resnet_v2_101_299.meta").Wait(); } else if (arch == ImageClassificationTrainer.Architecture.MobilenetV2) { var baseGitPath = @"https://tlcresources.blob.core.windows.net/image/MobileNetV2TensorFlow/mobilenet_v2.meta"; - DownloadIfNeeded(new Uri($"{baseGitPath}"), @"mobilenet_v2.meta"); + DownloadIfNeeded(new Uri($"{baseGitPath}"), @"mobilenet_v2.meta").Wait(); } else if (arch == ImageClassificationTrainer.Architecture.ResnetV250) { var baseGitPath = @"https://tlcresources.blob.core.windows.net/image/ResNetV250TensorFlow/resnet_v2_50_299.meta"; - DownloadIfNeeded(new Uri($"{baseGitPath}"), @"resnet_v2_50_299.meta"); + DownloadIfNeeded(new Uri($"{baseGitPath}"), @"resnet_v2_50_299.meta").Wait(); } return new DnnModel(GetSession(env, modelPath, metaGraph), modelPath); From e4e53ecfde53e679ddfd8ac2e67379de14c89bd9 Mon Sep 17 00:00:00 2001 From: Zeeshan Siddiqui Date: Sun, 27 Oct 2019 18:54:55 -0700 Subject: [PATCH 13/15] Use resource manager to download meta files. --- src/Microsoft.ML.Dnn/DnnUtils.cs | 70 +++++-------------- .../ImageClassificationTrainer.cs | 10 +-- 2 files changed, 24 insertions(+), 56 deletions(-) diff --git a/src/Microsoft.ML.Dnn/DnnUtils.cs b/src/Microsoft.ML.Dnn/DnnUtils.cs index 2f2010161d..b9e1690d2a 100644 --- a/src/Microsoft.ML.Dnn/DnnUtils.cs +++ b/src/Microsoft.ML.Dnn/DnnUtils.cs @@ -12,6 +12,7 @@ using System.Security.Principal; using System.Threading.Tasks; using Microsoft.ML.Data; +using Microsoft.ML.Internal.Utilities; using Microsoft.ML.Runtime; using Tensorflow; using static Microsoft.ML.Dnn.ImageClassificationTrainer; @@ -94,36 +95,19 @@ internal static Session LoadTFSession(IExceptionContext ectx, byte[] modelBytes, return new Session(graph); } - internal static async Task DownloadIfNeeded(Uri address, string fileName) + internal static void DownloadIfNeeded(IHostEnvironment env, string url, string dir, string fileName, int timeout) { - using (var client = new HttpClient()) + using (var ch = env.Start("Ensuring meta files are present.")) { - if (File.Exists(fileName)) + var ensureModel = ResourceManagerUtils.Instance.EnsureResource(env, ch, url, fileName, dir, timeout); + ensureModel.Wait(); + var errorResult = ResourceManagerUtils.GetErrorMessage(out var errorMessage, ensureModel.Result); + if (errorResult != null) { - var headerResponse = await client.GetAsync(address, HttpCompletionOption.ResponseHeadersRead); - var totalSizeInBytes = headerResponse.Content.Headers.ContentLength; - var currentSize = new FileInfo(fileName).Length; - - //If current file size is not equal to expected file size, re-download file - if (currentSize != totalSizeInBytes) - { - File.Delete(fileName); - var response = await client.GetAsync(address); - using FileStream fileStream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None); - using Stream contentStream = await response.Content.ReadAsStreamAsync(); - { - contentStream.CopyTo(fileStream); - } - } - } - else - { - var response = await client.GetAsync(address); - using FileStream fileStream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None); - using Stream contentStream = await response.Content.ReadAsStreamAsync(); - { - contentStream.CopyTo(fileStream); - } + var directory = Path.GetDirectoryName(errorResult.FileName); + var name = Path.GetFileName(errorResult.FileName); + throw ch.Except($"{errorMessage}\nMeta file could not be downloaded! " + + $@"Please copy the model file '{name}' from '{url}' to '{directory}'."); } } } @@ -301,34 +285,18 @@ internal static DnnModel LoadDnnModel(IHostEnvironment env, string modelPath, bo internal static DnnModel LoadDnnModel(IHostEnvironment env, Architecture arch, bool metaGraph = false) { - var modelPath = ImageClassificationTrainer.ModelLocation[arch]; - if (arch == ImageClassificationTrainer.Architecture.InceptionV3) + var modelFileName = ModelFileName[arch]; + int timeout = 10 * 60 * 1000; + string currentDirectory = Directory.GetCurrentDirectory(); + DownloadIfNeeded(env, modelFileName, currentDirectory, modelFileName, timeout); + if (arch == Architecture.InceptionV3) { - var baseGitPath = @"https://raw.githubusercontent.com/SciSharp/TensorFlow.NET/master/graph/InceptionV3.meta"; - DownloadIfNeeded(new Uri($"{baseGitPath}"), @"InceptionV3.meta").Wait(); - - baseGitPath = @"https://github.com/SciSharp/TensorFlow.NET/raw/master/data/tfhub_modules.zip"; - DownloadIfNeeded(new Uri($"{baseGitPath}"), @"tfhub_modules.zip").Wait(); + DownloadIfNeeded(env, @"tfhub_modules.zip",currentDirectory,@"tfhub_modules.zip",timeout); if (!Directory.Exists(@"tfhub_modules")) - ZipFile.ExtractToDirectory(Path.Combine(Directory.GetCurrentDirectory(), @"tfhub_modules.zip"), @"tfhub_modules"); - } - else if (arch == ImageClassificationTrainer.Architecture.ResnetV2101) - { - var baseGitPath = @"https://aka.ms/mlnet-resources/image/ResNet101Tensorflow/resnet_v2_101_299.meta"; - DownloadIfNeeded(new Uri($"{baseGitPath}"), @"resnet_v2_101_299.meta").Wait(); - } - else if (arch == ImageClassificationTrainer.Architecture.MobilenetV2) - { - var baseGitPath = @"https://tlcresources.blob.core.windows.net/image/MobileNetV2TensorFlow/mobilenet_v2.meta"; - DownloadIfNeeded(new Uri($"{baseGitPath}"), @"mobilenet_v2.meta").Wait(); - } - else if (arch == ImageClassificationTrainer.Architecture.ResnetV250) - { - var baseGitPath = @"https://tlcresources.blob.core.windows.net/image/ResNetV250TensorFlow/resnet_v2_50_299.meta"; - DownloadIfNeeded(new Uri($"{baseGitPath}"), @"resnet_v2_50_299.meta").Wait(); + ZipFile.ExtractToDirectory(Path.Combine(currentDirectory, @"tfhub_modules.zip"), @"tfhub_modules"); } - return new DnnModel(GetSession(env, modelPath, metaGraph), modelPath); + return new DnnModel(GetSession(env, modelFileName, metaGraph), modelFileName); } internal static Session GetSession(IHostEnvironment env, string modelPath, bool metaGraph = false) diff --git a/src/Microsoft.ML.Dnn/ImageClassificationTrainer.cs b/src/Microsoft.ML.Dnn/ImageClassificationTrainer.cs index 2cbcd5b8ca..e70cc53251 100644 --- a/src/Microsoft.ML.Dnn/ImageClassificationTrainer.cs +++ b/src/Microsoft.ML.Dnn/ImageClassificationTrainer.cs @@ -91,10 +91,10 @@ public enum Architecture /// /// Dictionary mapping model architecture to model location. /// - internal static IReadOnlyDictionary ModelLocation = new Dictionary + internal static IReadOnlyDictionary ModelFileName = new Dictionary { { Architecture.ResnetV2101, @"resnet_v2_101_299.meta" }, - { Architecture.InceptionV3, @"InceptionV3.meta" }, + { Architecture.InceptionV3, @"inception_v3.meta" }, { Architecture.MobilenetV2, @"mobilenet_v2.meta" }, { Architecture.ResnetV250, @"resnet_v2_50_299.meta" } }; @@ -514,11 +514,11 @@ internal ImageClassificationTrainer(IHostEnvironment env, Options options) Host.CheckNonEmpty(options.PredictedLabelColumnName, nameof(options.PredictedLabelColumnName)); _options = options; - _session = DnnUtils.LoadDnnModel(env, _options.Arch, true).Session; + _session = LoadDnnModel(env, _options.Arch, true).Session; _useLRScheduling = _options.LearningRateScheduler != null; _checkpointPath = _options.ModelSavePath ?? Path.Combine(Directory.GetCurrentDirectory(), _options.FinalModelPrefix + - ModelLocation[_options.Arch]); + ModelFileName[_options.Arch]); // Configure bottleneck tensor based on the model. var arch = _options.Arch; @@ -1093,7 +1093,7 @@ private void TrainAndEvaluateClassificationLayer(string trainBottleneckFilePath, private (Session, Tensor, Tensor, Tensor) BuildEvaluationSession(int classCount) { - var evalGraph = DnnUtils.LoadMetaGraph(ModelLocation[_options.Arch]); + var evalGraph = DnnUtils.LoadMetaGraph(ModelFileName[_options.Arch]); var evalSess = tf.Session(graph: evalGraph); Tensor evaluationStep = null; Tensor prediction = null; From 0124b71317d5999b9082439536da0a389af220b3 Mon Sep 17 00:00:00 2001 From: Zeeshan Siddiqui Date: Sun, 27 Oct 2019 18:58:35 -0700 Subject: [PATCH 14/15] remove test. --- .../TensorflowTests.cs | 99 ------------------- 1 file changed, 99 deletions(-) diff --git a/test/Microsoft.ML.Tests/ScenariosWithDirectInstantiation/TensorflowTests.cs b/test/Microsoft.ML.Tests/ScenariosWithDirectInstantiation/TensorflowTests.cs index 95483496c1..d767c79c41 100644 --- a/test/Microsoft.ML.Tests/ScenariosWithDirectInstantiation/TensorflowTests.cs +++ b/test/Microsoft.ML.Tests/ScenariosWithDirectInstantiation/TensorflowTests.cs @@ -1755,105 +1755,6 @@ public void TensorFlowImageClassificationEarlyStoppingDecreasing() Assert.InRange(lastEpoch, 1, 49); } - [TensorFlowTheory] - [InlineData(ImageClassificationTrainer.Architecture.ResnetV2101)] - [InlineData(ImageClassificationTrainer.Architecture.InceptionV3)] - public void TensorflowRedownloadModelFile(ImageClassificationTrainer.Architecture arch) - { - string assetsRelativePath = @"assets"; - string assetsPath = GetAbsolutePath(assetsRelativePath); - string imagesDownloadFolderPath = Path.Combine(assetsPath, "inputs", - "images"); - - //Download the image set and unzip - string finalImagesFolderName = DownloadImageSet( - imagesDownloadFolderPath); - - string fullImagesetFolderPath = Path.Combine( - imagesDownloadFolderPath, finalImagesFolderName); - - MLContext mlContext = new MLContext(seed: 1); - - //Load all the original images info - IEnumerable images = LoadImagesFromDirectory( - folder: fullImagesetFolderPath, useFolderNameAsLabel: true); - - IDataView shuffledFullImagesDataset = mlContext.Data.ShuffleRows( - mlContext.Data.LoadFromEnumerable(images), seed: 1); - - shuffledFullImagesDataset = mlContext.Transforms.Conversion - .MapValueToKey("Label") - .Fit(shuffledFullImagesDataset) - .Transform(shuffledFullImagesDataset); - - // Split the data 80:10 into train and test sets, train and evaluate. - TrainTestData trainTestData = mlContext.Data.TrainTestSplit( - shuffledFullImagesDataset, testFraction: 0.2, seed: 1); - - IDataView trainDataset = trainTestData.TrainSet; - IDataView testDataset = trainTestData.TestSet; - var validationSet = mlContext.Transforms.LoadRawImageBytes("Image", fullImagesetFolderPath, "ImagePath") // false indicates we want the image as a VBuffer - .Fit(testDataset) - .Transform(testDataset); - - //If model file exists, delete it and create empty file - //with the same name as model file to - //simulate incomplete model file scenario. - if (arch == ImageClassificationTrainer.Architecture.ResnetV2101) - { - - if (File.Exists(@"resnet_v2_101_299.meta")) - File.Delete(@"resnet_v2_101_299.meta"); - - using (File.Create(@"resnet_v2_101_299.meta")) { } - } - else if(arch == ImageClassificationTrainer.Architecture.InceptionV3) - { - if (File.Exists(@"tfhub_modules.zip")) - File.Delete(@"tfhub_modules.zip"); - if (Directory.Exists(@"tfhub_modules")) - Directory.Delete(@"tfhub_modules", true); - if (File.Exists(@"InceptionV3.meta")) - File.Delete(@"InceptionV3.meta"); - - using (File.Create(@"InceptionV3.meta")) { } - } - - var options = new ImageClassificationTrainer.Options() - { - FeatureColumnName = "Image", - LabelColumnName = "Label", - Arch = arch, - Epoch = 1, - BatchSize = 10, - MetricsCallback = (metrics) => Console.WriteLine(metrics), - TestOnTrainSet = false, - EarlyStoppingCriteria = null - }; - - //Create pipeline and run - var pipeline = mlContext.Transforms.LoadRawImageBytes("Image", fullImagesetFolderPath, "ImagePath") // false indicates we want the image as a VBuffer - .Append(mlContext.MulticlassClassification.Trainers.ImageClassification(options) - .Append(mlContext.Transforms.Conversion.MapKeyToValue(outputColumnName: "PredictedLabel", inputColumnName: "PredictedLabel"))); - - var trainedModel = pipeline.Fit(trainDataset); - - mlContext.Model.Save(trainedModel, shuffledFullImagesDataset.Schema, - "model.zip"); - - ITransformer loadedModel; - DataViewSchema schema; - using (var file = File.OpenRead("model.zip")) - loadedModel = mlContext.Model.Load(file, out schema); - - // Testing EvaluateModel: group testing on test dataset - IDataView predictions = trainedModel.Transform(testDataset); - var metrics = mlContext.MulticlassClassification.Evaluate(predictions); - - // Accuracy should be returned, indicating training loop ran successfully. - Assert.InRange(metrics.MicroAccuracy, 0.1, 1); - } - public static IEnumerable LoadImagesFromDirectory(string folder, bool useFolderNameAsLabel = true) { From e9f63f25cd9d3a45ee46bb208becc3c58c7d3b69 Mon Sep 17 00:00:00 2001 From: Zeeshan Siddiqui Date: Sun, 27 Oct 2019 19:10:35 -0700 Subject: [PATCH 15/15] remove unused namespaces. --- src/Microsoft.ML.Dnn/DnnUtils.cs | 2 -- .../ScenariosWithDirectInstantiation/TensorflowTests.cs | 1 - 2 files changed, 3 deletions(-) diff --git a/src/Microsoft.ML.Dnn/DnnUtils.cs b/src/Microsoft.ML.Dnn/DnnUtils.cs index b9e1690d2a..b63fe0eaf8 100644 --- a/src/Microsoft.ML.Dnn/DnnUtils.cs +++ b/src/Microsoft.ML.Dnn/DnnUtils.cs @@ -7,10 +7,8 @@ using System.IO; using System.IO.Compression; using System.Linq; -using System.Net.Http; using System.Security.AccessControl; using System.Security.Principal; -using System.Threading.Tasks; using Microsoft.ML.Data; using Microsoft.ML.Internal.Utilities; using Microsoft.ML.Runtime; diff --git a/test/Microsoft.ML.Tests/ScenariosWithDirectInstantiation/TensorflowTests.cs b/test/Microsoft.ML.Tests/ScenariosWithDirectInstantiation/TensorflowTests.cs index d767c79c41..5d82c4dd1b 100644 --- a/test/Microsoft.ML.Tests/ScenariosWithDirectInstantiation/TensorflowTests.cs +++ b/test/Microsoft.ML.Tests/ScenariosWithDirectInstantiation/TensorflowTests.cs @@ -17,7 +17,6 @@ using Microsoft.ML.Transforms; using Microsoft.ML.Transforms.Image; using Microsoft.ML.Transforms.TensorFlow; -using Tensorflow; using Xunit; using Xunit.Abstractions; using static Microsoft.ML.DataOperationsCatalog;