Bagikan melalui


Tutorial: Membuat aplikasi Windows Pembelajaran Mesin Desktop (C++)

API ML Windows dapat dimanfaat untuk berinteraksi dengan model pembelajaran mesin dengan mudah dalam aplikasi desktop C++ (Win32). Dengan menggunakan tiga langkah memuat, mengikat, dan mengevaluasi, aplikasi Anda dapat memperoleh manfaat dari kekuatan pembelajaran mesin.

Load -> Bind -> Evaluasi

Kami akan membuat versi sampel Deteksi Objek SqueezeNet yang agak disederhanakan, yang tersedia di GitHub. Anda dapat mengunduh sampel lengkap jika Anda ingin melihat seperti apa tampilannya setelah selesai.

Kita akan menggunakan C++/WinRT untuk mengakses API WinML. Lihat C++/WinRT untuk informasi selengkapnya.

Dalam tutorial ini, Anda akan mempelajari cara:

  • Memuat model pembelajaran mesin
  • Memuat gambar sebagai VideoFrame
  • Mengikat input dan output model
  • Mengevaluasi model dan mencetak hasil yang bermakna

Prasyarat

  • Visual Studio 2019 (atau Visual Studio 2017, versi 15.7.4 atau yang lebih baru)
  • Windows 10, versi 1809 atau yang lebih baru
  • Windows SDK, build 17763 atau yang lebih baru
  • Ekstensi Visual Studio untuk C++/WinRT
    1. Di Visual Studio, pilih > Ekstensi alat dan Pembaruan.
    2. Pilih Online di panel kiri dan cari "WinRT" menggunakan kotak pencarian di sebelah kanan.
    3. Pilih C++/WinRT, klik Unduh, dan tutup Visual Studio.
    4. Ikuti petunjuk penginstalan, lalu buka kembali Visual Studio.
  • Repositori Windows-Machine-Pembelajaran Github (Anda dapat mengunduhnya sebagai file ZIP atau mengkloning ke komputer Anda)

Membuat proyek

Pertama, kita akan membuat proyek di Visual Studio:

  1. Pilih File > Proyek Baru > untuk membuka jendela Proyek Baru.
  2. Di panel kiri, pilih Visual C++ > Windows Desktop terinstal>, dan di tengah, pilih Aplikasi Konsol Windows (C++/WinRT).
  3. Beri nama dan Lokasi pada proyek Anda, lalu klik OK.
  4. Di jendela Proyek Platform Windows Universal Baru, atur Versi Target dan Minimum untuk membangun 17763 atau yang lebih baru, dan klik OK.
  5. Pastikan menu dropdown di toolbar atas diatur ke Debug dan x64 atau x86 bergantung pada arsitektur komputer Anda.
  6. Tekan Ctrl+F5 untuk menjalankan program tanpa penelusuran kesalahan. Terminal harus terbuka dengan beberapa teks "Halo dunia". Tekan tombol apa pun untuk menutupnya.

Memuat model

Selanjutnya, kita akan memuat model ONNX ke dalam program kita menggunakan Pembelajaran Model.LoadFromFilePath:

  1. Di pch.h (di folder File Header), tambahkan pernyataan berikut include (ini memberi kami akses ke semua API yang akan kita butuhkan):

    #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. Di main.cpp (di folder File Sumber), tambahkan pernyataan berikut using :

    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. Tambahkan deklarasi variabel berikut setelah using pernyataan:

    // 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. Tambahkan deklarasi penerusan berikut setelah variabel global Anda:

    // Forward declarations
    void LoadModel();
    VideoFrame LoadImageFile(hstring filePath);
    void BindModel();
    void EvaluateModel();
    void PrintResults(IVectorView<float> results);
    void LoadLabels();
    
  5. Dalam main.cpp, hapus kode "Halo dunia" (semuanya dalam main fungsi setelah init_apartment).

  6. Temukan file SqueezeNet.onnx di klon lokal repositori Windows-Machine-Pembelajaran Anda. Ini harus terletak di \Windows-Machine-Pembelajaran\SharedContent\models.

  7. Salin jalur file dan tetapkan ke variabel Anda modelPath di mana kami mendefinisikannya di bagian atas. Ingatlah untuk mengawali string dengan L untuk menjadikannya string karakter yang lebar sehingga berfungsi dengan baik dengan hstring, dan untuk menghindari garis miring terbalik (\) dengan garis miring terbalik tambahan. Contohnya:

    hstring modelPath = L"C:\\Repos\\Windows-Machine-Learning\\SharedContent\\models\\SqueezeNet.onnx";
    
  8. Pertama, kita akan menerapkan metode .LoadModel Tambahkan metode berikut setelah main metode . Metode ini memuat model dan output berapa lama waktu yang dibutuhkan:

    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. Terakhir, panggil metode ini dari main metode :

    LoadModel();
    
  10. Jalankan program Anda tanpa penelusuran kesalahan. Anda akan melihat bahwa model Anda berhasil dimuat!

Muat gambar

Selanjutnya, kita akan memuat file gambar ke dalam program kita:

  1. Tambahkan metode berikut. Metode ini akan memuat gambar dari jalur yang diberikan dan membuat VideoFrame darinya:

    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. Tambahkan panggilan ke metode ini dalam main metode :

    imageFrame = LoadImageFile(imagePath);
    
  3. Temukan folder media di klon lokal repositori Windows-Machine-Pembelajaran Anda. Ini harus terletak di \Windows-Machine-Pembelajaran\SharedContent\media.

  4. Pilih salah satu gambar di folder tersebut, dan tetapkan jalur filenya ke imagePath variabel tempat kami menentukannya di bagian atas. Ingatlah untuk mengawalinya dengan untuk L menjadikannya string karakter yang lebar, dan untuk menghindari garis miring terbalik dengan garis miring terbalik lainnya. Contohnya:

    hstring imagePath = L"C:\\Repos\\Windows-Machine-Learning\\SharedContent\\media\\kitten_224.png";
    
  5. Jalankan program tanpa penelusuran kesalahan. Anda akan melihat gambar berhasil dimuat!

Mengikat input dan output

Selanjutnya, kita akan membuat sesi berdasarkan model dan mengikat input dan output dari sesi menggunakan Pembelajaran ModelBinding.Bind. Untuk informasi selengkapnya tentang pengikatan, lihat Mengikat model.

  1. Mengimplementasikan metode BindModel. Ini membuat sesi berdasarkan model dan perangkat, dan pengikatan berdasarkan sesi tersebut. Kami kemudian mengikat input dan output ke variabel yang telah kami buat menggunakan namanya. Kami kebetulan tahu sebelumnya bahwa fitur input diberi nama "data_0" dan fitur output diberi nama "softmaxout_1". Anda dapat melihat properti ini untuk model apa pun dengan membukanya di Netron, alat visualisasi model online.

    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. Tambahkan panggilan ke BindModel dari main metode :

    BindModel();
    
  3. Jalankan program tanpa penelusuran kesalahan. Input dan output model harus berhasil diikat. Kami hampir sampai!

Evaluasi model

Kita sekarang pada langkah terakhir dalam diagram di awal tutorial ini, Evaluasi. Kami akan mengevaluasi model menggunakan Pembelajaran ModelSession.Evaluate:

  1. Mengimplementasikan metode EvaluateModel. Metode ini mengambil sesi kami dan mengevaluasinya menggunakan pengikatan dan ID korelasi kami. ID korelasi adalah sesuatu yang mungkin dapat kita gunakan nanti untuk mencocokkan panggilan evaluasi tertentu dengan hasil output. Sekali lagi, kita tahu sebelumnya bahwa nama output adalah "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. Sekarang mari kita terapkan PrintResults. Metode ini mendapatkan tiga probabilitas teratas untuk objek apa yang bisa ada dalam gambar, dan mencetaknya:

    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. Kita juga perlu mengimplementasikan LoadLabels. Metode ini membuka file label yang berisi semua objek berbeda yang dapat dikenali model, dan mengurainya:

    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. Temukan file Labels.txt di klon lokal repositori Windows-Machine-Pembelajaran Anda. Ini harus dalam \Windows-Machine-Pembelajaran\Samples\SqueezeNetObjectDetection\Desktop\cpp.

  5. Tetapkan jalur file ini ke labelsFilePath variabel tempat kami mendefinisikannya di bagian atas. Pastikan untuk menghindari garis miring terbelakang dengan garis miring terbelakang lainnya. Contohnya:

    string labelsFilePath = "C:\\Repos\\Windows-Machine-Learning\\Samples\\SqueezeNetObjectDetection\\Desktop\\cpp\\Labels.txt";
    
  6. Tambahkan panggilan ke EvaluateModel main dalam metode :

    EvaluateModel();
    
  7. Jalankan program tanpa penelusuran kesalahan. Sekarang harus mengenali dengan benar apa yang ada dalam gambar! Berikut adalah contoh apa yang mungkin dihasilkannya:

    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
    

Langkah berikutnya

Hore, Anda memiliki deteksi objek yang berfungsi di aplikasi desktop C++! Selanjutnya, Anda dapat mencoba menggunakan argumen baris perintah untuk memasukkan model dan file gambar daripada hardcoding, mirip dengan apa yang dilakukan sampel di GitHub. Anda juga dapat mencoba menjalankan evaluasi pada perangkat yang berbeda, seperti GPU, untuk melihat perbedaan performanya.

Bermain-main dengan sampel lain di GitHub dan memperluasnya sesuka Anda!

Lihat juga

Catatan

Gunakan sumber daya berikut untuk bantuan dengan Windows ML:

  • Untuk mengajukan atau menjawab pertanyaan teknis tentang Windows ML, silakan gunakan tag windows-machine-learning di Stack Overflow.
  • Untuk melaporkan bug, silakan ajukan masalah di GitHub kami.