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
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
flower_photos_small_set
*.zip
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26124.0
MinimumVisualStudioVersion = 15.0.26124.0
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "ImageClassification.Train", "ImageClassification.Train\ImageClassification.Train.fsproj", "{85F30786-C364-4498-8CB9-208575B9A1AC}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{85F30786-C364-4498-8CB9-208575B9A1AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{85F30786-C364-4498-8CB9-208575B9A1AC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{85F30786-C364-4498-8CB9-208575B9A1AC}.Debug|x64.ActiveCfg = Debug|Any CPU
{85F30786-C364-4498-8CB9-208575B9A1AC}.Debug|x64.Build.0 = Debug|Any CPU
{85F30786-C364-4498-8CB9-208575B9A1AC}.Debug|x86.ActiveCfg = Debug|Any CPU
{85F30786-C364-4498-8CB9-208575B9A1AC}.Debug|x86.Build.0 = Debug|Any CPU
{85F30786-C364-4498-8CB9-208575B9A1AC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{85F30786-C364-4498-8CB9-208575B9A1AC}.Release|Any CPU.Build.0 = Release|Any CPU
{85F30786-C364-4498-8CB9-208575B9A1AC}.Release|x64.ActiveCfg = Release|Any CPU
{85F30786-C364-4498-8CB9-208575B9A1AC}.Release|x64.Build.0 = Release|Any CPU
{85F30786-C364-4498-8CB9-208575B9A1AC}.Release|x86.ActiveCfg = Release|Any CPU
{85F30786-C364-4498-8CB9-208575B9A1AC}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>

<ItemGroup>
<Compile Include="Program.fs" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.ML" Version="1.4.0" />
<PackageReference Include="Microsoft.ML.ImageAnalytics" Version="1.4.0" />
<PackageReference Include="Microsoft.ML.Vision" Version="1.4.0" />
<PackageReference Include="SciSharp.TensorFlow.Redist" Version="1.15.0" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
// Learn more about F# at http://fsharp.org

open System
open System.IO
open System.IO.Compression
open System.Net
open Microsoft.ML
open Microsoft.ML.Data
open Microsoft.ML.Transforms
open Microsoft.ML.Vision

// Define input and output schema
[<CLIMutable>]
type ImageData = {
ImagePath:string
Label:string
}

[<CLIMutable>]
type ImagePrediction = {
ImagePath:string
Label: string
PredictedLabel:string
}

// Helper functions
let downloadZippedImageSetAsync (fileName:string) (downloadUrl:string) (imageDownloadFolder:string) =
async {
let zippedPath = Path.Join(imageDownloadFolder, fileName)
let unzippedPath = Path.Join(imageDownloadFolder,Path.GetFileNameWithoutExtension(zippedPath));

if not (File.Exists zippedPath) then
let client = new WebClient()
client.DownloadFile(Uri(downloadUrl), zippedPath)

if not (Directory.Exists unzippedPath) then
ZipFile.ExtractToDirectory(zippedPath, unzippedPath)

return Path.Join(unzippedPath,Path.GetFileNameWithoutExtension(fileName))
}

let loadImagesFromDirectory (path:string) (useDirectoryAsLabel:bool) =

let files = Directory.GetFiles(path, "*",searchOption=SearchOption.AllDirectories)

files
|> Array.filter(fun file ->
(Path.GetExtension(file) = ".jpg") ||
(Path.GetExtension(file) = ".png"))
|> Array.map(fun file ->
let mutable label = Path.GetFileName(file)
if useDirectoryAsLabel then
label <- Directory.GetParent(file).Name
else
let mutable brk = false
for index in 0..label.Length do
while not brk do
if not (label.[index] |> Char.IsLetter) then
label <- label.Substring(0,index)
brk <- true

{ImagePath=file; Label=label}
)


[<EntryPoint>]
let main argv =

let fileName = "flower_photos_small_set.zip"
let downloadUrl = "https://mlnetfilestorage.file.core.windows.net/imagesets/flower_images/flower_photos_small_set.zip?st=2019-08-07T21%3A27%3A44Z&se=2030-08-08T21%3A27%3A00Z&sp=rl&sv=2018-03-28&sr=f&sig=SZ0UBX47pXD0F1rmrOM%2BfcwbPVob8hlgFtIlN89micM%3D"

// Download Data
let datasetPath =
__SOURCE_DIRECTORY__
|> downloadZippedImageSetAsync fileName downloadUrl
|> Async.RunSynchronously

// Initialize MLContext
let mlContext = MLContext()

// Get List of Images
let images = loadImagesFromDirectory datasetPath true

// Load Data into IDataView and Shuffle
let data =
images
|> mlContext.Data.LoadFromEnumerable
|> mlContext.Data.ShuffleRows

// Define preprocessing pipeline
let preprocessingPipeline =
EstimatorChain()
.Append(mlContext.Transforms.Conversion.MapValueToKey("LabelAsKey","Label"))
.Append(mlContext.Transforms.LoadRawImageBytes("Image", null, "ImagePath"))

// Preprocess the data
let preprocessedData =
let processingTransformer = data |> preprocessingPipeline.Fit
data |> processingTransformer.Transform

// Split data into train,validation, test sets
let train, validation, test =
preprocessedData
|> ( fun originalData ->
let trainValSplit = mlContext.Data.TrainTestSplit(originalData, testFraction=0.7)
let testValSplit = mlContext.Data.TrainTestSplit(trainValSplit.TestSet)
(trainValSplit.TrainSet, testValSplit.TrainSet, testValSplit.TestSet))

// Define ImageClassificationTrainer Options
let classifierOptions = ImageClassificationTrainer.Options()
classifierOptions.FeatureColumnName <- "Image"
classifierOptions.LabelColumnName <- "LabelAsKey"
classifierOptions.ValidationSet <- validation
classifierOptions.Arch <- ImageClassificationTrainer.Architecture.ResnetV2101
classifierOptions.MetricsCallback <- Action<ImageClassificationTrainer.ImageClassificationMetrics>(fun x -> printfn "%s" (x.ToString()))

// Define model training pipeline
let trainingPipeline =
EstimatorChain()
.Append(mlContext.MulticlassClassification.Trainers.ImageClassification(classifierOptions))
.Append(mlContext.Transforms.Conversion.MapKeyToValue("PredictedLabel","LabelAsKey"))

// Train the model
let trainedModel =
train
|> trainingPipeline.Fit

// Get Prediction IDataView
let predictions = test |> trainedModel.Transform

// Evaluate the model
let metrics = mlContext.MulticlassClassification.Evaluate(predictions,labelColumnName="LabelAsKey")
printfn "MacroAccurracy: %f | LogLoss: %f" metrics.MacroAccuracy metrics.LogLoss

// Save Model
mlContext.Model.Save(trainedModel, preprocessedData.Schema, Path.Join(__SOURCE_DIRECTORY__,"model.zip"))

// Display 5 predictions
mlContext.Data.CreateEnumerable<ImagePrediction>(predictions, reuseRowObject=true)
|> Seq.take 5
|> Seq.iter(fun prediction -> printfn "Original: %s | Predicted: %s" prediction.Label prediction.PredictedLabel)

0 // return an integer exit code
Loading