c# - ML.NET 架构不匹配 : expected VarVector<Byte> got Vector<Byte>

标签 c# schema ml.net

我一直在尝试修改有关图像分类的 ML.NET 教程,但遇到了架构不匹配问题,其中模型需要 VarVector,但它传递的是 Vector。

原始示例创建一个 IDataView,其中名为“ImagePath”的列填充了图像路径,然后使用 MLContext.Transforms.LoadRawImageBytes() 生成字节数据的“Image”列。然后将生成的 IDataView 输入到 Multiclassification.Trainers.ImageClassification() 对象。这非常简单。

我希望将其概括为涉及更多的图像预处理。我希望预处理管道降低图像分辨率,然后将图像裁剪为正确的大小,然后再将其输入 ImageClassification() 训练器。它遵循与示例相同的流程,但更改了处理管道以在图像处理管道中使用 LoadImages()、ResizeImage() 和 ExtractPixels() 估计器。下面是我正在使用的代码片段。

    // Fill an IDataView with the Label and ImagePath information:
    // The ImageData class is defined for this purpose and it is just:
    // 
    //  public class ImageData {
    //       public string ImagePath { get; set; }
    //       public string Label { get; set; }
    //  }
    // 
    // Create a bunch of image names and their labels into a list of ImageData objects.
    IEnumerable<ImageData> images = LoadImagesFromDirectory(folder: assetsRelativePath, 
                                                            useFolderNameAsLabel: true);
    IDataView imageData = mlContext.Data.LoadFromEnumerable(images);

    // Define the preprocessing pipeline to:
    // 1. Map the Label to a unique key
    // 2. Load the image data using the ImagePath column
    // 3. Reduce the resolution of the image data
    // 4. Crop the reduced resolution image data
    // 5. Transform the image data from type "Image" to a byte array.
    var preprocessingPipeline = mlContext.Transforms.Conversion.MapValueToKey(
                    inputColumnName: "Label",
                    outputColumnName: "LabelAsKey")
                .Append(mlContext.Transforms.LoadImages(
                    inputColumnName: "ImagePath",
                    outputColumnName: "RawImage",
                    imageFolder: assetsRelativePath))
                .Append(mlContext.Transforms.ResizeImages(
                    inputColumnName: "RawImage",
                    outputColumnName: "ResReduxImage",
                    imageWidth: 512,
                    imageHeight: 459,
                    resizing: Microsoft.ML.Transforms.Image.ImageResizingEstimator.ResizingKind.Fill))
                .Append(mlContext.Transforms.ResizeImages(
                    inputColumnName: "ResReduxImage",
                    outputColumnName: "CroppedImage",
                    imageWidth: 512,
                    imageHeight: 459,
                    resizing: Microsoft.ML.Transforms.Image.ImageResizingEstimator.ResizingKind.IsoCrop,
                    cropAnchor: Microsoft.ML.Transforms.Image.ImageResizingEstimator.Anchor.Center))
                .Append(mlContext.Transforms.ExtractPixels(
                    inputColumnName: "CroppedImage",
                    outputColumnName: "Image",
                    outputAsFloatArray: false,
                    colorsToExtract: ImagePixelExtractingEstimator.ColorBits.Red));

     // Transform the raw ImageData into the TransformedData that the Model will train on
     IDataView preProcessedData = preprocessingPipeline.Fit(imageData).Transform(imageData);

     // Partition the data set for training and validation
     TrainTestData trainSplit = mlContext.Data.TrainTestSplit(data: preProcessedData, testFraction: 0.4);
     TrainTestData validationTestSplit = mlContext.Data.TrainTestSplit(trainSplit.TestSet);

     IDataView trainSet = trainSplit.TrainSet;
     IDataView validationSet = validationTestSplit.TrainSet;
     IDataView testSet = validationTestSplit.TestSet;

     var classifierOptions = new ImageClassificationTrainer.Options()
     {
          FeatureColumnName = "Image",
          LabelColumnName = "LabelAsKey",
          ValidationSet = validationSet,
          Arch = ImageClassificationTrainer.Architecture.ResnetV2101,
          MetricsCallback = (metrics) => Console.WriteLine(metrics),
          TestOnTrainSet = false,
          ReuseTrainSetBottleneckCachedValues = true,
          ReuseValidationSetBottleneckCachedValues = true,
          WorkspacePath=workspaceRelativePath
     };

     var trainingPipeline = 
           mlContext.MulticlassClassification.Trainers.ImageClassification(classifierOptions)
           .Append(mlContext.Transforms.Conversion.MapKeyToValue("PredictedLabel"));

     // When I call Fit() in the next line of code, the following runtime error occurs: 
     // System.ArgumentOutOfRangeException: 'Schema mismatch for feature column 'Image': 
     // expected VarVector<Byte>, got Vector<Byte> Parameter name: inputSchema'
     ITransformer trainedModel = trainingPipeline.Fit(trainSet);

我对 VarVector 和 Vector 不匹配感到困惑。这两天我尝试了以下方法,但没有成功:

  • 定义要传递给转换的自定义 DataViewSchema
  • 通过从 IDataView 派生来定义自定义 DataView
  • 使用 CustomMapping 定义自定义映射<>更改类型

任何人都可以提供一些关于如何让自己摆脱困境的指导吗?我真的很想避免读取图像,对其进行预处理,将其写入文件,然后调用原始的 LoadRawImageBytes() 函数。我不想要额外的文件系统工作。

最佳答案

我想出了如何实现这一点。这里的问题是底层类型是正确的,但架构是错误的。我能够通过 CustomMapping 和一些属性魔法解决我的问题。请参阅 CustomMapping documentation了解 CustomMapping 的工作原理。

更正后的代码片段如下。

    // SOMEWHERE OUTSIDE OF THE FUNCTION DEFINE:
    // Define a class to represent the input column type for the custom transform
    class InputData 
    {
        [VectorType(1)] // attribute specifies vector type of known length
        public VBuffer<Byte> Image1; // the VBuffer<> type actually represents the data
    }

    // Define a class to represent the output column type for the custom transform
    class OutputData 
    {
        // THE MAGICAL FIX: attribute specifies vector type of unknown length (i.e. VarVector)
        [VectorType()]
        public VBuffer<Byte> Image; // the VBuffer<> type actually represents the data
    }

    // ------------------------------------------------------------------
    // INSIDE THE FUNCTION THAT WILL DO THE TRAINING
    // ------------------------------------------------------------------

    // Fill an IDataView with the Label and ImagePath information:
    // The ImageData class is defined for this purpose and it is just:
    // 
    //  public class ImageData {
    //       public string ImagePath { get; set; }
    //       public string Label { get; set; }
    //  }
    // 
    // Create a bunch of image names and their labels into a list of ImageData objects.
    IEnumerable<ImageData> images = LoadImagesFromDirectory(folder: assetsRelativePath, 
                                                            useFolderNameAsLabel: true);
    IDataView imageData = mlContext.Data.LoadFromEnumerable(images);

    // Define the preprocessing pipeline to:
    // 1. Map the Label to a unique key
    // 2. Load the image data using the ImagePath column
    // 3. Reduce the resolution of the image data
    // 4. Crop the reduced resolution image data
    // 5. Transform the image data from type "Image" to a byte array.
    var preprocessingPipeline = mlContext.Transforms.Conversion.MapValueToKey(
                    inputColumnName: "Label",
                    outputColumnName: "LabelAsKey")
                .Append(mlContext.Transforms.LoadImages(
                    inputColumnName: "ImagePath",
                    outputColumnName: "RawImage",
                    imageFolder: assetsRelativePath))
                .Append(mlContext.Transforms.ResizeImages(
                    inputColumnName: "RawImage",
                    outputColumnName: "ResReduxImage",
                    imageWidth: 512,
                    imageHeight: 459,
                    resizing: Microsoft.ML.Transforms.Image.ImageResizingEstimator.ResizingKind.Fill))
                .Append(mlContext.Transforms.ResizeImages(
                    inputColumnName: "ResReduxImage",
                    outputColumnName: "CroppedImage",
                    imageWidth: 512,
                    imageHeight: 459,
                    resizing: Microsoft.ML.Transforms.Image.ImageResizingEstimator.ResizingKind.IsoCrop,
                    cropAnchor: Microsoft.ML.Transforms.Image.ImageResizingEstimator.Anchor.Center))
                .Append(mlContext.Transforms.ExtractPixels(
                    inputColumnName: "CroppedImage",
                    outputColumnName: "Image1",
                    outputAsFloatArray: false,
                    colorsToExtract: ImagePixelExtractingEstimator.ColorBits.Red));

     // Transform the raw ImageData into the TransformedData that the Model will train on
     IDataView preProcessedData = 
           preprocessingPipeline.Fit(imageData).Transform(imageData);

     // Create an action representing the custom transform...
     // The data itself does not need to be changed at all, so this is just an 
     // identity transform
     Action<InputData, OutputData> convertVecType 
               = (input, output) => output.Image = input.Image1;
 
     var convertTypePipeline = mlContext.Transforms.CustomMapping(convertVecType, 
                                                                 "convertVecType");
     preProcessedData = 
              convertTypePipeline.Fit(preProcessedData).Transform(preProcessedData);

     // Partition the data set for training and validation
     TrainTestData trainSplit = mlContext.Data.TrainTestSplit(data: preProcessedData, testFraction: 0.4);
     TrainTestData validationTestSplit = mlContext.Data.TrainTestSplit(trainSplit.TestSet);

     IDataView trainSet = trainSplit.TrainSet;
     IDataView validationSet = validationTestSplit.TrainSet;
     IDataView testSet = validationTestSplit.TestSet;

     var classifierOptions = new ImageClassificationTrainer.Options()
     {
          FeatureColumnName = "Image",
          LabelColumnName = "LabelAsKey",
          ValidationSet = validationSet,
          Arch = ImageClassificationTrainer.Architecture.ResnetV2101,
          MetricsCallback = (metrics) => Console.WriteLine(metrics),
          TestOnTrainSet = false,
          ReuseTrainSetBottleneckCachedValues = true,
          ReuseValidationSetBottleneckCachedValues = true,
          WorkspacePath=workspaceRelativePath
     };

     var trainingPipeline = 
           mlContext.MulticlassClassification.Trainers.ImageClassification(classifierOptions)
           .Append(mlContext.Transforms.Conversion.MapKeyToValue("PredictedLabel"));

     // When I call Fit() in the next line of code, the following runtime error occurs: 
     // System.ArgumentOutOfRangeException: 'Schema mismatch for feature column 'Image': 
     // expected VarVector<Byte>, got Vector<Byte> Parameter name: inputSchema'
     ITransformer trainedModel = trainingPipeline.Fit(trainSet);

关于c# - ML.NET 架构不匹配 : expected VarVector<Byte> got Vector<Byte>,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63930046/

相关文章:

c# - Serilog - 如何手动将格式化消息呈现为字符串?

xml - Google Apps 检索用户信息

reactjs - 如何使用 yup.addMethod() 为国家/地区名称和代码编写自定义架构验证?

python - 更正 pb 文件以将 Tensorflow 模型移动到 ML.NET

c# - 三次 Hermite 样条曲线表现异常

c# - 使用 gmail 发送电子邮件。错误 : The server response was: 5. 5.1 需要身份验证

c# - Observable.Interval 等待 Action 完成

javascript - JSON Schema 中的 additionalItems 和 additionalProperties 有什么区别?

c# - 对 Enumerable 调用 .ToArray 会破坏 Enumerable

.net - 使用 ML.NET 对文本数组进行 OneHotEncoding