Skip to content

Commit

Permalink
Image Classification Sample cleanup (#4380)
Browse files Browse the repository at this point in the history
* Aligned line ending to 85 char limit

* Cleaned up Early Stopping sample

* Added comments to ResnetV2101TransferLearningTrainTestSplit

* Added comments to ResnetV2101TransferLearningEarlyStopping

* Add comments to ImageClassificationDefault sample

* Added comments to LearningRateSchedulingCifarResnetTransferLearning sample

* added '.', formatted output.

* fixed sample after rebase

* fix ealy stopping sample

* fix GetAbsolutePath function for samples.

* remove unnecessary try catch block from samples.

* review comments.
  • Loading branch information
ashbhandare authored and codemzs committed Nov 7, 2019
1 parent ab1dc25 commit 0b9308e
Show file tree
Hide file tree
Showing 4 changed files with 464 additions and 406 deletions.
Expand Up @@ -17,95 +17,97 @@ public class ImageClassificationDefault
{
public static void Example()
{
// Set the path for input images.
string assetsRelativePath = @"../../../assets";
string assetsPath = GetAbsolutePath(assetsRelativePath);

var outputMlNetModelFilePath = Path.Combine(assetsPath, "outputs",
"imageClassifier.zip");

string imagesDownloadFolderPath = Path.Combine(assetsPath, "inputs",
"images");

//Download the image set and unzip
//Download the image set and unzip, set the path to image folder.
string finalImagesFolderName = DownloadImageSet(
imagesDownloadFolderPath);

string fullImagesetFolderPath = Path.Combine(
imagesDownloadFolderPath, finalImagesFolderName);

try
{

MLContext mlContext = new MLContext(seed: 1);
mlContext.Log += MlContext_Log;

//Load all the original images info
IEnumerable<ImageData> images = LoadImagesFromDirectory(
folder: fullImagesetFolderPath, useFolderNameAsLabel: true);

IDataView shuffledFullImagesDataset = mlContext.Data.ShuffleRows(
mlContext.Data.LoadFromEnumerable(images));

shuffledFullImagesDataset = mlContext.Transforms.Conversion
.MapValueToKey("Label")
.Append(mlContext.Transforms.LoadRawImageBytes("Image",
fullImagesetFolderPath, "ImagePath"))
.Fit(shuffledFullImagesDataset)
.Transform(shuffledFullImagesDataset);

// Split the data 90:10 into train and test sets, train and
// evaluate.
TrainTestData trainTestData = mlContext.Data.TrainTestSplit(
shuffledFullImagesDataset, testFraction: 0.1, seed: 1);

IDataView trainDataset = trainTestData.TrainSet;
IDataView testDataset = trainTestData.TestSet;

var pipeline = mlContext.MulticlassClassification.Trainers
.ImageClassification(featureColumnName:"Image")
.Append(mlContext.Transforms.Conversion.MapKeyToValue(outputColumnName: "PredictedLabel",
inputColumnName: "PredictedLabel"));

Console.WriteLine("*** Training the image classification model " +
"with DNN Transfer Learning on top of the selected " +
"pre-trained model/architecture ***");

// Measuring training time
var watch = System.Diagnostics.Stopwatch.StartNew();

var trainedModel = pipeline.Fit(trainDataset);

watch.Stop();
long elapsedMs = watch.ElapsedMilliseconds;

Console.WriteLine("Training with transfer learning took: " +
(elapsedMs / 1000).ToString() + " seconds");

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);

EvaluateModel(mlContext, testDataset, loadedModel);

watch = System.Diagnostics.Stopwatch.StartNew();

// Predict image class using an in-memory image.
TrySinglePrediction(fullImagesetFolderPath, mlContext, loadedModel);

watch.Stop();
elapsedMs = watch.ElapsedMilliseconds;

Console.WriteLine("Prediction engine took: " +
(elapsedMs / 1000).ToString() + " seconds");
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
MLContext mlContext = new MLContext(seed: 1);
mlContext.Log += MlContext_Log;

// Load all the original images info
IEnumerable<ImageData> images = LoadImagesFromDirectory(
folder: fullImagesetFolderPath, useFolderNameAsLabel: true);

// Shuffle images.
IDataView shuffledFullImagesDataset = mlContext.Data.ShuffleRows(
mlContext.Data.LoadFromEnumerable(images));

// Apply transforms to the input dataset:
// MapValueToKey : map 'string' type labels to keys
// LoadImages : load raw images to "Image" column
shuffledFullImagesDataset = mlContext.Transforms.Conversion
.MapValueToKey("Label", keyOrdinality: Microsoft.ML.Transforms
.ValueToKeyMappingEstimator.KeyOrdinality.ByValue)
.Append(mlContext.Transforms.LoadRawImageBytes("Image",
fullImagesetFolderPath, "ImagePath"))
.Fit(shuffledFullImagesDataset)
.Transform(shuffledFullImagesDataset);

// Split the data 90:10 into train and test sets.
TrainTestData trainTestData = mlContext.Data.TrainTestSplit(
shuffledFullImagesDataset, testFraction: 0.1, seed: 1);

IDataView trainDataset = trainTestData.TrainSet;
IDataView testDataset = trainTestData.TestSet;

// Create the ImageClassification pipeline by just passing the
// input feature and label column name.
var pipeline = mlContext.MulticlassClassification.Trainers
.ImageClassification(featureColumnName:"Image")
.Append(mlContext.Transforms.Conversion.MapKeyToValue(
outputColumnName: "PredictedLabel",
inputColumnName: "PredictedLabel"));

Console.WriteLine("*** Training the image classification model " +
"with DNN Transfer Learning on top of the selected " +
"pre-trained model/architecture ***");

// Train the model.
// This involves calculating the bottleneck values, and then
// training the final layerSample output is:
// [Source=ImageClassificationTrainer; ImageClassificationTrainer, Kind=Trace] Phase: Bottleneck Computation, Dataset used: Train, Image Index: 1
// [Source=ImageClassificationTrainer; ImageClassificationTrainer, Kind=Trace] Phase: Bottleneck Computation, Dataset used: Train, Image Index: 2
// ...
// [Source=ImageClassificationTrainer; ImageClassificationTrainer, Kind=Trace] Phase: Training, Dataset used: Train, Batch Processed Count: 18, Learning Rate: 0.01 Epoch: 0, Accuracy: 0.9, Cross-Entropy: 0.481340
// ...
// [Source=ImageClassificationTrainer; ImageClassificationTrainer, Kind=Trace] Phase: Training, Dataset used: Train, Batch Processed Count: 18, Learning Rate: 0.004759203 Epoch: 25, Accuracy: 1, Cross-Entropy: 0.04848097
// [Source=ImageClassificationTrainer; ImageClassificationTrainer, Kind=Trace] Phase: Training, Dataset used: Train, Batch Processed Count: 18, Learning Rate: 0.004473651 Epoch: 26, Accuracy: 1, Cross-Entropy: 0.04930306
var trainedModel = pipeline.Fit(trainDataset);

Console.WriteLine("Training with transfer learning finished.");

// Save the trained model.
mlContext.Model.Save(trainedModel, shuffledFullImagesDataset.Schema,
"model.zip");

// Load the trained and saved model for prediction.
ITransformer loadedModel;
DataViewSchema schema;
using (var file = File.OpenRead("model.zip"))
loadedModel = mlContext.Model.Load(file, out schema);

// Evaluate the model on the test dataset.
// Sample output:
// Making bulk predictions and evaluating model's quality...
// Micro-accuracy: 0.925925925925926,macro-accuracy = 0.933333333333333
EvaluateModel(mlContext, testDataset, loadedModel);

// Predict on a single image class using an in-memory image.
// Sample output:
// Scores : [0.8657553,0.006911285,1.46484E-05,0.1266835,0.0006352618], Predicted Label : daisy
TrySinglePrediction(fullImagesetFolderPath, mlContext, loadedModel);

Console.WriteLine("Prediction on a single image finished.");

Console.WriteLine("Press any key to finish");
Console.ReadKey();
Expand All @@ -119,50 +121,50 @@ private static void MlContext_Log(object sender, LoggingEventArgs e)
}
}

// Predict on a single image.
private static void TrySinglePrediction(string imagesForPredictions,
MLContext mlContext, ITransformer trainedModel)
{
// Create prediction function to try one prediction
// Create prediction function to try one prediction.
var predictionEngine = mlContext.Model
.CreatePredictionEngine<InMemoryImageData, ImagePrediction>(trainedModel);
.CreatePredictionEngine<InMemoryImageData,
ImagePrediction>(trainedModel);

IEnumerable<InMemoryImageData> testImages = LoadInMemoryImagesFromDirectory(
imagesForPredictions, false);
// Load test images.
IEnumerable<InMemoryImageData> testImages =
LoadInMemoryImagesFromDirectory(imagesForPredictions, false);

// Create an in-memory image object from the first image in the test data.
InMemoryImageData imageToPredict = new InMemoryImageData
{
Image = testImages.First().Image
};

// Predict on the single image.
var prediction = predictionEngine.Predict(imageToPredict);

Console.WriteLine($"Scores : [{string.Join(",", prediction.Score)}], " +
$"Predicted Label : {prediction.PredictedLabel}");
}


// Evaluate the trained model on the passed test dataset.
private static void EvaluateModel(MLContext mlContext,
IDataView testDataset, ITransformer trainedModel)
{
Console.WriteLine("Making bulk predictions and evaluating model's " +
"quality...");

// Measuring time
var watch2 = System.Diagnostics.Stopwatch.StartNew();

// Evaluate the model on the test data and get the evaluation metrics.
IDataView predictions = trainedModel.Transform(testDataset);
var metrics = mlContext.MulticlassClassification.Evaluate(predictions);

Console.WriteLine($"Micro-accuracy: {metrics.MicroAccuracy}," +
$"macro-accuracy = {metrics.MacroAccuracy}");

watch2.Stop();
long elapsed2Ms = watch2.ElapsedMilliseconds;

Console.WriteLine("Predicting and Evaluation took: " +
(elapsed2Ms / 1000).ToString() + " seconds");
Console.WriteLine("Predicting and Evaluation complete.");
}

//Load the Image Data from input directory.
public static IEnumerable<ImageData> LoadImagesFromDirectory(string folder,
bool useFolderNameAsLabel = true)
{
Expand Down Expand Up @@ -197,8 +199,9 @@ private static void MlContext_Log(object sender, LoggingEventArgs e)
}
}

public static IEnumerable<InMemoryImageData>
LoadInMemoryImagesFromDirectory(string folder,
// Load In memory raw images from directory.
public static IEnumerable<InMemoryImageData>
LoadInMemoryImagesFromDirectory(string folder,
bool useFolderNameAsLabel = true)
{
var files = Directory.GetFiles(folder, "*",
Expand Down Expand Up @@ -232,6 +235,7 @@ private static void MlContext_Log(object sender, LoggingEventArgs e)
}
}

// Download and unzip the image dataset.
public static string DownloadImageSet(string imagesDownloadFolder)
{
// get a set of images to teach the network about the new classes
Expand All @@ -249,6 +253,7 @@ public static string DownloadImageSet(string imagesDownloadFolder)
return Path.GetFileNameWithoutExtension(fileName);
}

// Download file to destination directory from input URL.
public static bool Download(string url, string destDir, string destFileName)
{
if (destFileName == null)
Expand Down Expand Up @@ -278,6 +283,7 @@ public static bool Download(string url, string destDir, string destFileName)
return true;
}

// Unzip the file to destination folder.
public static void UnZip(String gzArchiveName, String destFolder)
{
var flag = gzArchiveName.Split(Path.DirectorySeparatorChar)
Expand All @@ -289,16 +295,17 @@ public static void UnZip(String gzArchiveName, String destFolder)

Console.WriteLine($"Extracting.");
ZipFile.ExtractToDirectory(gzArchiveName, destFolder);

File.Create(Path.Combine(destFolder, flag));
Console.WriteLine("");
Console.WriteLine("Extracting is completed.");
}

// Get absolute path from relative path.
public static string GetAbsolutePath(string relativePath)
{
FileInfo _dataRoot = new FileInfo(typeof(
ResnetV2101TransferLearningTrainTestSplit).Assembly.Location);
ImageClassificationDefault).Assembly.Location);

string assemblyFolderPath = _dataRoot.Directory.FullName;

Expand All @@ -307,6 +314,7 @@ public static string GetAbsolutePath(string relativePath)
return fullPath;
}

// InMemoryImageData class holding the raw image byte array and label.
public class InMemoryImageData
{
[LoadColumn(0)]
Expand All @@ -316,6 +324,7 @@ public class InMemoryImageData
public string Label;
}

// ImageData class holding the imagepath and label.
public class ImageData
{
[LoadColumn(0)]
Expand All @@ -325,6 +334,7 @@ public class ImageData
public string Label;
}

// ImagePrediction class holding the score and predicted label metrics.
public class ImagePrediction
{
[ColumnName("Score")]
Expand Down

0 comments on commit 0b9308e

Please sign in to comment.