Ausführung von SLMs auf Snapdragon-Geräten mit NPUs

Erfahren Sie, wie Sie SLMs auf Snapdragon-Geräten mit ONNX Runtime ausführen.

Modelle

Unterstützte Modelle sind derzeit

  • Phi-3.5 mini instruct
  • Llama 3.2 3B

Geräte mit Snapdragon NPUs benötigen Modelle in einem bestimmten Format und einer bestimmten Größe.

Anweisungen zur Generierung von Modellen in diesem Format finden Sie unter Modelle für Snapdragon erstellen

Sobald Sie das Modell erstellt oder heruntergeladen haben, legen Sie die Modelldateien an einem bekannten Ort ab. Diese Dateien bestehen aus

  • genai_config.json
  • tokenizer.json
  • tokenizer_config.json
  • special_tokens_map.json
  • quantizer.onnx
  • dequantizer.onnx
  • position-processor.onnx
  • ein Satz von Transformer-Modell-Binärdateien
    • Qualcomm-Kontext-Binärdateien (*.bin)
    • Metadaten der Kontext-Binärdateien (*.json)
    • ONNX-Wrapper-Modelle (*.onnx)

Python-Anwendung

Wenn auf Ihrem Gerät Python installiert ist, können Sie ein einfaches Frage-und-Antwort-Skript ausführen, um das Modell abzufragen.

Runtime installieren

pip install onnxruntime-genai

Skript herunterladen

curl https://raw.githubusercontent.com/microsoft/onnxruntime-genai/refs/heads/main/examples/python/model-qa.py -o model-qa.py

Skript ausführen

Dieses Skript geht davon aus, dass sich die Modelldateien in einem Ordner namens models\Phi-3.5-mini-instruct befinden.

python .\model-qa.py -e cpu -g -v --system_prompt "You are a helpful assistant. Be brief and concise." --chat_template "<|user|>\n{input} <|end|>\n<|assistant|>" -m ..\..\models\Phi-3.5-mini-instruct

Ein Blick in das Python-Skript

Das vollständige Python-Skript ist hier veröffentlicht: https://github.com/microsoft/onnxruntime-genai/blob/main/examples/python/model-qa.py. Das Skript nutzt die API auf folgende Standardweise:

  1. Modell laden

    model = og.Model(config)
    

    Dies lädt das Modell in den Speicher.

  2. Vorprozessoren erstellen und System-Prompt tokenisieren

     tokenizer = og.Tokenizer(model)
     tokenizer_stream = tokenizer.create_stream()
    
     # Optional
     system_tokens = tokenizer.encode(system_prompt)
    

    Dies erstellt einen Tokenizer und einen Tokenizer-Stream, der es ermöglicht, Tokens an den Benutzer zurückzugeben, während sie generiert werden.

  3. Interaktive Eingabeschleife

    while True:
        # Read prompt
        # Run the generation, streaming the output tokens
    
  4. Generierungsschleife

    # 1. Pre-process the prompt into tokens
    input_tokens = tokenizer.encode(prompt)
    
    # 2. Create parameters and generator (KV cache etc) and process the prompt
    params = og.GeneratorParams(model)
    params.set_search_options(**search_options)
    generator = og.Generator(model, params)
    generator.append_tokens(system_tokens + input_tokens)
    
    # 3. Loop until all output tokens are generated, printing
    # out the decoded token
    while not generator.is_done():
        generator.generate_next_token()
    
        new_token = generator.get_next_tokens()[0]
        print(tokenizer_stream.decode(new_token), end="", flush=True)
    
     print()
        
     # Delete the generator to free the captured graph before creating another one
     del generator
    

C++-Anwendung

Um die Modelle auf der Snapdragon NPU innerhalb einer C++-Anwendung auszuführen, verwenden Sie den Code von hier: https://github.com/microsoft/onnxruntime-genai/tree/main/examples/c.

Das Erstellen und Ausführen dieser Anwendung erfordert einen Windows-PC mit einer Snapdragon NPU sowie

  • cmake
  • Visual Studio 2022

Repository klonen

   git clone https://github.com/microsoft/onnxruntime-genai
   cd examples\c

ONNX Runtime installieren

Erfordert derzeit den Nightly-Build von ONNX Runtime, da es minutengenaue Änderungen an der QNN-Unterstützung für Sprachmodelle gibt.

Laden Sie die Nightly-Version der ONNX Runtime QNN-Binärdateien hier herunter.

   mkdir onnxruntime-win-arm64-qnn
   move Microsoft.ML.OnnxRuntime.QNN.1.22.0-dev-20250225-0548-e46c0d8.nupkg onnxruntime-win-arm64-qnn
   cd onnxruntime-win-arm64-qnn
   tar xvzf Microsoft.ML.OnnxRuntime.QNN.1.22.0-dev-20250225-0548-e46c0d8.nupkg
   copy runtimes\win-arm64\native\* ..\..\..\lib
   cd ..

onnxruntime-genai installieren

   curl https://github.com/microsoft/onnxruntime-genai/releases/download/v0.6.0/onnxruntime-genai-0.6.0-win-arm64.zip -o onnxruntime-genai-win-arm64.zip
   tar xvf onnxruntime-genai-win-arm64.zip
   cd onnxruntime-genai-0.6.0-win-arm64
   copy include\* ..\include
   copy lib\* ..\lib

Beispiel erstellen

   cmake -A arm64 -S . -B build -DPHI3-QA=ON
   cd build
   cmake --build . --config Release

Beispiel ausführen

   cd Release
   .\phi3_qa.exe <path_to_model>

Ein Blick in das C++-Beispiel

Die C++-Anwendung ist hier veröffentlicht: https://github.com/microsoft/onnxruntime-genai/blob/main/examples/c/src/phi3_qa.cpp. Das Skript nutzt die API auf folgende Standardweise:

  1. Modell laden

    auto model = OgaModel::Create(*config);
    

    Dies lädt das Modell in den Speicher.

  2. Vorprozessoren erstellen

    auto tokenizer = OgaTokenizer::Create(*model);
    auto tokenizer_stream = OgaTokenizerStream::Create(*tokenizer);
    

    Dies erstellt einen Tokenizer und einen Tokenizer-Stream, der es ermöglicht, Tokens an den Benutzer zurückzugeben, während sie generiert werden.

  3. Interaktive Eingabeschleife

    while True:
        # Read prompt
        # Run the generation, streaming the output tokens
    
  4. Generierungsschleife

    # 1. Pre-process the prompt into tokens
    auto sequences = OgaSequences::Create();
    tokenizer->Encode(prompt.c_str(), *sequences);
       
    # 2. Create parameters and generator (KV cache etc) and process the prompt
    auto params = OgaGeneratorParams::Create(*model);
    params->SetSearchOption("max_length", 1024);
    auto generator = OgaGenerator::Create(*model, *params);
    generator->AppendTokenSequences(*sequences);
    
    # 3. Loop until all output tokens are generated, printing
    # out the decoded token
    while (!generator->IsDone()) {
      generator->GenerateNextToken();
    
      if (is_first_token) {
        timing.RecordFirstTokenTimestamp();
        is_first_token = false;
      }
    
      const auto num_tokens = generator->GetSequenceCount(0);
      const auto new_token = generator->GetSequenceData(0)[num_tokens - 1];
      std::cout << tokenizer_stream->Decode(new_token) << std::flush;
    }