注意
PyTorch Mobile 现已停止积极维护。请查看 ExecuTorch,这是 PyTorch 的全新设备端推理库。您还可以查看 此页面,以了解有关如何使用 ExecuTorch 构建 iOS 应用的更多信息。
iOS
要开始在 iOS 上使用 PyTorch,我们建议探索以下 HelloWorld。
使用 Hello World 示例快速入门
HelloWorld 是一款简单的图像分类应用程序,演示了如何在 iOS 上使用 PyTorch C++ 库。代码是用 Swift 编写的,并使用 Objective-C 作为桥接。
要求
- XCode 11.0 或更高版本
- iOS 12.0 或更高版本
模型准备
让我们从模型准备开始。如果您熟悉 PyTorch,您可能已经知道如何训练和保存模型。如果您不知道,我们将使用预训练的图像分类模型 - MobileNet v2,该模型已打包在 TorchVision 中。要安装它,请运行以下命令。
我们强烈建议您按照 Pytorch Github 页面 的说明在本地机器上设置 Python 开发环境。
pip install torchvision
成功安装 TorchVision 后,让我们导航到 HelloWorld 文件夹并运行 trace_model.py
。该脚本包含跟踪和保存可在移动设备上运行的 torchscript 模型 的代码。
python trace_model.py
如果一切正常,model.pt
应该会生成并保存在 HelloWorld/HelloWorld/model
文件夹中。
要详细了解 TorchScript,请访问 pytorch.org 上的教程
通过 Cocoapods 安装 LibTorch-Lite
PyTorch C++ 库在 Cocoapods 中可用,要将其集成到我们的项目中,只需运行
pod install
现在是时候在 XCode 中打开 HelloWorld.xcworkspace
,选择 iOS 模拟器并启动它(cmd + R)。如果一切正常,我们应该会在模拟器屏幕上看到狼的图片以及预测结果。
代码演练
在本部分中,我们将逐步演练代码。
图像加载
让我们从图像加载开始。
let image = UIImage(named: "image.jpg")!
imageView.image = image
let resizedImage = image.resized(to: CGSize(width: 224, height: 224))
guard var pixelBuffer = resizedImage.normalized() else {
return
}
我们首先从我们的 bundle 中加载图像并将其调整为 224x224。然后我们调用此 normalized()
类别方法来规范化像素缓冲区。让我们仔细看看下面的代码。
var normalizedBuffer: [Float32] = [Float32](repeating: 0, count: w * h * 3)
// normalize the pixel buffer
// see https://pytorch.ac.cn/hub/pytorch_vision_resnet/ for more detail
for i in 0 ..< w * h {
normalizedBuffer[i] = (Float32(rawBytes[i * 4 + 0]) / 255.0 - 0.485) / 0.229 // R
normalizedBuffer[w * h + i] = (Float32(rawBytes[i * 4 + 1]) / 255.0 - 0.456) / 0.224 // G
normalizedBuffer[w * h * 2 + i] = (Float32(rawBytes[i * 4 + 2]) / 255.0 - 0.406) / 0.225 // B
}
乍一看,代码可能看起来很奇怪,但一旦我们理解了我们的模型,它就会变得有意义。输入数据是形状为 (3 x H x W) 的 3 通道 RGB 图像,其中 H 和 W 预期至少为 224。图像必须加载到 [0, 1]
的范围内,然后使用 mean = [0.485, 0.456, 0.406]
和 std = [0.229, 0.224, 0.225]
进行标准化。
TorchScript 模块
现在我们已经预处理了输入数据并且有一个预训练的 TorchScript 模型,下一步是使用它们来运行预测。为此,我们将首先将模型加载到应用程序中。
private lazy var module: TorchModule = {
if let filePath = Bundle.main.path(forResource: "model", ofType: "pt"),
let module = TorchModule(fileAtPath: filePath) {
return module
} else {
fatalError("Can't find the model file!")
}
}()
请注意,TorchModule
类是 torch::jit::mobile::Module
的 Objective-C 包装器。
torch::jit::mobile::Module module = torch::jit::_load_for_mobile(filePath.UTF8String);
由于 Swift 无法直接与 C++ 通信,因此我们必须要么使用 Objective-C 类作为桥接,要么为 C++ 库创建 C 包装器。出于演示目的,我们将在此 Objective-C 类中包装所有内容。
运行推理
现在是时候运行推理并获取结果了。
guard let outputs = module.predict(image: UnsafeMutableRawPointer(&pixelBuffer)) else {
return
}
同样,predict
方法只是一个 Objective-C 包装器。在后台,它调用 C++ forward
函数。让我们看看它是如何实现的。
at::Tensor tensor = torch::from_blob(imageBuffer, {1, 3, 224, 224}, at::kFloat);
c10::InferenceMode guard;
auto outputTensor = _impl.forward({tensor}).toTensor();
float* floatBuffer = outputTensor.data_ptr<float>();
C++ 函数 torch::from_blob
将从像素缓冲区创建一个输入张量。请注意,张量的形状为 {1,3,224,224}
,表示 {N, C, H, W}
,如我们在上一节中所讨论的。
c10::InferenceMode guard;
以上代码行告诉 PyTorch 只执行推理。
最后,我们可以调用此 forward
函数以获取输出张量并将其转换为 float
缓冲区。
auto outputTensor = _impl.forward({tensor}).toTensor();
float* floatBuffer = outputTensor.data_ptr<float>();
收集结果
输出张量是一个形状为 1x1000 的一维浮点数数组,其中每个值表示从图像中预测标签的置信度。下面的代码对数组进行排序并检索前三个结果。
let zippedResults = zip(labels.indices, outputs)
let sortedResults = zippedResults.sorted { $0.1.floatValue > $1.1.floatValue }.prefix(3)
PyTorch 演示应用程序
对于更复杂的用例,我们建议您查看 PyTorch 演示应用程序。演示应用程序包含两个展示案例。一个相机应用程序,它运行量化模型以实时预测来自设备后置摄像头的图像。以及一个基于文本的应用程序,它使用文本分类模型来预测输入字符串的主题。
更多 PyTorch iOS 演示应用程序
图像分割
图像分割 演示了一个 Python 脚本,该脚本将 PyTorch DeepLabV3 模型转换为移动应用程序可以使用,以及一个使用该模型分割图像的 iOS 应用程序。
目标检测
目标检测 演示了如何转换流行的 YOLOv5 模型并在 iOS 应用程序中使用它来检测照片、相机拍摄的照片或实时摄像头的图片中的物体。
神经机器翻译
神经机器翻译 演示了如何转换使用 PyTorch NMT 教程 中的代码训练的序列到序列神经机器翻译模型,并在 iOS 应用程序中使用该模型进行法语到英语的翻译。
问答
问答 演示了如何转换强大的 Transformer QA 模型,并在 iOS 应用程序中使用该模型来回答有关 PyTorch Mobile 及其他方面的问题。
视觉Transformer
视觉Transformer 演示了如何使用 Facebook 最新的视觉Transformer DeiT 模型进行图像分类,以及如何转换另一个视觉Transformer模型并在 iOS 应用程序中使用它来执行手写数字识别。
语音识别
语音识别 演示了如何将 Facebook AI 的 wav2vec 2.0(语音识别领域领先的模型之一)转换为 TorchScript,以及如何在 iOS 应用程序中使用脚本化模型执行语音识别。
视频分类
TorchVideo 演示了如何在 iOS 上使用新发布的 PyTorchVideo 中提供的预训练视频分类模型,在测试视频、照片库中的视频甚至实时视频上查看视频分类结果,并在视频播放时每秒更新一次。
PyTorch iOS 教程和配方
iOS 上的图像分割 DeepLabV3
关于如何在 iOS 上准备和运行 PyTorch DeepLabV3 图像分割模型的全面分步教程。
PyTorch Mobile 性能配方
在移动设备上使用 PyTorch 的性能优化配方列表。
融合模块配方
了解如何在量化之前将 PyTorch 模块列表融合到单个模块中以减小模型大小。
移动设备量化配方
了解如何在不损失太多精度的同时减小模型大小并使其运行得更快。
为移动设备编写脚本和优化
了解如何将模型转换为 TorchScipt 并(可选)对其进行优化以用于移动应用程序。
iOS 模型准备配方
了解如何在 iOS 项目中添加模型并使用 PyTorch iOS pod。
从源代码构建 PyTorch iOS 库
要跟踪 iOS 的最新更新,您可以从源代码构建 PyTorch iOS 库。
git clone --recursive https://github.com/pytorch/pytorch
cd pytorch
# if you are updating an existing checkout
git submodule sync
git submodule update --init --recursive
确保您在本地机器上正确安装了
cmake
和 Python。我们建议按照Pytorch Github 页面设置 Python 开发环境
为 iOS 模拟器构建 LibTorch-Lite
打开终端并导航到 PyTorch 根目录。运行以下命令(如果您已经为 iOS 设备构建了 LibTorch-Lite(见下文),请先运行rm -rf build_ios
)
BUILD_PYTORCH_MOBILE=1 IOS_PLATFORM=SIMULATOR ./scripts/build_ios.sh
构建成功后,所有静态库和头文件都将生成在build_ios/install
下
为 arm64 设备构建 LibTorch-Lite
打开终端并导航到 PyTorch 根目录。运行以下命令(如果您已经为 iOS 模拟器构建了 LibTorch-Lite,请先运行rm -rf build_ios
)
BUILD_PYTORCH_MOBILE=1 IOS_ARCH=arm64 ./scripts/build_ios.sh
构建成功后,所有静态库和头文件都将生成在build_ios/install
下
XCode 设置
在 XCode 中打开您的项目,转到项目目标的Build Phases
- Link Binaries With Libraries
,点击 + 号并添加位于build_ios/install/lib
中的所有库文件。导航到项目Build Settings
,将Header Search Paths的值设置为build_ios/install/include
,并将Library Search Paths设置为build_ios/install/lib
。
在构建设置中,搜索other linker flags。在下面添加一个自定义链接器标志
-all_load
要使用自定义构建的库,请替换#import <LibTorch-Lite/LibTorch-Lite.h>
(在TorchModule.mm
中),这是在通过 Cocoapods 使用 LibTorch-Lite 时所需的代码,替换为以下代码
#include <torch/csrc/jit/mobile/import.h>
#include <torch/csrc/jit/mobile/module.h>
#include <torch/script.h>
最后,通过选择构建设置,搜索Enable Bitcode并将值设置为No来禁用目标的 bitcode。
在 CocoaPods 中使用 Nightly PyTorch iOS 库
如果您想尝试 PyTorch iOS 中添加的最新功能,您可以在您的Podfile
中使用LibTorch-Lite-Nightly
pod,它包含 nightly 构建的库
pod 'LibTorch-Lite-Nightly'
然后运行pod install
将其添加到您的项目中。如果您希望将 nightly pod 更新到较新的版本,您可以运行pod update
以获取最新版本。但请注意,您可能需要使用最新的 PyTorch(使用最新的 PyTorch 代码或使用pip install --pre torch torchvision -f https://download.pytorch.org/whl/nightly/cpu/torch_nightly.html
之类的命令进行快速 nightly 安装)在移动设备上构建使用的模型,以避免在移动设备上运行模型时出现可能的模型版本不匹配错误。
自定义构建
从 1.4.0 开始,PyTorch 支持自定义构建。您现在可以构建仅包含模型所需的算子的 PyTorch 库。为此,请按照以下步骤操作
1. 验证您的 PyTorch 版本是否为 1.4.0 或更高版本。您可以通过检查torch.__version__
的值来做到这一点。
2. 要转储模型(例如MobileNetV2
)中的算子,请运行以下 Python 代码行
import torch, yaml
model = torch.jit.load('MobileNetV2.pt')
ops = torch.jit.export_opnames(model)
with open('MobileNetV2.yaml', 'w') as output:
yaml.dump(ops, output)
在上面的代码片段中,您首先需要加载 ScriptModule。然后,使用export_opnames
返回 ScriptModule 及其子模块的算子名称列表。最后,将结果保存到 yaml 文件中。
3. 要使用准备好的 yaml 算子列表在本地运行 iOS 构建脚本,请将从上一步生成的 yaml 文件传递到环境变量SELECTED_OP_LIST
中。此外,在参数中,指定BUILD_PYTORCH_MOBILE=1
以及平台/体系结构类型。以 arm64 构建为例,命令应为
SELECTED_OP_LIST=MobileNetV2.yaml BUILD_PYTORCH_MOBILE=1 IOS_ARCH=arm64 ./scripts/build_ios.sh
4. 构建成功后,您可以按照上面的XCode 设置部分将结果库集成到您的项目中。
5. 最后一步是在运行forward
之前添加一行 C++ 代码。这是因为默认情况下,JIT 会对算子进行一些优化(例如融合),这可能会破坏我们从模型中转储的算子的一致性。
torch::jit::GraphOptimizerEnabledGuard guard(false);
使用 PyTorch JIT 解释器
PyTorch JIT 解释器是 1.9 之前的默认解释器(我们 PyTorch 解释器的一个版本,其大小效率不高)。它在 1.9 中仍将受支持,并且可以在 CocoaPods 中使用
pod 'LibTorch', '~>1.9.0'
iOS 教程
观看以下视频,PyTorch 合作伙伴工程师 Brad Heintz 将逐步介绍为 iOS 项目设置 PyTorch 运行时的方法
相应的代码可以在这里找到这里。
此外,请查看我们的移动性能秘籍,其中介绍了如何优化您的模型以及如何通过基准测试检查优化是否有效。
API 文档
目前,iOS 框架直接使用 Pytorch C++ 前端 API。C++ 文档可以在这里找到这里。要了解更多信息,我们建议您浏览 PyTorch 网页上的C++ 前端教程。
问题和贡献
如果您有任何问题或想为 PyTorch 做出贡献,请随时提交问题或打开拉取请求与我们联系。