Aracılığıyla paylaş


Öğretici: Windows Machine Learning Masaüstü uygulaması oluşturma (C++)

C++ masaüstü (Win32) uygulamalarındaki makine öğrenmesi modelleriyle kolayca etkileşime geçmek için Windows ML API'leri kullanılabilir. Yükleme, bağlama ve değerlendirmenin üç adımını kullanarak uygulamanız makine öğrenmesinin gücünden yararlanabilir.

Yükle -> Bağla -> Değerlendir

GitHub'da bulunan SqueezeNet Nesne Algılama örneğinin biraz basitleştirilmiş bir sürümünü oluşturacağız. bitirdiğinizde nasıl olacağını görmek istiyorsanız, örneğin tamamını indirebilirsiniz.

WinML API'lerine erişmek için C++/WinRT kullanacağız. Daha fazla bilgi için bkz. C++/WinRT .

Bu öğreticide aşağıdakilerin nasıl yapılacağını öğreneceksiniz:

  • Makine öğrenmesi modeli yükleme
  • Görüntüyü VideoFrame olarak yükleme
  • Modelin girişlerini ve çıkışlarını bağlama
  • Modeli değerlendirme ve anlamlı sonuçlar yazdırma

Önkoşullar

Projeyi oluşturma

İlk olarak projeyi Visual Studio'da oluşturacağız:

  1. Dosya > Yeni > Proje'yi seçerek Yeni Proje penceresini açın.
  2. Sol bölmede Yüklü > Visual C++ > Windows Masaüstü'nü seçin ve ortada Windows Konsol Uygulaması (C++/WinRT) öğesini seçin.
  3. Projenize bir Ad ve Konum verin, ardından Tamam'a tıklayın.
  4. Yeni Evrensel Windows Platformu Projesi penceresinde Hedef ve En Düşük Sürümler'i 17763 veya sonraki bir sürüme ayarlayın ve Tamam'a tıklayın.
  5. Üst araç çubuğundaki açılan menülerin, bilgisayarınızın mimarisine bağlı olarak Hata Ayıkla ve x64 veya x86 olarak ayarlandığından emin olun.
  6. Programı hata ayıklamadan çalıştırmak için Ctrl+F5 tuşlarına basın. Terminal bir "Merhaba dünya" metniyle açılmalıdır. Kapatmak için herhangi bir tuşa basın.

Modeli yükleme

Şimdi LearningModel.LoadFromFilePath kullanarak ONNX modelini programımıza yükleyeceğiz:

  1. pch.h dosyasında (Üst Bilgi Dosyaları klasörüne) aşağıdaki include deyimleri ekleyin (bunlar bize ihtiyacımız olan tüm API'lere erişim sağlar):

    #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. main.cpp(Kaynak Dosyalar klasörüne) aşağıdaki using deyimleri ekleyin:

    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. using ifadelerinden sonra aşağıdaki değişken bildirimlerini ekleyin.

    // 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. Genel değişkenlerinizden sonra aşağıdaki iletme bildirimlerini ekleyin:

    // Forward declarations
    void LoadModel();
    VideoFrame LoadImageFile(hstring filePath);
    void BindModel();
    void EvaluateModel();
    void PrintResults(IVectorView<float> results);
    void LoadLabels();
    
  5. main.cpp'de, main'den sonraki init_apartment işlevindeki "Merhaba dünya" kodunu kaldırın.

  6. Windows-Machine-Learning deposunun yerel kopyasında SqueezeNet.onnx dosyasını bulun. \Windows-Machine-Learning\SharedContent\models konumunda olmalıdır.

  7. Dosya yolunu kopyalayın ve en üstte tanımladığımız değişkeninize modelPath atayın. Dizeyi bir geniş karakter dizgisi yapmak ve L ile düzgün çalışmasını sağlamak için başına bir hstring eklemeyi ve ters eğik çizgileri (\) kaçış karakteri olarak kaydetmek için ekstra bir ters eğik çizgi eklemeyi unutmayın. Örneğin:

    hstring modelPath = L"C:\\Repos\\Windows-Machine-Learning\\SharedContent\\models\\SqueezeNet.onnx";
    
  8. İlk olarak yöntemini uygulayacağız LoadModel . yönteminden sonra main aşağıdaki yöntemi ekleyin. Bu yöntem modeli yükler ve ne kadar sürdüğünü çıkış olarak oluşturur:

    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. Son olarak, bu yöntemi main yönteminden çağırın.

    LoadModel();
    
  10. Hata ayıklamadan programınızı çalıştırın. Modelinizin başarıyla yüklendiğini görmeniz gerekir!

Görüntüyü yükleme

Ardından görüntü dosyasını programımıza yükleyeceğiz:

  1. Aşağıdaki yöntemi ekleyin. Bu yöntem, verilen yoldan görüntüyü yükler ve görüntüden bir VideoFrame oluşturur:

    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. Bu yönteme, main yönteminde bir çağrı ekleyin.

    imageFrame = LoadImageFile(imagePath);
    
  3. Windows-Machine-Learning deposunun yerel kopyasında medya klasörünü bulun. \Windows-Machine-Learning\SharedContent\media konumunda olmalıdır.

  4. Bu klasördeki görüntülerden birini seçin ve dosya yolunu en üstte tanımladığımız değişkene imagePath atayın. Bunu geniş bir karakter dizesi yapmak ve ters eğik çizgilerden başka bir ters eğik çizgiyle kaçmak için bir ön eki L eklemeyi unutmayın. Örneğin:

    hstring imagePath = L"C:\\Repos\\Windows-Machine-Learning\\SharedContent\\media\\kitten_224.png";
    
  5. Programı hata ayıklamadan çalıştırın. Görüntünün başarıyla yüklendiğini görmeniz gerekir!

Girişi ve çıkışı bağlama

Ardından modeli temel alan bir oturum oluşturacak ve LearningModelBinding.Bind kullanarak oturumdaki giriş ve çıkışı bağlayacağız. Bağlama hakkında daha fazla bilgi için bkz Model bağlama.

  1. BindModel yöntemini uygulayın. Bu, modeli ve cihazı temel alan bir oturum ve bu oturumu temel alan bir bağlama oluşturur. Ardından girişleri ve çıkışları, adlarını kullanarak oluşturduğumuz değişkenlere bağlarız. Giriş özelliğinin "data_0" ve çıkış özelliğinin "softmaxout_1" olarak adlandırıldığını önceden biliyoruz. Bu özellikleri çevrimiçi bir model görselleştirme aracı olan Netron'da açarak herhangi bir modelin özelliklerini görebilirsiniz.

    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. BindModel çağrısını main yöntemine ekleyin.

    BindModel();
    
  3. Programı hata ayıklamadan çalıştırın. Modelin girişleri ve çıkışları başarıyla bağlanmalıdır. Neredeyse geldik!

Modeli değerlendirme

Şimdi bu öğreticinin başındaki diyagramdaki son adım olan Değerlendir'deyiz. LearningModelSession.Evaluate kullanarak modeli değerlendireceğiz:

  1. EvaluateModel yöntemini uygulayın. Bu yöntem oturumumuzu alır ve bağlama ve bağıntı kimliğimizi kullanarak değerlendirir. Bağıntı kimliği, daha sonra çıkış sonuçlarıyla belirli bir değerlendirme çağrısını eşleştirmek için kullanabileceğimiz bir kimliktir. Yine, çıkışın adının "softmaxout_1" olduğunu önceden biliyoruz.

    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. PrintResults'yi şimdi uygulayalım. Bu yöntem, görüntüde hangi nesnenin olabileceğine ilişkin ilk üç olasılığı alır ve bunları yazdırır:

    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. Ayrıca LoadLabels uygulamamız gerekir. Bu yöntem, modelin tanıyabileceği tüm farklı nesneleri içeren etiket dosyasını açar ve ayrıştırabilir:

    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. Windows-Machine-Learning deposunun yerel kopyasında Labels.txt dosyasını bulun. \Windows-Machine-Learning\Samples\SqueezeNetObjectDetection\Desktop\cpp içinde olmalıdır.

  5. Bu dosya yolunu labelsFilePath en üstte tanımladığımız değişkene atayın. Ters eğik çizgilerden başka bir ters eğik çizgiyle kaçış yaptığınızdan emin olun. Örneğin:

    string labelsFilePath = "C:\\Repos\\Windows-Machine-Learning\\Samples\\SqueezeNetObjectDetection\\Desktop\\cpp\\Labels.txt";
    
  6. EvaluateModel yönteminde main çağrısı ekleyin.

    EvaluateModel();
    
  7. Programı hata ayıklamadan çalıştırın. Artık görüntüdekileri doğru şekilde tanıması gerekir! Örnek olarak çıktısının ne olabileceği aşağıda verilmiştir:

    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
    

Sonraki Adımlar

Yaşasın, C++ masaüstü uygulamanızda nesne algılamayı çalıştırmayı başardınız! Ardından, GitHub'daki örneğin yaptığı gibi model ve görüntü dosyalarını sabit kodlamak yerine giriş yapmak için komut satırı bağımsız değişkenlerini kullanmayı deneyebilirsiniz. Performansın nasıl farklılık gösterdiğini görmek için değerlendirmeyi GPU gibi farklı bir cihazda çalıştırmayı da deneyebilirsiniz.

GitHub'da diğer örneklerle oynayın ve istediğiniz gibi genişletin!

Ayrıca bakınız

Uyarı

Windows ML ile ilgili yardım için aşağıdaki kaynakları kullanın:

  • Windows ML hakkında teknik sorular sormak veya yanıtlamak için stack overflow'dawindows-machine-learning etiketini kullanın.
  • Bir hatayı bildirmek için lütfen GitHub'ımızda bir sorun oluşturun.