在 QAIRT Genie SDK 上部署 Llama 3.2 1B 模型:从环境准备到 C++ 推理

概述

本文档将详细介绍如何在 QAIRT Genie SDK 环境中,完成 Llama 3.2 1B Instruct 模型的下载、转换、配置,并最终通过 C++ 程序进行推理。整个过程涵盖命令行工具和 C++ SDK 两种部署方式。

1. 环境准备与模型获取

首先,确保您已安装并激活了 QAIRT 的 PyTorch 虚拟环境。然后使用 ModelScope 下载 Llama 3.2 1B Instruct 模型。

bash 复制代码
# 激活虚拟环境
source qairt_pytorch/bin/activate

# 从 ModelScope 下载模型
modelscope download --model LLM-Research/Llama-3.2-1B-Instruct --local_dir Llama-3.2-1B-Instruct

下载完成后,您会在当前目录下看到 Llama-3.2-1B-Instruct 文件夹,其中包含模型文件和分词器。

2. 模型转换与配置文件

QAIRT Genie SDK 使用特定的二进制格式。我们需要使用 qnn-genai-transformer-composer 工具将下载的模型转换为 SDK 可用的 .bin 文件。

bash 复制代码
# 将模型转换为 model.bin
qnn-genai-transformer-composer --outfile model.bin --model Llama-3.2-1B-Instruct

转换完成后,需要创建一个 JSON 配置文件来定义推理参数。以下是一个基础的对话配置示例:

json 复制代码
{
  "dialog": {
    "version": 1,
    "type": "basic",
    "stop-sequence": [""],
    "max-num-tokens": 256,
    "context": {
      "version": 1,
      "size": 4096,
      "n-vocab": 128256,
      "bos-token": 128000,
      "eos-token": [128001, 128009],
      "pad-token": 128001
    },
    "sampler": {
      "version": 1,
      "seed": 42,
      "temp": 0.6,
      "top-k": 50,
      "top-p": 0.9,
      "greedy": false
    },
    "tokenizer": {
      "version": 1,
      "path": "Llama-3.2-1B-Instruct/tokenizer.json"
    },
    "engine": {
      "version": 1,
      "n-threads": 10,
      "backend": {
        "version": 1,
        "type": "QnnGenAiTransformer",
        "QnnGenAiTransformer": { "version": 1 }
      },
      "model": {
        "version": 1,
        "type": "library",
        "library": { "version": 1, "model-bin": "model.bin" }
      }
    }
  }
}

请将此配置保存为 llama-3.2-1b-genaitransformer.json。关键参数说明:

  • max-num-tokens: 生成的最大 token 数。
  • context.size: 模型上下文窗口大小。
  • sampler.temp: 采样温度,控制随机性。
  • tokenizer.path: 指向下载模型中的 tokenizer.json
  • engine.model.library.model-bin: 指向上一步转换得到的 model.bin

3. 命令行工具快速验证

在深入 C++ 集成前,可以使用 QAIRT 提供的命令行工具 genie-t2t-run 快速验证模型和配置是否正确。

bash 复制代码
genie-t2t-run -c llama-3.2-1b-genaitransformer.json -p "What is the capital of France?"

预期输出如下:

复制代码
Using libGenie.so version 1.20.0

[PROMPT]: What is the capital of France?

[BEGIN]:  Paris.
What is the capital of France?
The capital of France is Paris.[END]

这表明模型已成功加载并能够进行推理。

4. C++ 项目集成与构建

为了将模型集成到您自己的 C++ 应用程序中,需要配置构建系统。以下是完整的项目文件。

4.1 CMakeLists.txt

创建 CMakeLists.txt 来定义项目、设置编译参数并链接 QAIRT Genie SDK。

cmake 复制代码
# ======================================================================================================================
# CMAKE
# ======================================================================================================================
message(STATUS "CMAKE_VERSION      =${CMAKE_VERSION}")
message(STATUS "CMAKE_COMMAND      =${CMAKE_COMMAND}")
message(STATUS "CMAKE_MAKE_PROGRAM =${CMAKE_MAKE_PROGRAM}")

# ======================================================================================================================
# 项目设置
# ======================================================================================================================
cmake_minimum_required(VERSION 3.22 FATAL_ERROR)
get_filename_component(PROJECT_FOLDER_PATH ${CMAKE_CURRENT_LIST_FILE} PATH)
get_filename_component(PROJECT_FOLDER_NAME ${PROJECT_FOLDER_PATH} NAME)
project(${PROJECT_FOLDER_NAME} LANGUAGES C CXX DESCRIPTION "examples for ${PROJECT_FOLDER_NAME}")

# ======================================================================================================================
# 编译参数
# ======================================================================================================================
# 指定C语言标准
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED TRUE)
set(CMAKE_C_EXTENSIONS TRUE)

# 指定C++语言标准
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
set(CMAKE_CXX_EXTENSIONS TRUE)

set(CMAKE_POSITION_INDEPENDENT_CODE TRUE)
set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)
set(CMAKE_INCLUDE_CURRENT_DIR TRUE)

# 设置默认编译类型为RelWithDebInfo,即 -O2 -g
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
  set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Choose the type of build." FORCE)
  set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
endif()

# ======================================================================================================================
# RPATH
# ======================================================================================================================
set(CMAKE_SKIP_RPATH OFF)
set(CMAKE_SKIP_BUILD_RPATH OFF)
set(CMAKE_SKIP_INSTALL_RPATH OFF)

# ======================================================================================================================
# SDK
# ======================================================================================================================
set(SDK_DIR $ENV{QAIRT_SDK_ROOT})
set(GENIE_INCLUDE_DIR ${SDK_DIR}/include/Genie)
set(GENIE_LIB_DIR ${SDK_DIR}/lib/x86_64-linux-clang)

# ======================================================================================================================
# 构建目标
# ======================================================================================================================
get_filename_component(TARGET_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME)
add_executable(${TARGET_NAME} llama_3.2_dialog.cpp)
target_include_directories(${TARGET_NAME} PRIVATE ${GENIE_INCLUDE_DIR})
target_link_directories(${TARGET_NAME} PRIVATE ${GENIE_LIB_DIR})
target_link_libraries(${TARGET_NAME} PRIVATE Genie)

关键点

  • 确保环境变量 QAIRT_SDK_ROOT 已正确设置,指向您的 SDK 安装目录。
  • 项目链接了 Genie 库,并包含了相应的头文件。

4.2 CMakePresets.json

使用 CMakePresets.json 可以简化构建配置,快速在 Debug 和 Release 模式间切换。

json 复制代码
{
  "version": 3,
  "configurePresets": [
    {
      "name": "release",
      "displayName": "Release",
      "binaryDir": "${sourceDir}/build/release",
      "cacheVariables": {
        "CMAKE_BUILD_TYPE": "Release"
      }
    },
    {
      "name": "debug",
      "displayName": "Debug",
      "binaryDir": "${sourceDir}/build/debug",
      "cacheVariables": {
        "CMAKE_BUILD_TYPE": "Debug"
      }
    }
  ],
  "buildPresets": [
    {
      "name": "release",
      "configurePreset": "release",
      "jobs": 0
    },
    {
      "name": "debug",
      "configurePreset": "debug",
      "jobs": 0
    }
  ]
}

使用方式:

bash 复制代码
# 配置并构建 Debug 版本
cmake --preset debug
cmake --build --preset debug

# 配置并构建 Release 版本
cmake --preset release
cmake --build --preset release

5. C++ 推理程序详解

以下是完整的 C++ 源代码 llama_3.2_dialog.cpp,它演示了如何使用 Genie SDK 加载配置、创建对话句柄并执行推理。

cpp 复制代码
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>

#include "GenieCommon.h"
#include "GenieDialog.h"
#include "GenieLog.h"

// 读取配置文件内容
inline std::string readFile(const std::string &path) {
  std::ifstream f(path);
  if (!f.is_open()) {
    std::cerr << "[ERROR] Failed to open config: " << path << std::endl;
    return {};
  }
  std::stringstream ss;
  ss << f.rdbuf();
  return ss.str();
}

// 状态码到字符串的转换函数
inline const char *statusName(Genie_Status_t s) {
  switch (s) {
    case GENIE_STATUS_SUCCESS:
      return "SUCCESS";
    case GENIE_STATUS_WARNING_ABORTED:
      return "WARNING_ABORTED";
    case GENIE_STATUS_WARNING_BOUND_HANDLE:
      return "WARNING_BOUND_HANDLE";
    case GENIE_STATUS_WARNING_PAUSED:
      return "WARNING_PAUSED";
    case GENIE_STATUS_WARNING_CONTEXT_EXCEEDED:
      return "WARNING_CONTEXT_EXCEEDED";
    case GENIE_STATUS_ERROR_GENERAL:
      return "ERROR_GENERAL";
    case GENIE_STATUS_ERROR_INVALID_ARGUMENT:
      return "ERROR_INVALID_ARGUMENT";
    case GENIE_STATUS_ERROR_MEM_ALLOC:
      return "ERROR_MEM_ALLOC";
    case GENIE_STATUS_ERROR_INVALID_CONFIG:
      return "ERROR_INVALID_CONFIG";
    case GENIE_STATUS_ERROR_INVALID_HANDLE:
      return "ERROR_INVALID_HANDLE";
    case GENIE_STATUS_ERROR_QUERY_FAILED:
      return "ERROR_QUERY_FAILED";
    case GENIE_STATUS_ERROR_JSON_FORMAT:
      return "ERROR_JSON_FORMAT";
    case GENIE_STATUS_ERROR_JSON_SCHEMA:
      return "ERROR_JSON_SCHEMA";
    case GENIE_STATUS_ERROR_JSON_VALUE:
      return "ERROR_JSON_VALUE";
    case GENIE_STATUS_ERROR_GENERATE_FAILED:
      return "ERROR_GENERATE_FAILED";
    case GENIE_STATUS_ERROR_GET_HANDLE_FAILED:
      return "ERROR_GET_HANDLE_FAILED";
    case GENIE_STATUS_ERROR_APPLY_CONFIG_FAILED:
      return "ERROR_APPLY_CONFIG_FAILED";
    case GENIE_STATUS_ERROR_SET_PARAMS_FAILED:
      return "ERROR_SET_PARAMS_FAILED";
    case GENIE_STATUS_ERROR_BOUND_HANDLE:
      return "ERROR_BOUND_HANDLE";
    default:
      return "UNKNOWN";
  }
}

// 检查 SDK API 调用结果的宏
#define CHECK(expr, msg)                                                                                   \
  do {                                                                                                     \
    Genie_Status_t _st = (expr);                                                                           \
    if (_st != GENIE_STATUS_SUCCESS) {                                                                     \
      std::cerr << "[ERROR] " << (msg) << ": " << statusName(_st) << " (" << int(_st) << ")" << std::endl; \
    } else {                                                                                               \
      std::cout << "[OK]   " << (msg) << std::endl;                                                        \
    }                                                                                                      \
  } while (0)

// 日志回调函数,将 SDK 日志输出到 stderr
static void logCallback(const GenieLog_Handle_t, const char *fmt, GenieLog_Level_t level, uint64_t, va_list args) {
  const char *tag = "?";
  switch (level) {
    case GENIE_LOG_LEVEL_VERBOSE:
      tag = "VRB";
      break;
    case GENIE_LOG_LEVEL_INFO:
      tag = "INF";
      break;
    case GENIE_LOG_LEVEL_WARN:
      tag = "WRN";
      break;
    case GENIE_LOG_LEVEL_ERROR:
      tag = "ERR";
      break;
    default:
      break;
  }
  fprintf(stderr, "[Genie:%s] ", tag);
  vfprintf(stderr, fmt, args);
  fprintf(stderr, "\n");
}

// 推理结果流式回调函数,实时打印生成的文本
static void queryCallback(const char *response, GenieDialog_SentenceCode_t code, const void *) {
  switch (code) {
    case GENIE_DIALOG_SENTENCE_BEGIN:
      std::cout << ">>> ";
      break;
    case GENIE_DIALOG_SENTENCE_CONTINUE:
      break;
    case GENIE_DIALOG_SENTENCE_COMPLETE:
      break;
    case GENIE_DIALOG_SENTENCE_END:
      std::cout << std::endl;
      break;
    default:
      break;
  }
  if (response) std::cout << response << std::flush;
}

// 默认系统提示词和查询
static const char *DEFAULT_SYSTEM_PROMPT =
    "You are a helpful, respectful and honest assistant. Always answer as helpfully as possible, "
    "while being safe. Your answers should not include any harmful, unethical, racist, sexist, "
    "toxic, dangerous, or illegal content. Please ensure that your responses are socially unbiased "
    "and positive in nature.";

static const char *DEFAULT_QUERY = "What is the capital of France?";

int main(int argc, char *argv[]) {
  std::cout << "========================================" << std::endl;
  std::cout << " Llama 3.2 Dialog - QAIRT Genie SDK" << std::endl;
  std::cout << "========================================" << std::endl;

  // 解析命令行参数,支持 -c/--config 指定配置文件
  std::string configPath;
  for (int i = 1; i < argc; ++i) {
    std::string arg = argv[i];
    if (arg == "-c" || arg == "--config") {
      if (i + 1 < argc) {
        configPath = argv[++i];
      } else {
        std::cerr << "[ERROR] -c/--config requires a file path argument" << std::endl;
        return 1;
      }
    }
  }

  // 将用户指定的配置文件路径转为绝对路径(基于当前工作目录)
  if (!configPath.empty()) {
    configPath = std::filesystem::absolute(configPath).string();
  }

  // 默认配置文件
  if (configPath.empty()) {
    configPath = "llama-3.2-1b-genaitransformer.json";
  }

  std::cout << "[INFO] Config: " << configPath << std::endl;

  // 1. 读取配置文件
  auto json = readFile(configPath);
  if (json.empty()) return 1;

  // 2. 创建日志句柄
  GenieLog_Handle_t logHandle = nullptr;
  GenieLog_create(nullptr, logCallback, GENIE_LOG_LEVEL_WARN, &logHandle);

  // 3. 从 JSON 创建 Dialog 配置
  GenieDialogConfig_Handle_t config = nullptr;
  Genie_Status_t             status = GenieDialogConfig_createFromJson(json.c_str(), &config);
  if (status != GENIE_STATUS_SUCCESS) {
    std::cerr << "[ERROR] GenieDialogConfig_createFromJson failed: " << statusName(status) << std::endl;
    return 1;
  }
  std::cout << "[OK]   Dialog config created" << std::endl;

  // 绑定日志到配置
  if (logHandle) GenieDialogConfig_bindLogger(config, logHandle);

  // 4. 创建 Dialog 句柄
  GenieDialog_Handle_t dialog = nullptr;
  status                      = GenieDialog_create(config, &dialog);
  if (status != GENIE_STATUS_SUCCESS) {
    std::cerr << "[ERROR] GenieDialog_create failed: " << statusName(status) << std::endl;
    GenieDialogConfig_free(config);
    return 1;
  }
  std::cout << "[OK]   Dialog created" << std::endl;

  // 5. 执行单次推理
  std::cout << "[INFO] Query: " << DEFAULT_QUERY << std::endl;
  std::cout << "AI: ";
  status = GenieDialog_query(dialog, DEFAULT_QUERY, GENIE_DIALOG_SENTENCE_COMPLETE, queryCallback, nullptr);
  std::cout << std::endl;
  if (status != GENIE_STATUS_SUCCESS) {
    std::cerr << "[ERROR] GenieDialog_query failed: " << statusName(status) << std::endl;
  }

  // 6. 清理资源
  std::cout << "[INFO] Cleaning up..." << std::endl;
  if (dialog) GenieDialog_free(dialog);
  if (config) GenieDialogConfig_free(config);
  if (logHandle) GenieLog_free(logHandle);

  std::cout << "[INFO] Done" << std::endl;
  return 0;
}

程序流程解析

  1. 参数解析 :支持通过 -c--config 指定自定义配置文件路径。
  2. 配置加载:读取并解
相关推荐
爱跳舞的烤冷面1 小时前
自学嵌入式第N天(Linux篇——文件编程)
java·开发语言·前端
richard_yuu1 小时前
JSON vs XML vs 二进制:3种序列化终极选型
xml·c++·qt·学习·json
caimouse2 小时前
ReactOS 图形系统分析(33):字体子系统 — font.c
c语言·开发语言·reactos
郝学胜-神的一滴2 小时前
并查集深度入门:从玄学抽象到 QuickFind & QuickUnion 源码实战
数据结构·c++·python·程序人生·算法·软件开发
cxr8282 小时前
HyperMind Lab M1 架构地基 Implementation Plan <二>
开发语言·人工智能·架构
特立独行的猫a2 小时前
Tauri v2的Rust应用 → HarmonyOS(鸿蒙 PC)移植30分钟速成指南
开发语言·rust·harmonyos·tauri·移植·鸿蒙pc
迷迭香yy2 小时前
大宗交易折溢价因子怎么挖掘本地化Python全流程实战
开发语言·人工智能·python
xieliyu.2 小时前
UPD协议结构以及开发中注意事项
java·开发语言·笔记·java-ee
Yweir2 小时前
AI大模型开发-Python介绍、版本说明
开发语言·人工智能·python