dotnet / machinelearning

ML.NET is an open source and cross-platform machine learning framework for .NET.
https://dot.net/ml
MIT License
9.03k stars 1.88k forks source link

ML.Net Schema mismatch for feature column 'ImagePath': expected VarVector<Byte>, got String #5723

Open axiom2018 opened 3 years ago

axiom2018 commented 3 years ago

System information

Issue

Source code / logs

1) Main Code (Program.cs)

` string baseDir = Directory.GetCurrentDirectory(); string realDir = Path.GetFullPath(Path.Combine(baseDir, @"......\")); string imgFolderDir = realDir + "images\";

        // Get ref to images folder.
        //var imgFolder = Path.Combine(Environment.CurrentDirectory, "..", "..", "..", "images");

        // Go to images folder, get all files (*) from both folders.
        var file = Directory.GetFiles(imgFolderDir, "*", SearchOption.AllDirectories);

        /* For each file, create an imagedata object.  */
        var images = file.Select(f => new ImageData
        {
            ImagePath = f,
            Label = Directory.GetParent(f).Name
        });

        // Begin algorithm for ML.
        MLContext ml = new MLContext();

        /* Load the images.
           Shuffle them (for more randomness?)
        */
        var imageData = ml.Data.LoadFromEnumerable(images);
        var imgDataShuffle = ml.Data.ShuffleRows(imageData);

        // Split because we'd like to get a portion for training and another for testing.
        var testTrainData = ml.Data.TrainTestSplit(imgDataShuffle, testFraction: 0.2);

        var validateData = ml.Transforms.Conversion.MapValueToKey("LabelKey", "Label",
            keyOrdinality: Microsoft.ML.Transforms.ValueToKeyMappingEstimator.KeyOrdinality.ByValue)
            .Fit(testTrainData.TestSet)
            .Transform(testTrainData.TestSet);

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

        var model = pipeline.Fit(testTrainData.TrainSet);

        var predictions = model.Transform(testTrainData.TestSet);
        var metrics = ml.MulticlassClassification.Evaluate(predictions, labelColumnName: "LabelKey",
            predictedLabelColumnName: "PredictedLabel");

        Console.WriteLine("Log loss: {0}.", metrics.LogLoss);

        var predictionEngine = ml.Model.CreatePredictionEngine<ImageData, ImagePrediction>(model);

        var testImagesFolder = Path.Combine(Environment.CurrentDirectory, "..", "..", "..", "test");
        var testFiles = Directory.GetFiles(testImagesFolder, "*", SearchOption.AllDirectories);
        var testImages = testFiles.Select(file => new ImageData
        {
            ImagePath = file
        });

        VBuffer<ReadOnlyMemory<char>> keys = default;
        predictionEngine.OutputSchema["LabelKey"].GetKeyValues(ref keys);
        var originalLabels = keys.DenseValues().ToArray();

        foreach (var image in testImages)
        {
            // Get the prediction using the prediction engine itself of course.
            var prediction = predictionEngine.Predict(image);

            // And get the label.
            var labelIndex = prediction.PredictedLabel;

            // Write it out.
            Console.WriteLine("-Image Path:- {0}. -Score:- {1}. -Predicted Label- {2}.",
                Path.GetFileName(image.ImagePath),
                prediction.Score.Max(),
                originalLabels[labelIndex]);
        }

        Console.ReadLine();

2) ImageData (Input)

` public class ImageData { [LoadColumn(0)] public string ImagePath;

    [LoadColumn(1)]
    public string Label;
}`

3) ImagePrediction (Output)

` public class ImagePrediction { // Scores we get from the model. How sure it is on a guess. [ColumnName("Score")] public float[] Score;

    // The actual label. DNN doesn't give us a string for it.
    [ColumnName("PredictedLabel")]
    public uint PredictedLabel;
}`

Also, here's a link to the guy in the videos github so you can compare and contrast my code to his. What exactly am I doing wrong?

michaelgsharp commented 3 years ago

from a high level, the issue is just that the schema for ImagePath and the provided data don't match. The issue is that the ImageClassification trainer assumes that the pixels have already been pulled out of the image into a vector, and you are just providing the image path still. Depending on the exact transformations you need to do, you will need add something like this before you call the ImageClassification trainer:

var pipeline = mlContext.Transforms.LoadImages("ImageSource_featurized", "ImagePath")
                                      .Append(mlContext.Transforms.ResizeImages("ImageSource_featurized", 224, 224, "ImageSource_featurized"))
                                      .Append(mlContext.Transforms.ExtractPixels("ImageVector", "ImageSource_featurized"))

The LoadImages call will load the image into memory as a BitMap. ResizeImages will resize them (if needed, if you don't need to resize them you can remove this), and ExtractPixels will pull the pixels out from the BitMap into a 3d vector.

You will then need to use the name "ImageVector" (or rename it in the ExtractPixels call if you want) as the input for the ImageClassification trainer.

If you are still running into issues after this can you upload your project and at least 1 sample image as a zip?