在 C# 代码中使用 fastai(pytorch),如何使用均值和标准差对 Bitmap 进行归一化?

几天前,我在我的 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 },标准差分别是 { 0.229, 0.224, 0.225 })。因此,每个颜色的像素值必须使用这些值进行转换,以匹配训练图像。但在 C# 中如何实现这一点呢?我尝试过的方法是:

int imageSize = 256;double[] means = new double[] { 0.485, 0.456, 0.406 }; // 在 fastai 模型中使用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 将均值和标准差值缩放到 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);   }}

当然,这不起作用,因为颜色值不能为负(我后来才意识到这一点)。但如何在我的 C# 代码中使用 onnx 模型将归一化值调整到 -1 到 1 之间呢?

是否有其他方法可以向模型提供数据或处理归一化?

任何帮助都将不胜感激!


回答:

解决这个问题的一种方法是从 onnx 管道切换到 onnx Inferencesession,我认为这样更简单,也更容易理解:

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;       }}// 创建您的 Tensor 并根据需要添加转换。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;        }

希望这能帮助到有类似问题的人。

Related Posts

使用LSTM在Python中预测未来值

这段代码可以预测指定股票的当前日期之前的值,但不能预测…

如何在gensim的word2vec模型中查找双词组的相似性

我有一个word2vec模型,假设我使用的是googl…

dask_xgboost.predict 可以工作但无法显示 – 数据必须是一维的

我试图使用 XGBoost 创建模型。 看起来我成功地…

ML Tuning – Cross Validation in Spark

我在https://spark.apache.org/…

如何在React JS中使用fetch从REST API获取预测

我正在开发一个应用程序,其中Flask REST AP…

如何分析ML.NET中多类分类预测得分数组?

我在ML.NET中创建了一个多类分类项目。该项目可以对…

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注