Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
In dit artikel wordt uitgelegd hoe u een WinUI 3-app maakt die gebruikmaakt van een ONNX-model om objecten in een afbeelding te classificeren en het vertrouwen van elke classificatie weer te geven. Zie Aan de slag met AI in Windows voor meer informatie over het gebruik van AI- en machine learning-modellen in uw Windows-app.
Bij het gebruik van AI-functies raden we u aan het volgende te bekijken: Verantwoordelijke Generatieve AI-toepassingen en -functies ontwikkelen in Windows.
Wat is de ONNX-runtime?
ONNX Runtime is een platformoverschrijdende machine learning-modelversneller met een flexibele interface voor het integreren van hardwarespecifieke bibliotheken. ONNX Runtime kan worden gebruikt met modellen van PyTorch, Tensorflow/Keras, TFLite, scikit-learnen andere frameworks. Voor meer informatie, zie de ONNX Runtime website bij https://onnxruntime.ai/docs/.
In dit voorbeeld wordt de DirectML Execution Provider gebruikt die de verschillende hardwareopties op Windows-apparaten abstraheert en uitvoert en ondersteuning biedt voor de uitvoering van lokale accelerators, zoals de GPU en NPU.
Voorwaarden
- Op uw apparaat moet de ontwikkelaarsmodus zijn ingeschakeld. Zie voor meer informatie Uw apparaat inschakelen voor ontwikkeling.
- Visual Studio 2022 of hoger met de ontwikkelworkload voor .NET-desktops.
Een nieuwe C# WinUI-app maken
Maak in Visual Studio een nieuw project. Stel in het dialoogvenster Een nieuw project maken het taalfilter in op 'C#' en het projecttypefilter op 'winui'. Selecteer vervolgens de sjabloon Blank app, Packaged (WinUI3 in Desktop). Noem het nieuwe project "ONNXWinUIExample".
Verwijzingen toevoegen aan Nuget-pakketten
Klik in Solution Explorer met de rechtermuisknop op Afhankelijkheden en selecteer NuGet-pakketten beheren.... Selecteer in NuGet Package Manager het tabblad Bladeren . Zoek naar de volgende pakketten en selecteer voor elk pakket de meest recente stabiele versie in de vervolgkeuzelijst Versie en klik vervolgens op Installeren.
Pakket | Beschrijving |
---|---|
Microsoft.ML.OnnxRuntime.DirectML | Biedt API's voor het uitvoeren van ONNX-modellen op de GPU. |
SixLabors.ImageSharp | Biedt hulpprogramma's voor het verwerken van afbeeldingen voor modelinvoer. |
SharpDX.DXGI | Biedt API's voor toegang tot het DirectX-apparaat vanuit C#. |
Voeg de volgende using instructies toe aan de bovenkant van MainWindows.xaml.cs
om toegang te krijgen tot de API's van deze bibliotheken.
// 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;
Het model toevoegen aan uw project
Klik in Solution Explorer met de rechtermuisknop op uw project en selecteer Nieuwe map toevoegen>. Geef de nieuwe map de naam 'model'. In dit voorbeeld gebruiken we het resnet50-v2-7.onnx-model van https://github.com/onnx/models. Ga naar de repo voor model https://github.com/onnx/models/blob/main/validated/vision/classification/resnet/model/resnet50-v2-7.onnx. Klik op de knop *Onbewerkt bestand downloaden . Kopieer dit bestand naar de map "model" die u net hebt gemaakt.
Klik in Solution Explorer op het modelbestand en stel Kopiëren naar uitvoermap in op 'Kopiëren indien nieuwer'.
Een eenvoudige gebruikersinterface maken
In dit voorbeeld maken we een eenvoudige gebruikersinterface met een knop waarmee de gebruiker een afbeelding kan selecteren die kan worden geëvalueerd met het model, een besturingselement Afbeelding om de geselecteerde afbeelding weer te geven en een TextBlock om de objecten weer te geven die het model in de afbeelding heeft gedetecteerd en het vertrouwen van elke objectclassificatie.
Vervang in het MainWindow.xaml
bestand het standaard-StackPanel-element door de volgende XAML-code.
<!--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>
Het model initialiseren
Maak in het MainWindow.xaml.cs
bestand in de klasse MainWindow een helpermethode met de naam InitModel waarmee het model wordt geïnitialiseerd. Deze methode maakt gebruik van API's uit de SharpDX.DXGI-bibliotheek om de eerste beschikbare adapter te selecteren. De geselecteerde adapter is ingesteld in het SessionOptions-object voor de DirectML-uitvoeringsprovider in deze sessie. Ten slotte wordt een nieuwe deductieSessie geïnitialiseerd, waarbij het pad naar het modelbestand en de sessieopties worden doorgegeven.
// 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);
}
Een afbeelding laden en analyseren
Om het eenvoudig te maken, worden in dit voorbeeld alle stappen voor het laden en opmaken van de afbeelding, het aanroepen van het model en het weergeven van de resultaten in de knopklikhandler geplaatst. Houd er rekening mee dat we het asynchrone trefwoord toevoegen aan de knopklikhandler die is opgenomen in de standaardsjabloon, zodat we asynchrone bewerkingen in de handler kunnen uitvoeren.
// MainWindow.xaml.cs
private async void myButton_Click(object sender, RoutedEventArgs e)
{
...
}
Gebruik een FileOpenPicker om de gebruiker in staat te stellen een afbeelding op de computer te selecteren om deze te analyseren en weer te geven in de gebruikersinterface.
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;
Vervolgens moeten we de invoer verwerken om deze op te halen in een indeling die wordt ondersteund door het model. De bibliotheek SixLabors.ImageSharp wordt gebruikt om de afbeelding in 24-bits RGB-indeling te laden en het formaat van de afbeelding te wijzigen in 224 x 224 pixels. Vervolgens worden de pixelwaarden genormaliseerd met een gemiddelde van 255*[0,485, 0,456, 0,406] en standaarddeviatie van 255*[0,229, 0,224, 0,225]. De details van de indeling die het model verwacht, vindt u op de github-pagina het resnet-model.
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];
}
}
});
Vervolgens stellen we de invoer in door een OrtValue van het Tensor-type te maken boven op de matrix met beheerde afbeeldingsgegevens.
// 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 }
};
Als de deductiesessie nog niet is geïnitialiseerd, roept u de InitModel-helpermethode aan. Roep vervolgens de methode Uitvoeren aan om het model uit te voeren en de resultaten op te halen.
// Run inference
if (_inferenceSession == null)
{
InitModel();
}
using var runOptions = new RunOptions();
using IDisposableReadOnlyCollection<OrtValue> results = _inferenceSession.Run(runOptions, inputs, _inferenceSession.OutputNames);
Het model voert de resultaten uit als een systeemeigen tensorbuffer. Met de volgende code wordt de uitvoer geconverteerd naar een matrix met floats. Er wordt een softmax-functie toegepast, zodat de waarden in het bereik [0,1] en som tot 1 liggen.
// 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);
De index van elke waarde in de uitvoermatrix wordt toegewezen aan een label waarop het model is getraind en de waarde op die index is het vertrouwen van het model dat het label een object vertegenwoordigt dat in de invoerafbeelding is gedetecteerd. We kiezen de 10 resultaten met de hoogste betrouwbaarheidswaarde. In deze code worden enkele helperobjecten gebruikt die in de volgende stap worden gedefinieerd.
// 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
Helperobjecten declareren
De voorspellingsklasse biedt een eenvoudige manier om een objectlabel te koppelen aan een betrouwbaarheidswaarde. Voeg MainPage.xaml.cs
deze klasse toe in het naamruimteblok ONNXWinUIExample , maar buiten de klassedefinitie MainWindow .
internal class Prediction
{
public object Label { get; set; }
public float Confidence { get; set; }
}
Voeg vervolgens de LabelMap-helperklasse toe waarin alle objectlabels worden vermeld waarop het model is getraind, in een specifieke volgorde, zodat de labels worden toegewezen aan de indexen van de resultaten die door het model worden geretourneerd. De lijst met labels is te lang om hier volledig te presenteren. U kunt de volledige LabelMap-klasse kopiëren uit een voorbeeldcodebestand in de GITHub-opslagplaats ONNXRuntime en deze in het naamruimteblok ONNXWinUIExample plakken.
public class LabelMap
{
public static readonly string[] Labels = new[] {
"tench",
"goldfish",
"great white shark",
...
"hen-of-the-woods",
"bolete",
"ear",
"toilet paper"};
Het voorbeeld uitvoeren
Bouw het project en voer het uit. Klik op de knop Foto selecteren en kies een afbeeldingsbestand dat u wilt analyseren. U kunt de klassedefinitie van de LabelMap-helper bekijken om te zien wat het model kan herkennen en een afbeelding kunnen kiezen die mogelijk interessante resultaten heeft. Nadat het model is geïnitialiseerd, de eerste keer dat het wordt uitgevoerd, en nadat de verwerking van het model is voltooid, ziet u een lijst met objecten die zijn gedetecteerd in de afbeelding en de betrouwbaarheidswaarde van elke voorspelling.
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