ONNX Runtime ile WinUI uygulamanızda ONNX modellerini kullanmaya başlama
Bu makalede, bir görüntüdeki nesneleri sınıflandırmak ve her sınıflandırmanın güvenilirliğini görüntülemek için ONNX modeli kullanan bir WinUI 3 uygulaması oluşturma işleminde size yol gösterilir. Windows uygulamanızda yapay zeka ve makine öğrenmesi modellerini kullanma hakkında daha fazla bilgi için bkz. Windows 'da yapay zekayı kullanmaya başlama.
Yapay zeka özelliklerini kullanırken şunları gözden geçirmenizi öneririz: WindowsÜzerinde Sorumlu Üretken Yapay Zeka Uygulamaları ve Özellikleri Geliştirme.
ONNX çalışma zamanı nedir?
ONNX Runtime, donanıma özgü kitaplıkları tümleştirmeye yönelik esnek bir arabirime sahip platformlar arası bir makine öğrenmesi model hızlandırıcısıdır. ONNX Runtime PyTorch, Tensorflow/Keras, TFLite, scikit-learnve diğer çerçevelerden modellerle kullanılabilir. Daha fazla bilgi için ONNX Runtimekonumundaki https://onnxruntime.ai/docs/ web sitesine bakın.
Bu örnek, Windows cihazlarında farklı donanım seçeneklerini soyutlayan ve çalıştıran ve GPU ve NPU gibi yerel hızlandırıcılar arasında yürütmeyi destekleyen DirectML Execution Provider kullanır.
Önkoşullar
- Cihazınızda geliştirici modu etkinleştirilmiş olmalıdır. Daha fazla bilgi için bkz: Geliştirme için cihazınızı etkinleştirme.
- .NET masaüstü geliştirme iş yüküyle Visual Studio 2022 veya üzeri.
Yeni bir C# WinUI uygulaması oluşturma
Visual Studio'da yeni bir proje oluşturun. Yeni proje oluştur iletişim
Nuget paketlerine referans ekleme
Paket | Açıklama |
---|---|
Microsoft.ML.OnnxRuntime.DirectML | GPU'da ONNX modellerini çalıştırmak için API'ler sağlar. |
SixLabors.ImageSharp | Model girişi için görüntüleri işlemeye yönelik görüntü yardımcı programları sağlar. |
SharpDX.DXGI | C# dilinden DirectX cihazına erişmek için API'ler sağlar. |
Aşağıdaki 'i bu kitaplıklardan API'lere erişmek için yönergelerini kullanarak MainWindows.xaml.cs
'nin üstüne ekleyin.
// MainWindow.xaml.cs
using Microsoft.ML.OnnxRuntime;
using Microsoft.ML.OnnxRuntime.Tensors;
using SharpDX.DXGI;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
Modeli projenize ekleme
Çözüm Gezginibölümünde projenize sağ tıklayın ve Ekle - Yeni Klasör>seçin. Yeni klasörü "model" olarak adlandırın. Bu örnekte, resnet50-v2-7.onnx modelini https://github.com/onnx/models'den kullanacağız. https://github.com/onnx/models/blob/main/validated/vision/classification/resnet/model/resnet50-v2-7.onnxkonumunda modelin depo görünümüne gidin. *Ham dosyayı indir düğmesine tıklayın. Bu dosyayı yeni oluşturduğunuz "model" dizinine kopyalayın.
Çözüm Gezgini'nde model dosyasına tıklayın ve Çıkış Dizinine Kopyala "Daha Yeniyse Kopyala" olarak ayarlayın.
Basit bir kullanıcı arabirimi oluşturma
Bu örnekte, kullanıcının modelle değerlendirilecek bir görüntü seçmesine olanak sağlayan bir Düğmesi, seçili görüntüyü görüntülemek için bir Görüntü denetimi ve modelin görüntüde algılanan nesneleri ve her nesne sınıflandırmasının güvenilirliğini listelemek için bir TextBlock içeren basit bir kullanıcı arabirimi oluşturacağız.
<!--MainWindow.xaml-->
<Grid Padding="25" >
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Button x:Name="myButton" Click="myButton_Click" Grid.Column="0" VerticalAlignment="Top">Select photo</Button>
<Image x:Name="myImage" MaxWidth="300" Grid.Column="1" VerticalAlignment="Top"/>
<TextBlock x:Name="featuresTextBlock" Grid.Column="2" VerticalAlignment="Top"/>
</Grid>
Modeli başlatma
MainWindow.xaml.cs
dosyasında, MainWindow sınıfının içinde, modeli başlatacak InitModel adlı bir yardımcı yöntem oluşturun. Bu yöntem, kullanılabilir ilk bağdaştırıcıyı seçmek için SharpDX.DXGI kitaplığındaki API'leri kullanır. Seçilen bağdaştırıcı, bu oturumdaki DirectML yürütme sağlayıcısının SessionOptions nesnesinde ayarlanır. Son olarak, model dosyasının yolu ve oturum seçenekleri verilerek yeni bir InferenceSession başlatılır.
// MainWindow.xaml.cs
private InferenceSession _inferenceSession;
private string modelDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "model");
private void InitModel()
{
if (_inferenceSession != null)
{
return;
}
// Select a graphics device
var factory1 = new Factory1();
int deviceId = 0;
Adapter1 selectedAdapter = factory1.GetAdapter1(0);
// Create the inference session
var sessionOptions = new SessionOptions
{
LogSeverityLevel = OrtLoggingLevel.ORT_LOGGING_LEVEL_INFO
};
sessionOptions.AppendExecutionProvider_DML(deviceId);
_inferenceSession = new InferenceSession($@"{modelDir}\resnet50-v2-7.onnx", sessionOptions);
}
Görüntü yükleme ve analiz etme
Kolaylık olması için, bu örnekte görüntüyü yükleme ve biçimlendirme, modeli çağırma ve sonuçları görüntüleme adımlarının tümü düğme tıklama işleyicisine yerleştirilir. Varsayılan şablonda yer alan düğme tıklama işleyicisine, işleyicide asenkron işlemleri çalıştırabilmemiz için async anahtar sözcüğünü eklediğimize dikkat edin.
// MainWindow.xaml.cs
private async void myButton_Click(object sender, RoutedEventArgs e)
{
...
}
Kullanıcının kullanıcı arabiriminde analiz etmek ve görüntülemek üzere bilgisayarından bir görüntü seçmesine izin vermek için Bir FileOpenPicker kullanın.
FileOpenPicker fileOpenPicker = new()
{
ViewMode = PickerViewMode.Thumbnail,
FileTypeFilter = { ".jpg", ".jpeg", ".png", ".gif" },
};
InitializeWithWindow.Initialize(fileOpenPicker, WinRT.Interop.WindowNative.GetWindowHandle(this));
StorageFile file = await fileOpenPicker.PickSingleFileAsync();
if (file == null)
{
return;
}
// Display the image in the UI
var bitmap = new BitmapImage();
bitmap.SetSource(await file.OpenAsync(Windows.Storage.FileAccessMode.Read));
myImage.Source = bitmap;
Ardından girişi model tarafından desteklenen bir biçime getirmek için işlememiz gerekir. SixLabors.ImageSharp kitaplığı, görüntüyü 24 bit RGB biçiminde yüklemek ve görüntüyü 224x224 piksel olarak yeniden boyutlandırmak için kullanılır. Ardından piksel değerleri 255*[0,485, 0,456, 0,406] ortalaması ve 255*[0,229, 0,224, 0,225] standart sapması ile normalleştirilir. Modelin beklediği biçimin ayrıntıları, resnet modeligithub sayfasında bulunabilir.
using var fileStream = await file.OpenStreamForReadAsync();
IImageFormat format = SixLabors.ImageSharp.Image.DetectFormat(fileStream);
using Image<Rgb24> image = SixLabors.ImageSharp.Image.Load<Rgb24>(fileStream);
// Resize image
using Stream imageStream = new MemoryStream();
image.Mutate(x =>
{
x.Resize(new ResizeOptions
{
Size = new SixLabors.ImageSharp.Size(224, 224),
Mode = ResizeMode.Crop
});
});
image.Save(imageStream, format);
// Preprocess image
// We use DenseTensor for multi-dimensional access to populate the image data
var mean = new[] { 0.485f, 0.456f, 0.406f };
var stddev = new[] { 0.229f, 0.224f, 0.225f };
DenseTensor<float> processedImage = new(new[] { 1, 3, 224, 224 });
image.ProcessPixelRows(accessor =>
{
for (int y = 0; y < accessor.Height; y++)
{
Span<Rgb24> pixelSpan = accessor.GetRowSpan(y);
for (int x = 0; x < accessor.Width; x++)
{
processedImage[0, 0, y, x] = ((pixelSpan[x].R / 255f) - mean[0]) / stddev[0];
processedImage[0, 1, y, x] = ((pixelSpan[x].G / 255f) - mean[1]) / stddev[1];
processedImage[0, 2, y, x] = ((pixelSpan[x].B / 255f) - mean[2]) / stddev[2];
}
}
});
Ardından, yönetilen görüntü veri dizisinin üzerinde Tensor türünde bir OrtValue oluşturarak girişleri ayarlayacağız.
// Setup inputs
// Pin tensor buffer and create a OrtValue with native tensor that makes use of
// DenseTensor buffer directly. This avoids extra data copy within OnnxRuntime.
// It will be unpinned on ortValue disposal
using var inputOrtValue = OrtValue.CreateTensorValueFromMemory(OrtMemoryInfo.DefaultInstance,
processedImage.Buffer, new long[] { 1, 3, 224, 224 });
var inputs = new Dictionary<string, OrtValue>
{
{ "data", inputOrtValue }
};
Daha sonra, çıkarım oturumu henüz başlatılmadıysa, InitModel yardımcı yöntemini çağırın. Ardından modeli çalıştırmak ve sonuçları almak için Run yöntemini çağırın.
// Run inference
if (_inferenceSession == null)
{
InitModel();
}
using var runOptions = new RunOptions();
using IDisposableReadOnlyCollection<OrtValue> results = _inferenceSession.Run(runOptions, inputs, _inferenceSession.OutputNames);
Model sonuçları yerel bir tensor arabelleği olarak oluşturur. Aşağıdaki kod, çıkışı bir kayanlar dizisine dönüştürür. Değerlerin [0,1] aralığında ve toplamının 1 olması için softmax işlevi uygulanır.
// Postprocess output
// We copy results to array only to apply algorithms, otherwise data can be accessed directly
// from the native buffer via ReadOnlySpan<T> or Span<T>
var output = results[0].GetTensorDataAsSpan<float>().ToArray();
float sum = output.Sum(x => (float)Math.Exp(x));
IEnumerable<float> softmax = output.Select(x => (float)Math.Exp(x) / sum);
Çıkış dizisinin her bir değerinin indeksi, modelin eğitildiği bir etiketle eşleşir ve bu endeksteki değer, etiketin, giriş görüntüsünde algılanan bir nesneyi temsil ettiğine dair modelin güven düzeyidir. En yüksek güvenilirlik değerine sahip 10 sonucu seçiyoruz. Bu kod, sonraki adımda tanımlayacağımız bazı yardımcı nesneleri kullanır.
// Extract top 10
IEnumerable<Prediction> top10 = softmax.Select((x, i) => new Prediction { Label = LabelMap.Labels[i], Confidence = x })
.OrderByDescending(x => x.Confidence)
.Take(10);
// Print results
featuresTextBlock.Text = "Top 10 predictions for ResNet50 v2...\n";
featuresTextBlock.Text += "-------------------------------------\n";
foreach (var t in top10)
{
featuresTextBlock.Text += $"Label: {t.Label}, Confidence: {t.Confidence}\n";
}
} // End of myButton_Click
Yardımcı nesneleri bildirme
Tahmin sınıfı, bir nesne etiketini güvenilirlik değeriyle ilişkilendirmek için basit bir yol sağlar.
MainPage.xaml.cs
içinde, bu sınıfı ONNXWinUIExample ad alanı bloğunun içine, ancak MainWindow sınıf tanımının dışına ekleyin.
internal class Prediction
{
public object Label { get; set; }
public float Confidence { get; set; }
}
Ardından, etiketlerin model tarafından döndürülen sonuçların dizinlerine eşlenmesi için modelin eğitildiği tüm nesne etiketlerini listeleyen LabelMap yardımcı sınıfını belirli bir sırada ekleyin. Etiket listesi burada tam olarak sunulmak için çok uzun. LabelMap sınıfının tamamını ONNXRuntime github deposundaki bir örnek kod dosyasından kopyalayıp ONNXWinUIExample ad alanı bloğuna yapıştırabilirsiniz.
public class LabelMap
{
public static readonly string[] Labels = new[] {
"tench",
"goldfish",
"great white shark",
...
"hen-of-the-woods",
"bolete",
"ear",
"toilet paper"};
Örneği çalıştırma
Projeyi derleyin ve çalıştırın. Fotoğraf seç düğmesine tıklayın ve analiz etmek için bir resim dosyası seçin. Modelin tanıyabileceği şeyleri görmek ve ilginç sonuçlara sahip olabilecek bir görüntü seçmek için LabelMap yardımcı sınıf tanımına bakabilirsiniz. Model başlatıldıktan sonra, ilk kez çalıştırıldığında ve modelin işlenmesi tamamlandıktan sonra görüntüde algılanan nesnelerin listesini ve her tahminin güvenilirlik değerini görmeniz gerekir.
Top 10 predictions for ResNet50 v2...
-------------------------------------
Label: lakeshore, Confidence: 0.91674984
Label: seashore, Confidence: 0.033412453
Label: promontory, Confidence: 0.008877817
Label: shoal, Confidence: 0.0046836217
Label: container ship, Confidence: 0.001940886
Label: Lakeland Terrier, Confidence: 0.0016400366
Label: maze, Confidence: 0.0012478716
Label: breakwater, Confidence: 0.0012336193
Label: ocean liner, Confidence: 0.0011933135
Label: pier, Confidence: 0.0011284945
Ayrıca bkz.
- Windows'da yapay zekayı kullanmaya başlama
- https://onnxruntime.ai/docs/
- DirectML Execution Provider