在c#代码中使用fastai(pytorch),如何使用均值和标准差归一化位图?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在c#代码中使用fastai(pytorch),如何使用均值和标准差归一化位图?相关的知识,希望对你有一定的参考价值。

几天前,我为我的C#项目从tensorflow切换到fastai。但是现在我正面临标准化的问题。对于两者,我都使用onnx管道加载模型和数据。

var onnxPipeline = mLContext.Transforms.ResizeImages(resizing: ImageResizingEstimator.ResizingKind.Fill, outputColumnName: inputName,
                                                                 imageWidth: ImageSettings.imageWidth, imageHeight: ImageSettings.imageHeight,
                                                                 inputColumnName: nameof(ImageInputData.Image)) 
                .Append(mLContext.Transforms.ExtractPixels(outputColumnName: inputName, interleavePixelColors: true, scaleImage: 1 / 255f))
                .Append(mLContext.Transforms.ApplyOnnxModel(outputColumnName: outputName, inputColumnName: inputName, modelFile: onnxModelPath));

var emptyData = mLContext.Data.LoadFromEnumerable(new List<ImageInputData>());
var onnxModel = onnxPipeline.Fit(emptyData);

    class ImageInputData
    {
        [ImageType(ImageSettings.imageHeight, ImageSettings.imageWidth)]
        public Bitmap Image { get; set; }

        public ImageInputData(byte[] image)
        {
            using (var ms = new MemoryStream(image))
            {
                Image = new Bitmap(ms);
            }
        }
        public ImageInputData(Bitmap image)
        {
            Image = image;
        }
    }

使用fastai后,我了解到,如果使用特定的均值和标准差对数据进行归一化,则模型将具有更好的准确性(因为我使用了resnet34模型,因此其平均值应为{0.485,0.456,0.406} stds = {0.229,0.224 ,分别为0.225})。因此,必须使用这些值转换像素值(对于每种颜色的c。)以匹配训练图像。但是我如何在C#中实现呢?我到目前为止尝试过的是:

int imageSize = 256;
double[] means = new double[] { 0.485, 0.456, 0.406 }; // used in fastai model
double[] stds = new double[] { 0.229, 0.224, 0.225 };
Bitmap bitmapImage = inputBitmap;
Image image = bitmapImage;

Color[] pixels = new Color[imageSize * imageSize];
for (int x = 0; x < bitmapImage.Width; x++)
{
   for (int y = 0; y < bitmapImage.Height; y++)
   {
      Color pixel = bitmapImage.GetPixel(x, y);
      pixels[x + y] = pixel;
      double red = (pixel.R - (means[0] * 255)) / (stds[0] * 255); // *255 to scale the mean and std values to the Bitmap
      double gre = (pixel.G - (means[1] * 255)) / (stds[1] * 255);
      double blu = (pixel.B - (means[2] * 255)) / (stds[2] * 255);
      Color pixel_n = Color.FromArgb(pixel.A, (int)red, (int)gre, (int)blu);
      bitmapImage.SetPixel(x, y, pixel_n);

   }
}

当然,它不起作用,因为Colorvalues不能为负(我稍后才意识到)。但是我如何使用onnx-model在C#中为我的模型在-1和1之间实现这种归一化?

是否有其他方法可以提供模型或处理规范化?

任何帮助将不胜感激!

答案

解决此问题的一种方法是从onnx管道切换到onnx推理会话,在我看来,这是更容易理解的更好方法:

public List<double> UseOnnxSession(Bitmap image, string onnxModelPath)
{
      double[] means = new double[] { 0.485, 0.456, 0.406 };
      double[] stds = new double[] { 0.229, 0.224, 0.225 };

      using (var session = new InferenceSession(onnxModelPath))
      {
          List<double> scores = new List<double>();
          Tensor<float> t1 = ConvertImageToFloatData(image, means, stds);
          List<float> fl = new List<float>();

          var inputMeta = session.InputMetadata;
          var inputs = new List<NamedOnnxValue>()
          {
             NamedOnnxValue.CreateFromTensor<float>("input_1", t1)
          };
          using (var results = session.Run(inputs))
          {
              foreach (var r in results)
              {
                  var x = r.AsTensor<float>().First();
                  var y = r.AsTensor<float>().Last();
                  var softmaxScore = Softmax(new double[] { x, y });
                  scores.Add(softmaxScore[0]);
                  scores.Add(softmaxScore[1]);
              }
           }
           return scores;
       }
}

// Create your Tensor and add transformations as you need.
public static Tensor<float> ConvertImageToFloatData(Bitmap image, double[] means, double[] std)
{
      Tensor<float> data = new DenseTensor<float>(new[] { 1, 3, image.Width, image.Height });
      for (int x = 0; x < image.Width; x++)
      {
           for (int y = 0; y < image.Height; y++)
           {
               Color color = image.GetPixel(x, y);
               var red = (color.R - (float)means[0] * 255) / ((float)std[0] * 255);
               var gre = (color.G - (float)means[1] * 255) / ((float)std[1] * 255);
               var blu = (color.B - (float)means[2] * 255) / ((float)std[2] * 255);
               data[0, 0, x, y] = red;
               data[0, 1, x, y] = gre;
               data[0, 2, x, y] = blu;
            }
       }
       return data;
}

此外,我还必须在这些分数上使用我自己的Softmax方法,才能从模型中获得真实的概率:

        public double[] Softmax(double[] values)
        {
            double[] ret = new double[values.Length];
            double maxExp = values.Select(Math.Exp).Sum();
            for (int i = 0; i < values.Length; i++)
            {
                ret[i] = Math.Round((Math.Exp(values[i]) / maxExp), 4);
            }
            return ret;
        }

希望这可以帮助遇到类似问题的人。

以上是关于在c#代码中使用fastai(pytorch),如何使用均值和标准差归一化位图?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 PyTorch DataLoader 进行强化学习?

使用Google Cloud Platform的Fastai

在Win10上搭建fastai课程环境

深度学习系列7:fastai

AttributeError:数据集对象没有属性“c”FastAI

如何定义多类文本数据集(fastai)的日志计数比?