Sdílet prostřednictvím


Začínáme s Phi3 a dalšími jazykovými modely v aplikaci pro Windows pomocí ONNX Runtime Generative AI

Tento článek vás provede vytvořením aplikace WinUI 3, která používá model Phi3 a knihovnu ONNX Runtime Generative AI k implementaci jednoduché chatovací aplikace AI. Velké jazykové modely (LLM) umožňují do aplikace přidávat možnosti generování, transformace, odůvodnění a překladu textu. Další informace o používání modelů AI a strojového učení v aplikaci pro Windows najdete v tématu Začínáme s AI ve Windows. Více informací o runtime ONNX a generativní umělé inteligenci najdete v tématu Generativní AI s ONNX Runtime.

Při používání funkcí umělé inteligence doporučujeme zkontrolovat: vývoj zodpovědných generativních aplikací a funkcí umělé inteligence ve Windows.

Co je ONNX Runtime

ONNX Runtime je akcelerátor modelů strojového učení pro různé platformy s flexibilním rozhraním pro integraci knihoven specifických pro hardware. ONNX Runtime lze použít s modely z PyTorch, Tensorflow/Keras, TFLite, scikit-learna dalších architektur. Další informace najdete na webu ONNX Runtime na https://onnxruntime.ai/docs/.

Požadavky

  • Vaše zařízení musí mít povolený vývojářský režim. Další informace najdete v tématu Aktivovat vaše zařízení pro vývoj.
  • Visual Studio 2022 nebo novější s úlohou vývoje desktopových aplikací .NET

Vytvoření nové aplikace WinUI v C#

V sadě Visual Studio vytvořte nový projekt. V dialogovém okně Vytvořit nový projekt nastavte filtr jazyka na "C#" a filtr typu projektu na "WinUI", poté vyberte šablonu Prázdná aplikace, zabalená (WinUI3 na desktopu). Pojmenujte nový projekt GenAIExample.

Přidejte odkazy na balíček NuGet ONNX Runtime Generative AI

V Průzkumníku řešeníklikněte pravým tlačítkem na Závislosti a vyberte Spravovat balíčky NuGet.... Ve správci balíčků NuGet vyberte kartu Procházet. Vyhledejte "Microsoft.ML.OnnxRuntimeGenAI.DirectML", v rozevíracím seznamu Verze zvolte nejnovější stabilní verzi a poté klikněte na Nainstalovat.

Přidání souboru modelu a slovníku do projektu

V Průzkumníku řešeníklikněte pravým tlačítkem na váš projekt a vyberte Přidat>Novou složku. Pojmenujte novou složku Modely. V tomto příkladu budeme používat model z https://huggingface.co/microsoft/Phi-3-mini-4k-instruct-onnx/tree/main/directml/directml-int4-awq-block-128.

Existuje několik různých způsobů, jak načíst modely. V tomto průvodci použijeme příkazové rozhraní Hugging Face (CLI). Pokud modely získáte pomocí jiné metody, budete možná muset upravit cesty k souboru modelu v ukázkovém kódu. Informace o instalaci rozhraní příkazového řádku Hugging Face a nastavení účtu pro jeho použití najdete v tématu rozhraní příkazového řádku (CLI).

Po instalaci rozhraní příkazového řádku otevřete terminál, přejděte do Models adresáře, který jste vytvořili, a zadejte následující příkaz.

huggingface-cli download microsoft/Phi-3-mini-4k-instruct-onnx --include directml/* --local-dir .

Po dokončení operace ověřte, že existuje následující soubor: [Project Directory]\Models\directml\directml-int4-awq-block-128\model.onnx.

V Průzkumníku řešenírozbalte složku directml-int4-awq-block-128 a vyberte všechny soubory ve složce. V podokně Vlastnosti souboru nastavte Kopírovat do výstupního adresáře na Kopírovat, pokud je novější.

Přidání jednoduchého uživatelského rozhraní pro interakci s modelem

V tomto příkladu vytvoříme velmi zjednodušené uživatelské rozhraní, které má TextBox pro zadání výzvy, Button pro odeslání výzvy a TextBlock pro zobrazení stavových zpráv a odpovědí z modelu. Nahraďte výchozí prvek StackPanel v MainWindow.xaml následujícím kódem XAML.

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition/>
        <ColumnDefinition/>
    </Grid.ColumnDefinitions>
    <StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column ="0">
        <TextBox x:Name="promptTextBox" Text="Compose a haiku about coding."/>
        <Button x:Name="myButton" Click="myButton_Click">Submit prompt</Button>
    </StackPanel>
    <Border Grid.Column="1" Margin="20">
        <TextBlock x:Name="responseTextBlock" TextWrapping="WrapWholeWords"/>
    </Border>
</Grid>

Inicializujte model

V MainWindow.xaml.cspřidejte direktivu using pro Microsoft.ML.OnnxRuntimeGenAI oboru názvů.

using Microsoft.ML.OnnxRuntimeGenAI;

Deklarujte členské proměnné uvnitř definice třídy MainPage pro Model a Tokenizer. Nastavte umístění souborů modelu, které jsme přidali v předchozích krocích.

private Model? model = null;
private Tokenizer? tokenizer = null;
private readonly string ModelDir = 
    Path.Combine(AppDomain.CurrentDomain.BaseDirectory,
        @"Models\directml\directml-int4-awq-block-128");

Vytvořte pomocnou metodu pro asynchronní inicializaci modelu. Tato metoda volá konstruktor třídy Model a předává cestu k adresáři modelu. Dále vytvoří novou tokenizátor z modelu.

public Task InitializeModelAsync()
{

    DispatcherQueue.TryEnqueue(() =>
    {
        responseTextBlock.Text = "Loading model...";
    });

    return Task.Run(() =>
    {
        var sw = Stopwatch.StartNew();
        model = new Model(ModelDir);
        tokenizer = new Tokenizer(model);
        sw.Stop();
        DispatcherQueue.TryEnqueue(() =>
        {
            responseTextBlock.Text = $"Model loading took {sw.ElapsedMilliseconds} ms";
        });
    });
}

V tomto příkladu načteme model při aktivaci hlavního okna. Aktualizujte konstruktor stránky tak, aby registroval obslužnou rutinu pro událost Aktivovaná.

public MainWindow()
{
    this.InitializeComponent();
    this.Activated += MainWindow_Activated;
}

Událost Aktivovaná může být vyvolána vícekrát, takže v obslužné rutině události zkontrolujte, jestli má model před inicializací hodnotu null.

private async void MainWindow_Activated(object sender, WindowActivatedEventArgs args)
{
    if (model == null)
    {
        await InitializeModelAsync();
    }
}

Odeslat příkaz modelu

Vytvořte pomocnou metodu, která odešle výzvu modelu a poté asynchronně vrátí výsledky volajícímu pomocí IAsyncEnumerable.

V této metodě se třída Generator používá ve smyčce, která při každém průchodu volá funkci GenerateNextToken, aby načetla, co model předpovídá jako následující znaky, označované jako tokeny, založené na vstupní výzvě. Smyčka se spustí, dokud metoda generátoru IsDone nevrátí hodnotu true nebo dokud není přijat některý z tokenů "<|end|>", "<|system|>" nebo "<|user|>", což signalizuje, že můžeme přestat generovat tokeny.

public async IAsyncEnumerable<string> InferStreaming(string prompt)
{
    if (model == null || tokenizer == null)
    {
        throw new InvalidOperationException("Model is not ready");
    }

    var generatorParams = new GeneratorParams(model);

    var sequences = tokenizer.Encode(prompt);

    generatorParams.SetSearchOption("max_length", 2048);
    generatorParams.SetInputSequences(sequences);
    generatorParams.TryGraphCaptureWithMaxBatchSize(1);

    using var tokenizerStream = tokenizer.CreateStream();
    using var generator = new Generator(model, generatorParams);
    StringBuilder stringBuilder = new();
    while (!generator.IsDone())
    {
        string part;
        try
        {
            await Task.Delay(10).ConfigureAwait(false);
            generator.ComputeLogits();
            generator.GenerateNextToken();
            part = tokenizerStream.Decode(generator.GetSequence(0)[^1]);
            stringBuilder.Append(part);
            if (stringBuilder.ToString().Contains("<|end|>")
                || stringBuilder.ToString().Contains("<|user|>")
                || stringBuilder.ToString().Contains("<|system|>"))
            {
                break;
            }
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex);
            break;
        }

        yield return part;
    }
}

Přidání kódu uživatelského rozhraní pro odeslání výzvy a zobrazení výsledků

V obslužné rutině kliknutí na tlačítko nejprve ověřte, že model nemá hodnotu null. Vytvořte promptovací řetězec s výzvou systému a uživatele a zavolejte InferStreaminga aktualizujte TextBlock s každou částí odpovědi.

Model použitý v tomto příkladu byl natrénován tak, aby přijímal výzvy v následujícím formátu, kde systemPrompt je pokyny pro chování modelu a userPrompt je otázka od uživatele.

<|system|>{systemPrompt}<|end|><|user|>{userPrompt}<|end|><|assistant|>

Modely by měly dokumentovat pravidla pro výzvy. Pro tento model je formát zdokumentován na kartě modelu Huggingface.

private async void myButton_Click(object sender, RoutedEventArgs e)
{
    responseTextBlock.Text = "";

    if(model != null)
    {
        var systemPrompt = "You are a helpful assistant.";
        var userPrompt = promptTextBox.Text;

        var prompt = $@"<|system|>{systemPrompt}<|end|><|user|>{userPrompt}<|end|><|assistant|>";
        
        await foreach (var part in InferStreaming(prompt))
        {
            responseTextBlock.Text += part;
        }
    }
}

Spusťte příklad

Ve Visual Studio se v rozevíracím seznamu Konfigurace řešení ujistěte, že je cílový procesor nastavený na x64. Knihovna ONNXRuntime Generative AI nepodporuje x86. Sestavte a spusťte projekt. Počkejte, až TextBlock označí, že se model načetl. Do textového pole výzvy zadejte výzvu a klikněte na tlačítko Odeslat. Výsledky by se měly postupně objevit v bloku textu.

Viz také