使用大型模型
ONNX 模型的大小可能会因模型的复杂性和参数数量而有很大差异。它们可以小到几 KB,也可以大到几 GB。虽然 ONNX Runtime Web 旨在在浏览器中运行所有模型,但在使用大型模型时,仍需牢记一些注意事项。
目录
平台限制
在浏览器中使用大型模型时,您应该注意一些平台限制
ArrayBuffer 的最大大小
虽然 JavaScript 中 ArrayBuffer 的大小没有硬性限制,但每个浏览器都有自己的限制。例如,Chrome 中 ArrayBuffer 的最大大小为 0x7fe00000 字节(约 2GB)。使用 fetch
API 加载大型模型时要小心,因为如果您对大型文件调用 response.arrayBuffer()
,则可能会失败。
ONNX Runtime Web 通过使用 new WebAssembly.Memory()
来绕过此限制,从而创建 > 2GB 的数组缓冲区。但是,由 new WebAssembly.Memory()
创建的 ArrayBuffer 实例不可传输,因此无法与 Proxy 功能一起使用。
Protobuf 文件大小限制
ONNX 模型以 protobuf 格式序列化。protobuf 文件的最大大小为 2GB。如果 ONNX 模型大于 2GB,则通常使用外部数据生成。有关更多详细信息,请参见 外部数据。
WebAssembly 内存限制
WebAssembly 的内存限制为 4GB。这是 WebAssembly 模块由于 32 位寻址而可以访问的最大内存量。目前,ONNX Runtime Web 无法运行大于 4GB 的模型。将来,我们可能会通过使用 WASM64 或直接 GPU 权重加载来支持它。
缓存模型
为避免每次刷新页面时都加载模型,您可以使用 Cache API 或 Origin private file system 缓存模型。这样,可以从缓存加载模型,而不是每次都从服务器获取。
有关更多详细信息,请参见 Cache API 和 Origin private file system。
外部数据
当您使用大型 ONNX 模型时,它通常是使用外部数据生成的。由于 protobuf 文件大小限制,大于 2GB 的 ONNX 模型必须使用外部数据。外部数据是一个或多个单独的文件,通常由 ONNX 导出器生成。外部数据通常与 ONNX 模型文件放在同一目录中。
ONNX Runtime 支持加载带有外部数据的模型。这在 C/C++/Python API 中自动完成,无需任何额外步骤,因为这些语言绑定的 ONNX Runtime 可以访问文件系统。但是,在浏览器中,JavaScript 代码无法直接访问文件系统。因此,您需要多一个步骤才能将外部数据信息传递给 ONNX Runtime Web。
外部数据的工作原理
在深入了解细节之前,让我们首先了解 ONNX Runtime 中外部数据的工作原理。此信息很重要,否则这些步骤可能会让人感到困惑。
从技术上讲,ONNX 模型是一个 protobuf 文件。protobuf 文件包含模型图和权重。ONNX 规范允许将权重存储在 protobuf 文件中或外部数据文件中。当权重存储在 protobuf 文件中时,它将完全包含在 protobuf 文件中。当权重存储在外部数据文件中时,protobuf 文件包含该特定权重的以下信息
- “location”(字符串),指定外部数据文件的相对文件路径
- “offset”(整数),指定权重在外部数据文件中的起始字节偏移量
- “length”(整数),指定权重的字节长度
“location”通常由 ONNX 导出器确定。例如,导出器可能会将模型文件输出为 model_a.onnx
,并将外部数据文件输出为同一目录中的 model_a.data
。模型中的某些权重存储在 model_a.data
文件中,因此这些权重的“location”设置为 ./model_a.data
。此信息存储在文件 model_a.onnx
中。
这解释了为什么在原生平台中,务必确保永远不要重命名外部数据文件。如果重命名外部数据文件,则 protobuf 文件中的“location”将与实际文件名不匹配,并且 ONNX Runtime 将无法加载模型。
对于 ONNX Runtime Web,您始终需要将外部数据信息传递给 ONNX Runtime Web。重要的是要理解,protobuf 文件中定义的“location”与实际的外部文件路径是不同的概念。在以下章节的 JavaScript 代码中,这两个不同的概念将表示为“path”和“data”。
在 ONNX Runtime Web 中加载带有外部数据的模型
我们使用一个示例来说明如何在浏览器中加载带有外部数据的 ONNX 模型。假设我们有一个 ONNX 模型 model_a.onnx
和一个外部数据文件 model_a.data
。以下代码显示了如何在浏览器中加载带有外部数据的模型
const modelUrl = 'https://example.com/path/model_a.onnx';
const externalDataUrl = 'https://example.com/path/model_a.data';
const mySession = await ort.InferenceSession.create(modelUrl, {
...,
externalData: [
{
path: './model_a.data',
data: externalDataUrl
}
]
});
在上面的代码中,我们将外部数据信息传递给 InferenceSession.create()
方法。externalData
是一个对象数组,每个对象代表一个外部数据文件。该对象具有两个属性
path
(字符串),应与 protobuf 文件中权重的“location”信息匹配data
(字符串),指定外部数据文件。它可以是 URL、Blob 或 Uint8Array。
当您将模型和外部数据存储在 IndexedDB 中时,您可以从 IndexedDB 加载带有外部数据的模型。以下代码显示了如何从 IndexedDB 加载带有外部数据的模型
// assume loadFromIndexedDB() is a function implemented by your app that loads the data from the IndexedDB
// Load the model and external data from the IndexedDB
const modelBlob = await loadFromIndexedDB('model_a.onnx');
const externalDataBlob = await loadFromIndexedDB('model_a.data');
const mySession = await ort.InferenceSession.create(modelBlob, {
...,
externalData: [
{
path: './model_a.data',
data: externalDataBlob
}
]
});
有关更多详细信息,请参见 ONNX 外部数据。
故障排除
本节正在建设中。