• 文档 >
  • 在 Apple 平台上集成和运行 ExecuTorch
快捷方式

在 Apple 平台上集成和运行 ExecuTorch

作者: Anthony Shoumikhin

用于 iOS 和 macOS 的 ExecuTorch 运行时作为预构建的 .xcframework 二进制目标的集合进行分发。这些目标与 iOS 和 macOS 设备以及模拟器兼容,并且提供发布和调试模式。

  • executorch - 主要运行时组件

  • backend_coreml - Core ML 后端

  • backend_mps - MPS 后端

  • backend_xnnpack - XNNPACK 后端

  • kernels_custom - 自定义内核

  • kernels_optimized - 优化后的内核

  • kernels_portable - 可移植内核(用作参考的朴素实现)

  • kernels_quantized - 量化内核

将您的二进制文件与 ExecuTorch 运行时以及导出 ML 模型使用的任何后端或内核链接。建议将核心运行时链接到直接使用 ExecuTorch 的组件,并将内核和后端链接到主应用程序目标。

注意: 要访问日志,请链接到 ExecuTorch 运行时的调试版本,即 executorch_debug 框架。为了获得最佳性能,始终链接到交付成果的发布版本(那些没有 _debug 后缀的版本),这些版本已删除所有日志开销。

集成

设置

CMake

构建 Xcode 项目需要 CMake。通过 homebrew 安装通常不起作用;相反,请全局安装打包的应用程序和命令行工具。

  1. 从 https://cmake.org/download 下载 macOS .dmg 安装程序。

  2. 打开 .dmg

  3. 将 CMake 应用程序拖到 /Applications 文件夹。

  4. 在终端中,安装命令行工具:sudo /Applications/CMake.app/Contents/bin/cmake-gui --install

Swift 包管理器

预构建的 ExecuTorch 运行时、后端和内核可作为 Swift PM 包使用。

Xcode

在 Xcode 中,转到 File > Add Package Dependencies。将 ExecuTorch 仓库 的 URL 粘贴到搜索栏中并选择它。确保将分支名称更改为所需的 ExecuTorch 版本,例如“0.4.0”,或者仅使用“latest”分支名称来获取最新的稳定版本。

然后选择哪个 ExecuTorch 框架应该链接到哪个目标。

单击下面的屏幕截图以观看有关如何添加包并在 iOS 上运行简单的 ExecuTorch 模型的演示视频

Integrating and Running ExecuTorch on Apple Platforms

CLI

在您的包文件中添加对 ExecuTorch 的包和目标依赖项,如下所示

// swift-tools-version:5.0
import PackageDescription

let package = Package(
  name: "YourPackageName",
  products: [
    .library(name: "YourPackageName", targets: ["YourTargetName"]),
  ],
  dependencies: [
    // Use "latest" branch name for the latest stable build.
    .package(url: "https://github.com/pytorch/executorch.git", .branch("0.4.0"))
  ],
  targets: [
    .target(
      name: "YourTargetName",
      dependencies: [
        .product(name: "executorch", package: "executorch"),
        .product(name: "xnnpack_backend", package: "executorch")
      ]),
  ]
)

然后检查一切是否正常工作。

cd path/to/your/package

swift package resolve

# or just build it
swift build

本地构建

集成 ExecuTorch 运行时的另一种方法是从源代码本地构建必要的组件并链接到它们。这条路线比较复杂,但绝对可行。

  1. 安装 Xcode 15+ 和命令行工具。

xcode-select --install
  1. 克隆 ExecuTorch。

git clone https://github.com/pytorch/executorch.git --recursive --depth 1
cd executorch
  1. 设置 Python 3.10+ 并激活虚拟环境。

python3 -m venv .venv
source .venv/bin/activate
  1. 安装 Cmake 和其他有用的 PyPI 包。

pip install --upgrade cmake pip zstd
  1. 安装所需的依赖项,包括后端所需的依赖项,例如 Core MLMPS,如果您计划也构建它们。

./install_requirements.sh

# Optional dependencies for Core ML backend.
./backends/apple/coreml/scripts/install_requirements.sh

# And MPS backend.
./backends/apple/mps/install_requirements.sh
  1. 使用提供的脚本构建 .xcframeworks。

./build/build_apple_frameworks.sh --help

例如,以下调用将为 Apple 平台构建 ExecuTorch 运行时以及所有当前可用的内核和后端。

./build/build_apple_frameworks.sh --coreml --custom --mps --optimized --portable --quantized --xnnpack

构建成功后,生成的框架可以在 cmake-out 目录中找到。将它们复制到您的项目中,并将其链接到您的目标。

运行时 API

查看 C++ 运行时 API 教程,了解有关如何加载和运行导出模型的更多信息。建议在 macOS 或 iOS 上使用 C++ API,如果需要将其暴露给其他组件,则可以使用 Objective-C++ 和 Swift 代码进行包装。请参考 演示应用程序 作为此类设置的示例。

一旦链接到 executorch 运行时框架,目标现在可以导入所有 ExecuTorch 公共头文件。例如,在 Objective-C++ 中

#import <ExecuTorch/ExecuTorch.h>
#import <executorch/extension/module/module.h>

或者在 Swift 中

import ExecuTorch

注意:导入 ExecuTorch 伞形头文件(或 Swift 中的 ExecuTorch 模块)仅提供对日志记录 API 的访问。您仍然需要根据需要显式导入其他运行时头文件,例如 module.h。除了下面描述的日志记录之外,Objective-C 或 Swift 中不支持其他运行时 API。

日志记录

我们为 Objective-C 和 Swift 提供额外的日志记录 API,作为内部 ExecuTorch 机制的轻量级包装。要使用它,只需在 Objective-C 中导入主框架头文件。然后使用 ExecuTorchLog 接口(或 Swift 中的 Log 类)订阅您自己的 ExecuTorchLogSink 协议实现(或 Swift 中的 LogSink)以监听日志事件。

#import <ExecuTorch/ExecuTorch.h>
#import <os/log.h>

@interface MyClass : NSObject<ExecuTorchLogSink>
@end

@implementation MyClass

- (instancetype)init {
  self = [super init];
  if (self) {
#if DEBUG
    [ExecuTorchLog.sharedLog addSink:self];
#endif
  }
  return self;
}

- (void)dealloc {
#if DEBUG
  [ExecuTorchLog.sharedLog removeSink:self];
#endif
}

#if DEBUG
- (void)logWithLevel:(ExecuTorchLogLevel)level
           timestamp:(NSTimeInterval)timestamp
            filename:(NSString *)filename
                line:(NSUInteger)line
             message:(NSString *)message {
  NSString *logMessage = [NSString stringWithFormat:@"%@:%lu %@", filename, (unsigned long)line, message];
  switch (level) {
    case ExecuTorchLogLevelDebug:
      os_log_with_type(OS_LOG_DEFAULT, OS_LOG_TYPE_DEBUG, "%{public}@", logMessage);
      break;
    case ExecuTorchLogLevelInfo:
      os_log_with_type(OS_LOG_DEFAULT, OS_LOG_TYPE_INFO, "%{public}@", logMessage);
      break;
    case ExecuTorchLogLevelError:
      os_log_with_type(OS_LOG_DEFAULT, OS_LOG_TYPE_ERROR, "%{public}@", logMessage);
      break;
    case ExecuTorchLogLevelFatal:
      os_log_with_type(OS_LOG_DEFAULT, OS_LOG_TYPE_FAULT, "%{public}@", logMessage);
      break;
    default:
      os_log(OS_LOG_DEFAULT, "%{public}@", logMessage);
      break;
  }
}
#endif

@end

Swift 版本

import ExecuTorch
import os.log

public class MyClass {
  public init() {
    #if DEBUG
    Log.shared.add(sink: self)
    #endif
  }
  deinit {
    #if DEBUG
    Log.shared.remove(sink: self)
    #endif
  }
}

#if DEBUG
extension MyClass: LogSink {
  public func log(level: LogLevel, timestamp: TimeInterval, filename: String, line: UInt, message: String) {
    let logMessage = "\(filename):\(line) \(message)"
    switch level {
    case .debug:
      os_log(.debug, "%{public}@", logMessage)
    case .info:
      os_log(.info, "%{public}@", logMessage)
    case .error:
      os_log(.error, "%{public}@", logMessage)
    case .fatal:
      os_log(.fault, "%{public}@", logMessage)
    default:
      os_log("%{public}@", logMessage)
    }
  }
}
#endif

注意:在示例中,当代码未构建为调试模式时,日志会被有意地去除,即 DEBUG 宏未定义或等于零。

故障排除

缺少运算符或后端

如果在链接到某个 ExecuTorch 库后,您在运行时仍然收到未注册的内核或后端错误,则可能需要使用 -all_load-force_load $(BUILT_PRODUCTS_DIR)/<library_name> 链接器标志在应用程序启动时强制注册相应的组件,例如 -force_load $(BUILT_PRODUCTS_DIR)/libkernels_portable-ios-release.a

Swift PM

如果 Swift PM 抱怨包内容的校验和不匹配,请尝试清理缓存

rm -rf ~/Library/Caches/org.swift.swiftpm

文档

访问 PyTorch 的全面开发者文档

查看文档

教程

获取针对初学者和高级开发人员的深入教程

查看教程

资源

查找开发资源并获得问题的解答

查看资源