Delen via


Zelfstudie: Een Windows Machine Learning Desktop-toepassing maken (C++)

Windows ML-API's kunnen worden gebruikt om eenvoudig te communiceren met machine learning-modellen in C++-bureaubladtoepassingen (Win32). Met behulp van de drie stappen van laden, koppelen en evalueren kan uw applicatie profiteren van de kracht van machine learning.

Laden -> Binden -> Evalueren

We maken een enigszins vereenvoudigde versie van het SqueezeNet Object Detection-voorbeeld, dat beschikbaar is op GitHub. U kunt het volledige voorbeeld downloaden als u wilt zien hoe het zal zijn wanneer u klaar bent.

We gebruiken C++/WinRT om toegang te krijgen tot de WinML-API's. Zie C++/WinRT voor meer informatie.

In deze handleiding leer je hoe je:

  • Een machine learning-model laden
  • Een afbeelding laden als een VideoFrame
  • De invoer en uitvoer van het model binden
  • Het model evalueren en zinvolle resultaten afdrukken

Vereiste voorwaarden

Het project maken

Eerst maken we het project in Visual Studio:

  1. Selecteer Bestand > Nieuw > Project om het venster Nieuw project te openen.
  2. Selecteer in het linkerdeelvenster Geïnstalleerde > Visual C++ > Windows Desktop en selecteer in het midden Windows Console Application (C++/WinRT).
  3. Geef uw project een naam en locatie en klik vervolgens op OK.
  4. Stel in het venster Nieuw Universeel Windows-platformproject het doel en de minimale versies in op build 17763 of hoger en klik op OK.
  5. Zorg ervoor dat de vervolgkeuzelijsten in de bovenste werkbalk zijn ingesteld op Foutopsporing en x64 of x86 , afhankelijk van de architectuur van uw computer.
  6. Druk op Ctrl+F5 om het programma uit te voeren zonder foutopsporing. Er moet een terminal worden geopend met een beetje tekst 'Hallo wereld'. Druk op een willekeurige toets om deze te sluiten.

Het model laden

Vervolgens laden we het ONNX-model in ons programma met behulp van LearningModel.LoadFromFilePath:

  1. Voeg in pch.h (in de map Header Files ) de volgende include instructies toe (deze geven ons toegang tot alle API's die we nodig hebben):

    #include <winrt/Windows.AI.MachineLearning.h>
    #include <winrt/Windows.Foundation.Collections.h>
    #include <winrt/Windows.Graphics.Imaging.h>
    #include <winrt/Windows.Media.h>
    #include <winrt/Windows.Storage.h>
    
    #include <string>
    #include <fstream>
    
    #include <Windows.h>
    
  2. Voeg in main.cpp (in de map Bronbestanden ) de volgende using instructies toe:

    using namespace Windows::AI::MachineLearning;
    using namespace Windows::Foundation::Collections;
    using namespace Windows::Graphics::Imaging;
    using namespace Windows::Media;
    using namespace Windows::Storage;
    
    using namespace std;
    
  3. Voeg de volgende variabeledeclaraties toe na de using instructies:

    // Global variables
    hstring modelPath;
    string deviceName = "default";
    hstring imagePath;
    LearningModel model = nullptr;
    LearningModelDeviceKind deviceKind = LearningModelDeviceKind::Default;
    LearningModelSession session = nullptr;
    LearningModelBinding binding = nullptr;
    VideoFrame imageFrame = nullptr;
    string labelsFilePath;
    vector<string> labels;
    
  4. Voeg de volgende forward-declaraties toe na uw globale variabelen:

    // Forward declarations
    void LoadModel();
    VideoFrame LoadImageFile(hstring filePath);
    void BindModel();
    void EvaluateModel();
    void PrintResults(IVectorView<float> results);
    void LoadLabels();
    
  5. Verwijder in main.cpp de code 'Hallo wereld' (alles in de main functie erna init_apartment).

  6. Zoek het SqueezeNet.onnx-bestand in uw lokale kloon van de Windows-Machine Learning-opslagplaats . Deze moet zich bevinden in \Windows-Machine-Learning\SharedContent\models.

  7. Kopieer het bestandspad en wijs het toe aan uw modelPath variabele waar we het bovenaan hebben gedefinieerd. Vergeet niet om de tekenreeks te beginnen met een L zodat het een brede tekenreeks wordt, zodat deze goed werkt met hstring, en om backslashes (\) te escapen met een extra backslash. Voorbeeld:

    hstring modelPath = L"C:\\Repos\\Windows-Machine-Learning\\SharedContent\\models\\SqueezeNet.onnx";
    
  8. Eerst implementeren we de LoadModel methode. Voeg de volgende methode toe na de main methode. Met deze methode wordt het model geladen en wordt uitgevoerd hoe lang het duurde:

    void LoadModel()
    {
         // load the model
         printf("Loading modelfile '%ws' on the '%s' device\n", modelPath.c_str(), deviceName.c_str());
         DWORD ticks = GetTickCount();
         model = LearningModel::LoadFromFilePath(modelPath);
         ticks = GetTickCount() - ticks;
         printf("model file loaded in %d ticks\n", ticks);
    }
    
  9. Roep tot slot deze methode aan vanuit de main methode:

    LoadModel();
    
  10. Voer uw programma uit zonder foutopsporing. U zou moeten zien dat uw model succesvol is geladen!

De afbeelding laden

Vervolgens laden we het afbeeldingsbestand in ons programma:

  1. Voeg de volgende methode toe. Met deze methode wordt de afbeelding vanuit het opgegeven pad geladen en wordt er een VideoFrame van gemaakt:

    VideoFrame LoadImageFile(hstring filePath)
    {
        printf("Loading the image...\n");
        DWORD ticks = GetTickCount();
        VideoFrame inputImage = nullptr;
    
        try
        {
            // open the file
            StorageFile file = StorageFile::GetFileFromPathAsync(filePath).get();
            // get a stream on it
            auto stream = file.OpenAsync(FileAccessMode::Read).get();
            // Create the decoder from the stream
            BitmapDecoder decoder = BitmapDecoder::CreateAsync(stream).get();
            // get the bitmap
            SoftwareBitmap softwareBitmap = decoder.GetSoftwareBitmapAsync().get();
            // load a videoframe from it
            inputImage = VideoFrame::CreateWithSoftwareBitmap(softwareBitmap);
        }
        catch (...)
        {
            printf("failed to load the image file, make sure you are using fully qualified paths\r\n");
            exit(EXIT_FAILURE);
        }
    
        ticks = GetTickCount() - ticks;
        printf("image file loaded in %d ticks\n", ticks);
        // all done
        return inputImage;
    }
    
  2. Voeg een aanroep toe aan deze methode in de main methode:

    imageFrame = LoadImageFile(imagePath);
    
  3. Zoek de mediamap in uw lokale kloon van de Windows-Machine Learning-opslagplaats . Deze moet zich bevinden op \Windows-Machine-Learning\SharedContent\media.

  4. Kies een van de afbeeldingen in die map en wijs het bestandspad toe aan de imagePath variabele waar we deze bovenaan hebben gedefinieerd. Vergeet niet om het te voorzien van een voorvoegsel L om er een brede tekenreeks van te maken en om eventuele backslashes met een tweede backslash te escapen. Voorbeeld:

    hstring imagePath = L"C:\\Repos\\Windows-Machine-Learning\\SharedContent\\media\\kitten_224.png";
    
  5. Voer het programma uit zonder foutopsporing. U zou moeten zien dat de afbeelding succesvol is geladen!

De invoer en uitvoer binden

Vervolgens maken we een sessie op basis van het model en binden we de invoer en uitvoer van de sessie met behulp van LearningModelBinding.Bind. Zie Een model binden voor meer informatie over binding.

  1. Implementeer de methode BindModel. Hiermee maakt u een sessie op basis van het model en apparaat en een binding op basis van die sessie. Vervolgens binden we de invoer en uitvoer aan variabelen die we hebben gemaakt met behulp van hun namen. We weten van tevoren dat de invoerfunctie de naam 'data_0' heeft en dat de uitvoerfunctie de naam 'softmaxout_1' heeft. U kunt deze eigenschappen voor elk model zien door ze te openen in Netron, een hulpprogramma voor onlinemodelvisualisatie.

    void BindModel()
    {
        printf("Binding the model...\n");
        DWORD ticks = GetTickCount();
    
        // now create a session and binding
        session = LearningModelSession{ model, LearningModelDevice(deviceKind) };
        binding = LearningModelBinding{ session };
        // bind the intput image
        binding.Bind(L"data_0", ImageFeatureValue::CreateFromVideoFrame(imageFrame));
        // bind the output
        vector<int64_t> shape({ 1, 1000, 1, 1 });
        binding.Bind(L"softmaxout_1", TensorFloat::Create(shape));
    
        ticks = GetTickCount() - ticks;
        printf("Model bound in %d ticks\n", ticks);
    }
    
  2. Voeg een aanroep toe aan BindModel vanuit de main-methode.

    BindModel();
    
  3. Voer het programma uit zonder foutopsporing. De invoer en uitvoer van het model moeten succesvol worden gekoppeld. We zijn er bijna!

Het model evalueren

We zijn nu bezig met de laatste stap in het diagram aan het begin van deze zelfstudie, Evalueren. We evalueren het model met behulp van LearningModelSession.Evaluate:

  1. Implementeer de methode EvaluateModel. Deze methode neemt onze sessie en evalueert deze met behulp van onze binding en een correlatie-id. De correlatie-id is iets dat we later kunnen gebruiken om een bepaalde evaluatieoproep aan te passen aan de uitvoerresultaten. Nogmaals, we weten van tevoren dat de naam van de uitvoer 'softmaxout_1' is.

    void EvaluateModel()
    {
        // now run the model
        printf("Running the model...\n");
        DWORD ticks = GetTickCount();
    
        auto results = session.Evaluate(binding, L"RunId");
    
        ticks = GetTickCount() - ticks;
        printf("model run took %d ticks\n", ticks);
    
        // get the output
        auto resultTensor = results.Outputs().Lookup(L"softmaxout_1").as<TensorFloat>();
        auto resultVector = resultTensor.GetAsVectorView();
        PrintResults(resultVector);
    }
    
  2. Laten we nu PrintResults implementeren. Met deze methode worden de drie belangrijkste waarschijnlijkheden opgehaald voor welk object zich in de afbeelding kan bevinden en worden ze afgedrukt:

    void PrintResults(IVectorView<float> results)
    {
        // load the labels
        LoadLabels();
        // Find the top 3 probabilities
        vector<float> topProbabilities(3);
        vector<int> topProbabilityLabelIndexes(3);
        // SqueezeNet returns a list of 1000 options, with probabilities for each, loop through all
        for (uint32_t i = 0; i < results.Size(); i++)
        {
            // is it one of the top 3?
            for (int j = 0; j < 3; j++)
            {
                if (results.GetAt(i) > topProbabilities[j])
                {
                    topProbabilityLabelIndexes[j] = i;
                    topProbabilities[j] = results.GetAt(i);
                    break;
                }
            }
        }
        // Display the result
        for (int i = 0; i < 3; i++)
        {
            printf("%s with confidence of %f\n", labels[topProbabilityLabelIndexes[i]].c_str(), topProbabilities[i]);
        }
    }
    
  3. We moeten ook LoadLabels implementeren. Met deze methode wordt het labelbestand geopend dat alle verschillende objecten bevat die het model kan herkennen en parseert:

    void LoadLabels()
    {
        // Parse labels from labels file.  We know the file's entries are already sorted in order.
        ifstream labelFile{ labelsFilePath, ifstream::in };
        if (labelFile.fail())
        {
            printf("failed to load the %s file.  Make sure it exists in the same folder as the app\r\n", labelsFilePath.c_str());
            exit(EXIT_FAILURE);
        }
    
        std::string s;
        while (std::getline(labelFile, s, ','))
        {
            int labelValue = atoi(s.c_str());
            if (labelValue >= labels.size())
            {
                labels.resize(labelValue + 1);
            }
            std::getline(labelFile, s);
            labels[labelValue] = s;
        }
    }
    
  4. Zoek het Labels.txt bestand in uw lokale kloon van de Windows-Machine Learning-opslagplaats . Deze moet zich in \Windows-Machine-Learning\Samples\SqueezeNetObjectDetection\Desktop\cpp bevinden.

  5. Wijs dit bestandspad toe aan de labelsFilePath variabele waar we het bovenaan hebben gedefinieerd. Zorg ervoor dat u alle backslashes met een andere backslash escapet. Voorbeeld:

    string labelsFilePath = "C:\\Repos\\Windows-Machine-Learning\\Samples\\SqueezeNetObjectDetection\\Desktop\\cpp\\Labels.txt";
    
  6. Voeg een aanroep toe aan EvaluateModel in de main-methode.

    EvaluateModel();
    
  7. Voer het programma uit zonder foutopsporing. Het moet nu correct herkennen wat er in de afbeelding staat. Hier volgt een voorbeeld van wat het kan uitvoeren:

    Loading modelfile 'C:\Repos\Windows-Machine-Learning\SharedContent\models\SqueezeNet.onnx' on the 'default' device
    model file loaded in 250 ticks
    Loading the image...
    image file loaded in 78 ticks
    Binding the model...Model bound in 15 ticks
    Running the model...
    model run took 16 ticks
    tabby, tabby cat with confidence of 0.931461
    Egyptian cat with confidence of 0.065307
    Persian cat with confidence of 0.000193
    

Volgende stappen

Hoera, u hebt objectdetectie werkend gekregen in een C++-bureaubladtoepassing! Vervolgens kunt u opdrachtregelargumenten gebruiken om het model- en afbeeldingsbestanden in te voeren in plaats van ze te coderen, vergelijkbaar met wat het voorbeeld op GitHub doet. U kunt ook proberen de evaluatie uit te voeren op een ander apparaat, zoals de GPU, om te zien hoe de prestaties verschillen.

Speel met de andere voorbeelden op GitHub en breid ze uit zoals u wilt.

Zie ook

Opmerking

Gebruik de volgende bronnen voor hulp bij Windows ML:

  • Als u technische vragen over Windows ML wilt stellen of beantwoorden, gebruikt u de tag windows-machine learning op Stack Overflow.
  • Als u een fout wilt melden, kunt u een ticket aanmaken op onze GitHub-pagina .