MLContext
实验性: 这是一项实验性技术。在生产环境中使用前,请仔细检查浏览器兼容性表格。
Web 神经网络(WebNN)API 的 MLContext 接口表示神经网络计算工作负载及其执行的全局状态。它拥有用于编译和运行 MLGraph 对象的资源,负责创建以及读写 MLTensor 对象,并报告由实现定义的算子支持情况。
MLContext 通过 ML.createContext() 创建。每个上下文都有一个上下文类型:
| 上下文类型 | 创建方式 | 描述 |
|---|---|---|
"default" | createContext(options) | 用户代理根据 MLContextOptions 中的提示选择计算设备。 |
"webgpu" | createContext(gpuDevice) | 上下文由 WebGPU 的 GPUDevice 创建。 |
所有涉及张量数据的工作(图的执行、张量的写入和读取)都会按顺序排入上下文的时间线,因此 writeTensor()、dispatch() 和 readTensor() 等调用可以连续发出,无需逐个等待。
此特性仅在安全上下文 (HTTPS 或 localhost)中可用,并且在主线程和 Web Worker 中均可用。
Web IDL
typedef record<USVString, MLTensor> MLNamedTensors;
dictionary MLContextLostInfo {
DOMString message;
};
[SecureContext, Exposed=(Window, Worker)]
interface MLContext {
undefined dispatch(MLGraph graph, MLNamedTensors inputs, MLNamedTensors outputs);
Promise<MLTensor> createTensor(MLTensorDescriptor descriptor);
Promise<MLTensor> createExportableTensor(
MLTensorDescriptor descriptor, GPUDevice gpuDevice);
Promise<MLTensor> createConstantTensor(
MLOperandDescriptor descriptor, AllowSharedBufferSource inputData);
Promise<ArrayBuffer> readTensor(MLTensor tensor);
Promise<undefined> readTensor(MLTensor tensor, AllowSharedBufferSource outputData);
undefined writeTensor(MLTensor tensor, AllowSharedBufferSource inputData);
GPUBuffer exportToGPU(MLTensor tensor);
MLOpSupportLimits opSupportLimits();
undefined destroy();
readonly attribute boolean accelerated;
readonly attribute Promise<MLContextLostInfo> lost;
};实例属性
accelerated
只读的 boolean,表示上下文是否使用大规模并行加速(如 GPU 或 NPU),而不是仅使用 CPU 处理。它反映了创建上下文时所用的 accelerated 选项(默认为 true,对于 "webgpu" 上下文始终为 true)。
const context = await navigator.ml.createContext({ powerPreference: 'low-power' });
if (!context.accelerated) {
console.log('Running on CPU; consider a smaller model.');
}lost
只读的 Promise,当上下文丢失时(即其底层执行设备不再可用,或调用了 destroy()),它会兑现为一个 MLContextLostInfo 对象。该 Promise 永远不会被拒绝。
当上下文丢失时,由它创建的所有 MLGraph 和 MLTensor 都会被销毁,待处理的操作会失败,并且对它的新操作会以 InvalidStateError 被拒绝。要恢复,请创建新的上下文并重新构建图。
context.lost.then((info) => {
console.warn('MLContext lost:', info.message);
// Recreate the context, rebuild graphs, and reallocate tensors here.
});实例方法
| 方法 | 描述 |
|---|---|
dispatch() | 使用给定的输入和输出张量,调度已编译图的执行。 |
createTensor() | 创建内容初始化为零的 MLTensor。 |
createExportableTensor() | 创建可导出到 WebGPU GPUDevice 的 MLTensor。 |
createConstantTensor() | 创建以给定数据初始化的不可变常量 MLTensor。 |
readTensor() | 将张量的内容读回脚本。 |
writeTensor() | 向张量写入数据。 |
exportToGPU() | 将张量作为 GPUBuffer 导出到其关联的 GPUDevice。 |
opSupportLimits() | 返回此上下文对每个算子所支持的数据类型和秩。 |
destroy() | 释放与上下文关联的所有资源。 |
dispatch()
dispatch() 方法在上下文的时间线上调度已编译 MLGraph 的计算工作负载。
dispatch() 会立即返回,并且不提供执行已完成的信号。要获取结果,请对输出张量调用 readTensor() 并 await 它;读取会排在先前已调度的工作之后。
语法
dispatch(graph, inputs, outputs)参数
-
graph要执行的
MLGraph。它必须由与此上下文关联的构建器构建。 -
inputs一个对象(
MLNamedTensors),将每个图输入名称映射到一个MLTensor。它必须为每个图输入恰好包含一个张量,并且每个张量的dataType和shape必须与对应的输入操作数匹配。 -
outputs一个对象(
MLNamedTensors),将每个图输出名称映射到接收结果的MLTensor。它必须为每个图输出恰好包含一个张量,且dataType和shape相匹配。
返回值
无(undefined)。
异常
-
TypeError如果图或任何张量属于不同的上下文;如果某个张量已被销毁、当前处于导出状态或是常量张量;如果同一个张量在
inputs和outputs中出现多次;或者命名的张量与图的输入或输出不完全匹配,则抛出。 -
InvalidStateErrorDOMException如果图已被销毁,则抛出。
createTensor()
createTensor() 方法创建一个与此上下文关联的 MLTensor。新张量的所有字节都初始化为零。
语法
createTensor(descriptor)参数
-
descriptor一个
MLTensorDescriptor,指定张量的dataType和shape,以及脚本是否可以读取(readable)和/或写入(writable)它。
返回值
一个 Promise,兑现为新的 MLTensor。
异常
返回的 Promise 会以如下异常被拒绝:
-
InvalidStateErrorDOMException上下文已丢失,或在张量分配之前已丢失。
-
UnknownErrorDOMException无法分配张量内存。
createExportableTensor()
createExportableTensor() 方法创建一个 MLTensor,之后可通过 exportToGPU() 将其导出到给定的 WebGPU GPUDevice。其内容初始化为零。
上下文是否支持导出到特定的 GPUDevice 由实现定义,并且可能取决于上下文类型和平台。
语法
createExportableTensor(descriptor, gpuDevice)参数
-
descriptor -
gpuDevice张量可导出到的
GPUDevice。
返回值
一个 Promise,兑现为新的可导出 MLTensor。
异常
返回的 Promise 会以如下异常被拒绝:
-
InvalidStateErrorDOMException上下文已丢失,或
gpuDevice已丢失。 -
NotSupportedErrorDOMException上下文无法将张量导出到
gpuDevice。 -
UnknownErrorDOMException无法分配张量内存。
createConstantTensor()
createConstantTensor() 方法创建一个以 inputData 的副本初始化的常量 MLTensor。常量张量的 constant 属性为 true;它既不可读也不可写,并且不能绑定为 dispatch() 的输入或输出。
常量张量旨在传递给 MLGraphBuilder.constant(tensor),这样大型权重只需上传到设备一次,并且可以被多个图共享。在使用该张量的图构建完成后,可以销毁该常量张量。
语法
createConstantTensor(descriptor, inputData)参数
-
descriptor一个
MLOperandDescriptor,指定dataType和shape。 -
inputData一个
ArrayBuffer、SharedArrayBuffer或ArrayBufferView,其字节会被复制到张量中。参见缓冲区要求。
返回值
一个 Promise,兑现为新的常量 MLTensor。
异常
返回的 Promise 会以如下异常被拒绝:
-
TypeError描述符的形状无效,或
inputData不满足缓冲区要求。 -
InvalidStateErrorDOMException上下文已丢失。
-
UnknownErrorDOMException无法分配或初始化张量。
readTensor()
readTensor() 方法将 MLTensor 的内容从上下文的时间线读回脚本。读取发生在上下文中所有先前排队的工作之后,因此等待它就是等待 dispatch() 完成的方式。
它有两个重载:一个分配并返回新的 ArrayBuffer,另一个是自带缓冲区(BYOB)的变体,将数据写入你提供的缓冲区。
语法
readTensor(tensor)
readTensor(tensor, outputData)参数
-
tensor要读取的
MLTensor。它必须以readable: true创建。 -
outputData可选接收数据的
ArrayBuffer、SharedArrayBuffer或ArrayBufferView。参见缓冲区要求。
返回值
readTensor(tensor):一个Promise,兑现为包含张量数据副本的新ArrayBuffer。readTensor(tensor, outputData):一个Promise,在数据写入outputData后兑现为undefined。
异常
返回的 Promise 会以如下异常被拒绝:
-
TypeError张量属于不同的上下文、已被销毁、不是
readable,或当前处于导出状态;或者outputData不满足缓冲区要求,或在读取完成前已被分离。 -
InvalidStateErrorDOMException上下文已丢失,或张量在读取待处理期间被销毁。
-
UnknownErrorDOMException无法读取数据。
writeTensor()
writeTensor() 方法在上下文的时间线上将 inputData 的字节复制到 MLTensor 中。数据在调用该方法时同步复制,因此 inputData 在之后可以立即重用或修改。
与 dispatch() 类似,writeTensor() 在写入完成时不提供信号;之后的 dispatch() 和 readTensor() 调用会排在它之后。
语法
writeTensor(tensor, inputData)参数
-
tensor要写入的
MLTensor。它必须以writable: true创建。 -
inputData包含数据的
ArrayBuffer、SharedArrayBuffer或ArrayBufferView。参见缓冲区要求。
返回值
无(undefined)。
异常
-
TypeError如果张量属于不同的上下文、已被销毁、不是
writable或当前处于导出状态,或者inputData不满足缓冲区要求,则抛出。
exportToGPU()
exportToGPU() 方法将可导出的 MLTensor(通过 createExportableTensor() 创建)导出到其关联的 GPUDevice,并返回可供 WebGPU 命令使用的 GPUBuffer。
张量处于导出状态期间,GPUDevice 对其内容拥有独占访问权,使用该张量的 dispatch()、readTensor() 和 writeTensor() 调用会抛出 TypeError(或以其拒绝)。对返回的缓冲区调用 GPUBuffer.destroy() 会将张量归还给 WebNN,之后即可再次使用。
- 返回的缓冲区的
size等于张量的字节长度,usage为GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST。 - 对已导出的张量再次调用
exportToGPU()会返回同一个GPUBuffer。 - 用户代理会正确地排序传输:使用该缓冲区提交的 WebGPU 工作会在张量上待处理的 WebNN 工作之后运行,而
destroy()之后的 WebNN 工作会在缓冲区上待处理的 WebGPU 工作之后运行。无需手动同步。 - 导出不保证零拷贝;内容可能会被复制到 GPU 内存或从 GPU 内存复制出来。
- 在导出期间销毁张量或其上下文不会使
GPUBuffer失效。如果GPUDevice丢失,张量会保持导出状态,直到调用GPUBuffer.destroy()。
语法
exportToGPU(tensor)参数
-
tensor由此上下文创建的可导出
MLTensor。
返回值
与张量的 GPUDevice 关联的 GPUBuffer。
异常
-
TypeError如果张量属于不同的上下文、已被销毁,或不是通过
createExportableTensor()创建的,则抛出。 -
InvalidStateErrorDOMException如果张量的
GPUDevice已丢失,则抛出。
opSupportLimits()
opSupportLimits() 方法返回一个 MLOpSupportLimits 字典,描述此上下文在全局范围内以及针对每个算子所支持的数据类型和张量秩。可用它来选择在当前平台上运行最佳的模型变体(例如 float16 与 float32,或 "nchw" 与 "nhwc")。
每个实现至少支持 MLGraphBuilder 操作中为每个算子列出的必需数据类型和秩,并且可以支持更多。
opSupportLimits()并非旨在增加指纹识别的熵;在当前的实现中,相同的信息可以从操作系统和浏览器版本推断出来。
语法
opSupportLimits()参数
无。
返回值
一个 MLOpSupportLimits 字典。
destroy()
destroy() 方法释放与上下文关联的所有资源。它会使上下文丢失:lost Promise 会兑现为一条由实现定义的消息,并且由该上下文创建的所有图和张量都会被销毁。未完成的计算请求以及张量创建、读取和写入请求都会失败。对已经丢失的上下文调用 destroy() 不会执行任何操作。
语法
destroy()返回值
无(undefined)。
缓冲区要求
createConstantTensor()、readTensor(tensor, outputData) 和 writeTensor()(以及 MLGraphBuilder.constant())会根据张量描述符验证缓冲区:
- 缓冲区的字节长度必须等于描述符的字节长度(元素数量 × 元素大小)。
ArrayBuffer或SharedArrayBuffer始终被接受。- 如果
ArrayBufferView是Uint8Array(通用字节视图,例如WebAssembly.Memory的切片),或其类型与数据类型匹配,则被接受:
MLOperandDataType | 匹配的 ArrayBufferView | 元素大小 |
|---|---|---|
"float32" | Float32Array | 4 字节 |
"float16" | Float16Array | 2 字节 |
"int64" | BigInt64Array | 8 字节 |
"uint64" | BigUint64Array | 8 字节 |
"int32" | Int32Array | 4 字节 |
"uint32" | Uint32Array | 4 字节 |
"int8" | Int8Array | 1 字节 |
"uint8" | Uint8Array | 1 字节 |
在
Float16Array尚不可用的地方,请通过Uint16Array底层的ArrayBuffer(或Uint8Array视图)传递原始的 IEEE 754 半精度位。
字典
MLContextLostInfo
lost Promise 所兑现的值。
| 成员 | 类型 | 描述 |
|---|---|---|
message | DOMString | 一条由实现定义的消息,描述上下文丢失的原因。 |
MLOpSupportLimits
由 opSupportLimits() 返回。除了下面的顶层成员外,每个算子还有一个成员,以对应的 MLGraphBuilder 方法命名(例如 conv2d、add、softmax),其值描述该算子各操作数的限制。
dictionary MLOpSupportLimits {
MLInputOperandLayout preferredInputLayout;
[EnforceRange] unsigned long long maxTensorByteLength;
MLTensorLimits input;
MLTensorLimits constant;
MLTensorLimits output;
// ...plus one member per operator, e.g.:
// MLConv2dSupportLimits conv2d;
// MLBinarySupportLimits add;
// MLSingleInputSupportLimits softmax;
};| 成员 | 类型 | 描述 |
|---|---|---|
preferredInputLayout | MLInputOperandLayout | 对布局相关算子(如 conv2d())的首选输入布局("nchw" 或 "nhwc")。 |
maxTensorByteLength | unsigned long long | 支持的最大张量大小,以字节为单位。 |
input | MLTensorLimits | 图输入操作数的限制。 |
constant | MLTensorLimits | 图常量操作数的限制。 |
output | MLTensorLimits | 图输出操作数的限制。 |
| 算子名称 | 特定于算子的字典 | 该算子各操作数的限制,例如 MLSingleInputSupportLimits 或 MLBinarySupportLimits。参见 MLGraphBuilder 操作中的各个算子。 |
MLTensorLimits
typedef sequence<MLOperandDataType> MLDataTypeList;
dictionary MLTensorLimits {
MLDataTypeList dataTypes;
MLRankRange rankRange;
};| 成员 | 类型 | 描述 |
|---|---|---|
dataTypes | sequence<MLOperandDataType> | 支持的数据类型。 |
rankRange | MLRankRange | 支持的最小和最大秩。 |
MLRankRange
dictionary MLRankRange {
unsigned long min;
unsigned long max;
};| 成员 | 类型 | 描述 |
|---|---|---|
min | unsigned long | 支持的最小秩。 |
max | unsigned long | 支持的最大秩。 |
MLSingleInputSupportLimits
用于只有一个张量输入的算子,例如 relu()、softmax() 或 reshape()。
dictionary MLSingleInputSupportLimits {
MLTensorLimits input;
MLTensorLimits output;
};MLBinarySupportLimits
用于有两个张量输入 a 和 b 的算子,例如 add()、equal() 或 matmul()。
dictionary MLBinarySupportLimits {
MLTensorLimits a;
MLTensorLimits b;
MLTensorLimits output;
};其他算子使用专用字典,每个操作数对应一个 MLTensorLimits 成员,例如 MLConv2dSupportLimits 包含 input、filter、bias 和 output。它们与各个算子一起列在 MLGraphBuilder 操作中。
示例
使用 MLTensor 执行图
const descriptor = { dataType: 'float32', shape: [2, 2] };
const context = await navigator.ml.createContext();
const builder = new MLGraphBuilder(context);
// 1. 创建计算图 'C = 0.2 * A + B'。
const constant = builder.constant(descriptor, new Float32Array(4).fill(0.2));
const A = builder.input('A', descriptor);
const B = builder.input('B', descriptor);
const C = builder.add(builder.mul(A, constant), B);
// 2. 编译图。
const graph = await builder.build({ C });
// 3. 创建可重用的输入和输出张量。
const [inputTensorA, inputTensorB, outputTensorC] = await Promise.all([
context.createTensor({ dataType: A.dataType, shape: A.shape, writable: true }),
context.createTensor({ dataType: B.dataType, shape: B.shape, writable: true }),
context.createTensor({ dataType: C.dataType, shape: C.shape, readable: true }),
]);
// 4. 初始化输入。
context.writeTensor(inputTensorA, new Float32Array(4).fill(1.0));
context.writeTensor(inputTensorB, new Float32Array(4).fill(0.8));
// 5. 执行图。
context.dispatch(graph, { A: inputTensorA, B: inputTensorB }, { C: outputTensorC });
// 6. 读回计算结果。
const result = await context.readTensor(outputTensorC);
console.log('Output value:', new Float32Array(result)); // [1, 1, 1, 1]读取到现有缓冲区
重用输出缓冲区可以避免为每次推理分配新的 ArrayBuffer:
const output = new Float32Array(4);
for (const frame of frames) {
context.writeTensor(inputTensor, frame);
context.dispatch(graph, { input: inputTensor }, { output: outputTensor });
await context.readTensor(outputTensor, output);
render(output);
}通过常量张量共享权重
const weightDesc = { dataType: 'float32', shape: [4, 4] };
const weights = await context.createConstantTensor(weightDesc, new Float32Array(16).fill(0.5));
console.log(weights.constant); // true
async function buildGraph() {
const builder = new MLGraphBuilder(context);
const x = builder.input('x', { dataType: 'float32', shape: [1, 4] });
return builder.build({ y: builder.matmul(x, builder.constant(weights)) });
}
// 两个图重用相同的设备端权重。
const [graphA, graphB] = await Promise.all([buildGraph(), buildGraph()]);
weights.destroy(); // 图构建完成后即可安全销毁。检查算子支持限制
const context = await navigator.ml.createContext();
const limits = context.opSupportLimits();
console.log('Preferred layout:', limits.preferredInputLayout); // "nchw" or "nhwc"
console.log('Max tensor size:', limits.maxTensorByteLength, 'bytes');
// 如果 conv2d 在此上下文上支持 float16 输入,则选择 float16 模型。
const useFp16 = limits.conv2d.input.dataTypes.includes('float16');
const modelUrl = useFp16 ? 'model-fp16.onnx' : 'model-fp32.onnx';
// 检查 gather() 是否接受 int64 索引。
const supportsInt64Indices = limits.gather.indices.dataTypes.includes('int64');
// 检查 add() 支持的最大秩。
console.log('add() max rank:', limits.add.a.rankRange.max);与 WebGPU 互操作
// 1. 创建 WebGPU 和 WebNN 上下文。
const gpuAdapter = await navigator.gpu.requestAdapter();
const gpuDevice = await gpuAdapter.requestDevice();
const context = await navigator.ml.createContext();
// 2. 构建一个简单的图:y = x + 1。
const builder = new MLGraphBuilder(context);
const descriptor = { dataType: 'float32', shape: [4] };
const x = builder.input('x', descriptor);
const one = builder.constant(descriptor, new Float32Array([1, 1, 1, 1]));
const graph = await builder.build({ y: builder.add(x, one) });
// 3. 创建可导出的张量并运行 WebNN dispatch。
const inputTensor = await context.createExportableTensor(
{ ...descriptor, writable: true }, gpuDevice);
const outputTensor = await context.createExportableTensor(
{ ...descriptor, readable: true }, gpuDevice);
context.writeTensor(inputTensor, new Float32Array([1, 2, 3, 4]));
context.dispatch(graph, { x: inputTensor }, { y: outputTensor });
// 4. 导出到 WebGPU,并在 GPU 命令中使用该缓冲区。
const gpuBuffer = context.exportToGPU(outputTensor);
// ... use gpuBuffer in WebGPU compute or render passes ...
// 5. 将张量归还给 WebNN,然后再次通过 WebNN 读取。
gpuBuffer.destroy();
const result = new Float32Array(await context.readTensor(outputTensor));
console.log(result); // [2, 3, 4, 5]规范
| 规范 | 状态 |
|---|---|
Web 神经网络 API — MLContext 接口 | 候选推荐草案 |
最新的编辑草案见 webmachinelearning.github.io/webnn 。
浏览器兼容性
WebNN 正在积极开发中,不同浏览器、操作系统和硬件的支持情况各不相同。
createExportableTensor()和exportToGPU()是较新的新增功能,可能尚未实现。