在搭载 NPU 的骁龙设备上运行 SLM

了解如何在搭载 NPU 的骁龙设备上使用 ONNX Runtime 运行 SLM。

模型

当前支持的模型有

  • Phi-3.5 mini instruct
  • Llama 3.2 3B

搭载骁龙 NPU 的设备需要特定大小和格式的模型。

有关生成此格式模型的说明,请参阅 构建骁龙模型

构建或下载模型后,将模型资源放置在已知位置。这些资源包括

  • genai_config.json
  • tokenizer.json
  • tokenizer_config.json
  • special_tokens_map.json
  • quantizer.onnx
  • dequantizer.onnx
  • position-processor.onnx
  • 一组 Transformer 模型二进制文件
    • Qualcomm 上下文二进制文件 (*.bin)
    • 上下文二进制元数据 (*.json)
    • ONNX 包装模型 (*.onnx)

Python 应用程序

如果您的设备已安装 Python,您可以运行一个简单的问答脚本来查询模型。

安装运行时

pip install onnxruntime-genai

下载脚本

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

运行脚本

此脚本假定模型资源位于名为 models\Phi-3.5-mini-instruct 的文件夹中

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

Python 脚本内览

完整的 Python 脚本发布在此处:https://github.com/microsoft/onnxruntime-genai/blob/main/examples/python/model-qa.py。该脚本以以下标准方式使用 API

  1. 加载模型

    model = og.Model(config)
    

    这会将模型加载到内存中。

  2. 创建预处理器并标记化系统提示

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

    这将创建一个分词器和一个分词器流,允许在生成令牌时将其返回给用户。

  3. 交互式输入循环

    while True:
        # Read prompt
        # Run the generation, streaming the output tokens
    
  4. 生成循环

    # 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++ 应用程序

要在 C++ 应用程序中在骁龙 NPU 上运行模型,请使用此处的代码:https://github.com/microsoft/onnxruntime-genai/tree/main/examples/c。

构建和运行此应用程序需要一台配备骁龙 NPU 的 Windows PC,以及

  • cmake
  • Visual Studio 2022

克隆仓库

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

安装 onnxruntime

当前需要 onnxruntime 的 nightly build 版本,因为 QNN 对语言模型的支持有最新的更改。

从此链接下载 ONNX Runtime QNN 二进制文件的 nightly 版本 here

   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

   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

构建示例

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

运行示例

   cd Release
   .\phi3_qa.exe <path_to_model>

C++ 示例内览

C++ 应用程序发布在此处:https://github.com/microsoft/onnxruntime-genai/blob/main/examples/c/src/phi3_qa.cpp。该脚本以以下标准方式使用 API

  1. 加载模型

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

    这会将模型加载到内存中。

  2. 创建预处理器

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

    这将创建一个分词器和一个分词器流,允许在生成令牌时将其返回给用户。

  3. 交互式输入循环

    while True:
        # Read prompt
        # Run the generation, streaming the output tokens
    
  4. 生成循环

    # 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;
    }