Sdílet prostřednictvím


Kurz: Vytvoření desktopové aplikace Windows Machine Learning (C++)

Rozhraní API pro Windows ML je možné využít k snadné interakci s modely strojového učení v desktopových aplikacích C++ (Win32). Díky třem krokům načítání, vazby a vyhodnocování může vaše aplikace těžit z výkonu strojového učení.

Načtení –> vazba –> vyhodnocení

Vytvoříme poněkud zjednodušenou verzi ukázky detekce objektů SqueezeNet, která je k dispozici na GitHubu. Kompletní ukázku si můžete stáhnout, pokud chcete zjistit, jak bude vypadat po dokončení.

K přístupu k rozhraním API WinML použijeme C++/WinRT. Další informace najdete v tématu C++/WinRT .

V tomto kurzu se naučíte:

  • Načíst model strojového učení
  • Načtení obrázku jako VideoFrame
  • Vytvoření vazby vstupů a výstupů modelu
  • Vyhodnocení modelu a tisk smysluplných výsledků

Požadavky

Vytvoření projektu

Nejprve vytvoříme projekt v sadě Visual Studio:

  1. Výběrem Soubor > Nový > Projekt otevřete okno Nový projekt.
  2. V levém podokně vyberte Nainstalované > Visual C++ Windows Desktop >, a uprostřed vyberte Windows Console Application (C++/WinRT).
  3. Zadejte název a umístění projektu a klikněte na tlačítko OK.
  4. V okně Nový projekt univerzální platformy Windows nastavte cílové i minimální verze na build 17763 nebo novější a klikněte na tlačítko OK.
  5. Ujistěte se, že rozevírací nabídky na horní liště jsou nastavené na Debugging a podle architektury vašeho počítače buď na x64 nebo x86.
  6. Stisknutím kombinace kláves Ctrl+F5 spusťte program bez ladění. Terminál by se měl otevřít s textem "Hello world". Stisknutím libovolné klávesy ji zavřete.

Načtení modelu

V dalším kroku načteme model ONNX do našeho programu pomocí LearningModel.LoadFromFilePath:

  1. Do souboru pch.h (ve složce Soubory hlaviček ) přidejte následující include příkazy (ty nám poskytují přístup ke všem rozhraním API, která budeme potřebovat):

    #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. Do main.cpp (ve složce Zdrojové soubory ) přidejte následující using příkazy:

    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. Za příkazy using přidejte následující deklarace proměnných:

    // 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. Za globální proměnné přidejte následující předběžné deklarace:

    // Forward declarations
    void LoadModel();
    VideoFrame LoadImageFile(hstring filePath);
    void BindModel();
    void EvaluateModel();
    void PrintResults(IVectorView<float> results);
    void LoadLabels();
    
  5. V main.cpp odeberte kód "Hello world" (všechny části kódu ve funkci main po init_apartment).

  6. Vyhledejte soubor SqueezeNet.onnx v místním klonu úložiště Windows-Machine-Learning . Měl by být umístěný ve složce \Windows-Machine-Learning\SharedContent\models.

  7. Zkopírujte cestu k souboru a přiřaďte ji k proměnné modelPath , kde jsme ji definovali v horní části. Nezapomeňte před řetězec přidat L, aby se stal širokým znakovým řetězcem a správně fungoval s hstring, a upravit všechna zpětná lomítka (\) dodatečným zpětným lomítkem. Například:

    hstring modelPath = L"C:\\Repos\\Windows-Machine-Learning\\SharedContent\\models\\SqueezeNet.onnx";
    
  8. Nejprve implementujeme metodu LoadModel . Přidejte následující metodu za metodu main. Tato metoda načte model a zobrazí, jak dlouho to trvalo.

    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. Nakonec volejte tuto metodu main z metody:

    LoadModel();
    
  10. Spusťte program bez ladění. Měli byste vidět, že se model načítá úspěšně!

Načíst obrázek

V dalším kroku načteme soubor obrázku do našeho programu:

  1. Přidejte následující metodu. Tato metoda načte obrázek z dané cesty a vytvoří z ní videoframe :

    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. Přidejte volání této metody do metody main:

    imageFrame = LoadImageFile(imagePath);
    
  3. Vyhledejte složku médií v místním klonu úložiště Windows-Machine-Learning . Měla by být umístěna ve složce \Windows-Machine-Learning\SharedContent\media.

  4. Vyberte jeden z obrázků v této složce a přiřaďte jeho cestu k souboru proměnné imagePath, kterou jsme definovali nahoře. Nezapomeňte ho předepsat jako L řetězec širokých znaků a escapeovat všechna zpětná lomítka dalším zpětným lomítkem. Například:

    hstring imagePath = L"C:\\Repos\\Windows-Machine-Learning\\SharedContent\\media\\kitten_224.png";
    
  5. Spusťte program bez ladění. Měl by se zobrazit úspěšně načtený obrázek.

Propojte vstup a výstup

Dále vytvoříme relaci založenou na modelu a vytvoříme vazbu vstupu a výstupu z relace pomocí LearningModelBinding.Bind. Další informace o vazbě naleznete v části Vazba modelu.

  1. Implementujte metodu BindModel. Tím se vytvoří relace založená na modelu a zařízení a vazba na základě této relace. Potom svážeme vstupy a výstupy s proměnnými, které jsme vytvořili pomocí jejich názvů. Předem víme, že vstupní funkce má název "data_0" a výstupní funkce má název "softmaxout_1". Tyto vlastnosti můžete zobrazit pro libovolný model tak, že je otevřete v Netronu, online nástroji pro vizualizaci modelu.

    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. Přidejte volání BindModel z metody main:

    BindModel();
    
  3. Spusťte program bez ladění. Vstupy a výstupy modelu by měly být úspěšně svázány. Jsme skoro tam!

Vyhodnocení modelu

Teď jsme na posledním kroku diagramu na začátku tohoto kurzu Vyhodnocení. Model vyhodnotíme pomocí LearningModelSession.Evaluate:

  1. Implementujte metodu EvaluateModel. Tato metoda přebírá naši relaci a vyhodnocuje ji pomocí naší vazby a ID korelace. ID korelace je něco, co bychom později mohli použít k porovnání konkrétního volání vyhodnocení s výsledky výstupu. Znovu víme předem, že název výstupu je "softmaxout_1".

    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. Teď implementujme PrintResults. Tato metoda získá nejvyšší tři pravděpodobnosti pro objekt, který by mohl být na obrázku, a vytiskne je:

    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. Musíme také implementovat LoadLabels. Tato metoda otevře soubor popisků, který obsahuje všechny různé objekty, které model dokáže rozpoznat, a parsuje ho:

    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. Vyhledejte soubor Labels.txt v místním klonu úložiště Windows-Machine-Learning . Měla by být ve složce \Windows-Machine-Learning\Samples\SqueezeNetObjectDetection\Desktop\cpp.

  5. Přiřaďte tuto cestu k souboru proměnné labelsFilePath, kde jsme ji definovali na začátku. Nezapomeňte ošetřit všechna zpětná lomítka dalším zpětným lomítkem. Například:

    string labelsFilePath = "C:\\Repos\\Windows-Machine-Learning\\Samples\\SqueezeNetObjectDetection\\Desktop\\cpp\\Labels.txt";
    
  6. Přidejte volání do EvaluateModel metody pomocí main:

    EvaluateModel();
    
  7. Spusťte program bez ladění. Teď by měl správně rozpoznat, co je na obrázku! Tady je příklad toho, co může být výstup:

    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
    

Další kroky

Hooray, máte v desktopové aplikaci C++ funkční rozpoznávání objektů! Dále můžete zkusit použít argumenty příkazového řádku k zadání souborů modelu a obrázků místo jejich pevně zakódování, podobně jako ukázka na GitHubu. Můžete také zkusit spustit vyhodnocení na jiném zařízení, jako je GPU, a podívat se, jak se výkon liší.

Vyzkoušejte si další ukázky na GitHubu a rozšiřte je, jak chcete!

Viz také

Poznámka:

Pomoc s Windows ML vám poskytnou následující zdroje:

  • Pokud chcete pokládat nebo odpovídat na technické otázky týkající se Windows ML, použijte značku windows-machine-learning ve službě Stack Overflow.
  • Pokud chcete nahlásit chybu, zapište prosím problém na našem GitHubu .