Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions machine-learning/tutorials/TransferLearningTF.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.28307.271
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TransferLearningTF", "TransferLearningTF\TransferLearningTF.csproj", "{50CB6FC0-9A1A-4219-AFBD-9E5ACD6B17AA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{50CB6FC0-9A1A-4219-AFBD-9E5ACD6B17AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{50CB6FC0-9A1A-4219-AFBD-9E5ACD6B17AA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{50CB6FC0-9A1A-4219-AFBD-9E5ACD6B17AA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{50CB6FC0-9A1A-4219-AFBD-9E5ACD6B17AA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {1F2992B4-389D-4FEA-B6F0-65684DCF7EBE}
EndGlobalSection
EndGlobal
16 changes: 16 additions & 0 deletions machine-learning/tutorials/TransferLearningTF/ImageData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// <SnippetAddUsings>
using Microsoft.ML.Data;
// </SnippetAddUsings>
namespace TransferLearningTF
{
// <SnippetDeclareTypes>
public class ImageData
{
[LoadColumn(0)]
public string ImagePath;

[LoadColumn(1)]
public string Label;
}
// <SnippetDeclareTypes>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;

namespace TransferLearningTF
{
// <SnippetDeclareTypes>
public class ImagePrediction
{
public float[] Score;

public string PredictedLabelValue;
}
// </SnippetDeclareTypes>
}
206 changes: 206 additions & 0 deletions machine-learning/tutorials/TransferLearningTF/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
// <SnippetAddUsings>
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.Data.DataView;
using Microsoft.ML;
using Microsoft.ML.Core.Data;
using Microsoft.ML.Data;
using Microsoft.ML.ImageAnalytics;
using Microsoft.ML.Trainers;
// </SnippetAddUsings>

namespace TransferLearningTF
{
class Program
{
// <SnippetDeclareGlobalVariables>
static readonly string _assetsPath = Path.Combine(Environment.CurrentDirectory, "assets");
static readonly string _trainTagsTsv = Path.Combine(_assetsPath, "inputs-train", "data", "tags.tsv");
static readonly string _predictTagsTsv = Path.Combine(_assetsPath, "inputs-predict", "data", "tags.tsv");
static readonly string _trainImagesFolder = Path.Combine(_assetsPath, "inputs-train", "data");
static readonly string _predictImagesFolder = Path.Combine(_assetsPath, "inputs-predict", "data");
static readonly string _inceptionPb = Path.Combine(_assetsPath, "inputs-train", "inception", "tensorflow_inception_graph.pb");
static readonly string _inputImageClassifierZip = Path.Combine(_assetsPath, "inputs-predict", "imageClassifier.zip");
static readonly string _outputImageClassifierZip = Path.Combine(_assetsPath, "outputs", "imageClassifier.zip");
private static string LabelTokey = nameof(LabelTokey);
private static string ImageReal = nameof(ImageReal);
private static string PredictedLabelValue = nameof(PredictedLabelValue);
// </SnippetDeclareGlobalVariables>

static void Main(string[] args)
{
// Create MLContext to be shared across the model creation workflow objects
// <SnippetCreateMLContext>
MLContext mlContext = new MLContext(seed:1);
// </SnippetCreateMLContext>

// <SnippetCallReuseAndTuneInceptionModel>
ReuseAndTuneInceptionModel(mlContext, _trainTagsTsv, _trainImagesFolder, _inceptionPb, _outputImageClassifierZip);
// </CallSnippetReuseAndTuneInceptionModel>

// <SnippetCallClassifyImages>
ClassifyImages(mlContext, _predictTagsTsv, _predictImagesFolder, _outputImageClassifierZip);
// </SnippetCallClassifyImages>
}

// <SnippetInceptionSettings>
private struct InceptionSettings
{
public const int ImageHeight = 224;
public const int ImageWidth = 224;
public const float Mean = 117;
public const float Scale = 1;
public const bool ChannelsLast = true;
}
// </SnippetInceptionSettings>

// Build and train model
public static void ReuseAndTuneInceptionModel(MLContext mlContext, string dataLocation, string imagesFolder, string inputModelLocation, string outputModelLocation)
{

Console.WriteLine("Read model");
Console.WriteLine($"Model location: {inputModelLocation}");
Console.WriteLine($"Images folder: {_trainImagesFolder}");
Console.WriteLine($"Training file: {dataLocation}");
Console.WriteLine($"Default parameters: image size=({InceptionSettings.ImageWidth},{InceptionSettings.ImageHeight}), image mean: {InceptionSettings.Mean}");

// <SnippetLoadData>
var data = mlContext.Data.ReadFromTextFile<ImageData>(path: dataLocation, hasHeader: true);
// </SnippetLoadData>

// <SnippetMapValueToKey1>
var estimator = mlContext.Transforms.Conversion.MapValueToKey(outputColumnName: LabelTokey, inputColumnName: DefaultColumnNames.Label)
// </SnippetMapValueToKey1>
// The image transforms transform the images into the model's expected format.
// <SnippetImageTransforms>
.Append(mlContext.Transforms.LoadImages(_trainImagesFolder, (ImageReal, nameof(ImageData.ImagePath))))
.Append(mlContext.Transforms.Resize(outputColumnName: ImageReal, imageWidth: InceptionSettings.ImageWidth, imageHeight: InceptionSettings.ImageHeight, inputColumnName: ImageReal))
.Append(mlContext.Transforms.ExtractPixels(new ImagePixelExtractorTransformer.ColumnInfo(name: "input", inputColumnName: ImageReal, interleave: InceptionSettings.ChannelsLast, offset: InceptionSettings.Mean)))
// </SnippetImageTransforms>
// The ScoreTensorFlowModel transform scores the TensorFlow model and allows communication
// <SnippetScoreTensorFlowModel>
.Append(mlContext.Transforms.ScoreTensorFlowModel(modelLocation: inputModelLocation, outputColumnNames: new[] { "softmax2_pre_activation" }, inputColumnNames: new[] { "input" }))
// </SnippetScoreTensorFlowModel>
// <SnippetAddTrainer>
.Append(mlContext.MulticlassClassification.Trainers.LogisticRegression(labelColumn: LabelTokey, featureColumn: "softmax2_pre_activation"))
// </SnippetAddTrainer>
// <SnippetMapValueToKey2>
.Append(mlContext.Transforms.Conversion.MapKeyToValue((PredictedLabelValue, DefaultColumnNames.PredictedLabel)));
// </SnippetMapValueToKey2>

// Train the model
Console.WriteLine("=============== Training classification model ===============");
// Create and train the model based on the dataset that has been loaded, transformed.
// <SnippetTrainModel>
ITransformer model = estimator.Fit(data);
// </SnippetTrainModel>

// Process the training data through the model
// This is an optional step, but it's useful for debugging issues
// <SnippetTransformData>
var predictions = model.Transform(data);
// </SnippetTransformData>

// Create enumerables for both the ImageData and ImagePrediction DataViews
// for displaying results
// <SnippetEnumerateDataViews>
var imageData = mlContext.CreateEnumerable<ImageData>(data, false, true);
var imagePredictionData = mlContext.CreateEnumerable<ImagePrediction>(predictions, false, true);
// </SnippetEnumerateDataViews>

// Read the tags.tsv file and add the filepath to the image file name
// before loading into ImageData
// <SnippetCallPairAndDisplayResults1>
PairAndDisplayResults(imageData, imagePredictionData);
// </SnippetCallPairAndDisplayResults1>

// Get some performance metrics on the model using training data
Console.WriteLine("=============== Classification metrics ===============");

// <SnippetEvaluate>
var regressionContext = new MulticlassClassificationCatalog(mlContext);
var metrics = regressionContext.Evaluate(predictions, label: LabelTokey, predictedLabel: DefaultColumnNames.PredictedLabel);
// </SnippetEvaluate>

//<SnippetDisplayMetrics>
Console.WriteLine($"LogLoss is: {metrics.LogLoss}");
Console.WriteLine($"PerClassLogLoss is: {String.Join(" , ", metrics.PerClassLogLoss.Select(c => c.ToString()))}");
//</SnippetDisplayMetrics>

// Save the model to assets/outputs
Console.WriteLine("=============== Save model to local file ===============");

// <SnippetSaveModel>
using (var fileStream = new FileStream(outputModelLocation, FileMode.Create))
mlContext.Model.Save(model, fileStream);
// </SnippetSaveModel>

Console.WriteLine($"Model saved: {outputModelLocation}");
}

public static void ClassifyImages(MLContext mlContext, string dataLocation, string imagesFolder, string outputModelLocation)
{
Console.WriteLine($"=============== Loading model ===============");
Console.WriteLine($"Model loaded: {outputModelLocation}");

// Load the model
// <SnippetLoadModel>
ITransformer loadedModel;
using (var fileStream = new FileStream(outputModelLocation, FileMode.Open))
loadedModel = mlContext.Model.Load(fileStream);
// </SnippetLoadModel>

// Read the tags.tsv file and add the filepath to the image file name
// before loading into ImageData
// <SnippetReadFromTSV>
var imageData = ReadFromTsv(dataLocation, imagesFolder);
var imageDataView = mlContext.Data.ReadFromEnumerable<ImageData>(imageData);
// </SnippetReadFromTSV>

// <SnippetPredict>
var predictions = loadedModel.Transform(imageDataView);
var imagePredictionData = mlContext.CreateEnumerable<ImagePrediction>(predictions, false,true);
// </SnippetPredict>

Console.WriteLine("=============== Making classifications ===============");
// <SnippetCallPairAndDisplayResults2>
PairAndDisplayResults(imageData, imagePredictionData);
// </SnippetCallPairAndDisplayResults2>

}

private static void PairAndDisplayResults(IEnumerable<ImageData> imageNetData, IEnumerable<ImagePrediction> imageNetPredictionData)
{
// Builds pairs of (image, prediction) to sync up for display
// <SnippetBuildImagePredictionPairs>
IEnumerable<(ImageData image, ImagePrediction prediction)> imagesAndPredictions = imageNetData.Zip(imageNetPredictionData, (image, prediction) => (image, prediction));
// </SnippetBuildImagePredictionPairs>

// <SnippetDisplayPredictions>
foreach ((ImageData image, ImagePrediction prediction) item in imagesAndPredictions)
{
Console.WriteLine($"Image: {Path.GetFileName(item.image.ImagePath)} predicted as: {item.prediction.PredictedLabelValue} with score: {item.prediction.Score.Max()} ");
}
// </SnippetDisplayPredictions>
}

public static IEnumerable<ImageData> ReadFromTsv(string file, string folder)
{
//Need to parse through the tags.tsv file to combine the file path to the
// image name for the ImagePath property so that the image file can be found.

// <SnippetReadFromTsv>
return File.ReadAllLines(file)
.Select(line => line.Split('\t'))
.Select(line => new ImageData()
{
ImagePath = Path.Combine(folder, line[0]),
Label = line[1],
});
// </SnippetReadFromTsv>
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.2</TargetFramework>
<LangVersion>7.2</LangVersion>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.ML" Version="0.10.0" />
<PackageReference Include="Microsoft.ML.ImageAnalytics" Version="0.10.0" />
<PackageReference Include="Microsoft.ML.TensorFlow" Version="0.10.0" />
</ItemGroup>

<ItemGroup>
<None Update="assets\inputs-predict\data\broccoli.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="assets\inputs-predict\data\pizza3.jpg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="assets\inputs-predict\data\tags.tsv">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="assets\inputs-predict\data\teddy6.jpg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="assets\inputs-predict\data\toaster3.jpg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="assets\inputs-predict\imageClassifier.zip">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="assets\inputs-train\data\broccoli.jpg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="assets\inputs-train\data\pizza.jpg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="assets\inputs-train\data\pizza2.jpg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="assets\inputs-train\data\tags.tsv">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="assets\inputs-train\data\teddy2.jpg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="assets\inputs-train\data\teddy3.jpg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="assets\inputs-train\data\teddy4.jpg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="assets\inputs-train\data\toaster.jpg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="assets\inputs-train\data\toaster2.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="assets\inputs-train\data\wikimedia.md">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="assets\inputs-train\inception\imagenet.csv">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="assets\inputs-train\inception\imagenet.tsv">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="assets\inputs-train\inception\imagenet_comp_graph_label_strings.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="assets\inputs-train\inception\tensorflow_inception_graph.pb">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="assets\outputs\placeholder.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="assets\outputs\TextFile1.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
broccoli.png broccoli
pizza3.jpg pizza
teddy6.jpg teddy
toaster3.jpg toaster
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
MichalPL, [Ok Toaster](https://commons.wikimedia.org/wiki/File:OK._Toaster.jpg) [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/deed.en)
Agnieszka Kwiecień, [Spinach pizza](https://commons.wikimedia.org/wiki/File:Spinach_pizza.jpg), [GNU Free Documentation License v1.2 or later](https://en.wikipedia.org/wiki/en:GNU_Free_Documentation_License) and [CC BY-SA 3.0](https://creativecommons.org/licenses/by-sa/3.0/legalcode)
David Monniaux, [Broccoli DSC00862](https://commons.wikimedia.org/wiki/File:Broccoli_DSC00862.png), [GNU Free Documentation License v1.2 or later](https://en.wikipedia.org/wiki/en:GNU_Free_Documentation_License) and [CC BY-SA 3.0](https://creativecommons.org/licenses/by-sa/3.0/legalcode)
Earlgray.Ler2ra, [My teddy bear](https://commons.wikimedia.org/wiki/File:My_teddy_bear.jpg), [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/deed.en)
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
broccoli.jpg broccoli
pizza.jpg pizza
pizza2.jpg pizza
teddy2.jpg teddy
teddy3.jpg teddy
teddy4.jpg teddy
toaster.jpg toaster
toaster2.png toaster
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading