WinML-bővítmény létrehozása

Ez az útmutató bemutatja, hogyan hozhat létre natív C#-bővítményt, amely Windows Machine Learning (WinML) használatával működik az Electron-alkalmazásban. A WinML lehetővé teszi machine learning modellek (ONNX formátum) helyi futtatását Windows-eszközökön olyan feladatokhoz, mint a képbesorolás, az objektumészlelés stb.

Előfeltételek

Az útmutató megkezdése előtt győződjön meg arról, hogy a következőt tette:

Megjegyzés:

A WinML bármilyen Windows 10 (1809+) vagy Windows 11 eszközön fut. A legjobb teljesítmény érdekében a GPU-kkal vagy NPU-kkal rendelkező eszközök használata ajánlott, de az API cpu-n is működik.

Fontos

A WinML-bővítményhez a experimental Windows App SDK szükséges. Ha a telepítési útmutatóban a "Stabil SDK-k" winapp init lehetőséget választotta, frissítenie kell az SDK-verziót. Szerkessze winapp.yaml, és módosítsa a Microsoft.WindowsAppSDK verziót 2.0.0-experimental3 verzióra, majd futtassa a npx winapp restore frissítést.

1. lépés: Natív C#-bővítmény létrehozása

Hozzunk létre egy natív bővítményt, amely WinML API-kat fog használni. Egy C# sablont fogunk használni, amely a node-api-dotnet használatával hidalja át a JavaScriptet és a C#-ot.

npx winapp node create-addon --template cs --name winMlAddon

Ezzel létrehoz egy winMlAddon/ mappát a következőkkel:

  • addon.cs - A WinML API-kat meghívó C#-kód
  • winMlAddon.csproj – Projektfájl, amely a Windows SDK-ra és a Windows App SDK-ra való hivatkozásokat tartalmaz.
  • README.md – Dokumentáció a bővítmény használatáról

A parancs egy build-winMlAddon szkriptet is hozzáad a package.json bővítmény létrehozásához, valamint egy szkriptet clean-winMlAddon a buildelési összetevők tisztításához:

{
  "scripts": {
    "build-winMlAddon": "dotnet publish ./winMlAddon/winMlAddon.csproj -c Release",
    "clean-winMlAddon": "dotnet clean ./winMlAddon/winMlAddon.csproj"
  }
}

A sablon automatikusan mindkét SDK-ra mutató hivatkozásokat tartalmaz, így azonnal megkezdheti Windows API-k meghívását!

A bővítmény létrehozásával ellenőrizzük, hogy minden megfelelően van-e beállítva:

# Build the C# addon
npm run build-winMlAddon

Megjegyzés:

C++ bővítményt npx winapp node create-addon is létrehozhat (a --template jelölő nélkül). A C++ bővítmények node-addon-api használnak, és közvetlen hozzáférést biztosítanak Windows API-khoz maximális teljesítménnyel. További lehetőségekért tekintse meg a C++ értesítési bővítmény útmutatóját vagy a teljes parancsdokumentációt .

2. lépés: A SqueezeNet-modell letöltése és mintakód lekérése

Referenciaként az AI Fejlesztői katalógusból származó Classify Image mintát fogjuk használni. Ez a minta a SqueezeNet 1.1 modellt használja a képbesoroláshoz.

2.1. A modell letöltése

  1. Az AI Dev Gallery telepítése
  2. Menjen az képosztályozási példához
  3. Töltse le a SqueezeNet 1.1 modellt (támogatja a CPU-t, a GPU-t és az NPU-t)
  4. Kattintson a Mappa megnyitása elemre a .onnx fájl megkereséséhez

A SqueezeNet letöltése az AI Dev Galleryből

  1. Másolja a squeezenet1.1.onnx fájlt egy models/ mappába a project gyökérmappájában

Megjegyzés:

A modell közvetlenül a ONNX Model Zoo GitHub adattárból is letölthető

3. lépés: Szükséges NuGet-csomagok hozzáadása

A WinML-kód hozzáadása előtt további NuGet-csomagokat kell hozzáadnunk a képfeldolgozáshoz, az ONNX Runtime-hoz és a GenAI-támogatáshoz.

3.1. Directory.packages.props frissítése

Adja hozzá a következő csomagverziókat a Directory.packages.props fájlhoz a projekt gyökerében (a bővítmény létrehozásakor létre kellett volna hozni):

<Project>
  <PropertyGroup>
    <!-- Enable central package versioning -->
    <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
  </PropertyGroup>
  <ItemGroup>
    <PackageVersion Include="Microsoft.JavaScript.NodeApi" Version="0.9.17" />
    <PackageVersion Include="Microsoft.JavaScript.NodeApi.Generator" Version="0.9.17" />
    <!-- Add these packages for WinML -->
+   <PackageVersion Include="Microsoft.ML.OnnxRuntime.Extensions" Version="0.14.0" />
+   <PackageVersion Include="System.Drawing.Common" Version="9.0.9" />
+   <PackageVersion Include="Microsoft.Extensions.AI" Version="9.9.1" />
+   <PackageVersion Include="Microsoft.ML.OnnxRuntimeGenAI.Managed" Version="0.10.1" />
+   <PackageVersion Include="Microsoft.ML.OnnxRuntimeGenAI.WinML" Version="0.10.1" />
    
    <!-- These versions may be updated automatically during restore to match yaml -->
    <PackageVersion Include="Microsoft.WindowsAppSDK" Version="2.0.0-experimental3" />
    <PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.7175" />
  </ItemGroup>
</Project>

3.2. WinMlAddon.csproj frissítése

Nyissa meg a winMlAddon/winMlAddon.csproj, majd adja hozzá a csomaghivatkozásokat a <ItemGroup>-hez.

<ItemGroup>
  <PackageReference Include="Microsoft.JavaScript.NodeApi" />
  <PackageReference Include="Microsoft.JavaScript.NodeApi.Generator" />
  <!-- Add these packages for WinML -->
+ <PackageReference Include="Microsoft.ML.OnnxRuntime.Extensions" />
+ <PackageReference Include="System.Drawing.Common" />
+ <PackageReference Include="Microsoft.Extensions.AI" />
+ <PackageReference Include="Microsoft.ML.OnnxRuntimeGenAI.Managed" />
+ <PackageReference Include="Microsoft.ML.OnnxRuntimeGenAI.WinML" />
  
  <PackageReference Include="Microsoft.Windows.SDK.BuildTools" />
  <PackageReference Include="Microsoft.WindowsAppSDK" />
</ItemGroup>

Ezek a csomagok a következők:

  • Microsoft.ML.OnnxRuntime.Extensions – További operátorokat és segédprogramokat biztosít az ONNX-futtatókörnyezethez
  • System.Drawing.Common – Lehetővé teszi a képek betöltését és kezelését az előfeldolgozáshoz
  • Microsoft.Extensions.AI – AI-absztrakciók .NET
  • Microsoft.ML.OnnxRuntimeGenAI.Managed – Felügyelt kötések az ONNX Runtime GenAI-hoz
  • Microsoft.ML.OnnxRuntimeGenAI.WinML – WinML-integráció az ONNX Runtime GenAI-hoz

4. lépés: A mintakód hozzáadása

Az AI Dev Gallery a squeezenetes képbesorolás teljes implementációit mutatja be:

SqueezeNet-mintakód

Ezt a kódot az Electronhez igazítottuk, és a teljes implementáció megtalálható az elektron-winml mintában. A winMlAddon/ mappa tartalmazza az AI fejlesztői katalógusából származó módosított kódot.

Másolja a teljes winMlAddon/ mappát a minták/electron-winml/winMlAddon/ fájlból a projektgyökérbe, és cserélje le az 1. lépésben létrehozott mappát. A minta több olyan fájlt is tartalmaz, amelyek a addon.cs bővítmény létrehozásához és futtatásához szükségesek (segédosztályok a csevegőügyfélben Utils/stb.).

Fontos

Az egész mappát kell másolnia, nem csak addon.cs. A bővítmény az almappában található Utils/ segédfájloktól (Prediction.cs, , ImageNet.csBitmapFunctions.csstb.) függ.

Főbb megvalósítási részletek

Emeljük ki az implementáció fontos részeit és az AI Fejlesztői katalógus kódjának főbb különbségeit:

1. Projektgyökérútvonal-követelmény

Az AI Dev Gallery kódjától eltérően az Electron-bővítményhez a JavaScript-kódra van szükség a projekt gyökérútvonalának átadásához. Erre azért van szükség, mert:

  • A bővítménynek meg kell keresnie az ONNX-modellfájlt a models/ mappában
  • A natív függőségeket (DLL-eket) adott könyvtárakból kell betölteni
[JSExport]
public static async Task<Addon> CreateAsync(string projectRoot)
{
    if (!Path.Exists(projectRoot))
    {
        throw new Exception("Project root is invalid.");
    }

    var addon = new Addon(projectRoot);
    addon.PreloadNativeDependencies();

    string modelPath = Path.Join(projectRoot, "models", @"squeezenet1.1-7.onnx");
    await addon.InitModel(modelPath, ExecutionProviderDevicePolicy.DEFAULT, null, false, null);

    return addon;
}

Ez automatikusan kiválasztja a legjobb végrehajtási szolgáltatót (CPU, GPU vagy NPU) az eszköz képességei alapján.

2. Natív függőségek előre betöltése

A bővítmény tartalmaz egy metódust PreloadNativeDependencies() a szükséges DLL-ek betöltéséhez. Ez a megközelítés fejlesztési és éles forgatókönyvekben is működik, anélkül hogy a DLL-eket a projekt gyökérmappájába kellene másolni.

private void PreloadNativeDependencies()
{
    // Loads required DLLs from the winMlAddon build output
    // This ensures dependencies are available regardless of the execution context
}

Ezt az inicializálás során hívjuk meg a modell betöltése előtt, biztosítva, hogy az összes natív kódtár elérhető legyen.

3. Az Electron Forge konfigurálása csomagoláshoz

Annak érdekében, hogy a bővítmény megfelelően működjön az éles buildekben, konfigurálnia kell a csomagolót a következőre:

  1. Natív fájlok kicsomagolása – A DLL-eket, az ONNX-modelleket és a .node-fájlokat az ASAR-archívumon kívül kell elérni
  2. Felesleges fájlok kizárása – A csomag méretének megőrzése a buildösszetevők és az ideiglenes fájlok kizárásával

Az Electron Forge esetében frissítse a következőt forge.config.js:

// From samples/electron-winml/forge.config.js
module.exports = {
  packagerConfig: {
    asar: {
      // Unpack native files so they can be accessed by the addon
      unpack: "**/*.{dll,exe,node,onnx}"
    },
    ignore: [
      // Exclude .winapp folder (SDK packages and headers)
      /^\/.winapp\//,
      // Exclude MSIX packages
      "\\.msix$",
      // Exclude winMlAddon source files, but keep the dist folder
      /^\/winMlAddon\/(?!dist).+/
    ]
  },
  // ... rest of your config
};

A következőt teszi:

  1. asar.unpack – DLL-eket, végrehajtható fájlokat, .node bináris fájlokat és ONNX-modelleket nyer ki a app.asar.unpacked/ be

    • Ez a fájlrendszer elérési útjai segítségével teszi elérhetővé őket futtatókörnyezetben
    • A JavaScript-kód automatikusan módosítja az elérési utakat (lásd a app.asar fenti → app.asar.unpacked helyettesítését)
  2. ignore - Kizárja a végleges csomagból:

    • .winapp/ - SDK-csomagok és fejlécek (futásidőben nem szükséges)
    • .msix fájlok – Csomagolt kimenetek
    • winMlAddon/ forrásfájlok – Csak a dist/ lefordított bináris fájlokat tartalmazó mappát tárolja

Megjegyzés:

Ha másik csomagolóeszközt (elektronszerkesztőt stb.) használ, a natív függőségek kicsomagolásához és a fejlesztési fájlok kizárásához hasonló beállításokat kell konfigurálnia. Az ASAR kicsomagolási lehetőségeiről tekintse meg a csomagoló dokumentációját.

4. Képbesorolás

A ClassifyImage metódus feldolgoz egy képet, és előrejelzéseket ad vissza:

[JSExport]
public async Task<Prediction[]> ClassifyImage(string imagePath)
{
    // Loads the image, preprocesses it, and runs inference
    // Returns top predictions with labels and confidence scores
}

A teljes megvalósítási kezeli a következőt:

  • Képbetöltés és előfeldolgozás (átméretezés, normalizálás)
  • A modell következtetésének futtatása
  • Az eredmények feldolgozása után a legjobb előrejelzéseket kapja címkékkel és megbízhatósági pontszámokkal

Megjegyzés:

A teljes forráskód tartalmazza a képek előfeldolgozását, a tenzor létrehozását és az eredmények elemzését. A részletekért tekintse meg a minta implementációt .

A kód ismertetése

A bővítmény a következő fő függvényeket biztosítja:

  1. CreateAsync – Inicializálja a bővítményt, és betölti a SqueezeNet-modellt
  2. ClassifyImage – Kép elérési útját veszi fel, és besorolási előrejelzéseket ad vissza

A WinML a rendelkezésre állás alapján automatikusan kiválasztja a legjobb végrehajtási eszközt (CPU, GPU vagy NPU).

5. lépés: A C# bővítmény létrehozása

Most hozza létre a bővítményt:

npm run build-winMlAddon

Ez lefordítja a C#-kódot natív AOT használatával (előzetes fordítás), amely:

  • .node bináris formátumú (natív bővítmény) létrehozása
  • A nem használt kód levágása kisebb csomagméret esetén
  • nem .NET futtatókörnyezet szükséges a célgépeken
  • Natív teljesítményt nyújt

A lefordított bővítmény a következőben lesz: winMlAddon/dist/winMlAddon.node.

6. lépés: A bővítmény tesztelése

Most teszteljük a bővítmény működését úgy, hogy meghívjuk a fő folyamatból. Nyissa meg src/main.js és kövesse az alábbi lépéseket:

6.1. Töltsd be a bővítményt

Adja hozzá felül a kötelező utasításokat:

const winMlAddon = require('../winMlAddon/dist/winMlAddon.node');

6.2. Tesztfüggvény létrehozása

Adja hozzá ezt a függvényt a képbesorolás teszteléséhez:

const testWinML = async () => {
  console.log('Testing WinML addon...');
  
  try {
    let projectRoot = path.join(__dirname, '..');
    // Adjust path for packaged apps
    if (projectRoot.includes('app.asar')) {
      projectRoot = projectRoot.replace('app.asar', 'app.asar.unpacked');
    }
    
    const addon = await winMlAddon.Addon.createAsync(projectRoot);
    console.log('Model loaded successfully!');
    
    // Classify a sample image
    const imagePath = path.join(projectRoot, 'test-images', 'sample.jpg');
    const predictions = await addon.classifyImage(imagePath);
    
    console.log('Top predictions:');
    predictions.slice(0, 5).forEach((pred, i) => {
      console.log(`${i + 1}. ${pred.label}: ${(pred.confidence * 100).toFixed(2)}%`);
    });
  } catch (error) {
    console.error('Error testing WinML:', error.message);
  }
};

Főbb pontok:

  • Az elérési út módosítása (app.asarapp.asar.unpacked) biztosítja, hogy a kód a fejlesztési és a csomagolt alkalmazásokban is működjön
  • Ez hozzáfér a kicsomagolt natív fájlokhoz, amelyeket a forge.config.js-ben konfiguráltak.

6.3. A tesztfüggvény meghívása

Adja hozzá ezt a sort a függvény végéhez createWindow() :

testWinML();

6.4. Tesztképek előkészítése

Képbesorolás tesztelése:

  1. Hozzon létre egy test-images/ mappát a projekt gyökérkönyvtárában
  2. Adjon hozzá egy névvel ellátott sample.jpg tesztképet (a kód pontosan ezt a fájlnevet várja)
  3. A SqueezeNet modell 1000 különböző ImageNet-osztályt (állatok, objektumok, jelenetek stb.) ismer fel.

Az alkalmazás futtatásakor a besorolási eredmények megjelennek a konzolon!

Tip

Az IPC-kezelőkkel, a fájlkijelölési párbeszédpanelekkel és a felhasználói felülettel való teljes megvalósításért tekintse meg az elektron-winml mintát.

7. lépés: Hibakeresési identitás frissítése

Annak érdekében, hogy a Windows App SDK betöltse és elérhető legyen a használathoz, be kell állítanunk a hibakeresési identitást, amely biztosítja, hogy a keretrendszer be legyen töltve az alkalmazás futtatásakor. Hasonlóképpen, amikor módosítja Package.appxmanifest vagy módosítja a jegyzékben hivatkozott objektumokat (például az alkalmazásikonokat), frissítenie kell az alkalmazás hibakeresési identitását. Indítás:

npx winapp node add-electron-debug-identity

Ez a parancs:

  1. Olvassa a(z) Package.appxmanifest alkalmazást annak részletei és képességei megismeréséhez
  2. electron.exe Regisztrál az node_modules ideiglenes identitással
  3. Lehetővé teszi az identitáshoz szükséges API-k tesztelését teljes MSIX-csomagolás nélkül

Megjegyzés:

Ez a parancs már része a(z) postinstall telepítési útmutatóban hozzáadott szkriptnek, ezért automatikusan fut ezután: npm install. Azonban manuálisan kell futtatnia, amikor:

  • Módosítás Package.appxmanifest (képességek, identitás vagy tulajdonságok módosítása)
  • Alkalmazáseszközök frissítése (ikonok, emblémák stb.)

Most futtassa az alkalmazást:

npm start

Ellenőrizze a konzol kimenetét – látnia kell a WinML-teszt eredményeit!

⚠️ Ismert probléma: Az alkalmazás összeomlik vagy üres ablak (kattintson a kibontásához)

Van egy ismert Windows hiba a ritkán csomagolt Electron-alkalmazásokkal, amely miatt az alkalmazás összeomlik az indításkor, vagy nem rendereli a webes tartalmat. A problémát kijavítottuk Windows, de még nem propagáltuk az összes eszközre.

Kerülő megoldásért tekintse meg a fejlesztési környezet beállítását .

Következő lépések

Gratulálok! Sikeresen létrehozott egy natív bővítményt, amely képes gépi tanulási modellek futtatására a WinML használatával! 🎉

Most készen áll a következőre:

Vagy fedezze fel a többi útmutatót:

A modell testreszabása

Az ONNX-modell teljes integrálásához a következőkre lesz szüksége:

  1. Ismerje meg a modell bemeneteit – képeket, tenzorokat, sorozatokat stb.
  2. Megfelelő bemeneti kötések létrehozása – Az adatok konvertálása a WinML által várt formátumra
  3. A kimenetek feldolgozása – A modell előrejelzéseinek elemzése és értelmezése
  4. Hibák kezelése kecsesen – A modell betöltése és következtetése sikertelen lehet

További források

Hibaelhárítás

A buildelés a NU1010 használatával meghiúsul: a PackageReference-elemek nem határoznak meg megfelelő PackageVersion-t

Győződjön meg arról, hogy a winMlAddon.csproj-ban hivatkozott csomagoknak van megfelelő bejegyzésük a Directory.packages.props-ban. A szükséges csomagok teljes listáját a 3. lépésben találja.

"nem érvényes Win32-alkalmazás" a bővítmény betöltésekor

Ez azt jelenti, hogy a bővítmény a Node.js/Electron-futtatókörnyezetétől eltérő architektúrához készült. Ellenőrizze a Node.js architektúráját:

node -e "console.log(process.arch)"

Ezután építse újra a bővítményt a megfelelő célra.

# For x64 Node.js:
dotnet publish ./winMlAddon/winMlAddon.csproj -c Release -r win-x64

# For ARM64 Node.js:
dotnet publish ./winMlAddon/winMlAddon.csproj -c Release -r win-arm64

Ha nemrég módosította a Node.js telepítését, telepítse újra a node_modules-t, hogy megszerezze a megfelelő Electron bináris fájlt.

rm -rf node_modules package-lock.json
npm install

Segítségkérés

Boldog gépi tanulást! 🤖