Web 神经网络 API

W3C 候选推荐标准草案,

关于本文档的更多详细信息
此版本:
https://www.w3.org/TR/2026/CRD-webnn-20260910/
最新发布版本:
https://www.w3.org/TR/webnn/
编辑草案:
https://webmachinelearning.github.io/webnn/
先前版本:
历史记录:
https://www.w3.org/standards/history/webnn/
实现报告:
https://wpt.fyi/results/webnn?label=master&label=experimental&aligned&q=webnn
测试套件:
https://github.com/web-platform-tests/wpt/tree/master/webnn
反馈:
GitHub
规范内联
编辑:
Ningxin Hu英特尔公司
Dwayne Robinson微软 公司
前编辑:
Chai Chaoweeraprasit微软 公司
其他:
实现状态说明文档示例

摘要

本文档描述了一种专用于神经网络推理硬件加速的底层 API。

本文档的状态

本节描述本文档在发布时的状态。当前 W3C 出版物列表以及本技术报告的最新修订版可在 W3C 标准和草案索引中找到。

本文档由 Web 机器学习工作组 作为候选推荐标准草案,使用推荐标准 流程发布。

作为候选推荐标准发布并不意味着获得 W3C 及其成员的认可。候选 推荐标准草案整合了相较于上一版候选推荐标准的更改,工作组 打算将这些更改纳入后续的候选推荐标准快照。

这是一份草案文档,随时可能由其他文档更新、替换或废弃。除将其作为 正在进行的工作之外,不应引用本文档。

Web 机器学习工作组维护着一份工作组尚未处理的所有错误 报告列表。 强烈鼓励针对未解决的问题提交包含拟议规范文本的拉取请求。

本文档由一个依据 W3C 专利政策运作的工作组制作。 W3C 维护着一份 与该工作组交付成果相关的任何 专利披露的公开列表;该页面还 包含披露专利的说明。任何实际知晓某项 专利,并且认为该专利包含 必要 权利要求的个人,必须依照 W3C 专利政策第 6 节披露相关信息。

本文档受 2025 年 8 月 18 日 W3C 流程文档约束。

在候选推荐标准快照 2024 年 4 月 11 日2026 年 1 月 22 日之间,WebNN 规范经历了重大演进,包含超过 100 项重要更改。其中最显著的 新增内容包括用于增强 transformer 支持的第三批运算符、用于缓冲区 共享的 MLTensor API,以及一种新的抽象设备选择机制。API 表面已实现现代化,并且 根据更广泛的实现经验和开发者 反馈改进了互操作性。本规范版本加强了安全和隐私方面的考虑,包括 指纹识别缓解措施,并增加了新的无障碍考虑。这些更改反映了 规范朝产品就绪方向日趋成熟,改善了开发者易用性、扩大了后端 兼容性并提高了标准合规性。有关更多详细信息,请参阅 § 14 更改

本文档会随时维护和更新。本文档的某些部分仍在完善中,并且 预计进一步的改进将反映在修订后的候选推荐标准草案和快照中。

在请求过渡到建议推荐标准之前, 工作组将力求证明:

1. 简介

Web 神经网络 API 定义了一个面向 Web、与硬件无关的抽象层,它利用 操作系统和底层硬件平台的机器学习能力,而无需绑定到 特定于平台的能力。该抽象层满足主要机器学习 JavaScript 框架的需求,同时也允许熟悉 ML 领域的 Web 开发者编写自定义代码, 而无需借助库。

有关图示简介,请参阅说明文档

2. 用例

2.1. 应用用例

本节说明神经网络 推理硬件加速的应用级用例。这些用例中的所有应用都可以 构建于预训练深度神经网络(DNN)[models]之上。

注: 请注意,这里描述的一些用例 就其本质而言会侵犯隐私。计划将该 API 用于 此类用例的开发者应当确保使用该 API 是为了让用户受益, 用于用户能够理解并认可的目的。他们应当应用 Web 机器学习伦理原则 [webmachinelearning-ethics],并实施 适当的隐私风险缓解措施,例如透明度、数据最小化和用户控制。

注: § 3 无障碍 考虑提供了有关如何改善这些用例无障碍性的指导。

2.1.1. 人员检测

用户打开一个基于 Web 的视频会议应用,但她暂时 离开了房间。该应用使用对象检测来监测她是否位于 PC 前(例如,使用对象检测方法, 如使用单个 DNN 的 [SSD][YOLO]),以 检测摄像头输入帧中包含人员的区域。

当她回来时,应用会自动检测到她,并通知 其他在线用户她现在处于活动状态。

2.1.2. 语义分割

由于办公室没有可用的会议室,用户在 办公桌前通过基于 Web 的视频会议应用加入电话会议。在 电话会议期间,她不希望自己的房间和背景中的人员 可见。为了保护其他人员和周围环境的隐私, 应用运行机器学习模型,例如 [DeepLabv3+][MaskR-CNN][SegAny],从语义上 将图像划分成多个分段,并将 表示其他人员和背景的分段替换为另一张图片。

2.1.3. 骨架检测

基于 Web 的视频会议应用通过 运行支持实时人体姿态 估计的机器学习模型来跟踪用户的骨架姿态,例如 [PoseNet],以识别她的 手势和肢体语言。当 她举手时,她的麦克风会自动取消静音,她可以开始 在电话会议中发言。

2.1.4. 人脸识别

会议室中有多个人,他们使用 基于 Web 的视频会议应用加入在线会议。应用使用对象检测来检测 参与者的人脸(例如,使用对象检测 方法,如 [SSD]),并通过运行机器学习模型(例如 [FaceNet])检查每张人脸是否在 上一次会议中出现过, 该模型会验证两张人脸是否相同。

2.1.5. 面部关键点检测

用户希望在在线眼镜 商店中找到一副非常适合她的新眼镜。该在线商店提供基于 Web 的试戴模拟器,该模拟器运行 机器学习模型,例如人脸对齐网络 [FAN] 来检测面部关键点, 例如眼睛、鼻子、嘴巴等。当她选择一副眼镜时,模拟器 会将所选眼镜正确渲染到她 面部图像中检测到的眼睛位置上。

2.1.6. 风格迁移

用户正在在线商店寻找化妆品,并想知道哪种颜色可能 适合她的脸。在线商店显示化妆品的面部妆容示例图像, 并提供一个化妆模拟器,该模拟器运行类似 [ContextualLoss][PairedCycleGAN] 的机器学习模型,将 示例妆容图像的妆容风格迁移到她的面部图像。她可以通过模拟器查看所选妆容 在她脸上的效果。

2.1.7. 超分辨率

基于 Web 的视频会议应用正在接收来自对端的视频流,但 由于网络拥塞,视频分辨率降低。为了防止 感知到的视频质量下降,应用运行用于超分辨率的机器 学习模型,例如 [SRGAN] 来生成 更高分辨率的视频帧。

2.1.8. 图像描述生成

为了提高无障碍性,基于 Web 的演示应用通过运行机器学习模型(例如 [im2txt]) 自动生成图像描述, 该模型会预测演示幻灯片的说明性文字。

2.1.9. 文本生成图像

图像是现代 Web 体验的核心组成部分。在保护隐私的前提下,根据文本输入生成图像 的能力可实现 Web 应用和内容的视觉 个性化和适配。例如,一个 Web 应用可以将网页上的自然语言描述 或用户在文本提示中提供的描述作为输入,从而生成 与文本描述匹配的图像。由 潜在扩散模型架构 [LDM] 实现的这种文本生成图像用例构成了 其他 文本生成图像用例的基础。例如,图像修补,即利用新生成的内容有选择地修改 网页上现有图像的一部分; 或相反的图像扩展,即将原始图像扩展到其 原始尺寸之外,并用生成的内容填充空白区域。

2.1.10. 机器翻译

来自不同国家的多个人通过基于 Web 的实时 文本聊天应用进行交流。该应用使用 机器学习模型来翻译他们的对话,例如 [GNMT][OpenNMT],它会将 每段 文本翻译成不同的语言。

2.1.11. 情绪分析

用户正在通过基于 Web 的实时文本聊天应用与朋友聊天, 由于她看不到朋友的 脸,因此想知道朋友的感受。应用使用机器学习 模型分析朋友的情绪,例如 [DeepMoji], 该模型从输入文本中推断情绪,并显示 一个表示估计情绪的表情符号。

2.1.12. 视频摘要

基于 Web 的视频会议应用会录制接收到的视频流,并且 需要减少要存储的录制视频数据。应用通过使用用于 视频摘要的机器学习模型(例如 [Video-Summarization-with-LSTM]) 生成录制视频的 精简版本。

2.1.13. 噪声抑制

基于 Web 的视频会议应用会录制接收到的音频流,但 通常背景噪声无处不在。应用利用使用循环神经网络的实时 噪声抑制,例如 [RNNoise],来 抑制婴儿哭声或狗叫声之类的动态背景噪声,从而改善 视频会议中的音频体验。

2.1.14. 语音识别

语音识别,也称为语音转文本,可将口语 识别并转换为文本。语音 识别的应用示例包括转录、自动翻译、多模态交互、 实时字幕和虚拟助手。语音识别改善了 听觉内容的无障碍性,并能够以文本形式在保护隐私的前提下与此类 内容交互。常见 用例的示例包括使用 实时字幕观看视频或参与在线会议。诸如 [Whisper] 之类的模型在准确性 和稳健性方面接近人类,非常适合改善此类用例的无障碍性。

2.1.15. 文本生成

大型语言模型(LLM)支持各种文本生成用例,这些模型 能够执行需要具备预测文本序列中下一个项目这一通用能力的 任务。这类模型可以翻译文本、根据文本输入回答 问题、总结较大篇幅的文本,或根据文本输入生成 文本输出。与基于 RNN、CNN 或 LSTM 架构的 较旧模型相比,LLM 能够提供更好的性能,并进一步改善 本节讨论的许多其他用例的性能。 LLM 的示例包括 [t5-small][m2m100_418M][gpt2],以及 [llama-2-7b]

2.1.16. 检测伪造视频

用户在 Web 上接触到由“deepfake”生成的逼真伪造视频。 伪造视频可以将说话者的脸替换成总统的脸,以在政治上煽动 用户或操纵用户的观点。诸如 [FaceForensics++] 之类的 deepfake 检测 应用会分析视频,并保护用户免受 伪造视频或图像的影响。当她在 Web 上观看伪造视频时, 检测应用会实时提醒她该视频为欺诈视频。

2.2. 框架用例

本节收集了专用于神经网络推理硬件加速的底层 API 的 框架级用例。预计机器 学习框架将成为 Web 神经网络 API(WebNN API)的主要使用者,而通过 WebNN API 暴露的底层细节会对 一般 Web 开发者进行抽象。不过,也预计 对机器学习具有特定兴趣和能力的 Web 开发者会希望直接 与 WebNN API 交互,而不是使用更高层的 ML 框架。

2.2.1. 自定义层

Web 应用开发者希望在 WebNN API 上运行 DNN 模型。不过, 她发现一些激活函数,例如 [LeakyReLU][ELU] 等,并未包含在 WebNN API 中。为了解决此问题,她在 WebNN API 之上构建 这些附加激活函数的自定义层。 请注意,自定义层的范围除激活之外,还可以包括卷积、归一化 等。

2.2.2. 网络连接

一个 Web 应用使用 DNN 模型,该模型的上层卷积 层和下层全连接层的模型数据分别存储在不同文件中,因为 全连接层的模型数据会由于服务器端的微 调而定期更新。

因此,应用首先下载两个部分模型文件,并 将它们连接成单个模型。当模型更新时, 应用下载模型中经过微调的部分,并仅用它替换 全连接层。

2.2.3. 性能适配

Web 应用开发者担心她的 DNN 模型在 移动设备上的性能。她已确认,在没有 GPU 加速的移动设备上, 它可能运行得过慢。为了解决此问题,她的 Web 应用 参考 WebNN API 来确认加速是否可用,从而 使应用可以向没有加速能力的设备显示警告。

几周后,她开发了一个甚至可以在 CPU 上运行的小型 DNN 模型。为了适应 CPU 执行,她修改应用, 使应用在仅有 CPU 的设备上加载该小型模型。

2.2.4. 运算级执行

JavaScript ML 框架负责加载、解释和执行 ML 模型。在模型 执行阶段,框架遍历模型的各项运算,并在 CPU、GPU 或 ML 加速器等硬件设备上执行每项运算。为了避免设备之间不必要的数据复制, 框架选择同一设备来执行这些运算。对于计算密集型运算,例如 2D 卷积或矩阵乘法,框架使用 WebNN API,利用所选设备上可用的 ML 专用 加速来执行。

2.2.5. 与实时视频处理集成

通过使用实时视频处理,可以增强基于 WebRTC 的视频会议的用户体验。例如, 使用§ 2.1.2 语义 分割模型实现的背景模糊会模糊用户实时摄像头画面中的背景。为了满足此用例的性能 要求,WebNN API 与组成 媒体管线的其他 Web API 中的原语集成,以允许基于 WebNN API 对实时视频流进行转换。

3. 无障碍考虑

本节为 Web 作者提供有关如何改善由神经网络推理 硬件加速所支持的§ 2.1 应用用例无障碍性的指导。该指导不限于本 规范中概述的具体用例,并鼓励 Web 作者查阅 [wcag]以获取进一步的无障碍指导, 并查阅§ 6 伦理考虑以了解伦理 原则背景下的数字无障碍。

可以通过确保将§ 2.1.8 图像描述生成的描述 提供给屏幕阅读器及其他辅助技术(AT)用户来改善其无障碍性。鼓励 Web 作者确保 生成的图像描述在语义上与其相应图像关联,可以通过标准 alt 属性,也可以通过其他方式,这可能取决于描述是在页面首次加载时更新,还是 稍后因用户操作而更新。

§ 2.1.11 情绪分析可能会错误标记用户,从而错误分类用户, 导致歧视性体验。鼓励 Web 作者公开置信度分数,并为用户提供 关闭该功能的选项。

使用激进过滤器的§ 2.1.13 噪声抑制可能会完全滤除 构音障碍用户的语音,导致字幕和识别失败。鼓励 Web 作者提供 绕过或灵敏度控制,并且在实时字幕处于活动状态时,不要将噪声抑制硬编码为强制启用。

使用背景模糊分割的§ 2.2.5 与实时视频处理集成 有助于消除干扰,但可能增加过多延迟,从而破坏 唇读和实时字幕。鼓励 Web 作者为面向用户的键盘 和屏幕阅读器可操作的“背景模糊开/关”控制提供支持,并将其显示在其他无障碍/媒体 设置旁边。

§ 7.2 设备选择允许 Web 作者指示 对执行速度和功耗的偏好。鼓励实现者允许用户在 浏览器 UI 中覆盖 Web 作者的提示,以确保使用低端设备或对电池续航敏感设备的用户能够 保持字幕和其他关键无障碍功能的响应性,尤其是在便携式 AAC 或眼动 设置中。

4. 安全考虑

本规范定义了一个用于神经网络推理硬件加速的底层 API。该 API 被视为一项强大功能 [POWERFUL-FEATURES],因为它授予对用户计算机的底层访问权限。为了 满足强大功能的身份验证和机密性预期,并防止中间人 攻击,本规范定义的所有接口仅在安全上下文中可用。

使用§ 7.5 权限策略集成时,所有跨源框架默认禁用此 API。这可以防止 第三方内容使用此 API,除非嵌入页面明确设置授予 权限的策略。

此 API 允许从 WebGPU 规范定义的 GPUDevice 创建 MLContext。 有关此上下文安全特性的 更多信息,请参阅 WebGPU 安全考虑

此 API 提供了 GPU、CPU 和专用 ML 加速器硬件之间的抽象。使用 GPU 时,适用与 WebGPU 类似的拒绝服务考虑。使用 CPU 或专用 ML 加速器时,潜在资源争用的类型 不同,缓解措施将取决于实现和配置。实现应使用 平台提供的任何机制,以防止站点占用不公平数量的系统 资源。这些计算单元是共享资源,使用任何计算 API 都会影响 满负载系统上的总体性能。

一旦图被完全构建和编译,图中每个运算的输入形状 都会被推断并最终确定。边界检查发生在调用计算方法以针对实际数据执行 图时。在此阶段之前,没有任何实际数据绑定到已编译图。实现 负责确保根据届时已经推断出的数据形状执行适当的边界检查。

记录容易 发生越界访问的运算,以此作为实现者的指导。

实现必须防御基于被视为常量的数据发生变化而实施的控制流攻击。 例如,底层平台中的优化可能假定某个权重在整个 计算过程中保持不变。如果 API 允许保存权重的缓冲区内容在计算过程中 发生变化,那么这些优化假设将失效,从而导致底层 平台出现未定义行为。API 通过始终复制或转移缓冲区来缓解来自脚本的此类攻击, 但实现应考虑额外的防御措施,例如对被假定为 常量的数据进行进程隔离。

作为面向未来的措施,API 设计允许某些可以通用模拟的运算因 安全、性能或其他原因而弃用,而不会破坏兼容性。这通过 根据本 规范中定义的更小原始运算来定义高层函数而成为可能。这使得高层函数的原生实现可以被 polyfill 实现取代。

考虑到 CPU 当前在运行渲染器的进程之间共享的状态,调查侧信道 攻击的可行性。

为了不让攻击者能够针对可能包含缺陷的特定实现,§ 7.2 设备选择机制仅是一种提示,而 具体设备选择由实现决定——例如,用户代理可以选择永不 在已知存在漏洞的设备上运行模型。作为进一步的缓解措施,规范未定义设备枚举机制。

提示机制可部分 缓解该问题。调查其他缓解措施。

API 设计尽量减小已编译计算图的攻击面。托管各种运算的 MLGraphBuilder 接口是一种数据定义 API,因此不会执行任何内容, 只会构建数据。由此可见,潜在攻击仅限于通过调用 MLContext.dispatch() 方法在执行图之前将数据绑定到图时发生。这使实现者可以专注于强化 MLContext.dispatch() 方法。例如,通过确保它遵守数据边界,并在未遵守边界时适当 失败。

专门用于测量高分辨率时间的 Web API 会使用 降低分辨率、添加抖动、检测滥用和限制 API 调用等技术来缓解计时攻击 [hr-time-3]。WebNN 实现的 实际部署很可能会带来足够的抖动,使计时攻击 不切实际(例如,因为它们会使用 IPC),但建议实现者考虑并测试其 实现抵御计时攻击的能力。

注: 与 Unicode 序列相关的安全风险 在 label USVString 定义的上下文中进行了讨论。

4.1. 新增运算指南

本节为非规范性内容。

为确保本规范中定义的运算具有能够安全实现的形式, 本节包含有关应如何定义运算以降低潜在 实现问题的指导。预计这些指导将随时间推移而演变,以与行业最佳 实践保持一致:

总体而言,添加新功能时,应始终考虑技术架构组和隐私兴趣组在 [security-privacy-questionnaire] 中记录的安全和隐私影响。

5. 隐私考虑

与基于云的推理替代方案相比,此 API 通过将敏感用户 数据保留在浏览器沙箱内来改善隐私。图像、音频、视频流和其他个人 信息等输入数据永远不会离开用户设备,从而消除了与向远程 服务器传输数据和第三方数据处理相关的风险。

不过,作为一个与硬件加速能力紧密交互的强大本地计算 API, WebNN API 必须在性能优化与隐私保护之间取得平衡。该 API 包含多项 隐私保护措施,用于缓解指纹识别风险,同时仍可提供有效的机器 学习推理能力。

5.1. 指纹识别

按照设计,此 API 的目标是在以最佳性能和结果可靠性满足已识别的§ 2 用例所需的前提下,暴露最少量的信息。首先,API 通过标准化来缓解指纹识别:通过在各种 平台 API 之间定义一致行为,并最大限度减少符合规范的实现之间底层硬件差异 相关的信息泄漏。这通过以下方式实现:

总体设计确保实现可以在不同平台之间保持一致的接口, 同时提供必要的功能。通过抽象特定于平台的细节,无论底层加速由 CPU、GPU 还是专用 ML 硬件提供,该 API 都可以提供 保护隐私且可预测的行为。

注: MLContextOptions 正在积极开发中,并且预计设计会根据进一步的实现 经验和更广泛 Web 社区中的新用例而发生变化。

已提出 MLGraph.devices API 扩展,用于在图完全构建和编译后暴露实际选择用于 执行的设备。目前正在研究此 API 扩展的隐私影响。[问题 #836]

5.2. 执行时间分析

运算的计时特征可能提供有关底层硬件 性能的一些间接信息,这是任何计算 API 都固有的特征。在某些情况下,执行时间分析可以 间接揭示底层平台神经网络硬件加速 能力相对于另一个底层平台的性能。另请参阅§ 4 安全 考虑,了解有关计时攻击的进一步讨论。

注: 工作组欢迎提供进一步意见,讨论所提出的 执行时间分析指纹识别向量和缓解措施。

5.3. WebGPU 比较

与 WebGPU 不同,此 API 本身不支持编写自定义着色器;因此不容易受到 依赖着色器缓存或其他持久化数据的计时攻击。该 API 构建在浏览器或底层 OS 预先存在的着色器和低层原语之上。与 GPUDevice 交互的 Web 开发者应了解 WebGPU 编译缓存考虑

WebGPU API 将机器特定产物视为一项 隐私考虑。类似地,WebNN API 的计算单元调度在某些情况下也可能 引入指纹。不过,与 WebGPU 类似,此类指纹在每个供应商的大多数或所有 设备上都相同,从而缓解了该问题。此外,可以使用软件实现 进一步消除此类产物。

总体而言,预计此 API 的实现者应在适用情况下将WebGPU 隐私考虑应用于 其实现。

6. 伦理考虑

工作组已开始记录与在 Web 上使用机器学习相关的伦理问题, 以帮助识别其规范性规范应考虑哪些缓解措施。工作组 发布并维护一份 Web 机器学习伦理原则文档 [webmachinelearning-ethics],并通过专门的GitHub 仓库向 更广泛的社区开放贡献。

7. 编程模型

7.1. 概述

神经网络的核心是由数学运算组成的计算图。 这些运算是计算机视觉、自然语言处理和机器人技术等 现代机器学习技术的构建模块。 WebNN API 是用于构建、编译和执行神经网络计算 图的规范。

MLGraph 接口表示一个不可变的已编译计算图(即模型)。

MLGraphBuilder 接口作为构建器(工厂),用于构建计算图(其 ), 然后编译该图以创建 MLGraph

在 WebNN 中,计算图由对数据进行操作的 运算符组成,这些运算符是 图中的节点。MLOperand 是 对计算图内流动数据的表示,并构成图的边。MLOperand 包括用于推理的计算图输入值、用于推理的 常量(包括经过训练的权重)、 推理期间计算的中间值(通常称为激活值),以及推理的输出 值。一个运算符输入是一个或多个 MLOperand。一个 运算符输出是一个或 多个 MLOperand运算符具有特定于运算符的 参数来控制其行为,其中可以包含零个或多个 激活函数

MLGraphBuilder 接口的关键部分是诸如 gemm()relu() 这样的方法,它们创建一个运算符, 该运算符表示运行计算时要对输入数据执行的实际运算,并返回一个新的 MLOperand 来保存该运算符。创建 MLOperand 的方法会将任何输入激活连接到 运算符。每次方法调用都会返回一个不同的新值,而不会改变任何其他 MLOperand 的值。

一个运算符具有一个 标签,它是一个字符串,可以包含在诸如异常消息之类的诊断信息中。当创建一个运算符时,其标签会以实现定义的方式初始化,并且可以包含传入的 label

考虑添加一种 机制,用于报告 dispatch() 期间的错误。 [问题 #778]

在推理时,每个 MLOperand 都会 绑定到一个张量(实际数据),张量本质上是多维数组。张量的表示 取决于实现,但通常包括存储在某个缓冲区 (内存)中的数组数据,以及描述数组数据的一些元数据(例如其形状)。

计算图中的运算具有函数式语义。这使实现 有可能在多个张量之间共享数组数据。例如,reshape 或 slice 等运算的实现可能返回其输入张量的视图, 该视图与输入张量共享同一个缓冲区。(对于 reshape, 会共享全部数据,而对于 slice,则共享输入数据的一部分。) 实现可以如上所述对中间值使用视图。

执行之前,需要将用于计算一个或多个指定输出的计算图 转换、编译和优化。编译步骤的主要目的是启用跨越 两个或更多运算的优化,例如运算融合或循环融合。用户代理也可以在图转换期间执行这些 优化。

MLGraphBuilder.build() 方法会在后台编译图,而不会阻塞调用线程,并返回一个 Promise, 该 Promise 会兑现为一个 MLGraph。每个 MLGraphBuilder 最多只能构建一个 MLGraph

MLGraph 的底层实现将由运算符和操作数的特定平台表示组成, 这些表示对应于 MLGraphBuilder运算符MLOperand, 但它们对脚本不可见,并且可能是脚本所构建图的组合或分解形式。

一旦构建了 MLGraphMLContext.dispatch() 方法会异步执行图,对于 CPU 执行,会在单独工作线程中的并行时间线上执行; 对于 GPU,则会在 GPU 命令队列中的 GPU 时间线上执行。此方法立即返回, 不会阻塞调用线程,而实际执行会卸载到不同的时间线。调用者使用 MLNamedTensors 提供输入值,将输入 MLOperand 绑定到其值。调用者还为输出 MLOperand 提供 MLNamedTensors, 如果成功,这些输出将包含图执行的结果,可以使用 MLContext.readTensor(tensor) 方法将其读回脚本。这种执行方式支持 CPU、GPU 和 NPU 设备。

7.2. 设备选择

MLContext 接口表示神经网络执行的全局状态。一个重要的上下文状态是 底层执行设备,该设备负责管理资源,并促进神经网络图的编译和最终 执行。除了使用 MLContextOptions 的默认创建方法外,也可以从应用已经在使用的特定 GPUDevice 创建 MLContext

当 GPU 上下文执行一个图,并且该图的常量或输入以 ArrayBufferView 的形式位于系统内存中时,输入内容会自动从系统内存上传到 GPU 内存,并在图执行结束时下载回 ArrayBufferView 输出缓冲区所在的系统内存。只有在执行设备要求将数据从系统内存复制出去并再复制回来时, 才会发生这种数据上传和下载循环,例如 GPU 就属于这种情况。当设备是 CPU 设备时,不会发生这种情况。此外,图执行结果采用已知的布局格式。 虽然执行过程可能针对图中的中间结果使用原生内存访问 模式进行优化,但图中最后一个运算的输出必须 在图末尾将内容转换回已知布局格式,以维持调用者视角下预期的 行为。

当使用 MLContextOptions 创建 MLContext 时, 用户代理会在考虑这些选项的情况下选择并创建底层执行设备。

根据底层平台,用户代理可以选择不同的 CPU、NPU 和 GPU 设备组合。

有关此设计的历史和原理,请参阅设备 选择说明文档

7.3. 运算符

本节为非规范性内容。

WebNN API 定义了一组知名 CNN、RNN、transformer 和生成式 模型所需的运算符,用于满足关键§ 2.1 应用用例。每个 运算符的详细信息在本规范的规范性章节中定义,并按运算符 名称的字母顺序排列。下方 非规范性表格根据功能将这些运算符分组,以给出 API 表面积的功能概览。

注: 有些运算符属于多个类别。例如, clamp() 既是数学函数,也用作激活函数。

按类别划分的运算符
类别 运算符​
张量创建 input(), constant()
张量操作 concat(), expand(), gather(), gatherElements(), scatterElements(), gatherND(), scatterND(), where(), pad(), reshape(), slice(), split(), transpose(), resample2d(), reverse(), tile(), triangular()
张量量化 quantizeLinear(), dequantizeLinear()
张量类型转换 cast()
数学 add(), sub(), mul(), div(), max(), min(), clamp(), pow(), abs(), ceil(), cos(), erf(), exp(), floor(), identity(), log(), neg(), reciprocal(), roundEven(), sin(), sqrt(), tan(), tanh(), sign(), clamp()
逻辑 equal(), notEqual(), greater(), greaterOrEqual(), lesser(), lesserOrEqual(), logicalNot(), logicalAnd(), logicalOr(), logicalXor(), isNaN(), isInfinite()
矩阵乘法 matmul(), gemm()
卷积 conv2d(), convTranspose2d()
池化 averagePool2d(), l2Pool2d(), maxPool2d()
激活 clamp(), elu(), gelu(), hardSigmoid(), hardSwish(), leakyRelu(), linear(), prelu(), relu(), sigmoid(), softmax(), softplus(), softsign(), tanh()
归一化 batchNormalization(), instanceNormalization(), layerNormalization()
归约 argMin(), argMax(), reduceL1(), reduceL2(), reduceLogSum(), reduceLogSumExp(), reduceMax(), reduceMean(), reduceMin(), reduceProduct(), reduceSum(), reduceSumSquare(), cumulativeSum()
循环神经网络 gruCell(), gru(), lstmCell(), lstm()

7.4. 任务源

ML 任务源是一个 任务源,用于与 MLGraph 的异步编译和执行以及 MLContext 的创建相关的所有任务

给定一个全局对象 global 和一系列步骤 steps,要将 ML 任务排入队列,则使用 globalstepsML 任务源将全局任务排入队列

7.5. 权限策略集成

本规范定义了一个由字符串 "webnn" 标识的策略控制功能。 其默认允许列表'self'

8. API

8.1. navigator.ml 接口

ML 对象可分别通过 NavigatorWorkerNavigator 接口在 WindowWorkerGlobalScope 上下文中使用,并通过 navigator.ml 暴露。

interface mixin NavigatorML {
  [SecureContext, SameObject] readonly attribute ML ml;
};
Navigator includes NavigatorML;
WorkerNavigator includes NavigatorML;

8.2. ML 接口

enum MLPowerPreference {
  "default",
  "high-performance",
  "low-power"
};

dictionary MLContextOptions {
  MLPowerPreference powerPreference = "default";
  boolean accelerated = true;
};

[SecureContext, Exposed=(Window, Worker)]
interface ML {
  Promise<MLContext> createContext(optional MLContextOptions options = {});
  Promise<MLContext> createContext(GPUDevice gpuDevice);
};

8.2.1. MLContextOptions

注: MLContextOptions 正在积极开发中,预计其设计将根据进一步的实现 经验以及更广泛 Web 社区中的新用例发生变化。工作组正在考虑增加 API 控制,以允许定义后备设备、按首选顺序排列的多个设备,或 排除特定设备。正在讨论的其他考虑事项包括错误处理、最终 后备方案以及量化运算符。欢迎 Web 开发者、库作者、OS 和硬件供应商以及其他利益相关方通过 GitHub 对这些设计考虑事项提供反馈。有关指纹识别 考虑事项的进一步讨论,请参阅§ 5 隐私考虑

powerPreference 选项是一个 MLPowerPreference,表示应用与 功耗相关的偏好。它是以下值之一:

"default"
让用户代理选择最合适的行为。
"high-performance"
相比功耗,优先考虑执行速度。
"low-power"
相比执行速度等其他考虑因素,优先考虑功耗。

accelerated 选项表示应用与 大规模并行加速相关的偏好。此选项的优先级低于 powerPreference。 当设置为 true(默认)时,底层平台将尝试使用可用的 大规模并行加速器,例如 GPU 或 NPU,同时还取决于 powerPreference。 当设置为 false 时,应用表示其偏好使用 CPU 进行推理。如果存在 相互矛盾的输入,例如当 powerPreference"high-performance"acceleratedfalse 时,实现将选择底层 平台中可用的最佳匹配(例如高性能 CPU 模式,或者忽略 accelerated, 因为其优先级低于 powerPreference)。

8.2.2. createContext()

参数: 返回:一个 MLContext
给定领域 realmoptions(一个 GPUDeviceMLContextOptions),要创建 上下文,运行以下步骤:
  1. contextrealm 中一个新的 MLContext

  2. 如果 options 是一个 GPUDevice 对象,则:

    1. context.[[contextType]] 设置为 "webgpu"。

    2. context.[[powerPreference]] 设置为 "default"

    3. context.[[accelerated]] 设置为 true

  3. 否则:

    1. context.[[contextType]] 设置为 "default"。

    2. context.[[lost]] 设置为 realm 中的一个新的 promise

    3. 如果 options["powerPreference"] 存在,则将 context.[[powerPreference]] 设置为 options["powerPreference"]。

    4. 否则,将 context.[[powerPreference]] 设置为 "default"

    5. 如果 options["accelerated"] 存在,则将 context.[[accelerated]] 设置为 options["accelerated"]。

    6. 否则,将 context.[[accelerated]] 设置为 true

  4. 如果用户代理无法支持 context.[[contextType]], 则返回失败。

  5. 返回 context

createContext(options) 的步骤为:
  1. globalthis相关全局对象

  2. realmthis相关领域

  3. 如果 global关联 Document 未被允许使用 webnn 功能,则返回 realm一个新的 promise,该 promise 以 "SecurityError" DOMException 拒绝

  4. promiserealm一个新的 promise

  5. 并行运行以下步骤。

    1. context 为给定 realmoptions 创建上下文的结果。如果其返回失败,则使用 global 将 ML 任务排入队列,以使用 "NotSupportedError" DOMException 拒绝 promise,并中止这些步骤。

    2. 使用 global 将 ML 任务排入队列,以使用 context 兑现 promise

  6. 返回 promise

createContext(gpuDevice) 方法 的步骤为:
  1. globalthis相关全局对象

  2. realmthis相关领域

  3. 如果 global关联 Document 未被允许使用 webnn 功能,则返回 realm一个新的 promise,该 promise 以 "SecurityError" DOMException 拒绝

  4. promiserealm一个新的 promise

  5. 并行运行以下步骤。

    1. context 为给定 realmgpuDevice 创建上下文的结果。如果其返回失败,则使用 global 将 ML 任务排入队列,以使用 "NotSupportedError" DOMException 拒绝 promise,并中止这些步骤。

    2. 使用 global 将 ML 任务排入队列,以使用 context 兑现 promise

  6. 返回 promise

8.3. MLContext 接口

MLContext 接口表示神经网络计算工作负载和执行过程的全局状态。每个 MLContext 对象 都有相关联的上下文类型MLPowerPreference
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;
};
MLContext 具有以下内部槽:
[[contextType]],类型为上下文类型

MLContext上下文类型

[[powerPreference]],类型 为 MLPowerPreference

MLContextMLPowerPreference

[[accelerated]],类型为 boolean

MLContext 的 处理类型(CPU 或大规模并行处理)。

[[lost]],类型为 Promise<MLContextLostInfo>。

一个 Promise, 当 MLContext 的 底层执行设备不再可用时兑现。

[[timeline]]

MLContext 的计算单元上运算执行相关联的时间线。 这些运算包括在计算图上进行推理,以及修改 MLTensor[[data]]

更 严格地定义此时间线。[问题 #529]

上下文类型是 管理资源并促进神经网络图编译和执行的执行上下文类型:

"default"
根据用户偏好选项创建的上下文。
"webgpu"
从 WebGPU 设备创建的上下文。
accelerated getter 的步骤是返回 this.[[accelerated]]
给定 AllowSharedBufferSource bufferSourceMLOperandDescriptor descriptor,要使用描述符验证缓冲区,运行以下步骤:
  1. 如果 bufferSource字节长度不等于 descriptor字节长度,则返回 false。

  2. 根据 bufferSource 的类型切换:

    ArrayBuffer

    返回 true。

    SharedArrayBuffer

    返回 true。

    ArrayBufferView
    1. 如果 bufferSource 是一个 Uint8Array 对象,则返回 true。

    2. 如果根据此 表格bufferSourcedescriptordataType 匹配,则返回 true。

    3. 返回 false。

注: 无论 descriptordataType 为何,都支持使用 Uint8Array 作为表示 ArrayBuffer 切片的通用方式, 例如 WebAssembly.Memory 实例的一部分。鼓励开发者在编写 WebNN 代码时使用更具体的视图类型,以提高 可读性和可维护性。

给定一个 MLNamedTensors namedTensorsrecord<USVString, MLOperandDescriptor> namedDescriptors,要使用描述符验证张量
  1. 如果 namedTensors大小不等于 namedDescriptors大小,则返回 false。

  2. 对于 namedTensors每一个 nametensor

    1. 如果 tensor.[[isConstant]] 为 true,则返回 false。

    2. 如果 namedDescriptors[name] 不存在, 则返回 false。

    3. 如果 tensor.[[descriptor]]等于 namedDescriptors[name],则返回 false。

  3. 返回 true。

8.3.1. dispatch()

MLContext[[timeline]] 上调度已编译 MLGraph 的计算工作负载。

参数:

返回: undefined

注: dispatch() 本身不会提供任何 图执行已完成的信号。相反,调用者可以 await 读回 输出张量的结果。请参阅下方的 § 8.3.1.1 示例

dispatch(graph, inputs, outputs) 方法的步骤为:
  1. 如果 graph.[[context]] 不是 this,则抛出一个 TypeError

  2. 如果 graph.[[isDestroyed]] 为 true,则抛出一个 "InvalidStateError" DOMException

  3. allTensors 为一个 MLTensor列表, 由 inputs通过扩展 outputs组成。

  4. 如果 allTensors 包含任何重复的,则抛出一个 TypeError

  5. 对于 allTensors 中的每一个 tensor

    1. 如果 tensor.[[context]] 不是 this,则抛出一个 TypeError

    2. 如果 tensor.[[isDestroyed]] 为 true,则抛出一个 TypeError

    3. 如果 tensor 已导出,则抛出一个 TypeError

  6. 如果给定 inputsgraph.[[inputDescriptors]] 使用描述符验证张量 返回 false,则抛出一个 TypeError

  7. 如果给定 outputsgraph.[[outputDescriptors]] 使用描述符验证张量 返回 false,则抛出一个 TypeError

  8. 将以下步骤排入 graph.[[context]].[[timeline]]

    1. 运行以下步骤,但当 this 已丢失中止

      1. 给定 inputsoutputs,向 graph.[[implementation]] 发出计算请求。

        添加一种用于报告 图执行期间错误的机制。[问题 #778]

当使用张量创建常量操作数时,允许在 build 完成后销毁该张量。 实现应确保已编译图保持有效,并且不受这种 销毁的影响。

8.3.1.1. 示例
以下代码展示了使用 MLTensor 执行 MLGraph
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': 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. 执行图。
const inputs = {
  'A': inputTensorA,
  'B': inputTensorB
};
const outputs = {
  'C': outputTensorC
};
context.dispatch(graph, inputs, outputs);

// 6. 读回计算结果。
const result = await context.readTensor(outputTensorC);
console.log('输出值:', new Float32Array(result));  // [1, 1, 1, 1]

8.3.2. createTensor()

创建一个与此 MLContext 关联的 MLTensor

参数:

返回: Promise<MLTensor>。

createTensor(descriptor) 方法的步骤 为:
  1. globalthis相关全局对象

  2. realmthis相关领域

  3. 如果 this 已丢失,则返回 realm一个新的 promise,该 promise 以 "InvalidStateError" DOMException 拒绝

  4. tensor 为给定 thisdescriptor 创建 MLTensor 的结果。

  5. promiserealm一个新的 promise

  6. 将以下步骤排入 this.[[timeline]]

    1. 运行以下步骤,但当 this 已丢失中止

      1. 给定 descriptor,创建 tensor.[[data]] 并将所有字节初始化为零。

      2. 如果失败,则使用 global 将 ML 任务排入队列, 以使用 "UnknownError" DOMException 拒绝 promise, 并中止这些步骤。

      3. 否则,使用 global 将 ML 任务排入队列, 以使用 tensor 兑现 promise

    2. 如果已中止,则使用 global 将 ML 任务排入队列,以使用 "InvalidStateError" DOMException 拒绝 promise

  7. 返回 promise

8.3.3. createExportableTensor()

创建一个与此 MLContext 关联的 MLTensor, 该张量可以使用 exportToGPU() 导出到给定的 GPUDevice

参数:

返回: Promise<MLTensor>。

createExportableTensor(descriptor, gpuDevice) 方法的步骤为:
  1. globalthis相关全局对象

  2. realmthis相关领域

  3. 如果 this 已丢失,则返回 realm一个新的 promise,该 promise 以 "InvalidStateError" DOMException 拒绝

  4. 如果 gpuDevice 已丢失,则返回 realm一个新的 promise,该 promise 以 "InvalidStateError" DOMException 拒绝

  5. 如果 this 不支持将 MLTensor 导出到 gpuDevice,则返回 realm一个新的 promise,该 promise 以 "NotSupportedError" DOMException 拒绝

    注: MLContext 是否支持导出到给定的 GPUDevice实现定义的,并且可能取决于 MLContext上下文类型和 底层平台。

  6. tensor 为给定 thisdescriptorgpuDevice 创建 可导出 MLTensor 的结果。

  7. promiserealm一个新的 promise

  8. 将以下步骤排入 this.[[timeline]]

    1. 运行以下步骤,但当 this 已丢失中止

      1. 给定 descriptor,创建 tensor.[[data]] 并将所有字节初始化为零。

      2. 如果失败,则使用 global 将 ML 任务排入队列, 以使用 "UnknownError" DOMException 拒绝 promise, 并中止这些步骤。

      3. 否则,使用 global 将 ML 任务排入队列, 以使用 tensor 兑现 promise

    2. 如果已中止,则使用 global 将 ML 任务排入队列,以使用 "InvalidStateError" DOMException 拒绝 promise

  9. 返回 promise

8.3.4. createConstantTensor()

创建一个与此 MLContext 关联的常量 MLTensor

参数:

返回: Promise<MLTensor>。

createConstantTensor(descriptor, inputData) 方法的步骤为:
  1. globalthis相关全局对象

  2. realmthis相关领域

  3. 如果 this 已丢失,则返回 realm一个新的 promise,该 promise 以 "InvalidStateError" DOMException 拒绝

  4. 如果给定 descriptor 检查维度返回 false,则返回 realm一个新的 promise,该 promise 以 TypeError 拒绝

  5. 如果给定 inputDatadescriptor 使用描述符验证缓冲区返回 false,则返回 realm一个新的 promise,该 promise 以 TypeError 拒绝

  6. bytes 为给定 inputData 获取缓冲区源所持字节的副本的结果。

  7. 断言bytes长度等于 descriptor字节长度

  8. tensor 为给定 thisdescriptor 创建常量 MLTensor 的结果。

  9. promiserealm一个新的 promise

  10. 将以下步骤排入 this.[[timeline]]

    1. 运行以下步骤,但当 this 已丢失中止

      1. 给定 descriptor,创建 tensor.[[data]]

      2. 如果失败,则使用 global 将 ML 任务排入队列, 以使用 "UnknownError" DOMException 拒绝 promise, 并中止这些步骤。

      3. bytes 复制到 tensor.[[data]]

      4. 如果失败,则使用 global 将 ML 任务排入队列, 以使用 "UnknownError" DOMException 拒绝 promise, 并中止这些步骤。

      5. 否则,使用 global 将 ML 任务排入队列, 以使用 tensor 兑现 promise

    2. 如果已中止,则使用 global 将 ML 任务排入队列, 以使用 "InvalidStateError" DOMException 拒绝 promise

  11. 返回 promise

8.3.5. readTensor(tensor)

MLTensor[[data]]MLContext.[[timeline]] 读回脚本。

参数:

返回: Promise<ArrayBuffer>。 一个包含读取结果的缓冲区。

readTensor(tensor) 方法的步骤为:
  1. globalthis相关全局对象

  2. realmthis相关领域

  3. 如果 tensor.[[context]] 不是 this,则返回 realm一个新的 promise,该 promise 以 TypeError 拒绝

  4. 如果 tensor.[[isDestroyed]] 为 true,则返回 realm一个新的 promise,该 promise 以 TypeError 拒绝

  5. 如果 tensor.[[descriptor]].readable 为 false,则返回 realm一个新的 promise,该 promise 以 TypeError 拒绝

  6. 如果 tensor 已导出,则返回 realm一个新的 promise,该 promise 以 TypeError 拒绝

  7. promiserealm一个新的 promise

  8. promise 附加tensor.[[pendingPromises]]

  9. 将以下步骤排入 tensor.[[context]].[[timeline]]

    1. 运行以下步骤,但当 this 已丢失中止

      1. bytes 为一个包含 tensor.[[data]] 副本的字节序列

      2. 如果失败,则使用 global 将 ML 任务排入队列, 并运行以下步骤:

        1. tensor.[[pendingPromises]]移除 promise

        2. 以 "UnknownError" DOMException 拒绝 promise, 并中止这些步骤。

      3. 否则,使用 global 将 ML 任务排入队列, 并运行以下步骤:

        1. tensor.[[pendingPromises]]移除 promise

        2. buffer 为在 realm 中根据 bytes 创建一个 ArrayBuffer 的结果。

        3. buffer 兑现 promise

    2. 如果已中止,则使用 global 将 ML 任务排入队列,以使用 "InvalidStateError" DOMException 拒绝 promise

  10. 返回 promise

8.3.6. readTensor(tensor, outputData)

readTensor(tensor) 的自带缓冲区变体。 将 MLTensor[[data]] 读回到所提供的缓冲区中。

参数:

返回: Promise<undefined>。

readTensor(tensor, outputData) 方法的步骤为:
  1. globalthis相关全局对象

  2. realmthis相关领域

  3. 如果 tensor.[[context]] 不是 this,则返回 realm一个新的 promise,该 promise 以 TypeError 拒绝

  4. 如果 tensor.[[isDestroyed]] 为 true,则返回 realm一个新的 promise,该 promise 以 TypeError 拒绝

  5. 如果 tensor.[[descriptor]].readable 为 false,则返回 realm一个新的 promise,该 promise 以 TypeError 拒绝

  6. 如果 tensor 已导出,则返回 realm一个新的 promise,该 promise 以 TypeError 拒绝

  7. 如果给定 outputDatatensor.[[descriptor]] 使用描述符验证缓冲区 返回 false,则返回 realm一个新的 promise,该 promise 以 TypeError 拒绝

  8. promiserealm一个新的 promise

  9. promise 附加tensor.[[pendingPromises]]

  10. 将以下步骤排入 tensor.[[context]].[[timeline]]

    1. 运行以下步骤,但当 this 已丢失中止

      1. bytes 为一个包含 tensor.[[data]] 副本的字节序列

      2. 如果失败,则使用 global 将 ML 任务排入队列, 以运行以下步骤:

        1. tensor.[[pendingPromises]]移除 promise

        2. 以 "UnknownError" DOMException 拒绝 promise, 并中止这些步骤。

      3. 否则,使用 global 将 ML 任务排入队列, 以运行以下步骤:

        1. tensor.[[pendingPromises]]移除 promise

        2. 如果 outputData 已分离,则以 TypeError 拒绝 promise, 并中止这些步骤。

          注: 上面的使用描述符验证缓冲区会在 outputData 已分离时失败, 但 outputData 也可能在 该步骤与本步骤之间被分离。

        3. bytes 写入 outputData

        4. undefined 兑现 promise

    2. 如果已中止,则使用 global 将 ML 任务排入队列,以使用 "InvalidStateError" DOMException 拒绝 promise

  11. 返回 promise

8.3.7. writeTensor()

MLContext[[timeline]] 上,将数据写入 MLTensor[[data]]

参数:

返回: undefined

writeTensor(tensor, inputData) 方法的步骤为:
  1. 如果 tensor.[[context]] 不是 this,则抛出一个 TypeError

  2. 如果 tensor.[[isDestroyed]] 为 true,则抛出一个 TypeError

  3. 如果 tensor.[[descriptor]].writable 为 false,则抛出一个 TypeError

  4. 如果 tensor 已导出,则抛出一个 TypeError

  5. 如果给定 inputDatatensor.[[descriptor]] 使用描述符验证缓冲区 返回 false,则抛出一个 TypeError

  6. bytes 为给定 inputData 获取缓冲区源所持字节的副本的结果。

  7. 断言bytes长度等于 tensor.[[descriptor]]字节长度

  8. 将以下步骤排入 tensor.[[context]].[[timeline]]

    1. 运行以下步骤,但当 this 已丢失中止

      1. bytes 复制到 tensor.[[data]]

        添加一种用于报告写入张量时 发生错误的机制。[问题 #778]

注:dispatch() 类似, writeTensor() 本身不会提供写入已完成的信号。要检查张量的内容, 调用者可以 await 读回张量的结果。

8.3.8. exportToGPU()

将一个可导出的 MLTensor 作为 GPUBuffer 导出到其关联的 GPUDevice, 并将其借给该 GPUDevice, 直到返回的 GPUBuffer 被销毁。

参数:

返回:一个 GPUBuffer

当一个 MLTensor 已导出时, [[gpuDevice]]MLTensor 的 内容具有独占访问权,并且任何依赖该 MLTensor 的 WebNN 运算—— 包括 dispatch()readTensor(tensor)readTensor(tensor, outputData)writeTensor() ——都会抛出一个 TypeError

返回的 GPUBuffer 创建时,其 usageGPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST,且其 size 等于 MLTensor[[descriptor]]字节长度。当返回的 GPUBuffer 被销毁时,MLTensor 会被归还MLContext,并且 可以再次用于 WebNN 运算。

注: 无法保证导出 MLTensor 时 会实现零拷贝。如果为 MLTensor 提供支持的内存无法被 [[gpuDevice]] 访问,则导出可能需要将 MLTensor 的 内容复制到返回的 GPUBuffer 中, 并在 GPUBuffer 被销毁后再将其复制回来。

exportToGPU(tensor) 方法的步骤为:
  1. 如果 tensor.[[context]] 不是 this,则抛出一个 TypeError

  2. 如果 tensor.[[isDestroyed]] 为 true,则抛出一个 TypeError

  3. 如果 tensor.[[exportableToGPU]] 为 false,则抛出一个 TypeError

  4. realmthis相关领域

  5. gpuDevicetensor.[[gpuDevice]]

  6. 如果 gpuDevice 已丢失,则抛出一个 "InvalidStateError" DOMException

  7. 如果 tensor 已导出,则返回 tensor.[[exportedBuffer]]

  8. usageGPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST

  9. bufferrealm 中一个与 gpuDevice 关联的新 GPUBuffer, 其 sizetensor.[[descriptor]]字节长度,其 usageusage

  10. 将以下步骤排入 tensor.[[context]].[[timeline]]

    1. 运行以下步骤,但当 this 已丢失中止

      1. 确保 buffer 的内容反映 tensor.[[data]]

        注: 如果导出为 零拷贝,则 buffertensor.[[data]] 由同一块内存提供支持,并且不会执行复制。

  11. tensor.[[exportedBuffer]] 设置为 buffer

  12. 返回 buffer

用户代理必须确保,任何排入 gpuDeviceGPUQueue 且读取或写入 buffer 的运算,都不得在上面用于确保 buffer 的内容反映 tensor.[[data]] 的排队步骤完成之前执行。

注: 此顺序保证是用户代理内部的; 它不要求应用执行任何可观察到的同步。应用可以在 exportToGPU() 返回后立即安全地记录并提交使用 bufferGPUQueue 工作。

当由 exportToGPU() 返回的 GPUBuffer 被销毁时,用户代理必须给定导出该缓冲区所来自的 MLTensor 归还已导出的 MLTensor

注:MLTensor 或其 MLContext 已 导出时将其销毁,并不会使返回的 GPUBuffer 失效; GPUBuffer 在被销毁之前仍可由 [[gpuDevice]] 使用。

注: 如果当 MLTensor 已导出时, [[gpuDevice]] 变为已丢失,则返回的 GPUBuffer 不会被隐式销毁,并且 MLTensor 仍保持 已导出状态(因此 无法由 WebNN 运算使用),直到应用调用 destroy(); 即使 gpuDevice 已丢失,调用该方法仍然有效。

8.3.8.1. 示例
以下代码展示了使用可导出张量的最小 WebNN 到 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 y = builder.add(x, one);
const graph = await builder.build({y});

// 3. 创建可导出张量,运行 WebNN dispatch。
const inputTensor = await context.createExportableTensor(
    {dataType: 'float32', shape: [4], writable: true}, gpuDevice);
const outputTensor = await context.createExportableTensor(
    {dataType: 'float32', shape: [4], 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);
// ... 在 WebGPU 计算/渲染工作中使用 gpuBuffer ...

// 5. 将张量归还给 WebNN,然后再次从 WebNN 读取。
gpuBuffer.destroy();
const result = new Float32Array(await context.readTensor(outputTensor));
console.log(result);  // [2, 3, 4, 5]

8.3.9. opSupportLimits()

opSupportLimits() 暴露不同实现在运算符级别上的支持程度。鼓励 WebNN API 的使用者通过使用 opSupportLimits() 探测功能支持级别,以确定要部署到每个目标平台上的最佳模型架构。

注: opSupportLimits() API 并非旨在为浏览器指纹识别提供额外的熵。在当前实现中, 仅根据 OS 和浏览器版本即可推断这些功能支持信息。如果未来实现的多样性 有此必要,此 API 允许未来实现添加新的隐私缓解措施, 例如像 WebGPU 一样对能力进行分桶以减少熵。

有关指纹识别 考虑事项的进一步讨论,请参阅§ 5 隐私考虑

8.3.9.1. MLOpSupportLimits 字典
MLOpSupportLimits 具有以下顶层成员,除此之外,每个运算符都具有一个在其构建器方法中定义的对应成员。
dictionary MLOpSupportLimits {
  MLInputOperandLayout preferredInputLayout;
  [EnforceRange] unsigned long long maxTensorByteLength;
  MLTensorLimits input;
  MLTensorLimits constant;
  MLTensorLimits output;
};
preferredInputLayout, 类型为 MLInputOperandLayout

用于 conv2d() 等依赖布局的运算符的首选输入布局。

maxTensorByteLength, 类型为 unsigned long long

支持的张量最大长度,以字节为单位。

input, 类型为 MLTensorLimits

MLGraph 的输入 MLOperand 的 支持限制。

constant, 类型为 MLTensorLimits

MLGraph 的常量 MLOperand 的 支持限制。

output, 类型为 MLTensorLimits

MLGraph 的输出 MLOperand 的 支持限制。

8.3.9.2. MLRankRange 字典
dictionary MLRankRange {
  unsigned long min;
  unsigned long max;
};
min, 类型为 unsigned long

支持的最小秩。

max, 类型为 unsigned long

支持的最大秩。

8.3.9.3. MLTensorLimits 字典
typedef sequence<MLOperandDataType> MLDataTypeList;

dictionary MLTensorLimits {
  MLDataTypeList dataTypes;
  MLRankRange rankRange;
};
dataTypes, 类型为 MLDataTypeList

支持的数据类型。

rankRange, 类型为 MLRankRange

支持的最小秩和最大秩。

8.3.9.4. MLBinarySupportLimits 字典
dictionary MLBinarySupportLimits {
  MLTensorLimits a;
  MLTensorLimits b;
  MLTensorLimits output;
};
a, 类型为 MLTensorLimits

用于 a 操作数的 MLTensorLimits

b, 类型为 MLTensorLimits

用于 b 操作数的 MLTensorLimits

output, 类型为 MLTensorLimits

用于输出操作数的 MLTensorLimits

8.3.9.5. MLSingleInputSupportLimits 字典
dictionary MLSingleInputSupportLimits {
  MLTensorLimits input;
  MLTensorLimits output;
};
input, 类型为 MLTensorLimits

用于输入操作数的 MLTensorLimits

output, 类型为 MLTensorLimits

用于输出操作数的 MLTensorLimits

8.3.10. destroy()

destroy() 方法可被调用以释放与上下文关联的所有资源。任何未完成的计算请求 以及 MLTensor 创建/读取/写入请求都将失败。

destroy() 方法的步骤为:
  1. 如果 this 已丢失,则中止这些步骤。

  2. 使用一条实现定义的消息,运行丢失 this 的步骤。

    注: 一条表明 destroy() 已被调用的消息可帮助开发者区分上下文丢失的原因。

8.3.11. 错误

当用户代理确定一个 MLContext 不再可用于满足请求时,它必须为其运行上下文丢失步骤。

MLContext context上下文丢失 步骤为:
  1. globalcontext相关全局对象

  2. 使用 global 将 ML 任务排入队列,以运行以下步骤:

    1. 使用一条实现定义的消息,丢失 context

要使用 DOMString message 丢失 MLContext context
  1. info 为一个新的 MLContextLostInfo

  2. info.message 设置为 message

  3. info 兑现 context.[[lost]]

  4. 对于满足 graph.[[context]] 等于 this 的每个 MLGraph graph

    1. graph 作为 this,为 graph 运行 destroy() 方法的步骤。

  5. 对于满足 tensor.[[context]] 等于 this 的每个 MLTensor tensor

    1. tensor 作为 this,为 tensor 运行 destroy() 方法的步骤。

message, 类型为 DOMString

一条提供有关所发生错误信息的实现定义消息。

lost getter 的步骤是返回 this[[lost]] Promise

A MLContext 如果其 [[lost]] Promise 已敲定,则已丢失

8.4. MLGraph 接口

MLGraph 接口表示一个已编译的计算图。一个已编译的图一旦构建便不可变,之后无法 更改。
[SecureContext, Exposed=(Window, Worker)]
interface MLGraph {
  undefined destroy();
};
MLGraph 具有 以下内部槽:
[[context]],类型为 MLContext

与此 MLGraph 关联的 MLContext 类型上下文。

[[inputDescriptors]],类型 为 record<USVString, MLOperandDescriptor>

将此 MLGraph 的所有输入 MLOperand 中每个输入 MLOperand 的名称映射到其 MLOperandDescriptor

[[outputDescriptors]],类型 为 record<USVString, MLOperandDescriptor>

将此 MLGraph 的所有输出 MLOperand 中每个输出 MLOperand 的名称映射到其 MLOperandDescriptor

[[implementation]]

由用户代理提供的底层实现。

[[isDestroyed]],类型为 boolean

MLGraph.destroy() 方法的步骤是否已运行。一旦销毁,MLGraph 就无法再使用。

8.4.1. destroy()

destroy() 方法可被调用以释放与该图关联的所有资源。

destroy() 方法的步骤为:
  1. 如果 this.[[isDestroyed]] 为 true,则中止这些步骤。

  2. this.[[isDestroyed]] 设置为 true。

  3. this.[[context]].[[timeline]] 上将一个任务排入队列,以将此图所拥有的资源标记为可释放。

注: 由于不能再使用 此图将更多工作负载排入队列,因此一旦先前所有 使用此图提交的工作负载完成,实现即可释放与此图关联的任何额外资源分配。

8.5. MLOperandDescriptor 字典

MLOperandDescriptor 描述操作数的形状(维度)和数据类型。它们用于描述 MLGraph 的输入和 常量,并且每个 MLOperand 都有一个内部 MLOperandDescriptor

enum MLInputOperandLayout {
  "nchw",
  "nhwc"
};

enum MLOperandDataType {
  "float32",
  "float16",
  "int32",
  "uint32",
  "int64",
  "uint64",
  "int8",
  "uint8"
};

dictionary MLOperandDescriptor {
  required MLOperandDataType dataType;
  required sequence<[EnforceRange] unsigned long> shape;
};
dataType, 类型为 MLOperandDataType

操作数的数据类型。

shape, 类型为 sequence<[EnforceRange] unsigned long>

操作数的维度列表。对于标量操作数,该列表为空。

如果 A.dataType 等于 B.dataTypeA.shape 等于 B.shape,则一个 MLOperandDescriptor A 等于另一个 MLOperandDescriptor B
给定 MLOperandDataType dataType列表 shape,要创建 一个 MLOperandDescriptor, 运行以下步骤:
  1. descriptor 为一个新的 MLOperandDescriptor

  2. descriptor.dataType 设置为 dataType

  3. descriptor.shape 设置为 shape 的一个克隆

  4. 返回 descriptor

一个 MLOperandDescriptor desc字节长度是以下步骤返回的值:
  1. elementLength 为 1。

  2. 对于 desc.shape每一个 dimension

    1. elementLength 设置为 elementLength * dimension

  3. elementSize 为与 desc.dataType 根据此 表格匹配的某个 ArrayBufferView 类型的元素大小

  4. 返回 elementLength * elementSize

一个 MLOperandDescriptor desc元素数量是以下步骤返回的值:
  1. elementCount 为 1。

  2. 对于 desc.shape每一个 dimension

    1. elementCount 设置为 elementCount * dimension

  3. 返回 elementCount

有效维度是 大于零且位于 long 范围内的整数。 实现可以施加更小的上限。

有效张量 数量是大于零且小于或等于 8192 的整数。实现可以施加 更小的上限。

是否应支持大小为 0 的维度? [问题 #391]

给定 MLOperandDescriptor descriptor,要检查维度,运行以下步骤:
  1. 如果 descriptor.shape 的任何都不是有效 维度,则返回 false。

  2. 如果 descriptor.shape大小大到实现无法支持,则 返回 false。

    操作数维度的最大 数量尚未定义,但原生 ML API 通常具有支持的最大 大小。[问题 #456]

  3. 如果 descriptor元素数量不是一个有效 维度,则返回 false。

  4. 如果实现不支持 descriptor字节长度,则返回 false。

  5. 返回 true。

8.6. MLOperand 接口

MLOperand 表示因将一个运算的各个部分组合成一个 完整运算而正在构建的中间图。

例如,一个 MLOperand 可以 表示馈入某项运算的常量,或将多个常量组合到 某项运算中产生的结果。另请参阅 § 7 编程模型

[SecureContext, Exposed=(Window, Worker)]
interface MLOperand {
  readonly attribute MLOperandDataType dataType;
  readonly attribute FrozenArray<unsigned long> shape;
};

dictionary MLOperatorOptions {
  USVString label = "";
};

typedef (bigint or unrestricted double) MLNumber;
MLOperand 具有以下内部槽:
[[builder]],类型为 MLGraphBuilder

MLOperand 的 关联构建器对象。

[[descriptor]],类型为 MLOperandDescriptor

MLOperand 的 描述符。

[[name]],类型为字符串

MLOperand 的 名称(仅用于输入操作数)。

[[operator]],类型为运算符

MLOperand 对应运算符的引用。

[[constantTensor]],类型为 MLTensor

MLOperand 的 张量(仅用于常量操作数)。

MLOperanddataType 是其 [[descriptor]].dataType

MLOperandshape 是其 [[descriptor]].shape

MLOperand是其shape大小

dataTypegetter 步骤是 返回 thisdataType

shapegetter 步骤是 返回 thisshape

由于 [[builder]] 对象由 MLGraphBuilder() 构造函数绑定到一个 MLContext 对象,因此一个 MLOperand 也始终绑定到同一个 MLContext 对象。

如果某项运算仅支持 MLOperandDataType 的一个子集, 则该运算每个输入操作数(包括位置参数和选项)的允许的数据 类型会以以下方式给出:MLOperandDataType 的显式列表, 或要求操作数的 dataType 必须与另一个输入操作数的 dataType 相同的约束,或者使用 任意类型以允许任意 MLOperandDataType

实现可以支持比指定范围更少的操作数数据类型,但必须至少支持 指定的必需数据 类型。现在可以通过在 MLContext 上使用 opSupportLimits() 方法,并检查 Chromium 原型中 ONNX Runtime、LiteRT 和 CoreML 后端上 该运算对应成员的 dataTypes 值,来查询每项运算的支持情况。

必需数据 类型集合是根据广泛平台上的实现经验确定的,以确保 开发者能够通过设计仅使用这些数据类型的模型来生成可互操作的内容。本规范的 Web 平台测试使用此能力检测机制来验证所有允许的数据 类型的正确行为,但只支持必需数据类型也可以通过测试。

如果某项运算要求输入操作数具有特定,则该运算每个输入操作数(包括 位置参数和选项)的允许的秩会以显式秩(例如 1)给出,或者使用 N 以允许任意维度, 或者与另一个操作数的秩相同。更具体的约束很常见,例如当一个 输入操作数的形状必须可单向广播到另一个输入操作数,或与另一个输入操作数双向可广播时; 在这些情况下,允许的 秩会列为一个范围,并且具体的验证会作为该运算中的步骤给出。

实现可以对操作数的施加比指定范围更严格的下限和/或上限,但必须 至少支持指定的必需秩。现在可以通过在 MLContext 上使用 opSupportLimits() 方法,并检查 Chromium 原型中 ONNX Runtime、LiteRT 和 CoreML 后端上 该运算对应成员的 rankRange.minrankRange.max 值,来查询每项运算的支持情况。

必需秩集合是 根据广泛平台上的实现经验确定的,以确保开发者 能够通过设计仅由具有这些 秩的输入操作数组成的模型来生成可互操作的内容。

MLOperatorOptions 具有以下成员:

label, 类型为 USVString,默认为 ""

当使用 MLGraphBuilder 中创建 MLOperand 的方法创建一个运算符时,可以选择提供该值。 实现可以使用此值来初始化运算符标签

注: 标签并非旨在作为自然语言 字符串。它是与语言无关的标识符,类似于变量名或错误代码,例如 "mul#1234"

注: 鼓励实现使用开发者提供的 label 来增强错误消息并提高可调试性,包括图构建期间的同步 错误以及异步 build() 方法期间发生的错误。

在调试工具、日志或错误消息中显示开发者通过 label 提供的标签时,实现应对输出进行净化,以防止 安全风险,例如注入恶意 Unicode 序列(例如双向文本 欺骗 [UTR36]源代码欺骗 [UTS55] 及 其他问题)。例如,实现应转义或过滤控制字符(例如 U+202A 到 U+202E、U+2066 到 U+2069),或者使用安全的渲染机制来消除潜在的 欺骗。

8.6.1. 创建 MLOperand

MLOperand 对象由 MLGraphBuilder 的方法创建, 其内部使用以下算法。
给定 MLGraphBuilder builderMLOperandDescriptor desc,要创建一个 MLOperand,运行以下步骤:
  1. realmbuilder相关领域

  2. operandrealm 中一个新的 MLOperand

  3. operand.[[builder]] 设置为 builder

  4. operand.[[descriptor]] 设置为 desc

  5. 返回 operand

给定 MLOperand operand,要复制一个 MLOperand,运行以下步骤:
  1. builderoperand.[[builder]]

  2. realmbuilder相关领域

  3. resultrealm 中一个新的 MLOperand

  4. result.[[builder]] 设置为 builder

  5. result.[[descriptor]] 设置为 operand.[[descriptor]]

  6. 如果 operand.[[name]] 存在,则将 result.[[name]] 设置为 operand.[[name]]

  7. 返回 result

给定 MLGraphBuilder builderMLOperand operand,要验证操作数,如果 operand.[[builder]]builder,则返回 true,否则返回 false。

8.6.1.1. MLNumber

MLNumber 用于指定一个数值 选项的类型,该选项用于 MLOperand ,其可以是任意 MLOperandDataType, 包括两种 64 位整数类型("uint64""int64") 以及 32 位浮点类型("float32")。 实现根据对应的 MLOperandDataType 处理该值。 例如,如果调用 clamp(input, options) 时传入一个 MLOperand,其 dataType"uint32", 则 MLNumber 参数会被显式转换unsigned long

将该选项指定为 double 会在传递大于 253 的值时损失精度,而指定为 long long 则不允许使用大于 263 的值。

bigint数值类型的联合类型支持是 [WEBIDL] 中的新特性,实现支持也很有限。 鼓励原型实现对此方法提供反馈。[whatwg/webidl 问题 #1388]

8.7. MLTensorDescriptor 字典

MLTensorDescriptor 描述一个 MLTensor 的特征和能力。

dictionary MLTensorDescriptor : MLOperandDescriptor {
  boolean readable = false;
  boolean writable = false;
};
readable, 类型为 boolean,默认为 false

是否可以通过 readTensor(tensor)readTensor(tensor, outputData) 读取张量的内容。

writable, 类型为 boolean,默认为 false

是否可以通过 writeTensor() 写入张量的内容。

8.8. MLTensor 接口

MLTensor 接口表示一个可以用作 MLGraph 输入或输出的张量。为 MLTensor 提供支持的内存应 根据用于创建它的 MLContextMLTensorDescriptor 的要求,以实现定义的方式分配。涉及 MLTensor[[data]] 的运算发生在其关联 MLContext[[timeline]] 上。

有关如何分配 MLTensor实现定义要求可能包括一些约束,例如要求内存以特定字节对齐方式 分配,或在特定内存池中分配。

[SecureContext, Exposed=(Window, Worker)]
interface MLTensor {
  readonly attribute MLOperandDataType dataType;
  readonly attribute FrozenArray<unsigned long> shape;
  readonly attribute boolean readable;
  readonly attribute boolean writable;
  readonly attribute boolean constant;

  undefined destroy();
};
MLTensor 具有 以下内部槽:
[[context]],类型为 MLContext

MLTensor 的 关联上下文。

[[descriptor]],类型为 MLTensorDescriptor

MLTensor 的 描述符。

[[pendingPromises]],类型 为 Promise集合

与正在进行且尚未兑现的 MLContext.readTensor(tensor) 方法调用对应的 Promise。当 MLTensor 被销毁时,所有待处理的 promise 都将被拒绝。

[[isDestroyed]],类型为 boolean

MLTensor.destroy() 的步骤是否已运行。一旦销毁,MLTensor 就无法再使用。

[[data]],具有实现定义的类型

MLTensor 提供支持的字节。 此数据只能从 [[context]].[[timeline]] 访问或修改。

[[isConstant]],类型为 boolean

MLTensor 是否由创建常量 MLTensor所创建。

[[exportableToGPU]],类型 为 boolean

MLTensor 是否由创建可导出 MLTensor所创建,并且可以 使用 exportToGPU() 导出到一个 GPUDevice

[[gpuDevice]],类型为 GPUDevice 或 null

MLTensor 可以导出到的 GPUDevice, 如果 MLTensor 不可导出,则为 null。仅当 [[exportableToGPU]] 为 true 时设置。

[[exportedBuffer]],类型为 GPUBuffer 或 null

由于 exportToGPU() 而当前借给 [[gpuDevice]]GPUBuffer, 如果 MLTensor 当前未导出,则为 null。

MLTensordataType 是其 [[descriptor]]dataType

MLTensorshape 是其 [[descriptor]]shape

dataTypegetter 步骤是 返回 thisdataType

shapegetter 步骤是 返回 thisshape

readablegetter 步骤是 返回 this.[[descriptor]].readable

writablegetter 步骤是 返回 this.[[descriptor]].writable

constantgetter 步骤是 返回 this[[isConstant]]

一个 MLTensor 已导出,如果其 [[exportedBuffer]] 不为 null。

8.8.1. 创建 MLTensor

MLTensor 由 与其关联的 MLContext 创建。

给定 MLContext contextMLTensorDescriptor descriptor,要创建一个 MLTensor,运行以下步骤:
  1. realmcontext相关领域

  2. tensorrealm 中一个新的 MLTensor

  3. tensor.[[context]] 设置为 context

  4. tensor.[[descriptor]] 设置为 descriptor

  5. tensor.[[isDestroyed]] 设置为 false。

  6. tensor.[[isConstant]] 设置为 false。

  7. tensor.[[exportableToGPU]] 设置为 false。

  8. tensor.[[gpuDevice]] 设置为 null。

  9. tensor.[[exportedBuffer]] 设置为 null。

  10. 返回 tensor

给定 MLContext contextMLTensorDescriptor descriptorGPUDevice gpuDevice,要创建 可导出的 MLTensor,运行以下步骤:
  1. tensor 为给定 contextdescriptor 创建 MLTensor 的结果。

  2. tensor.[[exportableToGPU]] 设置为 true。

  3. tensor.[[gpuDevice]] 设置为 gpuDevice

  4. 返回 tensor

给定 MLTensor tensor,要归还 已导出的 MLTensor,运行以下步骤:
  1. 如果 tensor 未导出,则中止这些步骤。

  2. buffertensor.[[exportedBuffer]]

  3. tensor.[[exportedBuffer]] 设置为 null。

  4. 如果 tensor.[[isDestroyed]] 为 true,则中止这些步骤。

  5. 将以下步骤排入 tensor.[[context]].[[timeline]]

    1. 运行以下步骤,但当 tensor.[[context]] 已 丢失中止

      1. 确保 tensor.[[data]] 反映 buffer 的内容。

        注: 如果导出是 零拷贝的,则 buffertensor.[[data]] 由同一内存提供支持,并且不会执行复制。

用户代理必须确保,上面排入队列、用于确保 tensor.[[data]] 反映 buffer 内容的步骤,不得在此前所有排入 gpuDeviceGPUQueue 且读取或写入 buffer 的运算完成之前执行。

8.8.2. destroy()

释放与 MLTensor 关联的资源。此 方法是幂等的。

返回: undefined
destroy() 方法的步骤为:
  1. this.[[isDestroyed]] 设置为 true。

  2. 对于 this.[[pendingPromises]] 中的每一个 promise

    1. this.[[pendingPromises]]移除 promise

    2. 以一个 "InvalidStateError" DOMException 拒绝 promise

  3. 将以下步骤排入 this.[[context]].[[timeline]]

    1. 释放 this.[[data]]

注: 由于不能再使用 此张量将更多运算排入队列,因此一旦此前所有 使用此张量提交的运算完成,实现即可释放与此张量关联的任何额外资源分配。

8.8.3. 创建常量 MLTensor

常量 MLTensor 由 与其关联的 MLContext 创建。

给定 MLContext contextMLOperandDescriptor inputDescriptor,要创建 常量 MLTensor,运行以下步骤:
  1. realmcontext相关领域

  2. tensorrealm 中一个新的 MLTensor

  3. tensor.[[context]] 设置为 context

  4. tensorDescriptor 为一个新的 MLTensorDescriptor

  5. tensorDescriptor.readable 设置为 false。

  6. tensorDescriptor.writable 设置为 false。

  7. tensorDescriptor.dataType 设置为 inputDescriptor.dataType

  8. tensorDescriptor.shape 设置为 inputDescriptor.shape

  9. tensor.[[descriptor]] 设置为 tensorDescriptor

  10. tensor.[[isDestroyed]] 设置为 false。

  11. tensor.[[isConstant]] 设置为 true。

  12. tensor.[[exportableToGPU]] 设置为 false。

  13. tensor.[[gpuDevice]] 设置为 null。

  14. tensor.[[exportedBuffer]] 设置为 null。

  15. 返回 tensor

8.9. MLGraphBuilder 接口

MLGraphBuilder 接口定义了由§ 2 用例所确定的一组可 组合成计算图的运算。它还表示图构建会话的中间状态。

typedef record<USVString, MLOperand> MLNamedOperands;

[SecureContext, Exposed=(Window, Worker)]
interface MLGraphBuilder {
  // 从上下文构建图构建器。
  constructor(MLContext context);

  // 为图输入创建操作数。
  MLOperand input(USVString name, MLOperandDescriptor descriptor);

  // 为图常量创建操作数。
  MLOperand constant(MLOperandDescriptor descriptor,
                     AllowSharedBufferSource buffer);

  // 从指定类型的指定数值创建标量操作数。
  MLOperand constant(MLOperandDataType dataType, MLNumber value);

  // 从指定的常量张量创建操作数。
  MLOperand constant(MLTensor tensor);

  // 异步编译图,直至指定的输出操作数。
  Promise<MLGraph> build(MLNamedOperands outputs);
};
MLGraphBuilder.build() 方法会根据创建它的 MLContext 的类型, 将图构建器状态编译到指定输出操作数为止,形成已编译的图。当 MLContext[[contextType]] 被设置为 "default" 时,已编译图会在 MLGraph 返回之前立即初始化。此图初始化阶段对于后续图 执行的最佳性能非常重要。它通常涉及一个称为“权重预处理”的过程,其中图的所有常量输入 都会在操作系统层进行预处理和缓存,以供后续图执行调用使用。 初始化输入通常是在图构建期间通过 constant() 方法指定为常量操作数的常量权重数据。
MLGraphBuilder 具有以下内部槽:
[[context]],类型为 MLContext

与此 MLGraphBuilder 关联的 MLContext 类型上下文。

[[hasBuilt]],类型为 boolean

MLGraphBuilder.build() 是否已被调用。一旦完成构建,MLGraphBuilder 就无法再创建运算符或编译 MLGraph

一个 MLGraphBuilder 可以构建,如果其 [[hasBuilt]] 为 false,并且其 [[context]] 未丢失

8.9.1. MLGraphBuilder 构造函数

参数:
new MLGraphBuilder(context) 构造函数的步骤为:
  1. 如果 this相关全局对象关联 Document 未被允许使用 webnn 功能,则抛出一个 "SecurityError" DOMException

  2. 如果 context 已丢失,则抛出一个 "InvalidStateError" DOMException

  3. this.[[context]] 设置为 context

  4. this.[[hasBuilt]] 设置为 false。

8.9.2. 输入操作数

根据描述符创建一个具名 MLOperand, 可将其用作输入。

参数: 返回:一个 MLOperand
input(name, descriptor) 方法的步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果 name 为空,则抛出一个 TypeError

  3. 如果 this输入中的任何 MLOperand[[name]] 等于 name,则抛出一个 TypeError

  4. 如果给定 descriptor 检查维度返回 false,则抛出一个 TypeError

  5. 建立图连接:

    1. operand 为给定 thisdescriptor 创建 MLOperand 的结果。

    2. operand.[[name]] 设置为 name

    3. operand 添加到 this输入中。

  6. 返回 operand

MLGraphBuilder API 允许创建没有 输入操作数的 MLGraph。如果底层平台不支持这种情况,实现可以添加一个占位输入,或 将常量作为输入传递给图。

8.9.3. 常量操作数

创建一个可在 MLGraphBuilder 方法中使用的常量 MLOperand
8.9.3.1. constant(descriptor, buffer)
创建一个具有指定数据类型和形状且包含初始化数据的常量 MLOperand
参数: 返回:一个 MLOperand。 常量输出张量。
constant(descriptor, buffer) 方法的步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果给定 descriptor 检查维度返回 false,则抛出一个 TypeError

  3. 如果给定 bufferdescriptor 使用描述符验证缓冲区返回 false,则抛出一个 TypeError

  4. 建立图连接:

    1. operand 为给定 thisdescriptor 创建 MLOperand 的结果。

    2. bytes 为给定 buffer 获取缓冲区源所持字节的副本的结果。

    3. operand 添加到 this常量中,并以 bytes 作为 值。

  5. 返回 operand

8.9.3.2. constant(tensor)
创建一个具有指定数据类型和形状且包含已初始化数据的常量 MLOperand
参数: 返回:一个 MLOperand。 常量输出张量。
constant(tensor) 方法的步骤 为:
  1. 如果 tensor.[[context]] 不是 this.[[context]], 则抛出一个 TypeError

  2. 如果 tensor.[[isDestroyed]] 为 true,则抛出一个 TypeError

  3. 如果 tensor.[[isConstant]] 为 false,则抛出一个 TypeError

  4. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  5. 建立图连接:

    1. operand 为给定 thistensor.[[descriptor]] 创建 MLOperand 的结果。

    2. operand.[[constantTensor]] 设置为 tensor

    3. operand 添加到 this常量中,并以 tensor 作为 值。

  6. 返回 operand

8.9.3.3. constant(dataType, value)
创建一个具有指定值和数据类型的标量常量 MLOperand
当指定值超出指定输出数据类型的范围时,将发生数据截断,例如 将浮点值赋给 "int8" 数据类型等。
参数: 返回:一个 MLOperand。 常量输出。
constant(dataType, value) 方法的步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. value 设置为把 value 转换dataType 的结果。

  3. descriptor 为给定 dataType 和 « » 创建 MLOperandDescriptor 的结果。

  4. 建立图连接:

    1. operand 为给定 thisdescriptor 创建 MLOperand 的结果。

    2. operand 添加到 this常量中,并以 value 作为 值。

  5. 返回 operand

8.9.4. build 方法

将组合图构建到给定输出操作数为止,并异步生成计算图。
参数: 返回:Promise<MLGraph>。
build(outputs) 方法的步骤为:
  1. realmthis相关领域

  2. 如果 this 无法构建,则返回 realm一个新的 promise,该 promise 以一个 "InvalidStateError" DOMException 拒绝

  3. 如果 outputs 为空,则返回 realm一个新的 promise,该 promise 以一个 TypeError 拒绝

  4. 对于 outputs每一个 nameoperand

    1. 如果 name 为空,则返回 realm一个 新的 promise,该 promise 以一个 TypeError 拒绝

    2. 如果给定 thisoperand 验证操作数返回 false,则返回 realm一个新的 promise,该 promise 以一个 TypeError 拒绝

    3. 如果 operand 位于 this输入常量中,则返回 realm一个新的 promise,该 promise 以一个 TypeError 拒绝

    4. 如果 operand.[[constantTensor]] 存在且 operand.[[constantTensor]].[[isDestroyed]] 为 true,则返回 realm一个 新的 promise,该 promise 以一个 TypeError 拒绝

  5. operands 为一个新的空集合

  6. operators 为一个新的空集合

  7. inputs 为一个新的空集合

  8. queue 为一个包含 outputs的新队列

  9. queue 不 为空时:

    1. queue取出 operand

    2. operand 附加operands

    3. operand.[[operator]] 附加operators

    4. 如果 operand 位于 this输入中,则将 operand 附加inputs

    5. 对于 operand.[[operator]]输入中的每一个 input

      1. input 排入 queue

  10. globalthis相关全局对象

  11. graphrealm 中一个新的 MLGraph

  12. graph.[[context]] 设置为 this.[[context]]

  13. graph.[[isDestroyed]] 设置为 false。

  14. 对于 inputs 中的每一个 operand

    1. graph.[[inputDescriptors]][operand.[[name]]] 设置为 operand.[[descriptor]]

  15. 对于 outputs每一个 nameoperand

    1. graph.[[outputDescriptors]][name] 设置为 operand.[[descriptor]]

  16. this.[[hasBuilt]] 设置为 true。

  17. promiserealm一个新的 promise

  18. 将以下步骤排入 graph.[[context]].[[timeline]]

    1. 运行以下步骤,但当 graph.[[context]] 已 丢失中止

      1. graphImpl 为将 this连同 operandsoperatorsinputsoutputs,以及 graph.[[context]].[[powerPreference]]graph.[[context]].[[accelerated]] 转换为一种可由底层平台解释的实现定义格式的结果。

      2. 如果上一步失败,则使用 global 将 ML 任务排入队列,以使用一个 "OperationError" DOMException 拒绝 promise, 并中止这些步骤。

      3. graph.[[implementation]] 设置为 graphImpl

      4. 使用 global 将 ML 任务排入队列, 以使用 graph 兑现 promise

    2. 如果已中止,则使用 global 将 ML 任务排入队列, 以使用一个 "InvalidStateError" DOMException 拒绝 promise

  19. 返回 promise

注:输入操作数或常量操作数指定为图的 输出 会导致错误,因为这通常是对 API 的错误使用。调用者可以通过 引入 identity() 运算符来解决此问题。

8.9.5. argMin/argMax 运算

返回沿轴方向所有输入值中最小值或最大值的索引位置。若出现 并列,则返回值的确定方式取决于实现。
dictionary MLArgMinMaxOptions : MLOperatorOptions {
  boolean keepDimensions = false;
  MLOperandDataType outputDataType = "int32";
};

partial interface MLGraphBuilder {
  MLOperand argMin(MLOperand input, [EnforceRange] unsigned long axis,
                   optional MLArgMinMaxOptions options = {});
  MLOperand argMax(MLOperand input, [EnforceRange] unsigned long axis,
                   optional MLArgMinMaxOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits argMin;
  MLSingleInputSupportLimits argMax;
};

MLArgMinMaxOptions 具有以下成员:

keepDimensions, 类型 为 boolean,默认为 false

如果为 true,则保留大小为 1 的被归约维度。

outputDataType, 类型 为 MLOperandDataType,默认为 "int32"

一个 MLOperandDataType。 输出数据类型。

参数:

返回:一个 MLOperand。 如果 keepDimensions 为 true,则输出 N 维张量的等于 input;如果 keepDimensions 为 false,则等于 input - 1。值必须具有 outputDataType 类型,并位于范围 [0, N-1] 内,其中 N 是由 axis 指定的输入维度的大小。

argMin()/argMax() 的张量限制
操作数 允许的数据 类型 必需的 数据类型 允许的秩 必需的秩
input 任意类型 "float32", "float16", "int32" 1 到 N 1 到 5
输出 "int32", "int64" "int32" N 0 到 5

MLOpSupportLimits 对于 argMin()argMax() 具有以下成员:

argMin, 类型为 MLSingleInputSupportLimits

运算符 argMin() 的支持限制。

argMax, 类型为 MLSingleInputSupportLimits

运算符 argMax() 的支持限制。

给定字符串 opMLOperand inputunsigned long axisMLArgMinMaxOptions options,要创建 argMin/argMax 运算,运行以下步骤:
  1. 断言op 是 "argMin"、"argMax" 之一。

  2. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  3. 如果使用 thisinput 验证操作数返回 false,则抛出一个 TypeError

  4. 如果 axis 大于或等于 input,则抛出一个 TypeError

  5. 如果 options.outputDataType 不是输出张量的允许的数据类型(根据此表),则抛出一个 TypeError

  6. 如果 inputshape[axis] 大于 options.outputDataType 的 最大值,则抛出一个 TypeError

  7. outputShape 为给定 inputshape、« axis » 和 options.keepDimensions 计算归约输出 大小的结果。 如果其返回失败,则抛出一个 TypeError

  8. desc 为给定 options.outputDataTypeoutputShape 创建 MLOperandDescriptor 的结果。

  9. 建立图连接:

    1. operator 为给定 optionsop 运算的一个运算符

    2. output 为给定 thisdesc 创建 MLOperand 的结果。

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 input

    5. operator输出设置为 output

  10. 返回 output

支持以下 argMin/argMax 算法。
argMin(input, axis, options) 方法的步骤为:
  1. output 为给定 "argMin"、inputaxisoptions 创建 argMin/argMax 运算的结果。

    1. 如果其抛出错误,则重新抛出该错误。

  2. 返回 output

argMax(input, axis, options) 方法的步骤为:
  1. output 为给定 "argMax"、inputaxisoptions 创建 argMin/argMax 运算的结果。

    1. 如果其抛出错误,则重新抛出该错误。

  2. 返回 output

8.9.6. batchNormalization

使用 [Batch-Normalization] 对输入张量的值进行归一化。 在模型训练期间,对于每个输入特征,会跨批次维度中的所有样本计算该特征的均值和方差值。 随后在模型推理期间,将这些均值和方差值提供给此运算。
dictionary MLBatchNormalizationOptions : MLOperatorOptions {
  MLOperand scale;
  MLOperand bias;
  [EnforceRange] unsigned long axis = 1;
  double epsilon = 1e-5;
};

partial interface MLGraphBuilder {
  MLOperand batchNormalization(MLOperand input, MLOperand mean, MLOperand variance,
                               optional MLBatchNormalizationOptions options = {});
};

dictionary MLBatchNormalizationSupportLimits {
  MLTensorLimits input;
  MLTensorLimits mean;
  MLTensorLimits variance;
  MLTensorLimits scale;
  MLTensorLimits bias;
  MLTensorLimits output;
};

partial dictionary MLOpSupportLimits {
  MLBatchNormalizationSupportLimits batchNormalization;
};

MLBatchNormalizationOptions 具有以下成员:

scale, 类型为 MLOperand

缩放值的一维张量,其大小等于由 axis 表示的输入维度的大小。

bias, 类型为 MLOperand

偏置值的一维张量,其大小等于由 axis 表示的输入维度的大小。

axis, 类型为 unsigned long,默认为 1

输入形状中特征数量维度的索引,均值和方差值即针对该维度。 其值必须位于范围 [0, N-1] 内,其中 N 是输入张量的。默认 值为 1,对应于 "nchw" 数据布局中的通道("c")维度。

epsilon, 类型为 double,默认为 1e-5

用于防止除以零导致计算错误的一个小值。

参数:

返回:一个 MLOperand。 与 input 形状相同的批归一化 N 维张量。

batchNormalization() 的张量限制
操作数 允许的数据 类型 必需的 数据类型 允许的秩 必需的秩
input "float32", "float16" "float32", "float16" 1 到 N 3 到 5
mean input 相同 "float32", "float16" 1 1
variance input 相同 "float32", "float16" 1 1
scale input 相同 "float32", "float16" 1 1
bias input 相同 "float32", "float16" 1 1
输出 input 相同 "float32", "float16" input 相同 3 到 5

MLBatchNormalizationSupportLimits 具有以下成员:

input, 类型为 MLTensorLimits

用于输入操作数的 MLTensorLimits

mean, 类型为 MLTensorLimits

用于 mean 操作数的 MLTensorLimits

variance, 类型为 MLTensorLimits

用于 variance 操作数的 MLTensorLimits

scale, 类型为 MLTensorLimits

用于 scale 操作数的 MLTensorLimits

bias, 类型为 MLTensorLimits

用于 bias 操作数的 MLTensorLimits

output, 类型为 MLTensorLimits

用于输出操作数的 MLTensorLimits

MLOpSupportLimits 对于 batchNormalization() 具有以下成员:

batchNormalization, 类型为 MLBatchNormalizationSupportLimits

运算符 batchNormalization() 的支持限制。

batchNormalization(input, mean, variance, options) 方法的步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 thisinputmeanvarianceoptions.scale (如果其存在)以及 options.bias (如果其存在)中的任一个验证操作数返回 false,则抛出一个 TypeError

  3. 如果 inputdataType 不是其允许的数据类型 之一(根据此表),则抛出一个 TypeError

  4. 如果 options.axis 不在从 0 到 input(不含上界)的范围内,则抛出一个 TypeError

  5. 如果 meandataType 不是其允许的数据类型 之一(根据此表),则抛出一个 TypeError

  6. 如果 meanshape等于 « inputshape[options.axis] »,则抛出一个 TypeError

  7. 如果 variancedataType 不是其允许的数据类型 之一(根据此表),则抛出一个 TypeError

  8. 如果 varianceshape等于 « inputshape[options.axis] »,则抛出一个 TypeError

  9. options.epsilon 设置为把 options.epsilon 转换inputdataType 的结果。

  10. 如果 options.scale 存在,则:

    1. 如果其 dataType 不是其允许的数据类型之一(根据此表),则抛出一个 TypeError

    2. 如果其shape等于 « inputshape[options.axis] »,则抛出一个 TypeError

  11. 如果 options.bias 存在,则:

    1. 如果其 dataType 不是其允许的数据类型之一(根据此表),则抛出一个 TypeError

    2. 如果其shape等于 « inputshape[options.axis] »,则抛出一个 TypeError

  12. 建立图连接:

    1. operator 为给定 inputmeanvarianceoptions 的 "batchNormalization" 运算的一个运算符

    2. output 为给定 thisinput.[[descriptor]] 创建 MLOperand 的结果。

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 inputmeanvariance

    5. 如果 options.scale 存在,则将其添加到 operator输入中。

    6. 如果 options.bias 存在,则将其添加到 operator输入中。

    7. operator输出设置为 output

  13. 返回 output

当输入张量为 "nchw" 布局的 4 维张量时,此运算的行为可以按如下方式使用其他运算进行通用模拟,尽管用户 代理通常具有更高效的实现。当底层平台 不直接支持某项运算时,可以使用此分解作为模板来指导 实现。
function batchNormalization(builder, input, mean, variance, options) {
  const shape = [1, input.shape[options.axis], 1, 1];
  return builder.add(
    builder.mul(
      builder.reshape(options.scale, shape),
      builder.div(
        builder.sub(input, builder.reshape(mean, shape)),
        builder.sqrt(builder.add(
          builder.reshape(variance, shape),
          builder.constant(input.dataType, options.epsilon))))),
    builder.reshape(options.bias, shape));
}

8.9.7. cast

将输入张量中的每个元素转换为目标数据类型。
partial interface MLGraphBuilder {
  MLOperand cast(MLOperand input,
                 MLOperandDataType dataType,
                 optional MLOperatorOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits cast;
};
参数:

返回:一个 MLOperand。 与 input 形状相同、其中每个元素均已转换为目标数据类型的 N 维张量。

cast() 的张量限制
操作数 允许的数据 类型 必需的 数据类型 允许的秩 必需的秩
input 任意类型 "float32", "float16", "int32", "int8", "uint8" N 0 到 5
输出 任意类型 "float32", "float16", "int32", "int8", "uint8" input 相同 0 到 5

MLOpSupportLimits 对于 cast() 具有以下成员:

cast, 类型为 MLSingleInputSupportLimits

运算符 cast() 的支持限制。

根据下表,MLOperandDataType 之间的转换在某些情况下已有明确规定,而在其他情况下则为实现定义

给定 inputdataType(行) 和目标 dataType (列)时 cast() 运算的行为。
目标类型 输入类型 "float32", "float16" "int32", "uint32", "int64", "uint64", "int8", "uint8"
"float32", "float16" 如果在范围内,则取最接近的可表示值。

如果超出范围,则为 +/-Infinity。

如果在范围内,则截断。

如果超出范围,则为实现定义

"int32", "uint32", "int64", "uint64", "int8", "uint8" 如果在范围内,则取最接近的可表示值。

如果超出范围,则为 +/-Infinity。

如果在范围内,则保持相同值。

如果超出范围,则将最低 N 位重新解释为目标类型;对于 有符号类型,假定使用二进制补码。

注: 例如,将 -1 从 "int8" 转换为 "uint8" 时,规定结果为 255。但将 -1 从 "float32" 转换为 "uint8" 时,则为实现定义

cast(input, dataType, options) 方法的步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 thisinput 验证操作数返回 false,则抛出一个 TypeError

  3. 如果 dataType 不是输出张量的允许的数据类型(根据此表),则抛出一个 TypeError

  4. 建立图连接:

    1. operator 为给定 dataTypeoptions 的 "cast" 运算的一个运算符

    2. output 为给定 input 复制 MLOperand 的结果。

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 input

    5. operator输出设置为 output

  5. 返回 output

8.9.8. clamp

按元素将输入张量限制在由最小值和最大值指定的范围内。
dictionary MLClampOptions : MLOperatorOptions {
  MLNumber minValue;
  MLNumber maxValue;
};

partial interface MLGraphBuilder {
  MLOperand clamp(MLOperand input, optional MLClampOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits clamp;
};

MLClampOptions 具有以下成员:

minValue, 类型为 MLNumber

范围的最小值。如果未指定,则不会对范围的下限 执行限制。

maxValue, 类型为 MLNumber

范围的最大值。如果未指定,则不会对范围的上限 执行限制。

参数: 返回:
clamp() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input 任意类型 "float32", "float16" N 0 到 5
输出 input 相同 "float32", "float16" input 相同 0 到 5

MLOpSupportLimits 对于 clamp() 具有以下成员:

clamp, 类型为 MLSingleInputSupportLimits

运算符 clamp() 的支持限制。

clamp(input, options) 方法的 步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 thisinput 验证操作数返回 false,则抛出一个 TypeError

  3. 如果已给出 options.minValue,则令 minValue 为其值, 否则为 Infinity。

  4. options.minValue 设置为把 minValue 转换inputdataType 的结果。

  5. 如果已给出 options.maxValue,则令 maxValue 为其值, 否则为 -Infinity。

  6. options.maxValue 设置为把 maxValue 转换inputdataType 的结果。

  7. 如果 options.minValue 大于 options.maxValue, 则抛出一个 TypeError

  8. 建立图连接:

    1. output 为给定 input 复制 MLOperand 的结果。

    2. operator 为给定 options 的 "clamp" 运算的一个运算符

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 input

    5. operator输出设置为 output

  9. 返回 output

此运算的行为可以按如下方式使用其他运算进行通用模拟, 尽管用户代理通常具有更高效的实现。当 底层平台不直接支持某项运算时,可以使用此分解作为 模板来指导实现。
function clamp(builder, input, options) {
  if (options.minValue === undefined) {
    if (options.maxValue === undefined) {
      return input;
    } else {
      return builder.min(
        input, builder.constant(input.dataType, options.maxValue));
    }
  } else {
    if (options.maxValue === undefined) {
      return builder.max(
        input, builder.constant(input.dataType, options.minValue));
    } else {
      return builder.min(
        builder.max(input, builder.constant(input.dataType, options.minValue)),
        builder.constant(input.dataType, options.maxValue));
    }
  }
}

8.9.9. concat

沿给定轴连接输入张量。
partial interface MLGraphBuilder {
  MLOperand concat(sequence<MLOperand> inputs,
                   [EnforceRange] unsigned long axis,
                   optional MLOperatorOptions options = {});
};

dictionary MLConcatSupportLimits {
  MLTensorLimits inputs;
  MLTensorLimits output;
};

partial dictionary MLOpSupportLimits {
  MLConcatSupportLimits concat;
};
参数:

返回:一个 MLOperand。 沿 axis 连接所有输入后得到的张量。 输出张量具有相同的形状,但所有输入沿其连接的维度 除外。该维度的大小计算为所有输入在相同维度上的大小之和。

concat() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
inputs 任意类型 "float32", "float16", "int32" 1 到 N 1 到 5
输出 inputs相同 "float32", "float16", "int32" inputs相同 1 到 5

MLConcatSupportLimits 具有以下成员:

inputs, 类型为 MLTensorLimits

用于所有输入操作数的 MLTensorLimits

output, 类型为 MLTensorLimits

用于输出操作数的 MLTensorLimits

MLOpSupportLimits 对于 concat() 具有以下成员:

concat, 类型为 MLConcatSupportLimits

运算符 concat() 的支持限制。

concat(inputs, axis, options) 方法的步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 thisinputs 中任意验证操作数返回 false,则抛出一个 TypeError

  3. 如果 inputs大小不是有效张量数量,则抛出一个 TypeError

  4. firstinputs[0]。

  5. 如果 axis 大于或等于 first,则抛出一个 TypeError

  6. desc 为给定 firstdataTypefirstshape 创建 MLOperandDescriptor 的结果。

  7. desc.shape[axis] 设置为 firstshape[axis]。

  8. 对于从 1 到 inputs大小(不含上界)的范围中的每一个 index

    1. inputinputs[index]。

    2. 如果 inputdataType 不等于 firstdataType,则抛出 一个 TypeError

    3. 如果 input不等于 first, 则抛出一个 TypeError

    4. 对于从 0 到 input(不含上界)的范围中的每一个 dim

      如果除 axis 给出的维度之外,各操作数对应维度的形状和类型 不相同,则失败。
      1. 如果 dim 不等于 axis,且 inputshape[dim] 不等于 firstshape[dim],则抛出一个 TypeError

      2. 如果 dim 等于 axis,则:

        1. sizedesc.shape[axis] 与 inputshape[dim] 之和。

        2. 如果 size 不是一个有效 维度,则抛出一个 TypeError

        3. desc.shape[axis] 设置为 size

  9. 建立图连接:

    1. output 为给定 thisdesc 创建 MLOperand 的结果。

    2. operator 为给定 inputsaxisoptions 的 "concat" 运算的一个运算符

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 inputs

    5. operator输出设置为 output

  10. 返回 output

8.9.10. conv2d

给定 4 维输入张量和滤波器张量,计算二维卷积
enum MLConv2dFilterOperandLayout {
  "oihw",
  "hwio",
  "ohwi",
  "ihwo"
};

dictionary MLConv2dOptions : MLOperatorOptions {
  sequence<[EnforceRange] unsigned long> padding;
  sequence<[EnforceRange] unsigned long> strides;
  sequence<[EnforceRange] unsigned long> dilations;
  [EnforceRange] unsigned long groups = 1;
  MLInputOperandLayout inputLayout = "nchw";
  MLConv2dFilterOperandLayout filterLayout = "oihw";
  MLOperand bias;
};

partial interface MLGraphBuilder {
  MLOperand conv2d(MLOperand input,
                   MLOperand filter,
                   optional MLConv2dOptions options = {});
};

dictionary MLConv2dSupportLimits {
  MLTensorLimits input;
  MLTensorLimits filter;
  MLTensorLimits bias;
  MLTensorLimits output;
};

partial dictionary MLOpSupportLimits {
  MLConv2dSupportLimits conv2d;
};

MLConv2dOptions 具有以下成员:

padding, 类型为 sequence<[EnforceRange] unsigned long>

长度为 4 的列表:[beginningHeight, endingHeight, beginningWidth, endingWidth]。 指定添加到卷积输入各空间 维度开头和结尾的额外行和列。 默认值为 [0, 0, 0, 0]。

strides, 类型为 sequence<[EnforceRange] unsigned long>

长度为 2 的列表:[strideHeight, strideWidth]。 指定卷积输入各空间维度上滑动窗口的步幅。 默认值为 [1, 1]。

dilations, 类型为 sequence<[EnforceRange] unsigned long>

长度为 2 的列表:[dilationHeight, dilationWidth]。指定应用于卷积滤波器(核)的每个 空间维度的膨胀因子。 默认值为 [1, 1]。

groups, 类型为 unsigned long,默认为 1

输入通道和输出通道被划分成的组数。

inputLayout, 类型为 MLInputOperandLayout,默认为 "nchw"

按如下方式指定输入和输出张量的布局格式:

  • "nchw"

    • 输入张量:[batches, inputChannels, height, width]

    • 输出张量:[batches, outputChannels, height, width]

  • "nhwc"

    • 输入张量:[batches, height, width, inputChannels]

    • 输出张量:[batches, height, width, outputChannels]

filterLayout, 类型为 MLConv2dFilterOperandLayout,默认为 "oihw"

按如下方式指定滤波器张量的布局格式:

  • "oihw"[outputChannels, inputChannels/groups, height, width]

  • "hwio"[height, width, inputChannels/groups, outputChannels]

  • "ohwi"[outputChannels, height, width, inputChannels/groups]

  • "ihwo"[inputChannels/groups, height, width, outputChannels]

bias, 类型为 MLOperand

形状为 [outputChannels] 的额外一维张量,其值将加到 卷积结果中。

参数:

返回:一个 MLOperand。 包含卷积结果的输出 4 维张量。输出形状根据 inputLayout 解释。 更具体地说,对于 "nchw" 输入布局,输出张量的空间维度,即最后两个维度的大小 可按如下方式计算:

outputSize = 1 + (inputSize - (filterSize - 1) * dilation - 1 + beginningPadding + endingPadding) / stride

conv2d() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input "float32", "float16" "float32", "float16" 4 4
filter input 相同 "float32", "float16" 4 4
bias input 相同 "float32", "float16" 1 1
输出 input 相同 "float32", "float16" 4 4

MLConv2dSupportLimits 具有以下成员:

input, 类型为 MLTensorLimits

用于输入操作数的 MLTensorLimits

filter, 类型为 MLTensorLimits

用于 filter 操作数的 MLTensorLimits

bias, 类型为 MLTensorLimits

用于 bias 操作数的 MLTensorLimits

output, 类型为 MLTensorLimits

用于输出操作数的 MLTensorLimits

MLOpSupportLimits 对于 conv2d() 具有以下成员:

conv2d, 类型为 MLConv2dSupportLimits

运算符 conv2d() 的支持限制。

深度可分离 conv2d 运算是分组卷积的一种变体,用于 MobileNet 等模型,其中 groups = inputChannels = outputChannels,并且对于 "oihw" 布局,滤波器张量的形状为 [options.groups, 1, height, width]; 对于 "hwio" 布局,为 [height, width, 1, options.groups];对于 "ohwi" 布局,为 [options.groups, height, width, 1];对于 "ihwo" 布局,为 [1, height, width, options.groups]
给定无符号 整数 inputSizefilterSizebeginningPaddingendingPaddingstridedilation,要计算卷积输出大小,执行以下步骤。它们返回一个 数值。
  1. effectiveFilterSize 为 ( filterSize - 1 ) * dilation + 1。

  2. outputSize 为 ( inputSize - effectiveFilterSize + beginningPadding + endingPadding ) / stride + 1。

  3. 返回 outputSize

给定无符号 整数 inputHeightinputWidthfilterHeightfilterWidth,以及由 4 个无符号整数组成的列表 padding、由 2 个无符号整数组成的列表 strides,以及由 2 个无符号整数组成的列表 dilations,要计算 conv2d 输出大小,执行以下步骤。它们 返回一个由 2 个数值组成的列表
  1. outputHeight 为给定 inputHeightfilterHeightpadding[0]、 padding[1]、strides[0] 和 dilations[0] 计算卷积输出大小的结果。

  2. outputWidth 为给定 inputWidthfilterWidthpadding[2]、 padding[3]、strides[1] 和 dilations[1] 计算卷积输出大小的结果。

  3. 返回 « outputHeight, outputWidth »。

conv2d(input, filter, options) 方法的步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 thisinputfilter 以及 options.bias (如果其存在)中的任一个验证操作数返回 false,则抛出一个 TypeError

  3. 如果 inputdataType 不是其允许的数据类型 之一(根据此表),则抛出一个 TypeError

  4. 如果 input不是其允许的秩,则抛出一个 TypeError

  5. 如果 filter不是其允许的秩,则抛出一个 TypeError

  6. 如果 filterdataType 不是其允许的数据类型 之一(根据此表),则抛出一个 TypeError

  7. 如果 options.padding存在,则将其设置为列表 « 0, 0, 0, 0 »。

  8. 否则,如果 options.padding大小不为 4,则抛出一个 TypeError

  9. 如果 options.strides存在,则将其设置为列表 « 1, 1 »。

  10. 否则,如果 options.strides大小不为 2,则抛出一个 TypeError

  11. 如果 options.strides 中任何等于 0,则抛出一个 TypeError

  12. 如果 options.dilations存在,则将其设置为列表 « 1, 1 »。

  13. 否则,如果 options.dilations大小不为 2,则抛出一个 TypeError

  14. 如果 options.dilations 中任何等于 0,则抛出一个 TypeError

  15. 如果 options.groups 为 0,则抛出一个 TypeError

  16. 计算输出形状:

    1. inputShapeinputshape

    2. 根据 options.inputLayout 切换:

      "nchw"

      令 « batches, inputChannels, inputHeight, inputWidth » 为 inputShape

      "nhwc"

      令 « batches, inputHeight, inputWidth, inputChannels » 为 inputShape

    3. filterShapefiltershape

    4. 根据 options.filterLayout 切换:

      "hwio"

      令 « filterHeight, filterWidth, filterInputChannels, outputChannels » 为 filterShape

      "ohwi"

      令 « outputChannels, filterHeight, filterWidth, filterInputChannels » 为 filterShape

      "ihwo"

      令 « filterInputChannels, filterHeight, filterWidth, outputChannels » 为 filterShape

      "oihw"

      令 « outputChannels, filterInputChannels, filterHeight, filterWidth » 为 filterShape

    5. 如果 inputChannels % options.groups 不为 0,则抛出一个 TypeError

    6. 否则,如果 inputChannels / options.groups 不等于 filterInputChannels,则抛出 一个 TypeError

    7. 如果 outputChannels % options.groups 不为 0,则抛出一个 TypeError

    8. 如果 options.bias 存在,则:

      1. 如果其shape等于 « outputChannels »,则抛出一个 TypeError

      2. 如果其 dataType 不是其允许的数据类型之一(根据此表),则抛出一个 TypeError

    9. 令 « outputHeight, outputWidth » 为给定 inputHeightinputWidthfilterHeightfilterWidthoptions.paddingoptions.stridesoptions.dilations 计算 conv2d 输出 大小的结果。

    10. outputHeight 设置为 floor( outputHeight )。

    11. outputWidth 设置为 floor( outputWidth )。

    12. 如果 outputHeightoutputWidth 中任一个不是有效 维度,则抛出 一个 TypeError

    13. 根据 options.inputLayout 切换:

      "nchw"

      outputShape 为 « batches, outputChannels, outputHeight, outputWidth »。

      "nhwc"

      outputShape 为 « batches, outputHeight, outputWidth, outputChannels »。

    14. desc 为给定 inputdataTypeoutputShape 创建 MLOperandDescriptor 的结果。

  17. 建立图连接:

    1. output 为给定 thisdesc 创建 MLOperand 的结果。

    2. operator 为给定 optionsfilter 的 "conv2d" 运算的一个运算符

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 inputfilter

    5. 如果 options.bias 存在,则将其添加到 operator输入中。

    6. operator输出设置为 output

  18. 返回 output

8.9.11. convTranspose2d

给定 4 维输入张量和滤波器张量,计算二维转置卷积
enum MLConvTranspose2dFilterOperandLayout {
  "iohw",
  "hwoi",
  "ohwi"
};

dictionary MLConvTranspose2dOptions : MLOperatorOptions {
  sequence<[EnforceRange] unsigned long> padding;
  sequence<[EnforceRange] unsigned long> strides;
  sequence<[EnforceRange] unsigned long> dilations;
  sequence<[EnforceRange] unsigned long> outputPadding;
  sequence<[EnforceRange] unsigned long> outputSizes;
  [EnforceRange] unsigned long groups = 1;
  MLInputOperandLayout inputLayout = "nchw";
  MLConvTranspose2dFilterOperandLayout filterLayout = "iohw";
  MLOperand bias;
};

partial interface MLGraphBuilder {
  MLOperand convTranspose2d(MLOperand input, MLOperand filter,
                            optional MLConvTranspose2dOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLConv2dSupportLimits convTranspose2d;
};

MLConvTranspose2dOptions 具有以下成员:

padding, 类型为 sequence<[EnforceRange] unsigned long>

长度为 4 的列表:[beginningHeight, endingHeight, beginningWidth, endingWidth]。 指定添加到卷积输入各空间 维度开头和结尾的额外行和列。 默认值为 [0, 0, 0, 0]。

strides, 类型为 sequence<[EnforceRange] unsigned long>

长度为 2 的列表:[strideHeight, strideWidth]。 指定卷积输入各空间维度上滑动窗口的步幅。 默认值为 [1, 1]。

dilations, 类型为 sequence<[EnforceRange] unsigned long>

长度为 2 的列表:[dilationHeight, dilationWidth]。指定应用于卷积滤波器(核)的每个 空间维度的膨胀因子。 默认值为 [1, 1]。

outputPadding, 类型为 sequence<[EnforceRange] unsigned long>

长度为 2 的列表。 指定应用于输出张量各空间维度的填充值。当 strides 的值大于 1 时,需要显式填充值来消除转置卷积输出张量形状的歧义。

请注意,这些值仅在需要时用于消除输出形状的歧义;它并不 必然导致任何填充值被写入输出张量。

默认值为 [0, 0]。

outputSizes, 类型为 sequence<[EnforceRange] unsigned long>

长度为 2 的列表。 指定输出张量最后两个维度的大小。当显式指定输出大小时,outputPadding 中的输出填充值将被忽略。

如果未指定,则自动计算输出大小。

groups, 类型为 unsigned long,默认为 1

输入通道和输出通道被划分成的组数。

inputLayout, 类型为 MLInputOperandLayout,默认为 "nchw"

按如下方式指定输入和输出张量的布局格式:

  • "nchw"

    • 输入张量:[batches, inputChannels, height, width]

    • 输出张量:[batches, outputChannels, height, width]

  • "nhwc"

    • 输入张量:[batches, height, width, inputChannels]

    • 输出张量:[batches, height, width, outputChannels]

filterLayout, 类型为 MLConvTranspose2dFilterOperandLayout, 默认为 "iohw"

按如下方式指定滤波器张量的布局格式:

  • "iohw"[inputChannels, outputChannels/groups, height, width]

  • "hwoi"[height, width, outputChannels/groups, inputChannels]

  • "ohwi"[outputChannels/groups, height, width, inputChannels]

bias, 类型为 MLOperand

形状为 [outputChannels] 的额外一维张量,其值将加到 卷积结果中。

参数:

返回:一个 MLOperand。 包含转置卷积结果的输出 4 维张量。输出形状根据 inputLayout 解释。 更具体地说,除非显式指定 outputSizes, 否则需要 outputPadding 才能按如下方式计算输出张量的空间维度值:

outputSize = (inputSize - 1) * stride + (filterSize - 1) * dilation + 1 - beginningPadding - endingPadding + outputPadding

convTranspose2d() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input "float32", "float16" "float32", "float16" 4 4
filter input 相同 "float32", "float16" 4 4
bias input 相同 "float32", "float16" 1 1
输出 input 相同 "float32", "float16" 4 4

MLOpSupportLimits 对于 convTranspose2d() 具有以下成员:

convTranspose2d, 类型 为 MLConv2dSupportLimits

运算符 convTranspose2d() 的支持限制。

给定无符号整数 inputSizefilterSizebeginningPaddingendingPaddingstridedilation,要计算 convtranspose 输出大小, 执行以下步骤。它们返回一个 数值。
  1. effectiveFilterSize 为 ( filterSize - 1 ) * dilation + 1。

  2. outputSize 为 ( inputSize - 1 ) * stride + effectiveFilterSize - beginningPadding - endingPadding

  3. 返回 outputSize

convTranspose2d(input, filter, options) 方法的步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 thisinputfilter 以及 options.bias (如果其存在)中的任一个验证操作数返回 false,则抛出一个 TypeError

  3. 如果 input不是其允许的秩,则抛出一个 TypeError

  4. 如果 inputdataType 不是其允许的数据类型 之一(根据此表),则抛出一个 TypeError

  5. 如果 filter不是其允许的秩,则抛出一个 TypeError

  6. 如果 filterdataType 不是其允许的数据类型 之一(根据此表),则抛出一个 TypeError

  7. 如果 options.padding存在,则将其设置为列表 « 0, 0, 0, 0 »。

  8. 否则,如果 options.padding大小不为 4,则抛出一个 TypeError

  9. 如果 options.strides存在,则将其设置为列表 « 1, 1 »。

  10. 否则,如果 options.strides大小不为 2,则抛出一个 TypeError

  11. 如果 options.strides 中任何等于 0,则抛出一个 TypeError

  12. 如果 options.dilations存在,则将其设置为列表 « 1, 1 »。

  13. 否则,如果 options.dilations大小不为 2,则抛出一个 TypeError

  14. 如果 options.dilations 中任何等于 0,则抛出一个 TypeError

  15. 如果 options.outputPadding存在,则将其设置为列表 « 0, 0 »。

  16. 否则,如果 options.outputPadding大小不为 2,则抛出一个 TypeError

  17. 如果 options.outputSizes 存在,则:

    1. 如果其大小不为 2,则抛出 一个 TypeError

  18. 否则:

    1. 如果 options.outputPadding[0] 大于或等于 options.strides[0], 或 options.outputPadding[1] 大于或等于 options.strides[1], 则抛出一个 TypeError

  19. 如果 options.groups 为 0,则抛出一个 TypeError

  20. 计算输出形状:

    1. inputShapeinputshape

    2. 根据 options.inputLayout 切换:

      "nchw"

      令 « batches, inputChannels, inputHeight, inputWidth » 为 inputShape

      "nhwc"

      令 « batches, inputHeight, inputWidth, inputChannels » 为 inputShape

    3. filterShapefiltershape

    4. 根据 options.filterLayout 切换:

      "iohw"

      令 « filterInputChannels, filterOutputChannels, filterHeight, filterWidth » 为 filterShape

      "hwoi"

      令 « filterHeight, filterWidth, filterOutputChannels, filterInputChannels » 为 filterShape

      "ohwi"

      令 « filterOutputChannels, filterHeight, filterWidth, filterInputChannels » 为 filterShape

    5. 如果 inputChannels 不等于 filterInputChannels,则抛出一个 TypeError

    6. outputChannelsfilterOutputChannels * options.groups

    7. 如果 outputChannels 不是一个有效维度,则抛出 一个 TypeError

    8. 如果 options.bias 存在,则:

      1. 如果其shape等于 « outputChannels »,则抛出一个 TypeError

      2. 如果其 dataType 不是其允许的数据类型之一(根据此表),则抛出一个 TypeError

    9. calculatedOutputHeight 为给定 inputHeightfilterHeightpadding[0]、padding[1]、strides[0] 和 dilations[0] 计算 convtranspose 输出大小的结果。

    10. calculatedOutputWidth 为给定 inputWidthfilterWidthpadding[2]、padding[3]、strides[1] 和 dilations[1] 计算 convtranspose 输出大小的结果。

    11. 如果 options.outputSizes 存在,则:

      1. 令 « outputHeight, outputWidth » 为 options.outputSizes

      2. 如果 outputHeight 小于 calculatedOutputHeight,或 outputHeight 大于或等于 calculatedOutputHeight + strides[0],则抛出一个 TypeError

      3. 如果 outputWidth 小于 calculatedOutputWidth,或 outputWidth 大于或等于 calculatedOutputWidth + strides[1],则抛出一个 TypeError

    12. 否则:

      1. outputHeightcalculatedOutputHeight + options.outputPadding[0]。

      2. outputWidthcalculatedOutputWidth + options.outputPadding[1]。

    13. 如果 outputHeightoutputWidth 中任一个不是有效 维度,则抛出 一个 TypeError

    14. 根据 options.inputLayout 切换:

      "nchw"

      outputShape 为 « batches, outputChannels, floor( outputHeight ), floor( outputWidth ) »。

      "nhwc"

      outputShape 为 « batches, floor( outputHeight ), floor( outputWidth ), outputChannels »。

    15. desc 为给定 inputdataTypeoutputShape 创建 MLOperandDescriptor 的结果。

  21. 建立图连接:

    1. output 为给定 thisdesc 创建 MLOperand 的结果。

    2. operator 为给定 optionsfilter 的 "convTranspose2d" 运算的一个运算符

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 inputfilter

    5. 如果 options.bias 存在,则将其添加到 operator输入中。

    6. operator输出设置为 output

  22. 返回 output

8.9.12. cumulativeSum

沿给定轴计算一系列值的累加和,可以包含或不包含 当前值。
dictionary MLCumulativeSumOptions : MLOperatorOptions {
  boolean exclusive = false;
  boolean reversed = false;
};

partial interface MLGraphBuilder {
  MLOperand cumulativeSum(MLOperand input,
                          unsigned long axis,
                          optional MLCumulativeSumOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits cumulativeSum;
};
cumulativeSum() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input "float32", "float16", "int32", "uint32", "int64", "uint64" "float32", "float16", "int32" 1 到 N 1 到 5
输出 input 相同 "float32", "float16", "int32" input 相同 1 到 5

MLCumulativeSumOptions 具有以下成员:

exclusive, 类型为 boolean,默认为 false

是否在输出中包含或排除当前值,即包含式前缀和或 排除式前缀和 [Prefix-sum]。给定输入 [1,2,3,4],包含式求和将 产生输出 [1,3,6,10],而排除式求和将产生 [0,1,3,6]。默认 为包含式。

reversed, 类型为 boolean,默认为 false

是否沿活动轴反转求和方向,改为从高 坐标向低坐标开始。给定输入 [1,2,3,4],包含式正向求和将 产生输出 [1,3,6,10],而包含式反向求和将产生 [10,9,7,4]。默认为正向。

参数:

返回:

MLOpSupportLimits 对于 cumulativeSum() 具有以下成员:

cumulativeSum, 类型 为 MLSingleInputSupportLimits

运算符 cumulativeSum() 的支持限制。

cumulativeSum(input, axis, options) 方法的步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 验证操作数 thisinput 返回 false,则抛出一个 TypeError

  3. 如果 inputdataType 不是其允许的数据类型 之一(根据此表),则抛出一个 TypeError

  4. 如果 axis 大于或等于 input,则抛出一个 TypeError

  5. 建立图连接:

    1. output 为给定 input 复制 MLOperand 的结果。

    2. operator 为 "cumulativeSum" 运算和 options 的一个运算符

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 input

    5. operator输出设置为 output

  6. 返回 output

8.9.13. 逐元素二元运算

计算两个输入张量的逐元素二元加法、减法、乘法、除法、幂、最大值和最小值。

该运算将根据 [numpy-broadcasting-rule]进行广播。 输入张量必须是双向可广播的。输出张量的是输入张量的 最大。对于输出张量的 每个维度,其大小是输入张量沿该维度的最大大小。

partial interface MLGraphBuilder {
  MLOperand add(MLOperand a, MLOperand b, optional MLOperatorOptions options = {});
  MLOperand sub(MLOperand a, MLOperand b, optional MLOperatorOptions options = {});
  MLOperand mul(MLOperand a, MLOperand b, optional MLOperatorOptions options = {});
  MLOperand div(MLOperand a, MLOperand b, optional MLOperatorOptions options = {});
  MLOperand max(MLOperand a, MLOperand b, optional MLOperatorOptions options = {});
  MLOperand min(MLOperand a, MLOperand b, optional MLOperatorOptions options = {});
  MLOperand pow(MLOperand a, MLOperand b, optional MLOperatorOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLBinarySupportLimits add;
  MLBinarySupportLimits sub;
  MLBinarySupportLimits mul;
  MLBinarySupportLimits div;
  MLBinarySupportLimits max;
  MLBinarySupportLimits min;
  MLBinarySupportLimits pow;
};
参数:

返回:一个 MLOperand。 包含两个输入张量逐元素 二元运算结果的输出张量。

运算类型:
逐元素二元运算的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
a 任意类型 "float32", "float16", "int32" N 0 到 5
b a 相同 "float32", "float16", "int32" N 0 到 5
输出 a 相同 "float32", "float16", "int32" N 0 到 5

MLOpSupportLimits 对逐元素二元运算具有以下成员:

add, 类型为 MLBinarySupportLimits

运算符 add() 的支持限制。

sub, 类型为 MLBinarySupportLimits

运算符 sub() 的支持限制。

mul, 类型为 MLBinarySupportLimits

运算符 mul() 的支持限制。

div, 类型为 MLBinarySupportLimits

运算符 div() 的支持限制。

max, 类型为 MLBinarySupportLimits

运算符 max() 的支持限制。

min, 类型为 MLBinarySupportLimits

运算符 min() 的支持限制。

pow, 类型为 MLBinarySupportLimits

运算符 pow() 的支持限制。

给定字符串 opMLOperand aMLOperand bMLOperatorOptions options,要创建 逐元素二元运算,运行以下步骤:
  1. 断言op 是 "add"、"sub"、"mul"、"div"、"max"、 "min"、"pow" 之一。

  2. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  3. 如果使用 验证操作数 thisab 中任一个返回 false,则 抛出一个 TypeError

  4. 如果 adataType 不等于 bdataType,则抛出一个 TypeError

  5. outputShape 为对 ashapebshape 进行双向广播的结果。

    1. 如果其返回失败,则抛出 一个 TypeError

  6. descriptor 为给定 adataTypeoutputShape 创建 MLOperandDescriptor 的结果。

  7. 建立图连接:

    1. output 为给定 thisdescriptor 创建 MLOperand 的结果。

    2. operator 为给定 aboptionsop 运算的一个运算符

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 ab

    5. operator输出设置为 output

  8. 返回 output

逐元素二元运算算法按如下方式调用创建逐元素二元运算 步骤。
add(a, b, options) 方法的步骤为:
  1. output 为给定 "add"、aboptions 创建逐元素二元 运算的结果。

    1. 如果其抛出错误,则重新抛出该错误。

  2. 返回 output

sub(a, b, options) 方法的步骤为:
  1. output 为给定 "sub"、aboptions 创建逐元素二元 运算的结果。

    1. 如果其抛出错误,则重新抛出该错误。

  2. 返回 output

mul(a, b, options) 方法的步骤为:
  1. output 为给定 "mul"、aboptions 创建逐元素二元 运算的结果。

    1. 如果其抛出错误,则重新抛出该错误。

  2. 返回 output

div(a, b, options) 方法的步骤为:
  1. output 为给定 "div"、aboptions 创建逐元素二元 运算的结果。

    1. 如果其抛出错误,则重新抛出该错误。

  2. 返回 output

max(a, b, options) 方法的步骤为:
  1. output 为给定 "max"、aboptions 创建逐元素二元 运算的结果。

    1. 如果其抛出错误,则重新抛出该错误。

  2. 返回 output

min(a, b, options) 方法的步骤为:
  1. output 为给定 "min"、aboptions 创建逐元素二元 运算的结果。

    1. 如果其抛出错误,则重新抛出该错误。

  2. 返回 output

pow(a, b, options) 方法的步骤为:
  1. output 为给定 "pow"、aboptions 创建逐元素二元 运算的结果。

    1. 如果其抛出错误,则重新抛出该错误。

  2. 返回 output

8.9.14. 逐元素逻辑运算

逐元素比较输入张量,并针对比较结果返回一个值为 0(false)或 1(true)的 "uint8" 张量。对于单操作数运算,返回该运算的逻辑 结果。

对于多操作数运算,该运算将根据 [numpy-broadcasting-rule]进行广播。输入张量必须是双向可广播的。输出张量的是输入张量的 最大。对于输出张量的 每个维度,其大小是输入张量沿该维度的最大大小。

partial interface MLGraphBuilder {
  MLOperand equal(MLOperand a,
                  MLOperand b,
                  optional MLOperatorOptions options = {});
  MLOperand notEqual(MLOperand a,
                     MLOperand b,
                     optional MLOperatorOptions options = {});
  MLOperand greater(MLOperand a,
                    MLOperand b,
                    optional MLOperatorOptions options = {});
  MLOperand greaterOrEqual(MLOperand a,
                           MLOperand b,
                           optional MLOperatorOptions options = {});
  MLOperand lesser(MLOperand a,
                   MLOperand b,
                   optional MLOperatorOptions options = {});
  MLOperand lesserOrEqual(MLOperand a,
                          MLOperand b,
                          optional MLOperatorOptions options = {});
  MLOperand logicalNot(MLOperand a, optional MLOperatorOptions options = {});
  MLOperand logicalAnd(MLOperand a,
                       MLOperand b,
                       optional MLOperatorOptions options = {});
  MLOperand logicalOr(MLOperand a,
                      MLOperand b,
                      optional MLOperatorOptions options = {});
  MLOperand logicalXor(MLOperand a,
                       MLOperand b,
                       optional MLOperatorOptions options = {});
  MLOperand isNaN(MLOperand a, optional MLOperatorOptions options = {});
  MLOperand isInfinite(MLOperand a, optional MLOperatorOptions options = {});
};

dictionary MLLogicalNotSupportLimits {
  MLTensorLimits a;
  MLTensorLimits output;
};

partial dictionary MLOpSupportLimits {
  MLBinarySupportLimits equal;
  MLBinarySupportLimits notEqual;
  MLBinarySupportLimits greater;
  MLBinarySupportLimits greaterOrEqual;
  MLBinarySupportLimits lesser;
  MLBinarySupportLimits lesserOrEqual;
  MLLogicalNotSupportLimits logicalNot;
  MLBinarySupportLimits logicalAnd;
  MLBinarySupportLimits logicalOr;
  MLBinarySupportLimits logicalXor;
  MLLogicalNotSupportLimits isNaN;
  MLLogicalNotSupportLimits isInfinite;
};
参数:

返回:一个 MLOperand。 包含两个输入张量逐元素比较结果的输出张量。

equal()/notEqual()/greater()/greaterOrEqual()/lesser()/lesserOrEqual() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
a 任意类型 "float32", "float16", "int32" N 0 到 5
b a 相同 "float32", "float16", "int32" N 0 到 5
输出 "uint8" "uint8" N 0 到 5
logicalNot() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
a "uint8" "uint8" N 0 到 5
输出 "uint8" "uint8" N 0 到 5
logicalAnd()/logicalOr()/logicalXor() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
a "uint8" "uint8" N 0 到 5
b a 相同 "uint8" N 0 到 5
输出 "uint8" "uint8" N 0 到 5
isNaN()/isInfinite() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
a 任意类型 "float32", "float16" N 0 到 5
输出 "uint8" "uint8" N 0 到 5

MLLogicalNotSupportLimits 具有以下成员:

a, 类型为 MLTensorLimits

用于 a 操作数的 MLTensorLimits

output, 类型为 MLTensorLimits

用于输出操作数的 MLTensorLimits

MLOpSupportLimits 对逐元素逻辑运算具有以下成员:

equal, 类型为 MLBinarySupportLimits

运算符 equal() 的支持限制。

notEqual, 类型为 MLBinarySupportLimits

运算符 notEqual() 的支持限制。

greater, 类型为 MLBinarySupportLimits

运算符 greater() 的支持限制。

greaterOrEqual, 类型为 MLBinarySupportLimits

运算符 greaterOrEqual() 的支持限制。

lesser, 类型为 MLBinarySupportLimits

运算符 lesser() 的支持限制。

lesserOrEqual, 类型为 MLBinarySupportLimits

运算符 lesserOrEqual() 的支持限制。

logicalNot, 类型为 MLLogicalNotSupportLimits

运算符 logicalNot() 的支持限制。

logicalAnd, 类型为 MLBinarySupportLimits

运算符 logicalAnd() 的支持限制。

logicalOr, 类型为 MLBinarySupportLimits

运算符 logicalOr() 的支持限制。

logicalXor, 类型为 MLBinarySupportLimits

运算符 logicalXor() 的支持限制。

isNaN, 类型为 MLLogicalNotSupportLimits

运算符 isNaN() 的支持限制。

isInfinite, 类型为 MLLogicalNotSupportLimits

运算符 isInfinite() 的支持限制。

运算类型:
虽然运算 greaterOrEqual()lesserOrEqual() 都可以通过运算 logicalNot()lesser()greater() 来实现(换言之,builder.greaterOrEqual(a, b)builder.logicalNot(builder.lesser(a, b))),但它们被专门定义以处理 NaN 情况 并出于性能原因避免进行两次比较。
给定字符串 opMLOperand a、可选的 MLOperand bMLOperatorOptions options,要创建 逐元素逻辑运算,运行以下步骤:
  1. 断言op 是 "equal"、"notEqual"、"greater"、 "greaterOrEqual"、"lesser"、"lesserOrEqual"、"logicalNot"、"logicalAnd"、"logicalOr"、 "logicalXor"、"isNaN"、"isInfinite" 之一。

  2. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  3. 如果使用 验证操作数 thisa 返回 false,则抛出一个 TypeError

  4. 如果 op 是 "logicalNot"、"logicalAnd"、"logicalOr"、"logicalXor" 之一,则:

    1. 如果 adataType 不是 "uint8", 则抛出一个 TypeError

  5. 如果 op 是 "isNaN"、"isInfinite" 之一,则:

    1. 如果 adataType 不是 « "float32""float16" » 之一,则抛出一个 TypeError

  6. 如果传入了 b,则:

    1. 如果使用 验证操作数 thisb 返回 false,则抛出一个 TypeError

    2. 如果 adataType 不等于 bdataType,则抛出 一个 TypeError

    3. outputShape 为对 ashapebshape 进行双向广播的结果。如果其返回 失败,则抛出一个 TypeError

  7. 否则:

    1. outputShapeashape 的一个克隆

  8. descriptor 为给定 "uint8"outputShape 创建 MLOperandDescriptor 的结果。

  9. 建立图连接:

    1. output 为给定 thisdescriptor 创建 MLOperand 的结果。

    2. operator 为给定 a、(如果传入了 bboptionsop 运算的一个运算符

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 a 和(如果传入了 bb

    5. operator输出设置为 output

  10. 返回 output

逐元素逻辑运算算法按如下方式调用创建逐元素逻辑运算 步骤。
equal(a, b, options) 方法的步骤为:
  1. output 为给定 "equal"、aboptions 创建逐元素逻辑 运算的结果。

    1. 如果其抛出错误,则重新抛出该错误。

  2. 返回 output

notEqual(a, b, options) 方法的步骤为:
  1. output 为给定 "notEqual"、aboptions 创建逐元素逻辑 运算的结果。

    1. 如果其抛出错误,则重新抛出该错误。

  2. 返回 output

greater(a, b, options) 方法的步骤为:
  1. output 为给定 "greater"、aboptions 创建逐元素逻辑 运算的结果。

    1. 如果其抛出错误,则重新抛出该错误。

  2. 返回 output

greaterOrEqual(a, b, options) 方法的步骤为:
  1. output 为给定 "greaterOrEqual"、aboptions 创建逐元素逻辑 运算的结果。

    1. 如果其抛出错误,则重新抛出该错误。

  2. 返回 output

lesser(a, b, options) 方法的步骤为:
  1. output 为给定 "lesser"、aboptions 创建逐元素逻辑 运算的结果。

    1. 如果其抛出错误,则重新抛出该错误。

  2. 返回 output

lesserOrEqual(a, b, options) 方法的步骤为:
  1. output 为给定 "lesserOrEqual"、aboptions 创建逐元素逻辑 运算的结果。

    1. 如果其抛出错误,则重新抛出该错误。

  2. 返回 output

logicalNot(a, options) 方法的步骤为:
  1. output 为给定 "logicalNot"、aoptions 创建逐元素逻辑 运算的结果。

    1. 如果其抛出错误,则重新抛出该错误。

  2. 返回 output

logicalAnd(a, b, options) 方法的步骤为:
  1. output 为给定 "logicalAnd"、aboptions 创建逐元素逻辑 运算的结果。

    1. 如果其抛出错误,则重新抛出该错误。

  2. 返回 output

logicalOr(a, b, options) 方法的步骤为:
  1. output 为给定 "logicalOr"、aboptions 创建逐元素逻辑 运算的结果。

    1. 如果其抛出错误,则重新抛出该错误。

  2. 返回 output

logicalXor(a, b, options) 方法的步骤为:
  1. output 为给定 "logicalXor"、aboptions 创建逐元素逻辑 运算的结果。

    1. 如果其抛出错误,则重新抛出该错误。

  2. 返回 output

isNaN(a, options) 方法的 步骤为:
  1. output 为给定 "isNaN"、aoptions 创建逐元素逻辑 运算的结果。

    1. 如果其抛出错误,则重新抛出该错误。

  2. 返回 output

isInfinite(a, options) 方法的步骤为:
  1. output 为给定 "isInfinite"、aoptions 创建逐元素逻辑 运算的结果。

    1. 如果其抛出错误,则重新抛出该错误。

  2. 返回 output

8.9.15. 逐元素一元运算

对输入张量计算逐元素一元运算。
partial interface MLGraphBuilder {
  MLOperand abs(MLOperand input, optional MLOperatorOptions options = {});
  MLOperand ceil(MLOperand input, optional MLOperatorOptions options = {});
  MLOperand cos(MLOperand input, optional MLOperatorOptions options = {});
  MLOperand erf(MLOperand input, optional MLOperatorOptions options = {});
  MLOperand exp(MLOperand input, optional MLOperatorOptions options = {});
  MLOperand floor(MLOperand input, optional MLOperatorOptions options = {});
  MLOperand identity(MLOperand input, optional MLOperatorOptions options = {});
  MLOperand log(MLOperand input, optional MLOperatorOptions options = {});
  MLOperand neg(MLOperand input, optional MLOperatorOptions options = {});
  MLOperand reciprocal(MLOperand input, optional MLOperatorOptions options = {});
  MLOperand roundEven(MLOperand input, optional MLOperatorOptions options = {});
  MLOperand sin(MLOperand input, optional MLOperatorOptions options = {});
  MLOperand sign(MLOperand input, optional MLOperatorOptions options = {});
  MLOperand sqrt(MLOperand input, optional MLOperatorOptions options = {});
  MLOperand tan(MLOperand input, optional MLOperatorOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits abs;
  MLSingleInputSupportLimits ceil;
  MLSingleInputSupportLimits cos;
  MLSingleInputSupportLimits erf;
  MLSingleInputSupportLimits exp;
  MLSingleInputSupportLimits floor;
  MLSingleInputSupportLimits identity;
  MLSingleInputSupportLimits log;
  MLSingleInputSupportLimits neg;
  MLSingleInputSupportLimits reciprocal;
  MLSingleInputSupportLimits roundEven;
  MLSingleInputSupportLimits sin;
  MLSingleInputSupportLimits sign;
  MLSingleInputSupportLimits sqrt;
  MLSingleInputSupportLimits tan;
};
参数:

返回:一个 MLOperand。 包含输入张量逐元素 一元运算结果的输出张量。输出 张量的形状与输入张量的形状相同。

abs()/neg() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input "float32", "float16", "int64", "int32", "int8" "float32", "float16", "int32" N 0 到 5
输出 input 相同 "float32", "float16", "int32" input 相同 0 到 5
ceil()/cos()/erf()/exp()/floor()/log()/reciprocal()/roundEven()/sin()/sqrt()/tan() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input "float32", "float16" "float32", "float16" N 0 到 5
输出 input 相同 "float32", "float16" input 相同 0 到 5
identity() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input 任意类型 "float32", "float16", "int32" N 0 到 5
输出 input 相同 "float32", "float16", "int32" input 相同 0 到 5
sign() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input "float32", "float16", "int64", "int32", "int8" "float32", "float16", "int32" N 0 到 5
输出 input 相同 "float32", "float16", "int32" input 相同 0 到 5

MLOpSupportLimits 对逐元素一元运算具有以下成员:

abs, 类型为 MLSingleInputSupportLimits

运算符 abs() 的支持限制。

ceil, 类型为 MLSingleInputSupportLimits

运算符 ceil() 的支持限制。

cos, 类型为 MLSingleInputSupportLimits

运算符 cos() 的支持限制。

erf, 类型为 MLSingleInputSupportLimits

运算符 erf() 的支持限制。

exp, 类型为 MLSingleInputSupportLimits

运算符 exp() 的支持限制。

floor, 类型为 MLSingleInputSupportLimits

运算符 floor() 的支持限制。

identity, 类型为 MLSingleInputSupportLimits

运算符 identity() 的支持限制。

log, 类型为 MLSingleInputSupportLimits

运算符 log() 的支持限制。

neg, 类型为 MLSingleInputSupportLimits

运算符 neg() 的支持限制。

reciprocal, 类型为 MLSingleInputSupportLimits

运算符 reciprocal() 的支持限制。

roundEven, 类型为 MLSingleInputSupportLimits

运算符 roundEven() 的支持限制。

sin, 类型为 MLSingleInputSupportLimits

运算符 sin() 的支持限制。

sign, 类型为 MLSingleInputSupportLimits

运算符 sign() 的支持限制。

sqrt, 类型为 MLSingleInputSupportLimits

运算符 sqrt() 的支持限制。

tan, 类型为 MLSingleInputSupportLimits

运算符 tan() 的支持限制。

运算类型:
给定字符串 opMLOperand input、可选的列表 allowedDataTypesoptions,要创建 逐元素一元运算,运行以下 步骤:
  1. 断言op 是 "abs"、"ceil"、"cos"、"erf"、"exp"、 "floor"、"identity"、"log"、"neg"、"reciprocal"、"roundEven"、"sin"、"sign"、"sqrt"、"tan" 之一。

  2. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  3. 如果使用 验证操作数 thisinput 返回 false,则抛出一个 TypeError

  4. 如果已给出 allowedDataTypes,且其不包含 inputdataType,则抛出一个 TypeError

  5. 建立图连接:

    1. output 为给定 input 复制 MLOperand 的结果。

    2. operator 为给定 optionsop 运算的一个运算符

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 input

    5. operator输出设置为 output

  6. 返回 output

逐元素一元运算算法按如下方式调用创建逐元素一元运算步骤。
abs(input, options) 方法的 步骤为:
  1. output 为给定 "abs"、input、« "float32""float16""int64""int32""int8" » 和 options 创建逐元素一元 运算的结果。

    1. 如果其抛出错误,则重新抛出该错误。

  2. 返回 output

ceil(input, options) 方法的 步骤为:
  1. output 为给定 "ceil"、input、« "float32""float16" » 和 options 创建逐元素一元 运算的结果。

    1. 如果其抛出错误,则重新抛出该错误。

  2. 返回 output

cos(input, options) 方法的 步骤为:
  1. output 为给定 "cos"、input、« "float32""float16" » 和 options 创建逐元素一元 运算的结果。

    1. 如果其抛出错误,则重新抛出该错误。

  2. 返回 output

erf(input, options) 方法的 步骤为:
  1. output 为给定 "erf"、input、« "float32""float16" » 和 options 创建逐元素一元 运算的结果。

    1. 如果其抛出错误,则重新抛出该错误。

  2. 返回 output

exp(input, options) 方法的 步骤为:
  1. output 为给定 "exp"、input、« "float32""float16" » 和 options 创建逐元素一元 运算的结果。

    1. 如果其抛出错误,则重新抛出该错误。

  2. 返回 output

floor(input, options) 方法的 步骤为:
  1. output 为给定 "floor"、input、« "float32""float16" » 和 options 创建逐元素一元 运算的结果。

    1. 如果其抛出错误,则重新抛出该错误。

  2. 返回 output

identity(input, options) 方法的步骤为:
  1. output 为给定 "identity" inputoptions 创建逐元素一元 运算的结果。

    1. 如果其抛出错误,则重新抛出该错误。

  2. 返回 output

log(input, options) 方法的 步骤为:
  1. output 为给定 "log"、input、« "float32""float16" » 和 options 创建逐元素一元 运算的结果。

    1. 如果其抛出错误,则重新抛出该错误。

  2. 返回 output

neg(input, options) 方法的 步骤为:
  1. output 为给定 "neg"、input、« "float32""float16""int64""int32""int8" » 和 options 创建逐元素一元 运算的结果。

    1. 如果其抛出错误,则重新抛出该错误。

  2. 返回 output

reciprocal(input, options) 方法的步骤为:
  1. output 为给定 "reciprocal"、input、« "float32""float16" » 和 options 创建逐元素一元 运算的结果。

    1. 如果其抛出错误,则重新抛出该错误。

  2. 返回 output

roundEven(input, options) 方法的步骤为:
  1. output 为给定 "roundEven"、input、« "float32""float16" » 和 options 创建逐元素一元 运算的结果。

    1. 如果其抛出错误,则重新抛出该错误。

  2. 返回 output

sin(input, options) 方法的 步骤为:
  1. output 为给定 "sin"、input、« "float32""float16" » 和 options 创建逐元素一元 运算的结果。

    1. 如果其抛出错误,则重新抛出该错误。

  2. 返回 output

sign(input, options) 方法的 步骤为:
  1. output 为给定 "sign"、input、« "float32""float16""int64""int32""int8" » 和 options 创建逐元素一元 运算的结果。

    1. 如果其抛出错误,则重新抛出该错误。

  2. 返回 output

sqrt(input, options) 方法的 步骤为:
  1. output 为给定 "sqrt"、input、« "float32""float16" » 和 options 创建逐元素一元 运算的结果。

    1. 如果其抛出错误,则重新抛出该错误。

  2. 返回 output

tan(input, options) 方法的 步骤为:
  1. output 为给定 "tan"、input、« "float32""float16" » 和 options 创建逐元素一元 运算的结果。

    1. 如果其抛出错误,则重新抛出该错误。

  2. 返回 output

sign() 运算的行为可以按如下方式使用其他运算进行通用模拟,尽管用户 代理通常具有更高效的实现。当底层平台 不直接支持某项运算时,可以使用此分解作为模板来指导 实现。
function sign(builder, input, options) {
  const zero = builder.constant(input.dataType, 0);
  const positiveOne = builder.constant(input.dataType, 1);
  const negativeOne = builder.constant(input.dataType, -1);

  return builder.where(
    builder.greater(input, zero),
    positiveOne,
    builder.where(builder.lesser(input, zero), negativeOne, zero));
}

8.9.16. dequantizeLinear

使用缩放因子和零点偏置将整数张量反量化为浮点张量,其中 output = (input - zeroPoint) * scalescalezeroPoint 张量可以比 input 张量更小,因为它们是可分块广播的。
partial interface MLGraphBuilder {
  MLOperand dequantizeLinear(MLOperand input,
                             MLOperand scale,
                             MLOperand zeroPoint,
                             optional MLOperatorOptions options = {});
};

dictionary MLQuantizeDequantizeLinearSupportLimits {
  MLTensorLimits input;
  MLTensorLimits scale;
  MLTensorLimits zeroPoint;
  MLTensorLimits output;
};

partial dictionary MLOpSupportLimits {
  MLQuantizeDequantizeLinearSupportLimits dequantizeLinear;
};
参数:

返回:一个 MLOperand。 包含反量化值的输出张量。

dequantizeLinear() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input "uint8", "int8", "uint32", "int32" "uint8", "int8" N 0 到 5
scale "float32", "float16" "float32", "float16" input 相同 0 到 5
zeroPoint input 相同 "uint8", "int8", "int32" input 相同 0 到 5
输出 scale 相同 "float32", "float16" input 相同 0 到 5

MLQuantizeDequantizeLinearSupportLimits 具有以下成员:

input, 类型为 MLTensorLimits

用于 input 操作数的 MLTensorLimits

scale, 类型为 MLTensorLimits

用于 scale 操作数的 MLTensorLimits

zeroPoint, 类型为 MLTensorLimits

用于 zeroPoint 操作数的 MLTensorLimits

output, 类型为 MLTensorLimits

用于输出操作数的 MLTensorLimits

MLOpSupportLimits 对于 dequantizeLinear() 具有以下成员:

dequantizeLinear, 类型为 MLQuantizeDequantizeLinearSupportLimits

运算符 dequantizeLinear() 的支持限制。

dequantizeLinear(input, scale, zeroPoint, options) 方法的步骤为:
  1. 如果 this.[[hasBuilt]] 为 true,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 验证操作数 thisinputscalezeroPoint 中任一个返回 false,则抛出一个 TypeError

  3. 如果 inputdataType 不是其允许的数据类型 之一(根据此表),则抛出一个 TypeError

  4. 如果 scaledataType 不是其允许的数据类型 之一(根据此表),则抛出一个 TypeError

  5. 如果 zeroPointdataType 不是其允许的数据类型 之一(根据此表),则抛出一个 TypeError

  6. 如果 zeroPointdataType 不等于 inputdataType,则抛出一个 TypeError

  7. 如果 scalezeroPoint不等于 input,则抛出一个 TypeError

  8. 如果 scaleshape等于 zeroPointshape,则抛出一个 TypeError

  9. 如果对 scale分块广播 shapeinputshape 返回 false,则抛出一个 TypeError

  10. 如果对 zeroPoint分块广播 shapeinputshape 返回 false,则抛出一个 TypeError

  11. outputDescriptor 为给定 scale创建 MLOperandDescriptor 的结果,给定 scaledataTypeinputshape

  12. 建立图连接:

    1. output创建 MLOperand的结果,给定 thisoutputDescriptor

    2. operator 为 "dequantizeLinear" 运算的一个运算符,给定 inputscalezeroPointoptions

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 input

    5. operator输出设置为 output

  13. 返回 output

此运算的行为可以按如下方式使用其他运算进行通用模拟, 尽管用户代理通常具有更高效的实现。当 底层平台不直接支持某项运算时,可以使用此分解作为 模板来指导实现。
function dequantizeLinear(builder, input, scale, zeroPoint, options) {
  // output = (input - zeroPoint) * scale
  const floatInput = builder.cast(input, scale.dataType);
  const floatZeroPoint = builder.cast(zeroPoint, scale.dataType);
  const upsampledScale = blockwiseExpand(builder, scale, input.shape);
  const upsampledZeroPoint =
    blockwiseExpand(builder, floatZeroPoint, input.shape);
  return builder.mul(
    builder.sub(floatInput, upsampledZeroPoint), upsampledScale);
}

function blockwiseExpand(builder, input, outputShape) {
  // 给定原始输入和所需的输出形状,此操作会扩展每个轴
  // 方法是按该轴所需的次数重复该块。不过,后端
  // 实现可能具有效率高得多的上采样运算符,这些运算符
  // 可以接受多个维度,从而一次性按
  // 整数倍(类似 tile)使用最近邻重采样对所有维度进行上采样:
  // output = resample(scale, {sizes: input.shape})

  let output = input;

  for (let axis = 0; axis < input.shape.length; ++axis) {
    const oldShape = output.shape;
    const oldDimensionLength = oldShape[axis];
    const newDimensionLength = outputShape[axis];

    if (newDimensionLength != oldDimensionLength) {
      // 由于 tile/expand 只能接受整个维度的重复
      // 切片(不能沿某个轴重复单个元素),因此临时
      // 重塑张量,使其能够将元素广播到
      // 完整块大小,并利用插入的大小为 1 的维度。
      const elementRepeatCount = newDimensionLength / oldDimensionLength;
      const flattenedShape = getFlattenedShapeAroundAxis(oldShape, axis);
      const unexpandedShape =
        [flattenedShape[0], flattenedShape[1], 1, flattenedShape[2]];
      const expandedShape = [
        flattenedShape[0],
        flattenedShape[1],
        elementRepeatCount,
        flattenedShape[2]
      ];
      const reshapedInput = builder.reshape(output, unexpandedShape);
      output = builder.expand(reshapedInput, expandedShape);

      let newShape = [...oldShape];
      newShape[axis] = newDimensionLength;
      output = builder.reshape(output, newShape);
    }
  }

  return output;
}

// 计算给定轴之前和之后的展平形状,得到一个
// 3 元素列表:例如
// - inputShape = [2,3,4,5,6] 且 axis = 2 得到形状 [6,4,30]。
// - inputShape = [4] 且 axis = 0 得到形状 [1,4,1]。
function getFlattenedShapeAroundAxis(inputShape, axis) {
  axis = Math.max(Math.min(axis, inputShape.length - 1), 0);
  const shapeBefore = inputShape.slice(0, axis);
  const shapeAfter = inputShape.slice(axis + 1, inputShape.length);
  const countBefore = shapeBefore.reduce((a, b) => a * b, 1);
  const countAfter = shapeAfter.reduce((a, b) => a * b, 1);
  return [countBefore, inputShape[axis], countAfter];
}

8.9.17. quantizeLinear

使用缩放因子和零点偏置将浮点张量量化为整数张量(例如,对于 "uint8", output = clamp(roundEven(input / scale) + zeroPoint, 0, 255))。scalezeroPoint 张量可以比 input 张量更小,因为它们会进行分块广播
partial interface MLGraphBuilder {
  MLOperand quantizeLinear(MLOperand input,
                           MLOperand scale,
                           MLOperand zeroPoint,
                           optional MLOperatorOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLQuantizeDequantizeLinearSupportLimits quantizeLinear;
};
参数:

返回:一个 MLOperand。 包含量化值的输出张量。

quantizeLinear() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input "float32", "float16" "float32", "float16" N 0 到 5
scale input 相同 "float32", "float16" input 相同 0 到 5
zeroPoint "uint8", "int8", "uint32", "int32" "uint8", "int8" input 相同 0 到 5
输出 zeroPoint 相同 "uint8", "int8" input 相同 0 到 5

MLOpSupportLimits 对于 quantizeLinear() 具有以下成员:

quantizeLinear, 类型为 MLQuantizeDequantizeLinearSupportLimits

运算符 quantizeLinear() 的支持限制。

quantizeLinear(input, scale, zeroPoint, options) 方法的步骤为:
  1. 如果 this.[[hasBuilt]] 为 true,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 验证操作数 thisinputscalezeroPoint 中任一个返回 false,则抛出一个 TypeError

  3. 如果 inputdataType 不是其允许的数据类型 之一(根据此表),则抛出一个 TypeError

  4. 如果 scaledataType 不是其允许的数据类型 之一(根据此表),则抛出一个 TypeError

  5. 如果 scaledataType 不等于 inputdataType,则抛出一个 TypeError

  6. 如果 zeroPointdataType 不是其允许的数据类型 之一(根据此表),则抛出一个 TypeError

  7. 如果 scalezeroPoint不等于 input,则抛出一个 TypeError

  8. 如果 scaleshape等于 zeroPointshape,则抛出一个 TypeError

  9. 如果对 scale分块广播 shapeinputshape 返回 false,则抛出一个 TypeError

  10. 如果对 zeroPoint分块广播 shapeinputshape 返回 false,则抛出一个 TypeError

  11. outputDescriptor创建 MLOperandDescriptor的结果,给定 zeroPointdataTypeinputshape

  12. 建立图连接:

    1. output创建 MLOperand的结果,给定 thisoutputDescriptor

    2. operator 为 "quantizeLinear" 运算的一个运算符,给定 inputscalezeroPointoptions

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 input

    5. operator输出设置为 output

  13. 返回 output

此运算的行为可以按如下方式使用其他运算进行通用模拟, 尽管用户代理通常具有更高效的实现。当 底层平台不直接支持某项运算时,可以使用此分解作为 模板来指导实现。
function quantizeLinear(builder, input, scale, zeroPoint, options) {
  // output = clamp(roundEven(input / scale) + zeroPoint, 0, 255)
  // 注意,blockwiseExpand 在 dequantizeLinear 中定义。

  const floatZeroPoint = builder.cast(zeroPoint, scale.dataType);
  const upsampledScale = blockwiseExpand(builder, scale, input.shape);
  const upsampledZeroPoint =
    blockwiseExpand(builder, floatZeroPoint, input.shape);
  const quantizedInput = builder.roundEven(builder.div(input, upsampledScale));
  const zeroPointAdjustedInput =
    builder.add(quantizedInput, upsampledZeroPoint);
  const clampedInput =
    builder.clamp(zeroPointAdjustedInput, {'minValue': 0, 'maxValue': 255});
  return builder.cast(clampedInput, zeroPoint.dataType);
}

8.9.18. elu

对输入张量逐元素计算指数线性单元 函数(ELU)。计算遵循表达式 max(0, x) + alpha * (exp(min(0, x)) - 1)
dictionary MLEluOptions : MLOperatorOptions {
  double alpha = 1;
};

partial interface MLGraphBuilder {
  MLOperand elu(MLOperand input, optional MLEluOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits elu;
};

MLEluOptions 具有以下成员:

alpha, 类型为 double,默认为 1

一个标量乘数。

参数:

返回:

elu() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input "float32", "float16" "float32", "float16" N 0 到 5
输出 input 相同 "float32", "float16" input 相同 0 到 5

MLOpSupportLimits 对于 elu() 具有以下成员:

elu, 类型为 MLSingleInputSupportLimits

运算符 elu() 的支持限制。

elu(input, options) 方法的 步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 验证操作数 thisinput 返回 false,则抛出一个 TypeError

  3. 如果 inputdataType 不是其允许的数据类型 之一(根据此表),则抛出一个 TypeError

  4. options.alpha 设置为转换 options.alphainputdataType 的结果。

  5. 建立图连接:

    1. output 为给定 input 复制 MLOperand 的结果。

    2. operator 为 "elu" 运算的一个运算符,给定 options

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 input

    5. operator输出设置为 output

  6. 返回 output

此运算的行为可以按如下方式使用其他运算进行通用模拟, 尽管用户代理通常具有更高效的实现。当 底层平台不直接支持某项运算时,可以使用此分解作为 模板来指导实现。
function elu(builder, input, options) {
  return builder.add(
    builder.max(builder.constant(input.dataType, 0), input),
    builder.mul(
      builder.constant(input.dataType, options.alpha),
      builder.sub(
        builder.exp(builder.min(builder.constant(input.dataType, 0), input)),
        builder.constant(input.dataType, 1))));
}

8.9.19. expand

根据新形状,将输入张量中大小为 1 的任意维度扩展到更大的大小。该扩展 与 [numpy-broadcasting-rule] 一致。输入张量必须能单向广播到新形状;每个 维度的大小必须为 1,或根据新 形状与相应输出维度的大小匹配。
partial interface MLGraphBuilder {
  MLOperand expand(MLOperand input,
                   sequence<[EnforceRange] unsigned long> newShape,
                   optional MLOperatorOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits expand;
};
参数:

返回:一个 MLOperand。 具有扩展大小形状的张量。

expand() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input 任意类型 "float32", "float16", "int32" N 0 到 5
输出 input 相同 "float32", "float16", "int32" N 0 到 5

MLOpSupportLimits 对于 expand() 具有以下成员:

expand, 类型为 MLSingleInputSupportLimits

运算符 expand() 的支持限制。

expand(input, newShape, options) 方法的步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 验证操作数 thisinput 返回 false,则抛出一个 TypeError

  3. outputShape 为对 input单向广播 shapenewShape 的结果。

    1. 如果其返回失败,则抛出 一个 TypeError

  4. 如果 outputShape大小不是 输出张量的允许的秩(根据此表),则抛出一个 TypeError

  5. outputDescriptor创建 MLOperandDescriptor的结果,给定 inputdataTypeoutputShape

  6. 建立图连接:

    1. output创建 MLOperand的结果,给定 thisoutputDescriptor

    2. operator 为 "expand" 运算的一个运算符,给定 inputnewShapeoptions

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 input

    5. operator输出设置为 output

  7. 返回 output

8.9.20. gather

根据索引沿某个轴从输入张量中收集值。
dictionary MLGatherOptions : MLOperatorOptions {
  [EnforceRange] unsigned long axis = 0;
};

partial interface MLGraphBuilder {
  MLOperand gather(MLOperand input,
                   MLOperand indices,
                   optional MLGatherOptions options = {});
};

dictionary MLGatherSupportLimits {
  MLTensorLimits input;
  MLTensorLimits indices;
  MLTensorLimits output;
};

partial dictionary MLOpSupportLimits {
  MLGatherSupportLimits gather;
};

MLGatherOptions 具有以下成员:

axis, 类型为 unsigned long,默认为 0

获取所收集值的轴。其值必须位于范围 [0, N-1] 内,其中 N 是输入张量的

参数:

返回:一个 MLOperand。 输出 N 维张量,其等于 input + indices - 1。

indices 参数在构建图时无法为 gather() 限制到允许范围,因为输入直到 执行时才可知。如果底层平台未提供指定的限制行为,实现可以在已编译图中引入 clamp()。 同样,如果底层平台不支持负索引,实现可以在 已编译图中引入运算,将从维度末尾计算的负索引转换为正 索引。
gather() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input 任意类型 "float32", "float16", "int32", "int8", "uint8" 1 到 N 1 到 5
indices "int32", "uint32", "int64" "int32" N 0 到 5
输出 input 相同 "float32", "float16", "int32", "int8", "uint8" N 0 到 5

MLGatherSupportLimits 具有以下成员:

input, 类型为 MLTensorLimits

用于 input 操作数的 MLTensorLimits

indices, 类型为 MLTensorLimits

用于 indices 操作数的 MLTensorLimits

output, 类型为 MLTensorLimits

用于输出操作数的 MLTensorLimits

MLOpSupportLimits 对于 gather() 具有以下成员:

gather, 类型为 MLGatherSupportLimits

运算符 gather() 的支持限制。

gather(input, indices, options) 方法的步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 验证操作数 thisinputindices 中任一个返回 false,则抛出一个 TypeError

  3. 如果 indicesdataType 不是其允许的数据类型 之一(根据此表),则抛出一个 TypeError

  4. inputShapeinputshape,令 inputRankinput

  5. indicesShapeindicesshape

  6. axisoptions.axis

  7. 如果 axis 大于或等于 inputRank,则抛出一个 TypeError

  8. dimCount 为零。

  9. outputRank 为零。

  10. outputShape 为空列表。

  11. 对于每一个 inputShape 中的 size

    1. 如果 dimCount 等于 axis,则中断

    2. outputShape[dimCount] 设置为 size

    3. dimCount 加一。

  12. outputRank 设置为 dimCount

  13. dimCount 为零。

  14. 对于每一个 indicesShape 中的 size

    1. outputShape[outputRank + dimCount] 设置为 size

    2. dimCount 加一。

  15. outputRank 设置为 outputRank + dimCount

  16. dimCount 为零。

  17. 对于每一个 inputShape 中的 size

    1. 如果 dimCount 小于或等于 axis,则继续

    2. outputShape[outputRank + dimCount - axis - 1] 设置为 size

    3. dimCount 加一。

  18. desc创建 MLOperandDescriptor的结果,给定 inputdataTypeoutputShape

  19. 建立图连接:

    1. output创建 MLOperand的结果,给定 desc

    2. operator 为 "gather" 运算的一个运算符,给定 inputindicesoptions

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 inputindices

    5. operator输出设置为 output

  20. 返回 output

gather 在不同切片方案中的工作方式示例。
// 形状为 [4,3] 的 input:
//   [[ 0,  1,  2],
//    [10, 11, 12],
//    [20, 21, 22],
//    [30, 31, 32]]
const input = builder.constant(
  {dataType: 'float32', shape: [4, 3]},
  new Float32Array([0, 1, 2, 10, 11, 12, 20, 21, 22, 30, 31, 32]));

// axis = 0(默认)
// 形状为 [2] 的 indices:
//   [3,1]
// 形状为 [2,3] 的 output:
//   [[30, 31, 32],
//    [10, 11, 12]]

const indices1 =
  builder.constant({dataType: 'uint32', shape: [2]}, new Uint32Array([3, 1]));

const output1 = builder.gather(input, indices1);

// axis = 1
// 形状为 [3] 的 indices:
//   [2,1,1]
// 形状为 [4,3] 的 output:
//   [[ 2,  1,  1],
//    [12, 11, 11],
//    [22, 21, 21],
//    [32, 31, 31]]

const indices2 = builder.constant(
  {dataType: 'uint32', shape: [3]}, new Uint32Array([2, 1, 1]));

const output2 = builder.gather(input, indices2, {axis: 1});

// axis = 1
// 形状为 [2,2] 的 indices:
//   [[0, 1],
//    [1, 2]]
// 形状为 [4,2,2] 的 output:
//   [[[ 0,  1], [ 1,  2]],
//    [[10, 11], [11, 12]],
//    [[20, 21], [21, 22]],
//    [[30, 31], [31, 32]]]

const indices3 = builder.constant(
  {dataType: 'uint32', shape: [2, 2]}, new Uint32Array([0, 1, 1, 2]));

const output3 = builder.gather(input, indices3, {axis: 1});

8.9.21. gatherElements

根据索引沿某个轴从输入张量中收集值。
partial interface MLGraphBuilder {
  MLOperand gatherElements(MLOperand input,
                           MLOperand indices,
                           optional MLGatherOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLGatherSupportLimits gatherElements;
};
参数:

返回:一个 MLOperand。 输出 N 维张量,其等于 input

gatherElements() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input 任意类型 "float32", "float16", "int32" 1 到 N 1 到 5
indices "int32", "uint32", "int64" "int32" input 相同 1 到 5
输出 input 相同 "float32", "float16", "int32" input 相同 1 到 5

MLOpSupportLimits 对于 gatherElements() 具有以下成员:

gatherElements, 类型为 MLGatherSupportLimits

运算符 gatherElements() 的支持限制。

indices 参数在构建图时无法为 gatherElements() 限制到允许范围,因为输入直到 执行时才可知。如果底层平台未提供指定的限制行为,实现可以在已编译图中引入 clamp()。 同样,如果底层平台不支持负索引,实现可以在 已编译图中引入运算,将从维度末尾计算的负索引转换为正 索引。
gatherElements(input, indices, options) 方法的步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 验证操作数 thisinputindices 中任一个返回 false,则抛出一个 TypeError

  3. 如果 indicesdataType 不是其允许的数据类型 之一(根据此表),则抛出一个 TypeError

  4. 如果 inputindices 中任一个的 不是其允许的秩,则抛出一个 TypeError

  5. axisoptions.axis

  6. 如果 axis 大于或等于 input,则抛出一个 TypeError

  7. indicesShapeExpectedinputshape 的副本。

  8. indicesShapeExpected[axis] 设置为 indicesshape[axis]。

  9. 如果 indicesshape 不等于 indicesShapeExpected, 则抛出一个 TypeError

  10. 建立图连接:

    1. output 为给定 input 复制 MLOperand 的结果。

    2. operator 为 "gatherElements" 运算的一个运算符,给定 inputindicesoptions

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 inputindices

    5. operator输出设置为 output

  11. 返回 output

gatherElements 在不同切片方案中的工作方式示例。
// 形状为 [4,3] 的 input:
//   [[ 0,  1,  2],
//    [10, 11, 12],
//    [20, 21, 22],
//    [30, 31, 32]]
// 形状为 [2,3] 的 indices:
//   [[3, 1, 1],
//    [2, 0, 3]]
// axis = 0(默认)
// 形状为 [2,3] 的 output:
//   [[30, 11, 12],
//    [20,  1, 32]]

const input1 = builder.constant(
  {dataType: 'float32', shape: [4, 3]},
  new Float32Array([0, 1, 2, 10, 11, 12, 20, 21, 22, 30, 31, 32]));

const indices1 = builder.constant(
  {dataType: 'uint32', shape: [2, 3]}, new Uint32Array([3, 1, 1, 2, 0, 3]));

const output1 = builder.gatherElements(input1, indices1);

// 形状为 [4,3] 的 input:
//   [[ 0,  1,  2],
//    [10, 11, 12],
//    [20, 21, 22],
//    [30, 31, 32]]
// 形状为 [4,1] 的 indices:
//   [[2],
//    [1],
//    [0],
//    [2]],
// axis = 1
// 形状为 [4,1] 的 output:
//   [[ 2],
//    [11],
//    [20],
//    [32]]

const indices2 = builder.constant(
  {dataType: 'uint32', shape: [4, 1]}, new Uint32Array([2, 1, 0, 2]));

const output2 = builder.gatherElements(input1, indices2, {axis: 1});

// 形状为 [4,2,2] 的 input:
//   [[[  0,   1],
//     [ 10,  11]],
//    [[100, 101],
//     [110, 111]],
//    [[200, 201],
//     [210, 211]],
//    [[300, 301],
//     [310, 311]],]
// 形状为 [1,2,2] 的 indices:
//   [[[0, 2],
//     [1, 3]]],
// axis = 0
// 形状为 [1,2,2] 的 output:
//   [[[  0, 201],
//     [110, 311]]]

const inputData3 = new Float32Array(
  [0, 1, 10, 11, 100, 101, 110, 111, 200, 201, 210, 211, 300, 301, 310, 311]);

const input3 =
  builder.constant({dataType: 'float32', shape: [4, 2, 2]}, inputData3);

const indices3 = builder.constant(
  {dataType: 'uint32', shape: [1, 2, 2]}, new Uint32Array([0, 2, 1, 3]));

const output3 = builder.gatherElements(input3, indices3, {axis: 0});

8.9.22. gatherND

根据索引收集输入张量的切片。
partial interface MLGraphBuilder {
  MLOperand gatherND(MLOperand input,
                     MLOperand indices,
                     optional MLOperatorOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLGatherSupportLimits gatherND;
};
参数:

返回:一个 MLOperand。 输出 N 维张量,其等于 input + indices - indicesshape[-1] - 1。

gatherND() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input 任意类型 "float32", "float16", "int32", "int8", "uint8" 1 到 N 1 到 5
indices "int32", "uint32", "int64" "int32" 1 到 N 1 到 5
输出 input 相同 "float32", "float16", "int32", "int8", "uint8" N 0 到 5

MLOpSupportLimits 对于 gatherND() 具有以下成员:

gatherND, 类型为 MLGatherSupportLimits

运算符 gatherND() 的支持限制。

indices 参数在构建图时无法为 gatherND() 限制到允许范围,因为输入直到 执行时才可知。如果底层平台未提供指定的限制行为,实现可以在已编译图中引入 clamp()。 同样,如果底层平台不支持负索引,实现可以在 已编译图中引入运算,将从维度末尾计算的负索引转换为正 索引。
gatherND(input, indices, options) 方法的步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 验证操作数 thisinputindices 中任一个返回 false,则抛出一个 TypeError

  3. 如果 indicesdataType 不是允许的数据类型 之一(根据此表),则抛出一个 TypeError

  4. 如果 inputindices 中任一个的 不是其允许的秩,则抛出一个 TypeError

  5. inputShapeinputshape,令 inputRankinput

  6. indicesShapeindicesshape,令 indicesRankindices

  7. 如果 inputindices 中任一个的 不是其允许的秩,则抛出一个 TypeError

  8. indexableSizeindicesRank - 1。

  9. coordinateSizeindicesShape[indexableSize]。

  10. 如果 coordinateSize 大于 inputRank,则抛出一个 TypeError

  11. outputShape 为空列表。

  12. 对于每一个位于范围 0 到 indexableSize(不含上界)中的 index

    1. indicesShape[index] 追加outputShape

  13. 对于每一个位于范围 coordinateSizeinputRank(不含上界)中的 index

    1. inputShape[index] 追加outputShape

  14. outputDesc创建 MLOperandDescriptor的结果,给定 inputdataTypeoutputShape

  15. 建立图连接:

    1. output创建 MLOperand的结果,给定 outputDesc

    2. operator 为 "gatherND" 运算的一个运算符,给定 inputindicesoptions

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 inputindices

    5. operator输出设置为 output

  16. 返回 output

gatherND 在不同切片方案中的工作方式示例。
// 形状为 [2,2] 的 input:
//   [[0, 1],
//    [2, 3]]
// 形状为 [3,2] 的 indices:
//   [[0, 0],
//    [1, 1],
//    [1, 0]]
// 形状为 [3] 的 output:
//   [0, 3, 2]

const input1 = builder.constant(
  {dataType: 'float32', shape: [2, 2]}, new Float32Array([0, 1, 2, 3]));

const indices1 = builder.constant(
  {dataType: 'uint32', shape: [3, 2]}, new Uint32Array([0, 0, 1, 1, 1, 0]));

const output1 = builder.gatherND(input1, indices1);

// 形状为 [2,2] 的 input:
//   [[0, 1],
//    [2, 3]]
// 形状为 [2,1] 的 indices:
//   [[1],
//    [0]]
// 形状为 [2,2] 的 output:
//   [[2, 3]    <= 来自 input 坐标 [1, *] 的行 [2, 3]
//    [0, 1]]   <= 来自 input 坐标 [0, *] 的行 [0, 1]

const indices2 = builder.constant(
  {dataType: 'uint32', shape: [2, 1]}, new Uint32Array([1, 0]));

const output2 = builder.gatherND(input1, indices2);

// 形状为 [2,2,2] 的 input:
//   [[[0, 1],
//     [2, 3]],
//    [[4, 5],
//     [6, 7]]]
// 形状为 [2,2] 的 indices:
//   [[0, 1],
//    [1, 0]]
// 形状为 [2,2] 的 output:
//   [[2, 3],   <= 来自 input 坐标 [0, 1, *] 的行 [2, 3]
//    [4, 5]]   <= 来自 input 坐标 [1, 0, *] 的行 [4, 5]

const input2 = builder.constant(
  {dataType: 'float32', shape: [2, 2, 2]},
  new Float32Array([0, 1, 2, 3, 4, 5, 6, 7]));

const indices3 = builder.constant(
  {dataType: 'uint32', shape: [2, 2]}, new Uint32Array([0, 1, 1, 0]));

const output3 = builder.gatherND(input2, indices3);

// 形状为 [2,2,2] 的 input:
//   [[[0, 1],
//     [2, 3]],
//    [[4, 5],
//     [6, 7]]]
// 形状为 [3,1] 的 indices:
//   [[1],
//    [0],
//    [1]]
// 形状为 [3,2,2] 的 output:
//   [[[4, 5],   <= 来自 input 坐标 [1, *, *] 的块 [[4, 5], [6, 7]]
//     [6, 7]],
//    [[0, 1],   <= 来自 input 坐标 [0, *, *] 的块 [[0, 1], [2, 3]]
//     [2, 3]],
//    [[4, 5],   <= 来自 input 坐标 [1, *, *] 的块 [[4, 5], [6, 7]]
//     [6, 7]]]

const indices4 = builder.constant(
  {dataType: 'uint32', shape: [3, 1]}, new Uint32Array([1, 0, 1]));

const output4 = builder.gatherND(input2, indices4);

// 形状为 [2,2,2] 的 input:
//   [[[0, 1],
//     [2, 3]],
//    [[4, 5],
//     [6, 7]]]
// 形状为 [5,3] 的 indices:
//   [[0,0,1],
//    [0,1,0],
//    [1,0,0],
//    [1,1,0],
//    [1,1,1]]
// 形状为 [5] 的 output:
//   [1,2,4,6,7]

const indices5 = builder.constant(
  {dataType: 'uint32', shape: [5, 3]},
  new Uint32Array([0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1]));

const output5 = builder.gatherND(input2, indices5);

8.9.23. gelu

计算输入张量的高斯 误差线性单元函数(GELU)。计算遵循表达式 0.5 * x * (1 + erf(x / sqrt(2)))
partial interface MLGraphBuilder {
  MLOperand gelu(MLOperand input, optional MLOperatorOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits gelu;
};
参数:

返回:

gelu() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input "float32", "float16" "float32", "float16" N 0 到 5
输出 input 相同 "float32", "float16" input 相同 0 到 5

MLOpSupportLimits 对于 gelu() 具有以下成员:

gelu, 类型为 MLSingleInputSupportLimits

运算符 gelu() 的支持限制。

gelu(input, options) 方法的 步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 验证操作数 thisinput 返回 false,则抛出一个 TypeError

  3. 如果 inputdataType 不是其允许的数据类型 之一(根据此表),则抛出一个 TypeError

  4. 建立图连接:

    1. output 为给定 input 复制 MLOperand 的结果。

    2. operator 为给定 options 的 "gelu" 运算的一个运算符

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 input

    5. operator输出设置为 output

  5. 返回 output

此运算的行为可以按如下方式使用其他运算进行通用模拟, 尽管用户代理通常具有更高效的实现。当 底层平台不直接支持某项运算时,可以使用此分解作为 模板来指导实现。
function gelu(builder, input) {
  return builder.mul(
    builder.mul(input, builder.constant(input.dataType, 0.5)),
    builder.add(
      builder.constant(input.dataType, 1),
      builder.erf(builder.div(
        input, builder.sqrt(builder.constant(input.dataType, 2))))));
}

8.9.24. gemm

计算基础线性代数子程序的通用矩阵 乘法。计算遵循表达式 alpha * A * B + beta * C,其中 A 是形状为 [M, K][K, M] 的二维张量,B 是形状为 [K, N][N, K] 的二维张量,而 C 可以单向广播到形状 [M, N]AB 可以选择在计算前进行转置。
dictionary MLGemmOptions : MLOperatorOptions {
  MLOperand c;
  double alpha = 1.0;
  double beta = 1.0;
  boolean aTranspose = false;
  boolean bTranspose = false;
};

partial interface MLGraphBuilder {
  MLOperand gemm(MLOperand a, MLOperand b, optional MLGemmOptions options = {});
};

dictionary MLGemmSupportLimits {
  MLTensorLimits a;
  MLTensorLimits b;
  MLTensorLimits c;
  MLTensorLimits output;
};

partial dictionary MLOpSupportLimits {
  MLGemmSupportLimits gemm;
};

MLGemmOptions 具有以下成员:

c, 类型为 MLOperand

第三个输入张量。它可以是标量,也可以具有可单向广播到形状 [M, N] 的形状。当未指定时,计算方式相当于 c 为标量 0.0。

alpha, 类型为 double,默认为 1.0

第一个输入的乘数。

beta, 类型为 double,默认为 1.0

第三个输入 c 的乘数。

aTranspose, 类型为 boolean,默认为 false

指示是否在计算输出之前转置第一个输入。

bTranspose, 类型为 boolean,默认为 false

指示是否在计算输出之前转置第二个输入。

参数:

返回:一个 MLOperand。 形状为 [M, N] 的输出二维张量,其中包含所有输入计算所得的乘积。

gemm() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
a "float32", "float16" "float32", "float16" 2 2
b a 相同 "float32", "float16" 2 2
c a 相同 "float32", "float16" 0 到 2 0 到 2
输出 a 相同 "float32", "float16" 2 2

MLGemmSupportLimits 具有以下成员:

a, 类型为 MLTensorLimits

用于 a 操作数的 MLTensorLimits

b, 类型为 MLTensorLimits

用于 b 操作数的 MLTensorLimits

c, 类型为 MLTensorLimits

用于 c 操作数的 MLTensorLimits

output, 类型为 MLTensorLimits

用于输出操作数的 MLTensorLimits

MLOpSupportLimits 对于 gemm() 具有以下成员:

gemm, 类型为 MLGemmSupportLimits

运算符 gemm() 的支持限制。

gemm(a, b, options) 方法的步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 验证操作数 thisab 中任一个返回 false,则 抛出一个 TypeError

  3. 如果 ab 中任一个的dataType 不是 其允许的数据类型之一(根据此表),则抛出一个 TypeError

  4. 如果 ab 中任一个的 不是其允许的秩,则抛出一个 TypeError

  5. options.alpha 设置为转换 options.alphaadataType 的结果。

  6. options.beta 设置为转换 options.betaadataType 的结果。

  7. shapeA克隆 ashape所得的结果。

  8. shapeB克隆 bshape所得的结果。

  9. 如果 options.aTranspose 为 true,则反转 shapeA 中各项的顺序。

  10. 如果 options.bTranspose 为 true,则反转 shapeB 中各项的顺序。

  11. 如果 shapeA[1] 不等于 shapeB[0],则抛出一个 TypeError

  12. 如果 options.c 存在,则:

    1. 如果其不能单向广播到 形状 « shapeA[0], shapeB[1] »,则抛出 一个 TypeError

    2. 如果其 dataType 不是其允许的数据类型之一(根据此表),则抛出 一个 TypeError

  13. desc创建 MLOperandDescriptor的结果,给定 adataType 和 « shapeA[0], shapeB[1] »。

  14. 建立图连接:

    1. output创建 MLOperand的结果,给定 thisdesc

    2. operator 为 "gemm" 运算的一个运算符,给定 options

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 ab

    5. 如果 options.c 存在,则将其添加到 operator输入

    6. operator输出设置为 output

  15. 返回 output

此运算的行为可以按如下方式使用其他运算进行通用模拟, 尽管用户代理通常具有更高效的实现。当 底层平台不直接支持某项运算时,可以使用此分解作为 模板来指导实现。
function gemm(builder, a, b, options) {
  if (options.aTranspose)
    a = builder.transpose(a);

  if (options.bTranspose)
    b = builder.transpose(b);

  let ab = builder.matmul(
    builder.mul(builder.constant(a.dataType, options.alpha), a), b);
  return (
    options.c ?
      builder.add(
        ab,
        builder.mul(builder.constant(a.dataType, options.beta), options.c)) :
      ab);
}

8.9.25. gru

门控循环单元 [GRU] 循环网络使用更新门、重置门和新门来计算输出状态,该状态会在网络的时间序列中 传递到输出。
enum MLGruWeightLayout {
  "zrn",  // 更新-重置-新门的顺序
  "rzn"   // 重置-更新-新门的顺序
};

enum MLRecurrentNetworkActivation {
  "relu",
  "sigmoid",
  "tanh"
};

enum MLRecurrentNetworkDirection {
  "forward",
  "backward",
  "both"
};

dictionary MLGruOptions : MLOperatorOptions {
  MLOperand bias;
  MLOperand recurrentBias;
  MLOperand initialHiddenState;
  boolean resetAfter = true;
  boolean returnSequence = false;
  MLRecurrentNetworkDirection direction = "forward";
  MLGruWeightLayout layout = "zrn";
  sequence<MLRecurrentNetworkActivation> activations;
};

partial interface MLGraphBuilder {
  sequence<MLOperand> gru(MLOperand input,
                          MLOperand weight,
                          MLOperand recurrentWeight,
                          [EnforceRange] unsigned long steps,
                          [EnforceRange] unsigned long hiddenSize,
                          optional MLGruOptions options = {});
};

dictionary MLGruSupportLimits {
  MLTensorLimits input;
  MLTensorLimits weight;
  MLTensorLimits recurrentWeight;
  MLTensorLimits bias;
  MLTensorLimits recurrentBias;
  MLTensorLimits initialHiddenState;
  MLTensorLimits output0;
  MLTensorLimits output1;
};

partial dictionary MLOpSupportLimits {
  MLGruSupportLimits gru;
};

MLGruOptions 具有以下成员:

bias, 类型为 MLOperand

形状为 [numDirections, 3 * hiddenSize] 的二维输入偏置张量。该张量形状第二维中的偏置 向量顺序根据 layout 指定。

recurrentBias, 类型为 MLOperand

形状为 [numDirections, 3 * hiddenSize] 的二维循环偏置张量。该张量形状第二维中的 偏置向量顺序根据 layout 指定。

initialHiddenState, 类型为 MLOperand

形状为 [numDirections, batchSize, hiddenSize] 的三维初始隐藏状态张量。 未指定时,实现必须使用填充为零的张量。

resetAfter, 类型为 boolean,默认为 true

指示是在矩阵乘法之后还是之前应用重置门。

returnSequence, 类型为 boolean,默认为 false

指示除最后一个时间步的输出外,是否还返回包含每个时间步所有输出的整个 序列。

direction, 类型为 MLRecurrentNetworkDirection,默认为 "forward"

输入序列的处理方向。当设置为 "both" 时,权重和偏置张量形状的第一维大小必须为 2,并且输入会 在两个方向上处理。

layout, 类型为 MLGruWeightLayout,默认为 "zrn"

GRU 内部门的权重和偏置向量顺序,具体为 update (z)reset (r)new (n) 门,如权重和偏置张量形状的 第二维所示。

activations, 类型为 sequence<MLRecurrentNetworkActivation>

指定一对激活函数,第一个函数用于 更新门和重置门,第二个函数用于新门。未指定时,分别默认为 "sigmoid""tanh" 函数。

参数:

返回:sequence<MLOperand>。 第一个元素是形状为 [numDirections, batchSize, hiddenSize] 的三维张量,即网络 最后一个时间步的单元输出。此外,如果 returnSequence 设置为 true,则第二个元素是形状为 [steps, numDirections, batchSize, hiddenSize] 的四维输出张量,其中包含时间 序列中每个时间步的所有单元输出。

gru() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input "float32", "float16" "float32", "float16" 3 3
weight input 相同 "float32", "float16" 3 3
recurrentWeight input 相同 "float32", "float16" 3 3
bias input 相同 "float32", "float16" 2 2
recurrentBias input 相同 "float32", "float16" 2 2
initialHiddenState input 相同 "float32", "float16" 3 3
outputs[0] input 相同 "float32", "float16" 3 3
outputs[1] 如果 returnSequence 为 true input 相同 "float32", "float16" 4 4

MLGruSupportLimits 具有以下成员:

input, 类型为 MLTensorLimits

MLTensorLimits 用于 input 操作数。

weight, 类型为 MLTensorLimits

MLTensorLimits 用于 weight 操作数。

recurrentWeight, 类型为 MLTensorLimits

MLTensorLimits 用于 recurrentWeight 操作数。

bias, 类型为 MLTensorLimits

MLTensorLimits 用于 bias 操作数。

recurrentBias, 类型为 MLTensorLimits

MLTensorLimits 用于 recurrentBias 操作数。

initialHiddenState, 类型为 MLTensorLimits

MLTensorLimits 用于 initialHiddenState 操作数。

output0, 类型为 MLTensorLimits

MLTensorLimits 用于所有输出操作数[0]。

output1, 类型为 MLTensorLimits

MLTensorLimits 用于所有输出操作数[1]。

MLOpSupportLimitsgru() 具有以下成员:

gru, 类型为 MLGruSupportLimits

运算符 gru() 的支持限制。

gru(input, weight, recurrentWeight, steps, hiddenSize, options) 方法的步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 验证操作数,给定 this 以及 inputweightrecurrentWeightoptions.bias (如果它存在)、options.recurrentBias (如果它存在)以及 options.initialHiddenState (如果它存在)中的任一个返回 false,则抛出一个 TypeError

  3. 如果 inputweightrecurrentWeight 中任一个的 dataType 不是其允许的数据类型 之一(根据此表),则抛出一个 TypeError

  4. 如果 inputweightrecurrentWeight 中任一个的不是其允许的秩, 则抛出一个 TypeError

  5. 如果 inputshape[0] 不等于 steps,则抛出一个 TypeError

  6. batchSizeinputshape[1]。

  7. inputSizeinputshape[2]。

  8. 如果 options.direction"both", 则令 numDirections 为 2,否则为 1。

  9. 如果 weightshape等于 « numDirections, 3 * hiddenSize, inputSize »,则抛出一个 TypeError

  10. 如果 recurrentWeightshape等于 « numDirections, 3 * hiddenSize, hiddenSize »,则抛出一个 TypeError

  11. 如果 hiddenSize * 6 不是有效维度,则抛出一个 TypeError

    为什么是 hiddenSize * 6? 某些底层平台在单个偏置张量上运算,该张量由 biasrecurrentBias 拼接而成。 因此,3 * hiddenSize + 3 * hiddenSize 也需要是一个有效 维度
  12. 如果 options.bias 存在,则:

    1. 如果其 dataType 不是其允许的数据类型之一(根据此表),则抛出 一个 TypeError

    2. 如果其shape等于 « numDirections, 3 * hiddenSize »,则抛出 一个 TypeError

  13. 如果 options.recurrentBias 存在,则:

    1. 如果其 dataType 不是其允许的数据类型之一(根据此表),则抛出 一个 TypeError

    2. 如果其shape等于 « numDirections, 3 * hiddenSize »,则抛出 一个 TypeError

  14. 如果 options.initialHiddenState 存在,则:

    1. 如果其 dataType 不是其允许的数据类型之一(根据此表),则抛出 一个 TypeError

    2. 如果其shape等于 « numDirections, batchSize, hiddenSize »,则抛出一个 TypeError

  15. 如果 options.activations 存在,则:

    1. 如果其大小不是 2,则抛出 一个 TypeError

    2. activationsoptions.activations 的一个克隆

  16. 否则:

    1. activations 为 « "sigmoid", "tanh" »。

  17. 计算输出形状:

    1. desc0创建 MLOperandDescriptor 的结果,给定 inputdataType 和 « numDirections, batchSize, hiddenSize »。

    2. 如果 options.returnSequence 为 true,则:

      1. desc1创建 MLOperandDescriptor的结果,给定 inputdataType 和 « steps, numDirections, batchSize, hiddenSize »。

  18. 建立图连接:

    1. operator 为 "gru" 运算的一个运算符,给定 weightrecurrentWeightstepshiddenSizeoptions

    2. output0创建 MLOperand的结果,给定 thisdesc0

    3. 如果 options.returnSequence 为 true,则:

      1. output1创建 MLOperand的结果,给定 thisdesc1

      2. output列表 « output0, output1 »。

      3. output0.[[operator]]output1.[[operator]] 设置为 operator

    4. 否则:

      1. output列表 « output0 »。

      2. output0.[[operator]] 设置为 operator

    5. operator输入设置为 inputweightrecurrentWeight

    6. 如果 options.bias 存在,则将其添加到 operator输入中。

    7. 如果 options.recurrentBias 存在,则将其添加到 operator输入中。

    8. 如果 options.initialHiddenState 存在,则将其添加到 operator输入中。

    9. operator激活函数设置为 activations 的一个克隆

    10. operator输出设置为 output

  19. 返回 output

使用 squeeze() 辅助函数,此运算的行为可以 按如下方式使用其他运算进行通用模拟,尽管用户代理通常 具有更高效的实现。在底层平台不直接 支持某项运算的情况下,可以使用此分解作为模板来指导实现。
function gru(
  builder, input, weight, recurrentWeight, steps, hiddenSize, options) {
  const batchSize = input.shape[1];
  const inputSize = input.shape[2];
  const direction = options.direction || 'forward';
  const numDirections = (direction == 'both' ? 2 : 1);
  let hiddenState = options.initialHiddenState;

  if (!hiddenState) {
    const desc = {
      dataType: 'float32',
      shape: [numDirections, batchSize, hiddenSize]
    };
    const totalSize = numDirections * batchSize * hiddenSize;
    hiddenState = builder.constant(desc, new Float32Array(totalSize).fill(0));
  }

  let currentWeight = [];
  let currentRecurrentWeight = [];
  let currentBias = [];
  let currentRecurrentBias = [];
  let forwardSequence = null;
  let backwardSequence = null;
  let outputHidden = null;

  for (let dir = 0; dir < numDirections; ++dir) {
    currentWeight.push(squeeze(
      builder,
      builder.slice(weight, [dir, 0, 0], [1, 3 * hiddenSize, inputSize])));
    currentRecurrentWeight.push(squeeze(
      builder,
      builder.slice(
        recurrentWeight, [dir, 0, 0], [1, 3 * hiddenSize, hiddenSize])));
    currentBias.push(
      options.bias ?
        (squeeze(
          builder,
          builder.slice(options.bias, [dir, 0], [1, 3 * hiddenSize]))) :
        null);
    currentRecurrentBias.push(
      options.recurrentBias ?
        (squeeze(
          builder,
          builder.slice(
            options.recurrentBias, [dir, 0], [1, 3 * hiddenSize]))) :
        null);
    let currentHidden = squeeze(
      builder,
      builder.slice(hiddenState, [dir, 0, 0], [1, batchSize, hiddenSize]), [0]);

    for (let step = 0; step < steps; ++step) {
      const slice =
        (dir == 1 || direction == 'backward' ? steps - step - 1 : step);
      const currentInput = squeeze(
        builder,
        builder.slice(input, [slice, 0, 0], [1, batchSize, inputSize]), [0]);

      currentHidden = builder.gruCell(
        currentInput,
        currentWeight[dir],
        currentRecurrentWeight[dir],
        currentHidden,
        hiddenSize,
        {
          bias: currentBias[dir],
          recurrentBias: currentRecurrentBias[dir],
          resetAfter: options.resetAfter,
          layout: options.layout,
          activations: options.activations
        });

      if (options.returnSequence) {
        // 将二维的 currentHidden([batchSize, hiddenSize])扩展
        // 为四维([steps, numDirections, batchSize, hiddenSize])
        const expandedHiddenAs4D =
          builder.reshape(currentHidden, [1, 1, batchSize, hiddenSize]);

        if (direction == 'forward' || (dir == 0 && direction == 'both')) {
          forwardSequence = forwardSequence ?
            builder.concat([forwardSequence, expandedHiddenAs4D], 0) :
            expandedHiddenAs4D;
        } else if (
          direction == 'backward' || (dir == 1 && direction == 'both')) {
          backwardSequence = backwardSequence ?
            builder.concat([expandedHiddenAs4D, backwardSequence], 0) :
            expandedHiddenAs4D;
        }
      }
    }

    // 将二维的 currentHidden([batchSize, hiddenSize])扩展
    // 为三维([numDirections, batchSize, hiddenSize])
    const expandedHiddenAs3D =
      builder.reshape(currentHidden, [1, batchSize, hiddenSize]);
    outputHidden = outputHidden ?
      builder.concat([outputHidden, expandedHiddenAs3D], 0) :
      expandedHiddenAs3D;
  }

  if (options.returnSequence) {
    let outputSequence = null;

    if (direction == 'forward') {
      outputSequence = forwardSequence;
    } else if (direction == 'backward') {
      outputSequence = backwardSequence;
    } else if (direction == 'both') {
      // 沿轴 1(numDirections 维度)拼接
      outputSequence = builder.concat([forwardSequence, backwardSequence], 1);
    }

    return [outputHidden, outputSequence];
  } else {
    return [outputHidden];
  }
}

8.9.26. gruCell

门控循环单元 [GRU] 循环网络的单个时间步,使用更新门和重置门来计算隐藏状态,该状态会在循环网络的 时间序列中传递到输出。
dictionary MLGruCellOptions : MLOperatorOptions {
  MLOperand bias;
  MLOperand recurrentBias;
  boolean resetAfter = true;
  MLGruWeightLayout layout = "zrn";
  sequence<MLRecurrentNetworkActivation> activations;
};

partial interface MLGraphBuilder {
  MLOperand gruCell(MLOperand input,
                    MLOperand weight,
                    MLOperand recurrentWeight,
                    MLOperand hiddenState,
                    [EnforceRange] unsigned long hiddenSize,
                    optional MLGruCellOptions options = {});
};

dictionary MLGruCellSupportLimits {
  MLTensorLimits input;
  MLTensorLimits weight;
  MLTensorLimits recurrentWeight;
  MLTensorLimits hiddenState;
  MLTensorLimits bias;
  MLTensorLimits recurrentBias;
  MLTensorLimits output;
};

partial dictionary MLOpSupportLimits {
  MLGruCellSupportLimits gruCell;
};

MLGruCellOptions 具有以下成员:

bias, 类型为 MLOperand

形状为 [3 * hiddenSize] 的一维输入偏置张量。该张量形状 第二维中的偏置向量顺序根据 layout 指定。

recurrentBias, 类型为 MLOperand

形状为 [3 * hiddenSize] 的一维循环偏置张量。该张量形状 第二维中的偏置向量顺序根据 layout 指定。

resetAfter, 类型为 boolean,默认为 true

指示是在矩阵乘法之后还是之前应用重置门。

layout, 类型为 MLGruWeightLayout,默认为 "zrn"

GRU 内部门的权重和偏置向量顺序,具体为 update (z)reset (r)new (n) 门,如权重和偏置张量形状的 第二维所示。

activations, 类型为 sequence<MLRecurrentNetworkActivation>

指定一对激活函数,第一个函数用于 更新门和重置门,第二个函数用于新门。未指定时,分别默认为 "sigmoid""tanh" 函数。

参数:

返回:一个 MLOperand。 形状为 [batchSize, hiddenSize] 的二维张量,即循环网络单个时间 步的单元输出隐藏状态。

gruCell() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input "float32", "float16" "float32", "float16" 2 2
weight input 相同 "float32", "float16" 2 2
recurrentWeight input 相同 "float32", "float16" 2 2
hiddenState input 相同 "float32", "float16" 2 2
bias input 相同 "float32", "float16" 1 1
recurrentBias input 相同 "float32", "float16" 1 1
输出 input 相同 "float32", "float16" 2 2

MLGruCellSupportLimits 具有以下成员;

input, 类型为 MLTensorLimits

MLTensorLimits 用于 input 操作数。

weight, 类型为 MLTensorLimits

MLTensorLimits 用于 weight 操作数。

recurrentWeight, 类型为 MLTensorLimits

MLTensorLimits 用于 recurrentWeight 操作数。

hiddenState, 类型为 MLTensorLimits

MLTensorLimits 用于 hiddenState 操作数。

bias, 类型为 MLTensorLimits

MLTensorLimits 用于 bias 操作数。

recurrentBias, 类型为 MLTensorLimits

MLTensorLimits 用于 recurrentBias 操作数。

output, 类型为 MLTensorLimits

MLTensorLimits 用于 output 操作数。

MLOpSupportLimitsgruCell() 具有以下成员:

gruCell, 类型为 MLGruCellSupportLimits

运算符 gruCell() 的支持限制。

gruCell(input, weight, recurrentWeight, hiddenState, hiddenSize, options) 方法的步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 验证操作数,给定 this 以及 inputweightrecurrentWeighthiddenStateoptions.bias (如果它存在)以及 options.recurrentBias (如果它存在)中的任一个返回 false,则抛出一个 TypeError

  3. 如果 inputweightrecurrentWeighthiddenState 中任一个的dataType 不是其允许的 数据类型之一(根据此表),则抛出一个 TypeError

  4. 如果 inputweightrecurrentWeighthiddenState 中任一个的 不是其允许的 秩(根据此表),则抛出一个 TypeError

  5. batchSizeinputshape[0]。

  6. inputSizeinputshape[1]。

  7. 如果 weightshape等于 « 3 * hiddenSize, inputSize », 则抛出一个 TypeError

  8. 如果 recurrentWeightshape等于 « 3 * hiddenSize, hiddenSize », 则抛出一个 TypeError

  9. 如果 hiddenStateshape等于 « batchSize, hiddenSize »,则 抛出一个 TypeError

  10. 如果 hiddenSize * 6 不是有效维度,则抛出一个 TypeError

    为什么是 hiddenSize * 6? 某些底层平台在单个偏置张量上运算,该张量由 biasrecurrentBias 拼接而成。 因此,3 * hiddenSize + 3 * hiddenSize 也需要是一个有效 维度
  11. 如果 options.bias 存在,则:

    1. 如果其 dataType 不是其允许的数据类型之一(根据此表),则抛出 一个 TypeError

    2. 如果其shape等于 « 3 * hiddenSize »,则抛出 一个 TypeError

  12. 如果 options.recurrentBias 存在,则:

    1. 如果其 dataType 不是其允许的数据类型之一(根据此表),则抛出 一个 TypeError

    2. 如果其shape等于 « 3 * hiddenSize »,则抛出 一个 TypeError

  13. 如果 options.activations 存在,则:

    1. 如果其大小不是 2,则抛出 一个 TypeError

    2. activationsoptions.activations 的一个克隆

  14. 否则:

    1. activations 为 « "sigmoid", "tanh" »。

  15. desc创建 MLOperandDescriptor的结果,给定 inputdataType 和 « batchSize, hiddenSize »。

  16. 建立图连接:

    1. output创建 MLOperand的结果,给定 thisdesc

    2. operator 为 "gruCell" 运算的一个运算符,给定 weightrecurrentWeighthiddenStatehiddenSizeoptions

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 inputweightrecurrentWeighthiddenState

    5. 如果 options.bias 存在,则将其添加到 operator输入中。

    6. 如果 options.recurrentBias 存在,则将其添加到 operator输入中。

    7. operator激活函数设置为 activations 的一个克隆

    8. operator输出设置为 output

  17. 返回 output

当权重布局为默认的 "zrn" 布局,并且更新/重置门和新门的激活函数 分别为 sigmoid()tanh() 时,此运算的行为可以按如下方式使用其他运算进行通用模拟,尽管 用户代理通常具有更高效的实现。在底层平台 不直接支持某项运算的情况下,可以使用此分解作为模板来指导 实现。
function gruCell(
  builder, input, weight, recurrentWeight, hiddenState, hiddenSize, options) {
  const one = builder.constant(input.dataType, 1);
  const zero = builder.constant(input.dataType, 0);

  const inputSize = input.shape[1];

  // 更新门(z)
  let z = builder.sigmoid(builder.add(
    builder.add(
      (options.bias ? builder.slice(options.bias, [0], [hiddenSize]) : zero),
      (options.recurrentBias ?
         builder.slice(options.recurrentBias, [0], [hiddenSize]) :
         zero)),
    builder.add(
      builder.matmul(
        input,
        builder.transpose(
          builder.slice(weight, [0, 0], [hiddenSize, inputSize]))),
      builder.matmul(
        hiddenState,
        builder.transpose(
          builder.slice(recurrentWeight, [0, 0], [hiddenSize, hiddenSize]))))));

  // 重置门(r)
  let r = builder.sigmoid(builder.add(
    builder.add(
      (options.bias ? builder.slice(options.bias, [hiddenSize], [hiddenSize]) :
                      zero),
      (options.recurrentBias ?
         builder.slice(options.recurrentBias, [hiddenSize], [hiddenSize]) :
         zero)),
    builder.add(
      builder.matmul(
        input,
        builder.transpose(
          builder.slice(weight, [hiddenSize, 0], [hiddenSize, inputSize]))),
      builder.matmul(
        hiddenState,
        builder.transpose(builder.slice(
          recurrentWeight, [hiddenSize, 0], [hiddenSize, hiddenSize]))))));

  // 新门(n)
  let n;
  if (options.resetAfter) {
    n = builder.tanh(builder.add(
      (options.bias ?
         builder.slice(options.bias, [2 * hiddenSize], [hiddenSize]) :
         zero),
      builder.add(
        builder.matmul(
          input,
          builder.transpose(builder.slice(
            weight, [2 * hiddenSize, 0], [hiddenSize, inputSize]))),
        builder.mul(
          r,
          builder.add(
            (options.recurrentBias ?
               builder.slice(
                 options.recurrentBias, [2 * hiddenSize], [hiddenSize]) :
               zero),
            builder.matmul(
              hiddenState,
              builder.transpose(builder.slice(
                recurrentWeight,
                [2 * hiddenSize, 0],
                [hiddenSize, hiddenSize]))))))));
  } else {
    n = builder.tanh(builder.add(
      builder.add(
        (options.bias ?
           builder.slice(options.bias, [2 * hiddenSize], [hiddenSize]) :
           zero),
        (options.recurrentBias ?
           builder.slice(
             options.recurrentBias, [2 * hiddenSize], [hiddenSize]) :
           zero)),
      builder.add(
        builder.matmul(
          input,
          builder.transpose(builder.slice(
            weight, [2 * hiddenSize, 0], [hiddenSize, inputSize]))),
        builder.matmul(
          builder.mul(r, hiddenState),
          builder.transpose(builder.slice(
            recurrentWeight,
            [2 * hiddenSize, 0],
            [hiddenSize, hiddenSize]))))));
  }

  // 计算新的隐藏状态
  return builder.add(
    builder.mul(z, hiddenState), builder.mul(n, builder.sub(one, z)));
}

8.9.27. hardSigmoid

在输入张量上计算非平滑的硬 sigmoid 函数, 用它替代 sigmoid 函数以加快计算。
dictionary MLHardSigmoidOptions : MLOperatorOptions {
  double alpha = 0.2;
  double beta = 0.5;
};

partial interface MLGraphBuilder {
  MLOperand hardSigmoid(MLOperand input, optional MLHardSigmoidOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits hardSigmoid;
};

MLHardSigmoidOptions 具有以下成员:

alpha, 类型为 double,默认为 0.2

一个标量乘数。

beta, 类型为 double,默认为 0.5

一个标量加数。

参数:

返回:

hardSigmoid() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input "float32", "float16" "float32", "float16" N 0 到 5
输出 input 相同 "float32", "float16" input 相同 0 到 5

MLOpSupportLimitshardSigmoid() 具有以下成员:

hardSigmoid, 类型为 MLSingleInputSupportLimits

运算符 hardSigmoid() 的支持限制。

hardSigmoid(input, options) 方法的步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 验证操作数,给定 thisinput 返回 false,则抛出一个 TypeError

  3. 如果 inputdataType 不是其允许的数据类型 之一(根据此表),则抛出一个 TypeError

  4. options.alpha 设置为转换 options.alphainputdataType 的结果。

  5. options.beta 设置为转换 options.betainputdataType 的结果。

  6. 建立图连接:

    1. output 为给定 input 复制 MLOperand 的结果。

    2. operator 为 "hardSigmoid" 运算的一个运算符,给定 options

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 input

    5. operator输出设置为 output

  7. 返回 output

此运算的行为可以按如下方式使用其他运算进行通用模拟, 尽管用户代理通常具有更高效的实现。在底层平台 不直接支持某项运算的情况下,可以使用此分解作为 模板来指导实现。
function hardSigmoid(builder, input, options) {
  return builder.max(
    builder.min(
      builder.add(
        builder.mul(builder.constant(input.dataType, options.alpha), input),
        builder.constant(input.dataType, options.beta)),
      builder.constant(input.dataType, 1)),
    builder.constant(input.dataType, 0));
}

8.9.28. hardSwish

对输入张量逐元素计算由 [MobileNetV3] 引入的 非线性函数 y = x * max(0, min(6, (x + 3))) / 6
partial interface MLGraphBuilder {
  MLOperand hardSwish(MLOperand input, optional MLOperatorOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits hardSwish;
};
参数:

返回:

hardSwish() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input "float32", "float16" "float32", "float16" N 0 到 5
输出 input 相同 "float32", "float16" input 相同 0 到 5

MLOpSupportLimitshardSwish() 具有以下成员:

hardSwish, 类型为 MLSingleInputSupportLimits

运算符 hardSwish() 的支持限制。

hardSwish(input, options) 方法的步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 验证操作数,给定 thisinput 返回 false,则抛出一个 TypeError

  3. 如果 inputdataType 不是其允许的数据类型 之一(根据此表),则抛出一个 TypeError

  4. 建立图连接:

    1. output 为给定 input 复制 MLOperand 的结果。

    2. operator 为 "hardSwish" 运算的一个运算符,给定 options

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 input

    5. operator输出设置为 output

  5. 返回 output

此运算的行为可以按如下方式使用其他运算进行通用模拟, 尽管用户代理通常具有更高效的实现。在底层平台 不直接支持某项运算的情况下,可以使用此分解作为 模板来指导实现。
function hardSwish(builder, input, options) {
  return builder.div(
    builder.mul(
      input,
      builder.max(
        builder.constant(input.dataType, 0),
        builder.min(
          builder.constant(input.dataType, 6),
          builder.add(input, builder.constant(input.dataType, 3))))),
    builder.constant(input.dataType, 6));
}

8.9.29. instanceNormalization

使用[Instance-Normalization]对输入进行归一化。 与 batchNormalization() 不同,后者在模型训练期间使用的均值和方差值是在批次 维度中的所有样本上计算的,而实例归一化中使用的均值和方差值则 针对批次中每个独立样本的每个输入特征即时计算。
dictionary MLInstanceNormalizationOptions : MLOperatorOptions {
  MLOperand scale;
  MLOperand bias;
  double epsilon = 1e-5;
  MLInputOperandLayout layout = "nchw";
};

partial interface MLGraphBuilder {
  MLOperand instanceNormalization(
    MLOperand input,
    optional MLInstanceNormalizationOptions options = {});
};

dictionary MLNormalizationSupportLimits {
  MLTensorLimits input;
  MLTensorLimits scale;
  MLTensorLimits bias;
  MLTensorLimits output;
};

partial dictionary MLOpSupportLimits {
  MLNormalizationSupportLimits instanceNormalization;
};

MLInstanceNormalizationOptions 具有以下成员:

scale, 类型为 MLOperand

缩放值的一维张量,其大小等于 通道数,即输入特征维度的大小。例如,对于一个 input 张量,其布局为 "nchw" 时,该大小等于 inputshape[1]。

bias, 类型为 MLOperand

偏置值的一维张量,其大小等于 输入特征维度的大小。例如,对于一个 input 张量,其布局为 "nchw" 时,该大小等于 inputshape[1]。

epsilon, 类型为 double,默认为 1e-5

用于防止除以零导致计算错误的一个小值。

layout, 类型为 MLInputOperandLayout,默认为 "nchw"

输入的布局格式。

参数:

返回:一个 MLOperand。 与 input 形状相同的实例归一化四维张量。

instanceNormalization() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input "float32", "float16" "float32", "float16" 4 4
scale input 相同 "float32", "float16" 1 1
bias input 相同 "float32", "float16" 1 1
输出 input 相同 "float32", "float16" 4 4

MLNormalizationSupportLimits 具有以下成员:

input, 类型为 MLTensorLimits

MLTensorLimits 用于 input 操作数。

scale, 类型为 MLTensorLimits

MLTensorLimits 用于 scale 操作数。

bias, 类型为 MLTensorLimits

MLTensorLimits 用于 bias 操作数。

output, 类型为 MLTensorLimits

MLTensorLimits 用于 output 操作数。

MLOpSupportLimitsinstanceNormalization() 具有以下成员:

instanceNormalization, 类型为 MLNormalizationSupportLimits

运算符 instanceNormalization() 的支持限制。

instanceNormalization(input, options) 方法的步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 验证操作数,给定 this 以及 inputoptions.scale (如果它存在)以及 options.bias (如果它存在)中的任一个返回 false,则抛出一个 TypeError

  3. 如果 inputdataType 不是其允许的数据类型 之一(根据此表),则抛出一个 TypeError

  4. 如果 input不是其允许的秩,则抛出一个 TypeError

  5. options.epsilon 设置为转换 options.epsiloninputdataType 的结果。

  6. 如果 options.layout"nchw", 则令 axis 为 1,否则为 3。

  7. 如果 options.scale 存在,则:

    1. 如果其 dataType 不是其允许的数据类型之一(根据此表),则抛出一个 TypeError

    2. 如果其shape等于 « inputshape[axis] »,则抛出一个 TypeError

  8. 如果 options.bias 存在,则:

    1. 如果其 dataType 不是其允许的数据类型之一(根据此表),则抛出一个 TypeError

    2. 如果其shape等于 « inputshape[axis] »,则抛出一个 TypeError

  9. 建立图连接:

    1. output 为给定 input 复制 MLOperand 的结果。

    2. operator 为 "instanceNormalization" 运算的一个运算符, 给定 options

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 input

    5. 如果 options.scale 存在,则将其添加到 operator输入中。

    6. 如果 options.bias 存在,则将其添加到 operator输入中。

    7. operator输出设置为 output

  10. 返回 output

当输入张量是 "nchw" 布局的四维张量时,此运算的行为可以按如下方式使用其他运算进行通用模拟,尽管用户 代理通常具有更高效的实现。在底层平台 不直接支持某项运算的情况下,可以使用此分解作为模板来指导 实现。
function instanceNormalization(builder, input, options) {
  // 均值和方差值的归约发生在输入的空间
  // 维度上,例如输入张量的轴 2 和 3。
  const reduceOptions = {axes: [2, 3], keepDimensions: true};
  const mean = builder.reduceMean(input, reduceOptions);
  const variance = builder.reduceMean(
    builder.pow(builder.sub(input, mean), builder.constant(input.dataType, 2)),
    reduceOptions);

  // 缩放值和偏置值按每个输入特征应用
  // 例如输入张量的轴 1。
  const shape = [1, input.shape[1], 1, 1];
  return builder.add(
    builder.mul(
      builder.reshape(options.scale, shape),
      builder.div(
        builder.sub(input, mean),
        builder.sqrt(builder.add(variance, options.epsilon)))),
    builder.reshape(options.bias, shape));
}

8.9.30. layerNormalization

使用[Layer-Normalization]对输入进行归一化。与 batchNormalization() 不同,后者在模型 训练期间跨批次维度中的所有样本计算均值和方差值;也不同于 instanceNormalization(), 后者针对批次中每个独立样本的每个输入特征即时计算均值和方差值, 层归一化的均值和方差值则在批次中每个独立样本的所有输入 特征上即时计算。
dictionary MLLayerNormalizationOptions : MLOperatorOptions {
  MLOperand scale;
  MLOperand bias;
  sequence<[EnforceRange] unsigned long> axes;
  double epsilon = 1e-5;
};

partial interface MLGraphBuilder {
  MLOperand layerNormalization(MLOperand input,
                               optional MLLayerNormalizationOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLNormalizationSupportLimits layerNormalization;
};

MLLayerNormalizationOptions 具有以下成员:

scale, 类型为 MLOperand

缩放值的 N 维张量,其形状由 axes 成员决定,其中 axes 中的每个值表示具有缩放值的输入张量维度。例如,对于 axes 值 [1,2,3],该张量的形状是输入 第 1、2 和 3 维对应大小的列表。当此成员不存在时,缩放值假定为 1。

bias, 类型为 MLOperand

偏置值的 N 维张量,其形状由 axes 成员决定,其中 axes 中的每个值表示具有偏置值的输入张量维度。例如,对于 axes 值 [1,2,3],该张量的形状是输入 第 1、2 和 3 维对应大小的列表。当此成员不存在时,偏置值假定为 0。

axes, 类型为 sequence<[EnforceRange] unsigned long>

要进行归约的输入维度索引。当此成员不存在时,将其视为 给出了除第一维之外的所有维度(例如,对于四维输入张量,axes = [1,2,3])。也就是说,均值和方差值的归约是在每个独立批次的所有 输入特征上计算的。如果为空,则不归约任何维度。

epsilon, 类型为 double,默认为 1e-5

用于防止除以零导致计算错误的一个小值。

参数:

返回:一个 MLOperand。 与 input 形状相同的层归一化 N 维张量。

layerNormalization() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input "float32", "float16" "float32", "float16" N 0 到 5
scale input 相同 "float32", "float16" N 0 到 5
bias input 相同 "float32", "float16" N 0 到 5
输出 input 相同 "float32", "float16" input 相同 0 到 5

MLOpSupportLimitslayerNormalization() 具有以下成员:

layerNormalization, 类型为 MLNormalizationSupportLimits

运算符 layerNormalization() 的支持限制。

layerNormalization(input, options) 方法的步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 验证操作数,给定 this 以及 inputoptions.scale (如果它存在)以及 options.bias (如果它存在)中的任一个返回 false,则抛出一个 TypeError

  3. 如果 inputdataType 不是其允许的数据类型 之一(根据此表),则抛出一个 TypeError

  4. 如果 options.axes存在,则将 options.axes 设置为一个新的列表;如果 input 大于 1,则该列表为从 1 到 input(不含上界)的范围,否则为空列表

  5. 否则,如果 options.axes 包含重复值,或者其任何不在 从 0 到 input(不含上界)的范围内,则抛出一个 TypeError

  6. options.epsilon 设置为转换 options.epsiloninputdataType 的结果。

  7. 如果 options.scale 存在,则:

    1. 如果其 dataType 不是其允许的数据类型之一(根据此表),则抛出一个 TypeError

    2. 如果其不等于 options.axes大小,则抛出 一个 TypeError

  8. 如果 options.bias 存在,则:

    1. 如果其 dataType 不是其允许的数据类型之一(根据此表),则抛出一个 TypeError

    2. 如果其不等于 options.axes大小,则抛出 一个 TypeError

  9. 对于每一个位于从 0 到 options.axes大小(不含上界)的范围内的 index

    1. axisoptions.axes[index]。

    2. 如果 axis 大于或等于 input,则抛出一个 TypeError

    3. sizeinputshape[axis]。

    4. 如果 options.scale 存在,则:

      1. 如果其shape[index] 不等于 size,则抛出一个 TypeError

    5. 如果 options.bias 存在,则:

      1. 如果其shape[index] 不等于 size,则抛出一个 TypeError

  10. 建立图连接:

    1. output 为给定 input 复制 MLOperand 的结果。

    2. operator 为 "layerNormalization" 运算的一个运算符,给定 options

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 input

    5. 如果 options.scale 存在,则将其添加到 operator输入中。

    6. 如果 options.bias 存在,则将其添加到 operator输入中。

    7. operator输出设置为 output

  11. 返回 output

当 axes 参数设置为 [1,2,3] 时,此运算的行为可以 按如下方式使用其他运算进行通用模拟,尽管用户代理通常具有更高效的 实现。在底层平台不直接支持某项运算的情况下,此 分解可以作为指导实现的模板。
function layerNormalization(builder, input, options) {
  // 均值和方差值的归约发生在空间
  // 维度上,并跨输入的所有输入特征(即所有通道)
  // 张量。
  const reduceOptions = {axes: [1, 2, 3], keepDimensions: true};
  const mean = builder.reduceMean(input, reduceOptions);
  const variance = builder.reduceMean(
    builder.pow(builder.sub(input, mean), builder.constant(input.dataType, 2)),
    reduceOptions);

  // 缩放张量和偏置张量的形状由 axes 参数中的值
  // 所指定的输入形状决定(即 [1,2,3])。
  return builder.add(
    builder.mul(
      options.scale,
      builder.div(
        builder.sub(input, mean),
        builder.sqrt(builder.add(variance, options.epsilon)))),
    options.bias);
}

8.9.31. leakyRelu

对输入张量逐元素计算泄漏版 线性整流函数。计算遵循表达式 max(0, x) + alpha * min(0, x)
dictionary MLLeakyReluOptions : MLOperatorOptions {
  double alpha = 0.01;
};

partial interface MLGraphBuilder {
  MLOperand leakyRelu(MLOperand input, optional MLLeakyReluOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits leakyRelu;
};

MLLeakyReluOptions 具有以下成员:

alpha, 类型为 double,默认为 0.01

一个标量乘数。

参数:

返回:

leakyRelu() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input "float32", "float16" "float32", "float16" N 0 到 5
输出 input 相同 "float32", "float16" input 相同 0 到 5

MLOpSupportLimitsleakyRelu() 具有以下成员:

leakyRelu, 类型为 MLSingleInputSupportLimits

运算符 leakyRelu() 的支持限制。

leakyRelu(input, options) 方法的步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 验证操作数,给定 thisinput 返回 false,则抛出一个 TypeError

  3. 如果 inputdataType 不是其允许的数据类型 之一(根据此表),则抛出一个 TypeError

  4. options.alpha 设置为转换 options.alphainputdataType 的结果。

  5. 建立图连接:

    1. output 为给定 input 复制 MLOperand 的结果。

    2. operator 为 "leakyRelu" 运算的一个运算符,给定 options

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 input

    5. operator输出设置为 output

  6. 返回 output

此运算的行为可以按如下方式使用其他运算进行通用模拟, 尽管用户代理通常具有更高效的实现。在底层平台 不直接支持某项运算的情况下,可以使用此分解作为 模板来指导实现。
function leakyRelu(builder, input, options) {
  return builder.add(
    builder.max(builder.constant(input.dataType, 0), input),
    builder.mul(
      builder.constant(input.dataType, options.alpha),
      builder.min(builder.constant(input.dataType, 0), input)));
}

8.9.32. linear

在输入张量上计算线性函数 y = alpha * x + beta
dictionary MLLinearOptions : MLOperatorOptions {
  double alpha = 1;
  double beta = 0;
};

partial interface MLGraphBuilder {
  MLOperand linear(MLOperand input, optional MLLinearOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits linear;
};

MLLinearOptions 具有以下成员:

alpha, 类型为 double,默认为 1

一个标量乘数。

beta, 类型为 double,默认为 0

一个标量加数。

参数:

返回:

linear() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input "float32", "float16" "float32", "float16" N 0 到 5
输出 input 相同 "float32", "float16" input 相同 0 到 5

MLOpSupportLimitslinear() 具有以下成员:

linear, 类型为 MLSingleInputSupportLimits

运算符 linear() 的支持限制。

linear(input, options) 方法的步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 验证操作数,给定 thisinput 返回 false,则抛出一个 TypeError

  3. 如果 inputdataType 不是其允许的数据类型 之一(根据此表),则抛出一个 TypeError

  4. options.alpha 设置为转换 options.alphainputdataType 的结果。

  5. options.beta 设置为转换 options.betainputdataType 的结果。

  6. 建立图连接:

    1. output 为给定 input 复制 MLOperand 的结果。

    2. operator 为 "linear" 运算的一个运算符,给定 options

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 input

    5. operator输出设置为 output

  7. 返回 output

此运算的行为可以按如下方式使用其他运算进行通用模拟, 尽管用户代理通常具有更高效的实现。在底层平台 不直接支持某项运算的情况下,可以使用此分解作为 模板来指导实现。
function linear(builder, input, options) {
  return builder.add(
    builder.mul(input, builder.constant(input.dataType, options.alpha)),
    builder.constant(input.dataType, options.beta));
}

8.9.33. lstm

长短期记忆 [LSTM] 循环网络使用输入门、输出门、遗忘门和单元门来计算输出状态,该状态会在网络的 时间序列中传递到输出。
enum MLLstmWeightLayout {
  "iofg", // 输入-输出-遗忘-单元门的顺序
  "ifgo"  // 输入-遗忘-单元-输出门的顺序
};

dictionary MLLstmOptions : MLOperatorOptions {
  MLOperand bias;
  MLOperand recurrentBias;
  MLOperand peepholeWeight;
  MLOperand initialHiddenState;
  MLOperand initialCellState;
  boolean returnSequence = false;
  MLRecurrentNetworkDirection direction = "forward";
  MLLstmWeightLayout layout = "iofg";
  sequence<MLRecurrentNetworkActivation> activations;
};

partial interface MLGraphBuilder {
  sequence<MLOperand> lstm(MLOperand input,
                           MLOperand weight,
                           MLOperand recurrentWeight,
                           [EnforceRange] unsigned long steps,
                           [EnforceRange] unsigned long hiddenSize,
                           optional MLLstmOptions options = {});
};

dictionary MLLstmSupportLimits {
  MLTensorLimits input;
  MLTensorLimits weight;
  MLTensorLimits recurrentWeight;
  MLTensorLimits bias;
  MLTensorLimits recurrentBias;
  MLTensorLimits peepholeWeight;
  MLTensorLimits initialHiddenState;
  MLTensorLimits initialCellState;
  MLTensorLimits output0;
  MLTensorLimits output1;
  MLTensorLimits output2;
};

partial dictionary MLOpSupportLimits {
  MLLstmSupportLimits lstm;
};

MLLstmOptions 具有以下成员:

bias, 类型为 MLOperand

形状为 [numDirections, 4 * hiddenSize] 的二维输入偏置张量。该张量形状第二维中的偏置 向量顺序根据 layout 指定。

recurrentBias, 类型为 MLOperand

形状为 [numDirections, 4 * hiddenSize] 的二维循环偏置张量。该张量形状第一维中的 偏置向量顺序根据 layout 指定。

peepholeWeight, 类型为 MLOperand

形状为 [numDirections, 3 * hiddenSize] 的窥孔二维权重张量。权重向量的打包 顺序分别对应 input (i)output (o)forget (f) 门。

initialHiddenState, 类型为 MLOperand

形状为 [numDirections, batchSize, hiddenSize] 的三维初始隐藏状态张量。未 指定时,实现必须使用填充为零的张量。

initialCellState, 类型为 MLOperand

形状为 [numDirections, batchSize, hiddenSize] 的三维初始隐藏状态张量。未 指定时,实现必须使用填充为零的张量。

returnSequence, 类型为 boolean,默认为 false

指示除最后一个时间步的输出外,是否还返回包含每个时间步所有输出的整个 序列。

direction, 类型为 MLRecurrentNetworkDirection,默认为 "forward"

输入序列的处理方向。当设置为 "both" 时,权重和偏置张量形状的第一维大小必须为 2,并且输入会 在两个方向上处理。

layout, 类型为 MLLstmWeightLayout,默认为 "iofg"

LSTM 内部门的权重和偏置向量顺序,具体为 input (i)output (o)forget (f)cell (g) 门,如权重和偏置张量形状的第一维所示。

activations, 类型为 sequence<MLRecurrentNetworkActivation>

三个激活函数的列表,第一个用于 input (i)forget (f)output (o) 门,第二个 用于 cell (g) 门,最后一个用于在与输出门的结果结合以形成输出隐藏状态之前 过滤输出单元状态。未 指定时,分别默认为由 "sigmoid""tanh""tanh" 函数组成的序列。

参数:

返回:sequence<MLOperand>。 第一个元素是形状为 [numDirections, batchSize, hiddenSize] 的三维张量,即网络最后一个时间步的输出 隐藏状态。第二个元素是形状为 [numDirections, batchSize, hiddenSize] 的三维张量,即网络最后一个时间步的输出单元状态。 此外,如果 returnSequence 设置为 true,则第三个元素是形状为 [steps, numDirections, batchSize, hiddenSize] 的四维输出张量,其中包含时间序列中每个时间步的所有输出。

lstm() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input "float32", "float16" "float32", "float16" 3 3
weight input 相同 "float32", "float16" 3 3
recurrentWeight input 相同 "float32", "float16" 3 3
bias input 相同 "float32", "float16" 2 2
recurrentBias input 相同 "float32", "float16" 2 2
peepholeWeight input 相同 "float32", "float16" 2 2
initialHiddenState input 相同 "float32", "float16" 3 3
initialCellState input 相同 "float32", "float16" 3 3
outputs[0] input 相同 "float32", "float16" 3 3
outputs[1] input 相同 "float32", "float16" 3 3
outputs[2] 如果 returnSequence 为 true input 相同 "float32", "float16" 4 4

MLLstmSupportLimits 具有以下成员:

input, 类型为 MLTensorLimits

MLTensorLimits 用于 input 操作数。

weight, 类型为 MLTensorLimits

MLTensorLimits 用于 weight 操作数。

recurrentWeight, 类型为 MLTensorLimits

MLTensorLimits 用于 recurrentWeight 操作数。

bias, 类型为 MLTensorLimits

MLTensorLimits 用于 bias 操作数。

recurrentBias, 类型为 MLTensorLimits

MLTensorLimits 用于 recurrentBias 操作数。

peepholeWeight, 类型为 MLTensorLimits

MLTensorLimits 用于 peepholeWeight 操作数。

initialHiddenState, 类型为 MLTensorLimits

MLTensorLimits 用于 initialHiddenState 操作数。

initialCellState, 类型为 MLTensorLimits

MLTensorLimits 用于 initialCellState 操作数。

output0, 类型为 MLTensorLimits

MLTensorLimits 用于所有输出操作数[0]。

output1, 类型为 MLTensorLimits

MLTensorLimits 用于所有输出操作数[1]。

output2, 类型为 MLTensorLimits

MLTensorLimits 用于所有输出操作数[2]。

MLOpSupportLimitslstm() 具有以下成员:

lstm, 类型为 MLLstmSupportLimits

运算符 lstm() 的支持限制。

lstm(input, weight, recurrentWeight, steps, hiddenSize, options) 方法的步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 验证操作数,给定 this 以及 inputweightrecurrentWeightoptions.bias (如果它存在)、options.recurrentBias (如果它存在)、options.peepholeWeight (如果它存在)、options.initialHiddenState (如果它存在)以及 options.initialCellState (如果它存在)中的任一个返回 false,则抛出一个 TypeError

  3. 如果 options.direction"both", 则令 numDirections 为 2,否则为 1。

  4. 如果 inputweightrecurrentWeight 中任一个的 dataType 不是其允许的数据类型 之一(根据此表),则抛出一个 TypeError

  5. 如果 inputweightrecurrentWeight 中任一个的不是其允许的秩, 则抛出一个 TypeError

  6. 如果 steps 为 0,则抛出一个 TypeError

  7. 如果 inputshape[0] 不等于 steps,则抛出一个 TypeError

  8. batchSizeinputshape[1]。

  9. inputSizeinputshape[2]。

  10. 如果 weightshape等于 « numDirections, 4 * hiddenSize, inputSize »,则抛出一个 TypeError

  11. 如果 recurrentWeightshape等于 « numDirections, 4 * hiddenSize, hiddenSize »,则抛出一个 TypeError

  12. 如果 hiddenSize * 8 不是有效维度,则抛出一个 TypeError

    为什么是 hiddenSize * 8? 某些底层平台在单个偏置张量上运算,该张量由 biasrecurrentBias 拼接而成。 因此,4 * hiddenSize + 4 * hiddenSize 也需要是一个有效 维度
  13. 如果 options.bias 存在,则:

    1. 如果其 dataType 不是其允许的数据类型之一(根据此表),则抛出 一个 TypeError

    2. 如果其shape等于 « numDirections, 4 * hiddenSize »,则抛出 一个 TypeError

  14. 如果 options.recurrentBias 存在,则:

    1. 如果其 dataType 不是其允许的数据类型之一(根据此表),则抛出 一个 TypeError

    2. 如果其shape等于 « numDirections, 4 * hiddenSize »,则抛出 一个 TypeError

  15. 如果 options.peepholeWeight 存在,则:

    1. 如果其 dataType 不是其允许的数据类型之一(根据此表),则抛出 一个 TypeError

    2. 如果其shape等于 « numDirections, 3 * hiddenSize »,则抛出 一个 TypeError

  16. 如果 options.initialHiddenState 存在,则:

    1. 如果其 dataType 不是其允许的数据类型之一(根据此表),则抛出 一个 TypeError

    2. 如果其shape等于 « numDirections, batchSize, hiddenSize »,则抛出一个 TypeError

  17. 如果 options.initialCellState 存在,则:

    1. 如果其 dataType 不是其允许的数据类型之一(根据此表),则抛出 一个 TypeError

    2. 如果其shape等于 « numDirections, batchSize, hiddenSize »,则抛出一个 TypeError

  18. 如果 options.activations 存在,则:

    1. 如果其大小不是 3,则抛出 一个 TypeError

    2. activationsoptions.activations 的一个克隆

  19. 否则:

    1. activations 为 « "sigmoid", "tanh", "tanh" »。

  20. 计算输出形状:

    1. desc创建 MLOperandDescriptor 的结果,给定 inputdataType 和 « numDirections, batchSize, hiddenSize »。

    2. 如果 options.returnSequence 为 true,则:

      1. desc2创建 MLOperandDescriptor的结果,给定 inputdataType 和 « steps, numDirections, batchSize, hiddenSize »。

  21. 建立图连接:

    1. operator 为 "lstm" 运算的一个运算符,给定 weightrecurrentWeightstepshiddenSizeoptions

    2. output0创建 MLOperand的结果,给定 thisdesc

    3. output1创建 MLOperand的结果,给定 thisdesc

    4. 如果 options.returnSequence 为 true,则:

      1. output2创建 MLOperand的结果,给定 thisdesc2

      2. output列表 « output0, output1, output2 »。

      3. output0.[[operator]]output1.[[operator]]output2.[[operator]] 设置为 operator

    5. 否则:

      1. output列表 « output0, output1 »。

      2. output0.[[operator]]output1.[[operator]] 设置为 operator

    6. operator输入设置为 inputweightrecurrentWeight

    7. 如果 options.bias 存在,则将其添加到 operator输入中。

    8. 如果 options.recurrentBias 存在,则将其添加到 operator输入中。

    9. 如果 options.peepholeWeight 存在,则将其添加到 operator输入中。

    10. 如果 options.initialHiddenState 存在,则将其添加到 operator输入中。

    11. 如果 options.initialCellState 存在,则将其添加到 operator输入中。

    12. operator激活函数设置为 activations 的一个克隆

    13. operator输出设置为 output

  22. 返回 output

使用 squeeze() 辅助函数,此运算的行为可以 按如下方式使用其他运算进行通用模拟,尽管用户代理通常 具有更高效的实现。在底层平台不直接 支持某项运算的情况下,可以使用此分解作为模板来指导实现。
function lstm(
  builder, input, weight, recurrentWeight, steps, hiddenSize, options) {
  const batchSize = input.shape[1];
  const inputSize = input.shape[2];
  const direction = options.direction || 'forward';
  const numDirections = (direction == 'both' ? 2 : 1);
  let hiddenState = options.initialHiddenState;
  let cellState = options.initialCellState;

  if (!hiddenState) {
    const desc = {
      dataType: 'float32',
      shape: [numDirections, batchSize, hiddenSize]
    };
    const totalSize = numDirections * batchSize * hiddenSize;
    hiddenState = builder.constant(desc, new Float32Array(totalSize).fill(0));
  }

  if (!cellState) {
    const desc = {
      dataType: 'float32',
      shape: [numDirections, batchSize, hiddenSize]
    };
    const totalSize = numDirections * batchSize * hiddenSize;
    cellState = builder.constant(desc, new Float32Array(totalSize).fill(0));
  }

  let currentWeight = [];
  let currentRecurrentWeight = [];
  let currentBias = [];
  let currentRecurrentBias = [];
  let currentPeepholeWeight = [];
  let forwardSequence = null;
  let backwardSequence = null;
  let outputHidden = null;
  let outputCell = null;

  for (let dir = 0; dir < numDirections; ++dir) {
    currentWeight.push(squeeze(
      builder,
      builder.slice(weight, [dir, 0, 0], [1, 4 * hiddenSize, inputSize])));
    currentRecurrentWeight.push(squeeze(
      builder,
      builder.slice(
        recurrentWeight, [dir, 0, 0], [1, 4 * hiddenSize, hiddenSize])));
    currentBias.push(
      options.bias ?
        (squeeze(
          builder,
          builder.slice(options.bias, [dir, 0], [1, 4 * hiddenSize]))) :
        null);
    currentRecurrentBias.push(
      options.recurrentBias ?
        (squeeze(
          builder,
          builder.slice(
            options.recurrentBias, [dir, 0], [1, 4 * hiddenSize]))) :
        null);
    currentPeepholeWeight.push(
      options.peepholeWeight ?
        (squeeze(
          builder,
          builder.slice(
            options.peepholeWeight, [dir, 0], [1, 3 * hiddenSize]))) :
        null);

    let currentHidden = squeeze(
      builder,
      builder.slice(hiddenState, [dir, 0, 0], [1, batchSize, hiddenSize]), [0]);
    let currentCell = squeeze(
      builder,
      builder.slice(cellState, [dir, 0, 0], [1, batchSize, hiddenSize]), [0]);

    for (let step = 0; step < steps; ++step) {
      const slice =
        (dir == 1 || direction == 'backward' ? steps - step - 1 : step);
      const currentInput = squeeze(
        builder,
        builder.slice(input, [slice, 0, 0], [1, batchSize, inputSize]), [0]);

      [currentHidden, currentCell] = builder.lstmCell(
        currentInput,
        currentWeight[dir],
        currentRecurrentWeight[dir],
        currentHidden,
        currentCell,
        hiddenSize,
        {
          bias: currentBias[dir],
          recurrentBias: currentRecurrentBias[dir],
          peepholeWeight: currentPeepholeWeight[dir],
          layout: options.layout,
          activations: options.activations
        });

      if (options.returnSequence) {
        // 将二维的 currentHidden([batchSize, hiddenSize])扩展
        // 为四维([steps, numDirections, batchSize, hiddenSize])
        const expandedHiddenAs4D =
          builder.reshape(currentHidden, [1, 1, batchSize, hiddenSize]);

        if (direction == 'forward' || (dir == 0 && direction == 'both')) {
          forwardSequence = forwardSequence ?
            builder.concat([forwardSequence, expandedHiddenAs4D], 0) :
            expandedHiddenAs4D;
        } else if (
          direction == 'backward' || (dir == 1 && direction == 'both')) {
          backwardSequence = backwardSequence ?
            builder.concat([expandedHiddenAs4D, backwardSequence], 0) :
            expandedHiddenAs4D;
        }
      }
    }

    // 将二维的 currentHidden([batchSize, hiddenSize])扩展
    // 为三维([numDirections, batchSize, hiddenSize])
    const expandedHiddenAs3D =
      builder.reshape(currentHidden, [1, batchSize, hiddenSize]);
    outputHidden = outputHidden ?
      builder.concat([outputHidden, expandedHiddenAs3D], 0) :
      expandedHiddenAs3D;

    // 将二维的 currentCell([batchSize, hiddenSize])扩展
    // 为三维([numDirections, batchSize, hiddenSize])
    const expandedCellAs3D =
      builder.reshape(currentCell, [1, batchSize, hiddenSize]);
    outputCell = outputCell ?
      builder.concat([outputCell, expandedCellAs3D], 0) :
      expandedCellAs3D;
  }

  if (options.returnSequence) {
    let outputSequence = null;

    if (direction == 'forward') {
      outputSequence = forwardSequence;
    } else if (direction == 'backward') {
      outputSequence = backwardSequence;
    } else if (direction == 'both') {
      // 沿轴 1(numDirections 维度)拼接
      outputSequence = builder.concat([forwardSequence, backwardSequence], 1);
    }

    return [outputHidden, outputCell, outputSequence];
  } else {
    return [outputHidden, outputCell];
  }
}

8.9.34. lstmCell

长短期记忆 [LSTM] 循环网络的单个时间步,使用单元状态、输入门、输出门和遗忘 门来计算下一个时间步的单元状态和隐藏状态,该状态会在网络的 时间序列中传递到输出。
dictionary MLLstmCellOptions : MLOperatorOptions {
  MLOperand bias;
  MLOperand recurrentBias;
  MLOperand peepholeWeight;
  MLLstmWeightLayout layout = "iofg";
  sequence<MLRecurrentNetworkActivation> activations;
};

partial interface MLGraphBuilder {
  sequence<MLOperand> lstmCell(MLOperand input,
                               MLOperand weight,
                               MLOperand recurrentWeight,
                               MLOperand hiddenState,
                               MLOperand cellState,
                               [EnforceRange] unsigned long hiddenSize,
                               optional MLLstmCellOptions options = {});
};

dictionary MLLstmCellSupportLimits {
  MLTensorLimits input;
  MLTensorLimits weight;
  MLTensorLimits recurrentWeight;
  MLTensorLimits hiddenState;
  MLTensorLimits cellState;
  MLTensorLimits bias;
  MLTensorLimits recurrentBias;
  MLTensorLimits peepholeWeight;
  MLTensorLimits output0;
  MLTensorLimits output1;
};

partial dictionary MLOpSupportLimits {
  MLLstmCellSupportLimits lstmCell;
};

MLLstmCellOptions 具有以下成员:

bias, 类型为 MLOperand

形状为 [4 * hiddenSize] 的一维输入偏置张量。张量形状 第一维中偏置向量的顺序根据 layout 指定。

recurrentBias, 类型为 MLOperand

形状为 [4 * hiddenSize] 的一维循环偏置张量。张量形状 第一维中偏置向量的顺序根据 layout 指定。

peepholeWeight, 类型为 MLOperand

形状为 [3 * hiddenSize] 的一维窥孔权重张量。权重向量的打包 顺序分别对应 input (i)output (o)forget (f) 门。

layout, 类型为 MLLstmWeightLayout,默认为 "iofg"

LSTM 内部门的权重和偏置向量的顺序,具体为 input (i)output (o)forget (f)cell (g) 门,如权重和偏置张量形状的第一维所示。

activations, 类型为 sequence<MLRecurrentNetworkActivation>

三个激活函数的列表,第一个用于 input (i)forget (f)output (o) 门,第二个 用于 cell (g) 门,最后一个用于过滤输出单元状态, 然后再将其与输出门的结果组合以形成输出隐藏状态。未 指定时,分别默认为由 "sigmoid""tanh""tanh" 函数组成的序列。

参数:

返回: sequence<MLOperand>。 第一个元素是循环网络当前时间步的输出隐藏状态。后 一个元素是输出单元状态。两个元素都是形状为 [batchSize, hiddenSize] 的二维张量。

lstmCell() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input "float32", "float16" "float32", "float16" 2 2
weight 与……相同 input "float32", "float16" 2 2
recurrentWeight 与……相同 input "float32", "float16" 2 2
hiddenState 与……相同 input "float32", "float16" 2 2
cellState 与……相同 input "float32", "float16" 2 2
bias 与……相同 input "float32", "float16" 1 1
recurrentBias 与……相同 input "float32", "float16" 1 1
peepholeWeight 与……相同 input "float32", "float16" 1 1
outputs[0] 与……相同 input "float32", "float16" 2 2
outputs[1] 与……相同 input "float32", "float16" 2 2

MLLstmCellSupportLimits 具有以下成员:

input, 类型为 MLTensorLimits

MLTensorLimits 用于 input 操作数。

weight, 类型为 MLTensorLimits

MLTensorLimits 用于 weight 操作数。

recurrentWeight, 类型为 MLTensorLimits

MLTensorLimits 用于 recurrentWeight 操作数。

hiddenState, 类型为 MLTensorLimits

MLTensorLimits 用于 hiddenState 操作数。

cellState, 类型为 MLTensorLimits

MLTensorLimits 用于 cellState 操作数。

bias, 类型为 MLTensorLimits

MLTensorLimits 用于 bias 操作数。

recurrentBias, 类型为 MLTensorLimits

MLTensorLimits 用于 recurrentBias 操作数。

peepholeWeight, 类型为 MLTensorLimits

MLTensorLimits 用于 peepholeWeight 操作数。

output0, 类型为 MLTensorLimits

MLTensorLimits 用于所有 output 操作数[0]。

output1, 类型为 MLTensorLimits

MLTensorLimits 用于所有 output 操作数[1]。

MLOpSupportLimitslstmCell() 具有以下成员:

lstmCell, 类型为 MLLstmCellSupportLimits

运算符 lstmCell() 的支持限制。

lstmCell(input, weight, recurrentWeight, hiddenState, cellState, hiddenSize, options) 方法的步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 验证操作数,给定 this 以及 inputweightrecurrentWeighthiddenStatecellStateoptions.bias (如果它存在)、options.recurrentBias (如果它存在)以及 options.peepholeWeight (如果它存在)中的任一个返回 false,则抛出一个 TypeError

  3. 如果 inputweightrecurrentWeighthiddenStatecellState 中任一个的dataType 不是其允许的数据类型之一(根据此表),则抛出一个 TypeError

  4. 如果 inputweightrecurrentWeighthiddenStatecellState 中任一个的不是其允许的秩,则抛出一个 TypeError

  5. batchSizeinputshape[0]。

  6. inputSizeinputshape[1]。

  7. 如果 weightshape等于 « 4 * hiddenSize, inputSize », 则抛出一个 TypeError

  8. 如果 recurrentWeightshape等于 « 4 * hiddenSize, hiddenSize », 则抛出一个 TypeError

  9. 如果 hiddenStateshape等于 « batchSize, hiddenSize »,则 抛出一个 TypeError

  10. 如果 cellStateshape等于 « batchSize, hiddenSize »,则 抛出一个 TypeError

  11. 如果 hiddenSize * 8 不是有效维度,则抛出一个 TypeError

    为什么是 hiddenSize * 8? 某些底层平台在单个偏置张量上运算,该张量由 biasrecurrentBias 拼接而成。 因此,4 * hiddenSize + 4 * hiddenSize 也需要是一个有效 维度
  12. 如果 options.bias 存在,则:

    1. 如果其 dataType 不是其允许的数据类型之一(根据此表),则抛出 一个 TypeError

    2. 如果其shape等于 « 4 * hiddenSize »,则抛出 一个 TypeError

  13. 如果 options.recurrentBias 存在,则:

    1. 如果其 dataType 不是其允许的数据类型之一(根据此表),则抛出 一个 TypeError

    2. 如果其shape等于 « 4 * hiddenSize »,则抛出 一个 TypeError

  14. 如果 options.peepholeWeight 存在,则:

    1. 如果其 dataType 不是其允许的数据类型之一(根据此表),则抛出 一个 TypeError

    2. 如果其shape等于 « 3 * hiddenSize »,则抛出 一个 TypeError

  15. 如果 options.activations 存在,则:

    1. 如果其大小不是 3,则抛出 一个 TypeError

    2. activations 为一个克隆options.activations

  16. 否则:

    1. activations 为 « "sigmoid", "tanh", "tanh" »。

  17. desc 为一个新的 MLOperandDescriptor

  18. desc.shape 设置为列表 « batchSize, hiddenSize »。

  19. desc.dataType 设置为 inputdataType

  20. 建立图连接:

    1. output0创建一个 MLOperand 的结果,给定 thisdesc

    2. output1创建一个 MLOperand 的结果,给定 thisdesc

    3. output列表 « output0, output1 »。

    4. operator 为一个运算符,用于 "lstmCell" 运算,给定 weightrecurrentWeighthiddenStatecellStatehiddenSizeoptions

    5. output0.[[operator]]output1.[[operator]] 设置为 operator

    6. operator输入设置为 inputweightrecurrentWeighthiddenStatecellState

    7. 如果 options.bias 存在,则将其添加到 operator输入中。

    8. 如果 options.recurrentBias 存在,则将其添加到 operator输入中。

    9. 如果 options.peepholeWeight 存在,则将其添加到 operator输入中。

    10. operator激活函数设置为一个克隆activations

    11. operator输出设置为 output

  21. 返回 output

当权重布局为默认的 "iofg" 布局,并且输入/遗忘/输出门以及 单元门/用于输出隐藏状态的单元状态过滤器的激活函数分别为 sigmoid()tanh() 时,此运算的行为可以按如下方式使用其他运算进行通用模拟,尽管 用户代理通常具有更高效的实现。在底层平台 不直接支持某项运算的情况下,可以使用此分解作为模板来指导 实现。
function lstmCell(
  builder,
  input,
  weight,
  recurrentWeight,
  hiddenState,
  cellState,
  hiddenSize,
  options) {
  const zero = builder.constant(input.dataType, 0);

  const inputSize = input.shape[1];

  // 输入门(i)
  let i = builder.sigmoid(builder.add(
    builder.mul(
      cellState,
      (options.peepholeWeight ?
         builder.slice(options.peepholeWeight, [0], [hiddenSize]) :
         zero)),
    builder.add(
      builder.add(
        (options.bias ? builder.slice(options.bias, [0], [hiddenSize]) : zero),
        (options.recurrentBias ?
           builder.slice(options.recurrentBias, [0], [hiddenSize]) :
           zero)),
      builder.add(
        builder.matmul(
          input,
          builder.transpose(
            builder.slice(weight, [0, 0], [hiddenSize, inputSize]))),
        builder.matmul(
          hiddenState,
          builder.transpose(builder.slice(
            recurrentWeight, [0, 0], [hiddenSize, hiddenSize])))))));

  // 遗忘门(f)
  let f = builder.sigmoid(builder.add(
    builder.mul(
      cellState,
      (options.peepholeWeight ?
         builder.slice(options.peepholeWeight, [2 * hiddenSize], [hiddenSize]) :
         zero)),
    builder.add(
      builder.add(
        (options.bias ?
           builder.slice(options.bias, [2 * hiddenSize], [hiddenSize]) : zero),
        (options.recurrentBias ?
           builder.slice(
             options.recurrentBias, [2 * hiddenSize], [hiddenSize]) :
           zero)),
      builder.add(
        builder.matmul(
          input,
          builder.transpose(builder.slice(
            weight, [2 * hiddenSize, 0], [hiddenSize, inputSize]))),
        builder.matmul(
          hiddenState,
          builder.transpose(builder.slice(
            recurrentWeight,
            [2 * hiddenSize, 0],
            [hiddenSize, hiddenSize])))))));

  // 单元门(g)
  let g = builder.tanh(builder.add(
    builder.add(
      (options.bias ?
         builder.slice(options.bias, [3 * hiddenSize], [hiddenSize]) :
         zero),
      (options.recurrentBias ?
         builder.slice(options.recurrentBias, [3 * hiddenSize], [hiddenSize]) :
         zero)),
    builder.add(
      builder.matmul(
        input,
        builder.transpose(
          builder.slice(weight, [3 * hiddenSize, 0], [hiddenSize, inputSize]))),
      builder.matmul(
        hiddenState,
        builder.transpose(builder.slice(
          recurrentWeight, [3 * hiddenSize, 0], [hiddenSize, hiddenSize]))))));

  // 输出门(o)
  let o = builder.sigmoid(builder.add(
    builder.mul(
      cellState,
      (options.peepholeWeight ?
         builder.slice(options.peepholeWeight, [hiddenSize], [hiddenSize]) :
         zero)),
    builder.add(
      builder.add(
        (options.bias ?
           builder.slice(options.bias, [hiddenSize], [hiddenSize]) :
           zero),
        (options.recurrentBias ?
           builder.slice(options.recurrentBias, [hiddenSize], [hiddenSize]) :
           zero)),
      builder.add(
        builder.matmul(
          input,
          builder.transpose(
            builder.slice(weight, [hiddenSize, 0], [hiddenSize, inputSize]))),
        builder.matmul(
          hiddenState,
          builder.transpose(builder.slice(
            recurrentWeight, [hiddenSize, 0], [hiddenSize, hiddenSize])))))));

  // 输出单元状态(ct)
  let ct = builder.add(builder.mul(f, cellState), builder.mul(i, g));

  // 输出隐藏状态(ht)
  let ht = builder.mul(o, builder.tanh(ct));

  return [ht, ct];
}

8.9.35. matmul

计算两个输入张量的矩阵乘积。
partial interface MLGraphBuilder {
  MLOperand matmul(MLOperand a, MLOperand b, optional MLOperatorOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLBinarySupportLimits matmul;
};
参数:

返回:一个 MLOperand。 包含两个输入张量矩阵 乘积的输出张量。

按如下方式计算两个输入张量的矩阵乘积:
matmul() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
a "float32", "float16" "float32", "float16" 2 到 N 2 到 5
b 与……相同 a "float32", "float16" 2 到 N 2 到 5
输出 与……相同 a "float32", "float16" 2 到 N 2 到 5

MLOpSupportLimitsmatmul() 具有以下成员:

matmul, 类型为 MLBinarySupportLimits

运算符 matmul() 的支持限制。

matmul(a, b, options) 方法的步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 验证操作数,给定 this 以及 ab 中任一个返回 false,则 抛出一个 TypeError

  3. 如果 ab 中任一个的dataType不是 其允许的数据类型之一(根据此表),则抛出一个 TypeError

  4. 计算输出形状:

    1. shapeA 为一个克隆ashape

    2. rankAa

    3. shapeB 为一个克隆bshape

    4. rankBb

    5. 如果 rankArankB 中任一个小于 2,则抛出一个 TypeError

    6. colsAshapeA[rankA - 1]。

    7. rowsAshapeA[rankA - 2]。

    8. colsBshapeB[rankB - 1]。

    9. rowsBshapeB[rankB - 2]。

    10. 如果 colsA 不等于 rowsB,则抛出 一个 TypeError

    11. batchShapeA 为一个克隆shapeA,其中空间维度(最后 2 项)已移除

    12. batchShapeB 为一个克隆shapeB,其中空间维度(最后 2 项)已移除

    13. outputShape双向广播 batchShapeAbatchShapeB 的结果。如果返回失败,则抛出一个 TypeError

    14. 附加 « rowsA, colsB » 到 outputShape

    15. desc创建 MLOperandDescriptor 的结果,给定 adataTypeoutputShape

  5. 建立图连接:

    1. output创建一个 MLOperand 的结果,给定 thisdesc

    2. operator 为一个运算符,用于 "matmul" 运算,给定 options

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 ab

    5. operator输出设置为 output

  6. 返回 output

8.9.36. pad

使用常量值或镜像值在边缘扩充张量。
enum MLPaddingMode {
  "constant",
  "edge",
  "reflection"
};

dictionary MLPadOptions : MLOperatorOptions {
  MLPaddingMode mode = "constant";
  MLNumber value = 0;
};

partial interface MLGraphBuilder {
  MLOperand pad(MLOperand input,
                sequence<[EnforceRange] unsigned long> beginningPadding,
                sequence<[EnforceRange] unsigned long> endingPadding,
                optional MLPadOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits pad;
};

MLPadOptions 具有以下成员:

mode, 类型为 MLPaddingMode,默认为 "constant"

填充张量的不同方式。

value, 类型为 MLNumber,默认为 0

mode 设置为 "constant" 时的填充值。

参数:

返回:一个 MLOperand。 填充后的输出张量。输出张量的每个维度可按如下方式计算:

output size = beginning padding + input size + ending padding

pad() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input 任意 "float32", "float16" N 0 到 5
输出 与……相同 input "float32", "float16" 与……相同 input 0 到 5

MLOpSupportLimitspad() 具有以下成员:

pad, 类型为 MLSingleInputSupportLimits

运算符 pad() 的支持限制。

pad(input, beginningPadding, endingPadding, options) 方法的步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 验证操作数,给定 thisinput 返回 false,则抛出一个 TypeError

  3. 如果 beginningPadding大小endingPadding大小并非 都等于 input,则抛出一个 TypeError

  4. descinput.[[descriptor]] 的副本。

  5. outputShapeinputshape 的副本。

  6. 对于每一个 index,位于范围 0 到 outputShape, 不含上界:

    1. 根据 options.mode 进行切换:

      "constant"

      不执行任何操作。

      "edge"

      不执行任何操作。

      "reflection"
      1. 如果 beginningPadding[index] 大于或等于 outputShape[index],则抛出一个 TypeError

      2. 如果 endingPadding[index] 大于或等于 outputShape[index],则抛出一个 TypeError

    2. outputShape[index] 加上 beginningPadding[index] 的值。

    3. outputShape[index] 加上 endingPadding[index] 的值。

  7. 如果 outputShape 中的任何不是有效维度,则抛出一个 TypeError

  8. options.value 设置为转换 options.valueinputdataType 的结果。

  9. desc.shape 设置为 outputShape

  10. 建立图连接:

    1. output创建一个 MLOperand 的结果,给定 thisdesc

    2. operator 为一个运算符,用于 "padding" 运算,给定 beginningPaddingendingPaddingoptions

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 input

    5. operator输出设置为 output

  11. 返回 output

常量、边缘和反射填充的示例:
// 输入:[[1,2,3], [4,5,6]]
const input = builder.constant(
  {dataType: 'float32', shape: [2, 3]}, new Float32Array([1, 2, 3, 4, 5, 6]));

const beginningPadding = [1, 2];
const endingPadding = [1, 2];

// "constant" 填充:
//    [[0,0,0,0,0,0,0],
//     [0,0,1,2,3,0,0],
//     [0,0,4,5,6,0,0],
//     [0,0,0,0,0,0,0]]
builder.pad(input, beginningPadding, endingPadding);

// "edge" 填充:
//    [[1,1,1,2,3,3,3],
//     [1,1,1,2,3,3,3],
//     [4,4,4,5,6,6,6],
//     [4,4,4,5,6,6,6]]
builder.pad(input, beginningPadding, endingPadding, {mode: 'edge'});

// "reflection" 填充:
//    [[6,5,4,5,6,5,4],
//     [3,2,1,2,3,2,1],
//     [6,5,4,5,6,5,4],
//     [3,2,1,2,3,2,1]]
builder.pad(input, beginningPadding, endingPadding, {mode: 'reflection'});

8.9.37. 池化运算

对输入张量上移动窗口内的所有元素计算池化运算。
enum MLRoundingType {
  "floor",
  "ceil"
};

dictionary MLPool2dOptions : MLOperatorOptions {
  sequence<[EnforceRange] unsigned long> windowDimensions;
  sequence<[EnforceRange] unsigned long> padding;
  sequence<[EnforceRange] unsigned long> strides;
  sequence<[EnforceRange] unsigned long> dilations;
  MLInputOperandLayout layout = "nchw";
  MLRoundingType outputShapeRounding = "floor";
  sequence<[EnforceRange] unsigned long> outputSizes;
};

partial interface MLGraphBuilder {
  MLOperand averagePool2d(MLOperand input, optional MLPool2dOptions options = {});
  MLOperand l2Pool2d(MLOperand input, optional MLPool2dOptions options = {});
  MLOperand maxPool2d(MLOperand input, optional MLPool2dOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits averagePool2d;
  MLSingleInputSupportLimits l2Pool2d;
  MLSingleInputSupportLimits maxPool2d;
};

MLPool2dOptions 具有以下成员:

windowDimensions, 类型为 sequence<[EnforceRange] unsigned long>

长度为 2 的列表:[windowHeight, windowWidth]。 指定滑动窗口的维度。 窗口维度的默认值是输入形状的高度和宽度维度。

padding, 类型为 sequence<[EnforceRange] unsigned long>

长度为 4 的列表:[beginningHeight, endingHeight, beginningWidth, endingWidth]。 指定添加到卷积输入每个空间 维度开头和末尾的额外行和列。 默认值为 [0,0,0,0]。

strides, 类型为 sequence<[EnforceRange] unsigned long>

长度为 2 的列表:[strideHeight, strideWidth]。 指定卷积输入每个空间维度的滑动窗口步幅。 默认值为 [1,1]。

dilations, 类型为 sequence<[EnforceRange] unsigned long>

长度为 2 的列表:[dilationHeight, dilationWidth]。指定应用于 卷积滤波器(内核)的每个空间维度的膨胀因子。 默认值为 [1,1]。

layout, 类型为 MLInputOperandLayout,默认为 "nchw"

按如下方式指定输入和输出张量的布局格式:

  • "nchw"

    • 输入张量:[batches, inputChannels, height, width]

    • 输出张量:[batches, outputChannels, height, width]

  • "nhwc"

    • 输入张量:[batches, height, width, inputChannels]

    • 输出张量:[batches, height, width, outputChannels]

outputShapeRounding, 类型为 MLRoundingType,默认为 "floor"

用于计算输出形状的舍入函数,具体取决于所需的是完整窗口还是部分窗口 结果。

outputSizes, 类型为 sequence<[EnforceRange] unsigned long>

长度为 2 的列表:[outputHeight, outputWidth] 指定输出张量两个空间维度的大小。 当显式指定输出大小时,outputShapeRounding 会被忽略。 如果未指定,则自动计算输出大小。

参数:

返回:一个 MLOperand。 包含归约 结果的输出四维张量。逻辑形状根据 layout 的值解释。 更具体地说,如果 outputShapeRounding"floor", 则输出张量单个维度的空间维度可按如下方式计算:

output size = floor(1 + (input size - filter size + beginning padding + ending padding) / stride)

或者,如果 outputShapeRounding"ceil"

output size = ceil(1 + (input size - filter size + beginning padding + ending padding) / stride)

averagePool2d()/l2Pool2d() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input "float32", "float16" "float32", "float16" 4 4
输出 与……相同 input "float32", "float16" 4 4
maxPool2d() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input 任意 "float32", "float16" 4 4
输出 与……相同 input "float32", "float16" 4 4

MLOpSupportLimits 对池化运算具有以下成员:

averagePool2d, 类型为 MLSingleInputSupportLimits

运算符 averagePool2d() 的支持限制。

l2Pool2d, 类型为 MLSingleInputSupportLimits

运算符 l2Pool2d() 的支持限制。

maxPool2d, 类型为 MLSingleInputSupportLimits

运算符 maxPool2d() 的支持限制。

全局池化运算,例如最大池化运算,是池化的一种变体,其中 窗口维度就是输入形状的空间维度(最后两个维度),如下所示。
// “全局”最大池化
builder.maxPool2d(input);
创建池化运算,给定字符串 opMLOperand inputMLPool2dOptions options 和可选的列表 allowedDataTypes,运行以下步骤:
  1. 断言op 是 "averagePool2d"、"l2Pool2d"、 "maxPool2d" 之一。

  2. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  3. 如果使用 验证操作数,给定 thisinput 返回 false,则抛出一个 TypeError

  4. 如果给定了 allowedDataTypes 且它不包含 inputdataType,则抛出一个 TypeError

  5. 如果 input不是 4,则抛出一个 TypeError

  6. 根据 options.layout 进行切换:

    "nchw"

    令 « batches, channels, inputHeight, inputWidth » 为 inputshape

    "nhwc"

    令 « batches, inputHeight, inputWidth, channels » 为 inputshape

  7. 如果 options.windowDimensions存在,则将 options.windowDimensions 设置为 « inputHeight, inputWidth »。

  8. 如果 options.windowDimensions大小不是 2,则抛出一个 TypeError

  9. 如果 options.windowDimensions 中任何等于 0,则抛出一个 TypeError

  10. 如果 options.outputSizes 存在,或者如果 options.padding存在,则将 options.padding 设置为列表 « 0, 0, 0, 0 »。

  11. 如果 options.padding大小不是 4,则抛出一个 TypeError

  12. 如果 options.strides存在,则将 options.strides 设置为列表 « 1, 1 »。

  13. 如果 options.strides大小不是 2,则抛出一个 TypeError

  14. 如果 options.strides 中任何为 0,则抛出一个 TypeError

  15. 如果 options.outputSizes 存在,则:

    1. 如果其大小不是 2,则抛出 一个 TypeError

    2. 如果其并非小于 options.strides 在相同维度(索引)处的, 则抛出一个 TypeError

  16. 如果 options.dilations存在,则将 options.dilations 设置为列表 « 1, 1 »。

  17. 如果 options.dilations大小不是 2,则抛出一个 TypeError

  18. 如果 options.dilations 中任何为 0,则抛出一个 TypeError

  19. descinput.[[descriptor]] 的副本。

  20. 计算输出形状:

    1. 令 « windowHeight, windowWidth » 为 options.windowDimensions

    2. 令 « calculatedOutputHeight, calculatedOutputWidth » 为 计算 conv2d 输出 大小的结果,给定 inputHeightinputWidthwindowHeightwindowWidthoptions.paddingoptions.stridesoptions.dilations

    3. 如果 options.outputSizes 存在,则:

      1. 令 « outputHeight, outputWidth » 为 options.outputSizes

      2. 如果既不是 outputHeight 等于 floor( calculatedOutputHeight ) 且 outputWidth 等于 floor( calculatedOutputWidth ),也不是 outputHeight 等于 ceil( calculatedOutputHeight ) 且 outputWidth 等于 ceil( calculatedOutputWidth ),则抛出一个 TypeError

    4. 否则:

      1. 令 « outputHeight, outputWidth » 为 « calculatedOutputHeight, calculatedOutputWidth »。

      2. 根据 options.outputShapeRounding 进行切换:

        "floor"
        1. outputWidth 设置为 floor(outputWidth)。

        2. outputHeight 设置为 floor(outputHeight)。

        "ceil"
        1. outputWidth 设置为 ceiling(outputWidth)。

        2. outputHeight 设置为 ceiling(outputHeight)。

    5. 如果 outputHeightoutputWidth 中任一个不是有效 维度,则抛出 一个 TypeError

    6. 根据 options.layout 进行切换:

      "nchw"

      outputShape 为 « batches, channels, outputHeight, outputWidth »。

      "nhwc"

      outputShape 为 « batches, outputHeight, outputWidth, channels »。

    7. desc.shape 设置为 outputShape

  21. 建立图连接:

    1. output创建一个 MLOperand 的结果,给定 thisdesc

    2. operator 为一个运算符,用于 op 运算,给定 options

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 input

    5. operator输出设置为 output

  22. 返回 output

支持以下池化算法。
averagePool2d(input, options) 方法的步骤为:
  1. output创建一个 池化运算的结果,给定 "averagePool2d"、inputoptions 和 « "float32", "float16" »。

    1. 如果这抛出错误,则重新抛出该错误。

  2. 返回 output

l2Pool2d(input, options) 方法的步骤为:
  1. output创建一个 池化运算的结果,给定 "l2Pool2d"、inputoptions 和 « "float32", "float16" »。

    1. 如果这抛出错误,则重新抛出该错误。

  2. 返回 output

maxPool2d(input, options) 方法的步骤为:
  1. output创建一个 池化运算的结果,给定 "maxPool2d"、inputoptions

    1. 如果这抛出错误,则重新抛出该错误。

  2. 返回 output

8.9.37.1. averagePool2d
计算特征图各个区域的平均值,并使用它创建池化后的特征图。有关更多详细信息,请参阅 § 8.9.37 池化运算
8.9.37.2. l2Pool2d
将 L2 范数函数应用于输入特征图的一个区域。L2 范数是 其元素平方和的平方根。有关更多详细信息,请参阅 § 8.9.37 池化运算
8.9.37.3. maxPool2d
计算特征图各个区域的最大值,并使用它创建池化后的特征图。有关更多详细信息,请参阅 § 8.9.37 池化运算

8.9.38. prelu

对输入张量逐元素计算参数化 线性整流函数(Parametric ReLU)。Parametric ReLU 是一种泄漏 ReLU,与使用像 0.01 这样的标量斜率不同,它将斜率(泄漏 系数)变成一个在此运算的模型训练阶段学习的参数。计算 遵循表达式 max(0, x) + slope * min(0, x)

该运算将广播 ,根据 [numpy-broadcasting-rule]。输入张量必须双向可广播。输出张量的 是输入张量的最大值。 对于输出张量的每个维度,其大小是输入张量沿该维度 大小的最大值。

partial interface MLGraphBuilder {
  MLOperand prelu(MLOperand input,
                  MLOperand slope,
                  optional MLOperatorOptions options = {});
};

dictionary MLPreluSupportLimits {
  MLTensorLimits input;
  MLTensorLimits slope;
  MLTensorLimits output;
};

partial dictionary MLOpSupportLimits {
  MLPreluSupportLimits prelu;
};
参数:

返回:

prelu() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input "float32", "float16", "int64", "int32", "int8" "float32", "float16" N 0 到 5
slope 与……相同 input "float32", "float16" N 0 到 5
输出 与……相同 input "float32", "float16" N 0 到 5

MLPreluSupportLimits 具有以下成员:

input, 类型为 MLTensorLimits

MLTensorLimits 用于 input 操作数。

slope, 类型为 MLTensorLimits

MLTensorLimits 用于 slope 操作数。

output, 类型为 MLTensorLimits

MLTensorLimits 用于 output 操作数。

MLOpSupportLimitsprelu() 具有以下成员:

prelu, 类型为 MLPreluSupportLimits

运算符 prelu() 的支持限制。

prelu(input, slope, options) 方法的步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 验证操作数,给定 this 以及 inputslope 中任一个返回 false,则抛出一个 TypeError

  3. 如果 inputslope 中任一个的dataType不是其允许的数据类型之一(根据此表),则抛出一个 TypeError

  4. outputShape双向广播 slopeshapeinputshape 的结果。

    1. 如果返回失败,则抛出 一个 TypeError

  5. descriptor创建 MLOperandDescriptor 的结果,给定 inputdataTypeoutputShape

  6. 建立图连接:

    1. output创建一个 MLOperand 的结果,给定 thisdescriptor

    2. operator 为一个运算符,用于 "prelu" 运算,给定 slopeoptions

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 inputslope

    5. operator输出设置为 output

  7. 返回 output

此运算的行为可以按如下方式使用其他运算进行通用模拟, 尽管用户代理通常具有更高效的实现。在底层 平台不直接支持某项运算的情况下,可以使用此分解作为 模板来指导实现。
function prelu(builder, input, slope) {
  return builder.add(
    builder.max(builder.constant(input.dataType, 0), input),
    builder.mul(
      slope, builder.min(builder.constant(input.dataType, 0), input)));
}

8.9.39. 归约运算

沿所有维度归约输入张量,或沿 axes 数组参数中指定的轴进行归约。对于每个指定的轴,具有该索引的维度会被归约,即结果张量 将不包含该维度,除非指定了 keepDimensions。 结果张量的值使用指定的归约函数计算,该函数 以归约维度中的所有输入值作为参数。
dictionary MLReduceOptions : MLOperatorOptions {
  sequence<[EnforceRange] unsigned long> axes;
  boolean keepDimensions = false;
};

partial interface MLGraphBuilder {
  MLOperand reduceL1(MLOperand input, optional MLReduceOptions options = {});
  MLOperand reduceL2(MLOperand input, optional MLReduceOptions options = {});
  MLOperand reduceLogSum(MLOperand input, optional MLReduceOptions options = {});
  MLOperand reduceLogSumExp(MLOperand input, optional MLReduceOptions options = {});
  MLOperand reduceMax(MLOperand input, optional MLReduceOptions options = {});
  MLOperand reduceMean(MLOperand input, optional MLReduceOptions options = {});
  MLOperand reduceMin(MLOperand input, optional MLReduceOptions options = {});
  MLOperand reduceProduct(MLOperand input, optional MLReduceOptions options = {});
  MLOperand reduceSum(MLOperand input, optional MLReduceOptions options = {});
  MLOperand reduceSumSquare(MLOperand input, optional MLReduceOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits reduceL1;
  MLSingleInputSupportLimits reduceL2;
  MLSingleInputSupportLimits reduceLogSum;
  MLSingleInputSupportLimits reduceLogSumExp;
  MLSingleInputSupportLimits reduceMax;
  MLSingleInputSupportLimits reduceMean;
  MLSingleInputSupportLimits reduceMin;
  MLSingleInputSupportLimits reduceProduct;
  MLSingleInputSupportLimits reduceSum;
  MLSingleInputSupportLimits reduceSumSquare;
};

MLReduceOptions 具有以下成员:

axes, 类型为 sequence<[EnforceRange] unsigned long>

要归约的维度,这也指定输入张量中的哪些值用于 归约函数。列表中的轴必须位于 [0, N-1] 范围内,其中 N 是输入 张量的

如果不存在,则归约所有维度。归约函数的输入值为 输入张量中的所有值。

如果存在且非空,则归约函数的输入值为输入张量 指定维度中的所有值。

如果存在且为空,则不归约任何维度,输出张量的形状与 输入张量的形状相同;归约函数分别应用于张量中的每个值 。

keepDimensions, 类型为 boolean,默认为 false

如果为 true,则输出与输入具有相同的秩,并将任何归约维度的大小设置为 1。

参数:

返回:一个 MLOperand。 输出 N 维张量,其位于 0 到 input范围内(含端点), 具体取决于 axeskeepDimensions。 如果输入操作数是标量,则归约函数应用于该标量值,输出 也是标量。

reduceL1()/reduceSum()/reduceSumSquare() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input "float32", "float16", "int32", "uint32", "int64", "uint64" "float32", "float16", "int32" N 0 到 5
输出 与……相同 input "float32", "float16", "int32" N 0 到 5
reduceL2()/reduceLogSum()/reduceLogSumExp()/reduceMean() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input "float32", "float16" "float32", "float16" N 0 到 5
输出 与……相同 input "float32", "float16" N 0 到 5
reduceMax()/reduceMin() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input 任意 "float32", "float16", "int32" N 0 到 5
输出 与……相同 input "float32", "float16", "int32" N 0 到 5
reduceProduct() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input "float32", "float16", "int32", "uint32", "int64", "uint64" "float32", "float16" N 0 到 5
输出 与……相同 input "float32", "float16" N 0 到 5

MLOpSupportLimits 对归约运算具有以下成员:

reduceL1, 类型为 MLSingleInputSupportLimits

运算符 reduceL1() 的支持限制。

reduceL2, 类型为 MLSingleInputSupportLimits

运算符 reduceL2() 的支持限制。

reduceLogSum, 类型为 MLSingleInputSupportLimits

运算符 reduceLogSum() 的支持限制。

reduceLogSumExp, 类型为 MLSingleInputSupportLimits

运算符 reduceLogSumExp() 的支持限制。

reduceMax, 类型为 MLSingleInputSupportLimits

运算符 reduceMax() 的支持限制。

reduceMean, 类型为 MLSingleInputSupportLimits

运算符 reduceMean() 的支持限制。

reduceMin, 类型为 MLSingleInputSupportLimits

运算符 reduceMin() 的支持限制。

reduceProduct, 类型为 MLSingleInputSupportLimits

运算符 reduceProduct() 的支持限制。

reduceSum, 类型为 MLSingleInputSupportLimits

运算符 reduceSum() 的支持限制。

reduceSumSquare, 类型为 MLSingleInputSupportLimits

运算符 reduceSumSquare() 的支持限制。

归约类型:
计算归约输出大小,给定一个 无符号整数列表 inputShape、一个可选的无符号整数列表 axes布尔值 keepDimensions,执行以下步骤。它们返回 一个新的无符号整数列表 ,或失败。
  1. inputRankinputShape大小

  2. 如果未给定 axes,则令 axes范围 0 到 inputRank,不含上界。

  3. 否则,如果 axes 包含重复值,或者其任何不在 范围 0 到 inputRank 内(不含上界),则返回 失败。

  4. 如果 keepDimensions 为 true,则:

    1. outputShapeinputShape 的一个克隆

    2. 对于每一个 axes 中的 axis

      1. outputShape[axis] 设置为 1。

  5. 否则:

    1. outputShape 为一个空列表

    2. 对于每一个 index,位于 范围 0 到 inputRank 内(不含上界):

      1. 如果 axes包含 index,则附加 inputShape[index] 到 outputShape

  6. 返回 outputShape

创建归约运算,给定字符串 opMLOperand inputMLReduceOptions options 和可选的列表 allowedDataTypes,运行以下步骤:
  1. 断言op 是 "reduceL1"、"reduceL2"、 "reduceLogSum"、"reduceLogSumExp"、"reduceMax"、"reduceMean"、"reduceMin"、"reduceProduct"、 "reduceSum"、"reduceSumSquare" 之一。

  2. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  3. 如果使用 验证操作数,给定 thisinput 返回 false,则抛出一个 TypeError

  4. 如果给定了 allowedDataTypes 且它不包含 inputdataType,则抛出一个 TypeError

  5. outputShape计算归约输出 大小的结果,给定 inputshapeoptions.axes (如果它存在)和 options.keepDimensions。 如果返回失败,则抛出一个 TypeError

  6. desc创建 MLOperandDescriptor的结果,给定 inputdataTypeoutputShape

  7. 建立图连接:

    1. output创建一个 MLOperand的结果,给定 thisdesc

    2. operator 为一个运算符,用于 op 运算,给定 options

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 input

    5. operator输出设置为 output

  8. 返回 output

支持以下归约算法。
reduceL1(input, options) 方法的步骤为:
  1. output创建归约运算 的结果,给定 "reduceL1"、inputoptions 和 « "float32", "float16", "int32", "uint32", "int64", "uint64" »。

    1. 如果这抛出错误,则重新抛出该错误。

  2. 返回 output

reduceL2(input, options) 方法的步骤为:
  1. output创建归约运算 的结果,给定 "reduceL2"、inputoptions 和 « "float32", "float16" »。

    1. 如果这抛出错误,则重新抛出该错误。

  2. 返回 output

reduceLogSum(input, options) 方法的步骤为:
  1. output创建归约运算 的结果,给定 "reduceLogSum"、inputoptions 和 « "float32", "float16" »。

    1. 如果这抛出错误,则重新抛出该错误。

  2. 返回 output

reduceLogSumExp(input, options) 方法的步骤为:
  1. output创建归约运算 的结果,给定 "reduceLogSumExp"、inputoptions 和 « "float32", "float16" »。

    1. 如果这抛出错误,则重新抛出该错误。

  2. 返回 output

reduceMax(input, options) 方法的步骤为:
  1. output创建归约运算 的结果,给定 "reduceMax"、inputoptions

    1. 如果这抛出错误,则重新抛出该错误。

  2. 返回 output

reduceMean(input, options) 方法的步骤为:
  1. output创建归约运算 的结果,给定 "reduceMean"、inputoptions 和 « "float32", "float16" »。

    1. 如果这抛出错误,则重新抛出该错误。

  2. 返回 output

reduceMin(input, options) 方法的步骤为:
  1. output创建归约运算 的结果,给定 "reduceMin"、inputoptions

    1. 如果这抛出错误,则重新抛出该错误。

  2. 返回 output

reduceProduct(input, options) 方法的步骤为:
  1. output创建归约运算 的结果,给定 "reduceProduct"、inputoptions 和 « "float32", "float16", "int32", "uint32", "int64", "uint64" »。

    1. 如果这抛出错误,则重新抛出该错误。

  2. 返回 output

reduceSum(input, options) 方法的步骤为:
  1. output创建归约运算 的结果,给定 "reduceSum"、inputoptions 和 « "float32", "float16", "int32", "uint32", "int64", "uint64" »。

    1. 如果这抛出错误,则重新抛出该错误。

  2. 返回 output

reduceSumSquare(input, options) 方法的步骤为:
  1. output创建归约运算 的结果,给定 "reduceSumSquare"、inputoptions 和 « "float32", "float16", "int32", "uint32", "int64", "uint64" »。

    1. 如果这抛出错误,则重新抛出该错误。

  2. 返回 output

多种归约运算的行为可以按如下方式使用其他 运算进行通用模拟,尽管用户代理通常具有更高效的实现。在 底层平台不直接支持某项运算的情况下,可以使用此分解 作为模板来指导实现。
function reduceLogSum(builder, input, options) {
  return builder.log(builder.reduceSum(input, options));
}

function reduceLogSumExp(builder, input, options) {
  return builder.log(builder.reduceSum(builder.exp(input), options));
}

function reduceSumSquare(builder, input, options) {
  return builder.reduceSum(builder.pow(input, 2), options);
}
某些底层平台不直接支持类似 keepDimensions 的选项。这不会影响底层张量数据,只会影响形状。例如,如果输入形状 为 [2, 3, 4],轴为 1,并且 keepDimensions 为 true,则预期输出形状为 [2, 1 ,4]。如果底层平台从不保留归约 维度,则会生成形状为 [2, 4] 的输出。实现可以引入一个无操作的 reshape,将其变为 [2, 1, 4]。如果 keepDimensions 为 false,但底层平台始终保留归约维度,也可以引入类似的无操作 reshape。

8.9.40. relu

计算输入张量的线性整流函数
partial interface MLGraphBuilder {
  MLOperand relu(MLOperand input, optional MLOperatorOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits relu;
};
参数:

返回:

relu() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input "float32", "float16", "int64", "int32", "int8" "float32", "float16" N 0 到 5
输出 与……相同 input "float32", "float16" 与……相同 input 0 到 5

MLOpSupportLimitsrelu() 具有以下成员:

relu, 类型为 MLSingleInputSupportLimits

运算符 relu() 的支持限制。

relu(input, options) 方法的 步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 验证操作数,给定 thisinput 返回 false,则抛出一个 TypeError

  3. 如果 inputdataType 不是其允许的数据类型之一 (根据此表),则抛出一个 TypeError

  4. 建立图连接:

    1. output复制 MLOperand的结果,给定 input

    2. operator 为一个运算符,用于 "relu" 运算,给定 options

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 input

    5. operator输出设置为 output

  5. 返回 output

此运算的行为可以按如下方式使用其他运算进行通用模拟, 尽管用户代理通常具有更高效的实现。在底层 平台不直接支持某项运算的情况下,可以使用此分解作为 模板来指导实现。
function relu(builder, input) {
  return builder.max(builder.constant(input.dataType, 0), input);
}

8.9.41. resample2d

根据轴和缩放因子,将张量值从源维度重采样到目标 维度。
enum MLInterpolationMode {
  "nearest-neighbor",
  "linear"
};

dictionary MLResample2dOptions : MLOperatorOptions {
  MLInterpolationMode mode = "nearest-neighbor";
  sequence<float> scales;
  sequence<[EnforceRange] unsigned long> sizes;
  sequence<[EnforceRange] unsigned long> axes;
};

partial interface MLGraphBuilder {
  MLOperand resample2d(MLOperand input, optional MLResample2dOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits resample2d;
};
参数:

返回:一个 MLOperand。 输出四维张量。

MLResample2dOptions 具有以下成员:

mode, 类型为 MLInterpolationMode,默认为 "nearest-neighbor"

用于填充输出张量值的插值算法。

两种算法都从以下输入开始,这些输入针对每个空间轴计算(基于 axes), 其中 inputSizeinput 张量的shape给出, outputSizesizesscales 给出, 而 outputCoordinate 标识正在计算的输出张量中的元素。

scale = outputSize / inputSize
unclampedCoordinate = (outputCoordinate + 0.5) / scale - 0.5
inputCoordinate = clamp(unclampedCoordinate, 0, inputSize - 1)
对于输出张量中给定的 outputCoordinate.xoutputCoordinate.y 位置, 上述方程给出有理数 inputCoordinate.xinputCoordinate.y
nearest-neighbor

上面计算出的 inputCoordinate.xinputCoordinate.y 被用作 最近邻采样算法的输入,以按如下方式计算输出张量值 :

x = ceil(inputCoordinate.x - 0.5)
y = ceil(inputCoordinate.y - 0.5)
output tensor value = input tensor value at (x, y)
linear

上面计算出的 inputCoordinate.xinputCoordinate.y 被用作 双线性采样算法的输入,以按如下方式计算输出张量值:

x0 = floor(inputCoordinate.x)
x1 = ceil(inputCoordinate.x)
y0 = floor(inputCoordinate.y)
y1 = ceil(inputCoordinate.y)
vx0y0 = input tensor value at (x0, y0)
vx1y0 = input tensor value at (x1, y0)
vx0y1 = input tensor value at (x0, y1)
vx1y1 = input tensor value at (x1, y1)
tx = inputCoordinate.x - x0
ty = inputCoordinate.y - y0

vy0 = vx0y0 * (1 - tx) + vx1y0 * tx
vy1 = vx0y1 * (1 - tx) + vx1y1 * tx
output tensor value = vy0 * (1 - ty) + vy1 * ty
scales, 类型为 sequence<float>

长度为 2 的列表。 指定来自 axes 的每个输入维度的缩放因子: [scaleForFirstAxis, scaleForSecondAxis]。 默认值为 [1.0, 1.0]。

sizes, 类型为 sequence<[EnforceRange] unsigned long>

长度为 2 的列表。 指定来自 axes 的每个输入维度的目标大小: [sizeForFirstAxis, sizeForSecondAxis]。当指定 sizes 时,scales 会被忽略,因为缩放因子的值由输入的目标大小推导得出。

axes, 类型为 sequence<[EnforceRange] unsigned long>

长度为 2 的列表。 指定应用插值算法的输入张量两个维度。 默认值为 [2, 3]。

resample2d() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input "float32", "float16", "uint8", "int8" "float32", "float16" 4 4
输出 与……相同 input "float32", "float16" 4 4

MLOpSupportLimitsresample2d() 具有以下成员:

resample2d, 类型为 MLSingleInputSupportLimits

运算符 resample2d() 的支持限制。

resample2d(input, options) 方法的步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 验证操作数,给定 thisinput 返回 false,则抛出一个 TypeError

  3. 如果 inputdataType 不是其允许的数据类型之一 (根据此表),则抛出一个 TypeError

  4. 如果 input不是其允许的秩,则抛出一个 TypeError

  5. 如果 options.scales存在,则将其设置为列表 « 1.0, 1.0 »。

  6. 否则,如果其任何小于或等于 0,或者其大小不是 2,则抛出一个 TypeError

  7. 如果 options.sizes 存在,并且其大小不是 2, 或者其任何为 0,则抛出一个 TypeError

  8. 如果 options.axes存在,则将其设置为列表 « 2, 3 »。

  9. 否则,如果 options.axes 包含重复值,或者其任何不在 范围 0 到 input内(不含上界),则抛出一个 TypeError

  10. 计算输出形状:

    1. inputDescriptorinput.[[descriptor]]

    2. outputShapeinputDescriptor.shape 的一个克隆

    3. 对于每一个 index,位于 范围 0 到 options.axes大小内(不含上界):

      1. 如果 options.sizes 存在,则令 sizeoptions.sizes[index]。

      2. 否则,令 size 为 floor(inputshape[options.axes[index]] * options.scales[index])。

      3. 如果 size 不是有效维度,则抛出一个 TypeError

      4. outputShape[options.axes[index]] 设置为 size

    4. desc创建 MLOperandDescriptor的结果, 给定 inputDescriptor.dataTypeoutputShape

  11. 建立图连接:

    1. output创建一个 MLOperand的结果,给定 thisdesc

    2. operator 为一个运算符,用于 "resample2d" 运算,给定 options

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 input

    5. operator输出设置为 output

  12. 返回 output

具体的采样算法基于现有机器学习框架中广泛使用的算法。例如,在从以下 [4, 4] 输入张量(仅考虑空间维度)执行 linear 重采样时:
[   0   1   2   3  ]
[   0   1   2   3  ]
[  12  13  14  15  ]
[  12  13  14  15  ]

对于 [8, 8] 输出张量,预期值为:

[   0   0.25   0.75   1.25   1.75   2.25   2.75   3  ]
[   0   0.25   0.75   1.25   1.75   2.25   2.75   3  ]
[   0   0.25   0.75   1.25   1.75   2.25   2.75   3  ]
[   3   3.25   3.75   4.25   4.75   5.25   5.75   6  ]
[   9   9.25   9.75  10.25  10.75  11.25  11.75  12  ]
[  12  12.25  12.75  13.25  13.75  14.25  14.75  15  ]
[  12  12.25  12.75  13.25  13.75  14.25  14.75  15  ]
[  12  12.25  12.75  13.25  13.75  14.25  14.75  15  ]

这具有一些便利的特性:采样均匀分布、对称、对图像 镜像稳健,并且角点值对齐。

8.9.42. reshape

将张量的形状更改为新形状。Reshape 不会复制或更改张量的内容。它只会 更改张量的逻辑形状,以供后续运算使用。
partial interface MLGraphBuilder {
  MLOperand reshape(MLOperand input,
                    sequence<[EnforceRange] unsigned long> newShape,
                    optional MLOperatorOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits reshape;
};
参数:

返回:一个 MLOperand。 输出张量。输出 张量的值与输入张量的值相同。输出 张量的形状由 newShape 指定。

reshape() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input 任意 "float32", "float16", "int32", "int8", "uint8" N 0 到 5
输出 与……相同 input "float32", "float16", "int32", "int8", "uint8" N 0 到 5

MLOpSupportLimitsreshape() 具有以下成员:

reshape, 类型为 MLSingleInputSupportLimits

运算符 reshape() 的支持限制。

reshape(input, newShape, options) 方法的步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 验证操作数,给定 thisinput 返回 false,则抛出一个 TypeError

  3. 如果 newShape大小不是输出张量的允许的秩(根据此表),则抛出一个 TypeError

  4. outputShape 为一个空的 unsigned long 数组。

  5. 如果 newShape大小为 0,则将 outputShape 设置为一个空列表 ,用于表示标量。

  6. 如果 newShape 中的任何不是有效维度,则抛出一个 TypeError

  7. inputElementCountinputshape 中所有的乘积。 空维度产生的 inputElementCount 为 1。

  8. 如果 newShape 中所有值的乘积不等于 inputElementCount, 则抛出一个 TypeError

  9. descinput.[[descriptor]] 的副本。

  10. desc.shape 设置为 newShape

  11. 建立图连接:

    1. output创建一个 MLOperand的结果,给定 thisdesc

    2. operator 为一个运算符,用于 "reshape" 运算,给定 options

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 input

    5. operator输出设置为 output

  12. 返回 output

8.9.43. reverse

沿给定轴反转张量。
dictionary MLReverseOptions : MLOperatorOptions {
  sequence<[EnforceRange] unsigned long> axes;
};

partial interface MLGraphBuilder {
  MLOperand reverse(MLOperand input, optional MLReverseOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits reverse;
};

MLReverseOptions 具有以下成员:

axes, 类型为 sequence<[EnforceRange] unsigned long>

要反转的输入维度索引。当此成员不存在时,视为 反转所有维度。如果显式传入空值,则不反转任何维度。

参数:

返回:

reverse() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input 任意 "float32", "float16", "int32" N 0 到 5
输出 与……相同 input "float32", "float16", "int32" 与……相同 input 0 到 5

MLOpSupportLimitsreverse() 具有以下成员:

reverse, 类型为 MLSingleInputSupportLimits

运算符 reverse() 的支持限制。

reverse(input, options) 方法的步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 验证操作数,给定 thisinput 返回 false,则抛出一个 TypeError

  3. 如果 inputdataType 不是其允许的数据类型之一 (根据此表),则抛出一个 TypeError

  4. inputRankinput

  5. 如果未给定 axes,则令 axes范围 0 到 inputRank,不含上界。

  6. 否则,如果 axes 包含重复值,或者其任何元素不在范围 0 到 inputRank 内(不含上界),则返回 失败。

  7. 建立图连接:

    1. output复制 MLOperand的结果,给定 input

    2. operator 为一个运算符,用于 "reverse" 运算和 options

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 input

    5. operator输出设置为 output

  8. 返回 output

8.9.44. scatterElements

根据索引,沿某个轴将 updates 张量中的值散布到输入张量副本之上。
dictionary MLScatterOptions : MLOperatorOptions {
  [EnforceRange] unsigned long axis = 0;
};

partial interface MLGraphBuilder {
  MLOperand scatterElements(MLOperand input,
                            MLOperand indices,
                            MLOperand updates,
                            optional MLScatterOptions options = {});
};

dictionary MLScatterSupportLimits {
  MLTensorLimits input;
  MLTensorLimits indices;
  MLTensorLimits updates;
  MLTensorLimits output;
};

partial dictionary MLOpSupportLimits {
  MLScatterSupportLimits scatterElements;
};

MLScatterOptions 具有以下成员:

axis, 类型为 unsigned long,默认为 0

获取散布值所沿的轴。其值必须位于 [0, N-1] 范围内,其中 N 是输入张量的

参数:

返回:一个 MLOperand。 输出 N 维张量,其等于 input

scatterElements() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input 任意 "float32", "float16", "int32" 1 到 N 1 到 5
indices "int32", "uint32", "int64" "int32" 与……相同 input 1 到 5
updates 与……相同 input "float32", "float16", "int32" 与……相同 input 1 到 5
输出 与……相同 input "float32", "float16", "int32" 与……相同 input 1 到 5

MLScatterSupportLimits 具有以下成员:

input, 类型为 MLTensorLimits

MLTensorLimits 用于 input 操作数。

indices, 类型为 MLTensorLimits

MLTensorLimits 用于 indices 操作数。

updates, 类型为 MLTensorLimits

MLTensorLimits 用于 updates 操作数。

output, 类型为 MLTensorLimits

MLTensorLimits 用于 output 操作数。

MLOpSupportLimitsscatterElements() 具有以下成员:

scatterElements, 类型为 MLScatterSupportLimits

运算符 scatterElements() 的支持限制。

在构建图时,无法将 indices 参数传给 scatterElements() 后将其限制在允许范围内,因为直到执行时输入才可知。 如果底层平台未提供指定的限制行为,实现可以在已编译图中引入 clamp() 。类似地,如果底层平台不支持负索引,实现可以在 已编译图中引入运算,将从维度末尾计算的负索引转换为正 索引。
scatterElements(input, indices, updates, options) 方法的步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 验证操作数,给定 this 以及 inputindicesupdates 中任一个返回 false,则抛出一个 TypeError

  3. 如果 indicesdataType 不是其允许的数据类型之一 (根据此表),则抛出一个 TypeError

  4. 如果 updatesdataType 不等于 inputdataType,则抛出一个 TypeError

  5. 如果 inputindicesupdates 中任一个的 不是其允许的秩, 则抛出一个 TypeError

  6. axisoptions.axis

  7. 如果 axis 大于或等于 input,则抛出一个 TypeError

  8. indicesShapeExpectedinputshape 的副本。

  9. indicesShapeExpected[axis] 设置为 indicesshape[axis]。

  10. 如果 indicesshape等于 indicesShapeExpected,则抛出一个 TypeError

  11. 如果 updatesshape等于 indicesshape,则抛出一个 TypeError

  12. 建立图连接:

    1. output复制 MLOperand的结果,给定 input

    2. operator 为一个运算符,用于 "scatterElements" 运算,给定 inputindicesupdatesoptions

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 inputindicesupdates

    5. operator输出设置为 output

  13. 返回 output

scatterElements 在不同切片方案下如何工作的示例。
// 形状为 [4,3] 的 input:
//   [[ 0,  1,  2],
//    [10, 11, 12],
//    [20, 21, 22],
//    [30, 31, 32]]
// 形状为 [2,3] 的 indices:
//   [[3, 1, 1],
//    [2, 0, 3]]
// 形状为 [2,3] 的 updates:
//   [[-1, -2, -3],
//    [-4, -5, -6]]
// axis = 0(默认值)
// 形状为 [4,3] 的 output:
//   [[ 0, -5,  2],
//    [10, -2, -3],
//    [-4, 21, 22],
//    [-1, 31, -6]]

const input1 = builder.constant(
  {dataType: 'float32', shape: [4, 3]},
  new Float32Array([0, 1, 2, 10, 11, 12, 20, 21, 22, 30, 31, 32]));

const indices1 = builder.constant(
  {dataType: 'uint32', shape: [2, 3]}, new Uint32Array([3, 1, 1, 2, 0, 3]));

const updates1 = builder.constant(
  {dataType: 'float32', shape: [2, 3]},
  new Uint32Array([-1, -2, -3, -4, -5, -6]));

const output1 = builder.scatterElements(input1, indices1, updates1);

// 形状为 [4,3] 的 input:
//   [[ 0,  1,  2],
//    [10, 11, 12],
//    [20, 21, 22],
//    [30, 31, 32]]
// 形状为 [4,1] 的 indices:
//   [[2],
//    [1],
//    [0],
//    [2]],
// 形状为 [4,1] 的 updates:
//   [[-1],
//    [-2],
//    [-3],
//    [-4]],
// axis = 1
// 形状为 [4,3] 的 output:
//   [[ 0,  1, -1],
//    [10, -2, 12],
//    [-3, 21, 22],
//    [30, 31, -4]]

const indices2 = builder.constant(
  {dataType: 'uint32', shape: [4, 1]}, new Uint32Array([2, 1, 0, 2]));

const updates2 = builder.constant(
  {dataType: 'float32', shape: [4, 1]}, new Uint32Array([-1, -2, -3, -4]));

const output2 = builder.scatterElements(input1, indices2, updates2, {axis: 1});

// 形状为 [4,2,2] 的 input:
//   [[[  0,   1],
//     [ 10,  11]],
//    [[100, 101],
//     [110, 111]],
//    [[200, 201],
//     [210, 211]],
//    [[300, 301],
//     [310, 311]],]
// 形状为 [1,2,2] 的 indices:
//   [[[0, 2],
//     [1, 3]]],
// 形状为 [1,2,2] 的 updates:
//   [[[-1, -2],
//     [-3, -4]]],
// axis = 0
// 形状为 [4,2,2] 的 output:
//   [[[ -1,   1],
//     [ 10,  11]],
//    [[100, 101],
//     [ -3, 111]],
//    [[200,  -2],
//     [210, 211]],
//    [[300, 301],
//     [310,  -4]],]

const inputData3 = new Float32Array(
  [0, 1, 10, 11, 100, 101, 110, 111, 200, 201, 210, 211, 300, 301, 310, 311]);

const input3 =
  builder.constant({dataType: 'float32', shape: [4, 2, 2]}, inputData3);

const indices3 = builder.constant(
  {dataType: 'uint32', shape: [1, 2, 2]}, new Uint32Array([0, 2, 1, 3]));

const updates3 = builder.constant(
  {dataType: 'float32', shape: [1, 2, 2]}, new Uint32Array([-1, -2, -3, -4]));

const output3 = builder.scatterElements(input3, indices3, updates3, {axis: 0});

8.9.45. scatterND

根据索引,将 update 张量中的值切片散布到输入张量副本之上。
partial interface MLGraphBuilder {
  MLOperand scatterND(MLOperand input,
                      MLOperand indices,
                      MLOperand updates,
                      optional MLOperatorOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLScatterSupportLimits scatterND;
};
参数:

返回:一个 MLOperand。 输出 N 维张量,其等于 input + indices - indicesshape[-1] - 1。

scatterND() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input 任意 "float32", "float16", "int32" 1 到 N 1 到 5
indices "int32", "uint32", "int64" "int32" 1 到 N 1 到 5
updates 与……相同 input "float32", "float16", "int32" N 0 到 5
输出 与……相同 input "float32", "float16", "int32" 1 到 N 1 到 5

MLOpSupportLimitsscatterND() 具有以下成员:

scatterND, 类型为 MLScatterSupportLimits

运算符 scatterND() 的支持限制。

在构建图时,无法将传给 indices 参数 scatterND() 限制在允许范围内,因为直到执行时输入才可知。 如果底层平台未提供指定的限制行为,实现可以在已编译图中引入 clamp()。 类似地,如果底层平台不支持负索引,实现可以在 已编译图中引入运算,将从维度末尾计算的负索引转换为正 索引。
scatterND(input, indices, updates, options) 方法的步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 验证操作数,给定 this 以及 inputindicesupdates 中任一个返回 false,则抛出一个 TypeError

  3. 如果 indicesdataType 不是允许的数据类型之一 (根据此表),则抛出一个 TypeError

  4. 如果 updatesdataType 不等于 inputdataType,则抛出一个 TypeError

  5. 如果 inputindicesupdates 中任一个的不是其允许的秩, 则抛出一个 TypeError

  6. inputShapeinputshape,并令 inputRankinput

  7. indicesShapeindicesshape,并令 indicesRankindices

  8. indexableSizeindicesRank - 1。

  9. coordinateSizeindicesShape[indexableSize]。

  10. 如果 coordinateSize 大于 inputRank,则抛出一个 TypeError

  11. expectedUpdatesShape 为一个空列表。

  12. 对于每一个 index,位于范围 0 到 indexableSize 内(不含上界):

    1. 附加 indicesShape[index] 到 expectedUpdatesShape

  13. 对于每一个 index,位于范围 coordinateSizeinputRank 内(不含上界):

    1. 附加 inputShape[index] 到 expectedUpdatesShape

  14. 如果 updatesshape等于 expectedUpdatesShape,则抛出一个 TypeError

  15. outputShapeinputshape 的副本。

  16. outputDesc创建 MLOperandDescriptor的结果,给定 inputdataTypeoutputShape

  17. 建立图连接:

    1. output创建一个 MLOperand的结果,给定 outputDesc

    2. operator 为一个运算符,用于 "scatterND" 运算,给定 inputindicesupdatesoptions

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 inputindicesupdates

    5. operator输出设置为 output

  18. 返回 output

scatterND 在不同切片方案下如何工作的示例。
// 形状为 [8] 的 input:
//   [0, 1, 2, 3, 4, 5, 6, 7]
// 形状为 [4, 1] 的 indices:
//   [[4],
//    [3],
//    [1],
//    [7]]
// 形状为 [4] 的 updates:
//   [-1, -2, -3, -4]
// 形状为 [8] 的 output:
//   [0, -3, 2, -2, -1, 5, 6, -4]

const input1 = builder.constant(
  {dataType: 'float32', shape: [8]},
  new Float32Array([0, 1, 2, 3, 4, 5, 6, 7]));

const indices1 = builder.constant(
  {dataType: 'uint32', shape: [4, 1]}, new Uint32Array([4, 3, 1, 7]));

const updates1 = builder.constant(
  {dataType: 'uint32', shape: [4]}, new Uint32Array([-1, -2, -3, -4]));

const output1 = builder.scatterND(input1, indices1, updates1);

// 形状为 [2,2] 的 input:
//   [[0, 1],
//    [2, 3]]
// 形状为 [2,2] 的 indices:
//   [[0, 0],
//    [1, 1]]
// 形状为 [2] 的 updates:
//   [-1, -2]
// 形状为 [2,2] 的 output:
//   [[-1,  1],   <= -1 写入输出坐标 [0, 0]
//    [ 2, -2]]   <= -2 写入输出坐标 [1, 1]

const input2 = builder.constant(
  {dataType: 'float32', shape: [2, 2]}, new Float32Array([0, 1, 2, 3]));

const indices2 = builder.constant(
  {dataType: 'uint32', shape: [2, 2]}, new Uint32Array([0, 0, 1, 1]));

const updates2 =
  builder.constant({dataType: 'uint32', shape: [2]}, new Uint32Array([-1, -2]));

const output2 = builder.scatterND(input2, indices2, updates2);

// 形状为 [3,2] 的 input:
//   [[0, 1],
//    [2, 3],
//    [4, 5]]
// 形状为 [2,1] 的 indices:
//   [[2],
//    [0]]
// 形状为 [2,2] 的 updates:
//   [[-1, -2],
//    [-3, -4]]
// 形状为 [3,2] 的 output:
//   [[-3 ,-4],    <= [-3, -4] 写入输出坐标 [0, *]
//    [ 2,  3],
//    [-1, -2]]    <= [-1, -2] 写入输出坐标 [2, *]

const input3 = builder.constant(
  {dataType: 'float32', shape: [3, 2]}, new Float32Array([0, 1, 2, 3, 4, 5]));

const indices3 = builder.constant(
  {dataType: 'uint32', shape: [2, 1]}, new Uint32Array([1, 0]));

const updates3 = builder.constant(
  {dataType: 'uint32', shape: [2, 2]}, new Uint32Array([-1, -2, -3, 4]));

const output3 = builder.scatterND(input3, indices3, updates3);

// 形状为 [2,2,2] 的 input:
//   [[[0, 1],
//     [2, 3]],
//    [[4, 5],
//     [6, 7]]]
// 形状为 [2,2] 的 indices:
//   [[0, 1],
//    [1, 0]]
// 形状为 [2,2] 的 updates:
//   [[-1, -2],
//    [-3, -4]]
// 形状为 [2,2,2] 的 output:
//   [[[ 0,  1],
//     [-1, -2]],   <= [-1, -2] 写入输出坐标 [0, 1, *]
//    [[-3, -4],    <= [-3, -4] 写入输出坐标 [1, 0, *]
//     [ 6,  7]]]

const input4 = builder.constant(
  {dataType: 'float32', shape: [2, 2, 2]},
  new Float32Array([0, 1, 2, 3, 4, 5, 6, 7]));

const indices4 = builder.constant(
  {dataType: 'uint32', shape: [2, 2]}, new Uint32Array([0, 1, 1, 0]));

const updates4 = builder.constant(
  {dataType: 'uint32', shape: [2, 2]}, new Uint32Array([-1, -2, -3, 4]));

const output4 = builder.scatterND(input4, indices4, updates4);

8.9.46. sigmoid

计算输入张量的 sigmoid 函数。 计算遵循表达式 1 / (exp(-x) + 1)
partial interface MLGraphBuilder {
  MLOperand sigmoid(MLOperand input, optional MLOperatorOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits sigmoid;
};
参数:

返回:

sigmoid() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input "float32", "float16" "float32", "float16" N 0 到 5
输出 与……相同 input "float32", "float16" 与……相同 input 0 到 5

MLOpSupportLimitssigmoid() 具有以下成员:

sigmoid, 类型为 MLSingleInputSupportLimits

运算符 sigmoid() 的支持限制。

sigmoid(input, options) 方法的步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 验证操作数,给定 thisinput 返回 false,则抛出一个 TypeError

  3. 如果 inputdataType 不是其允许的数据类型之一 (根据此表),则抛出一个 TypeError

  4. 建立图连接:

    1. output复制 MLOperand的结果,给定 input

    2. operator 为一个运算符,用于 "sigmoid" 运算,给定 options

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 input

    5. operator输出设置为 output

  5. 返回 output

此运算的行为可以按如下方式使用其他运算进行通用模拟, 尽管用户代理通常具有更高效的实现。在底层 平台不直接支持某项运算的情况下,可以使用此分解作为 模板来指导实现。
function sigmoid(builder, input) {
  return builder.div(
    builder.constant(input.dataType, 1),
    builder.add(
      builder.exp(builder.neg(input)), builder.constant(input.dataType, 1)));
}

8.9.47. slice

生成输入张量的一个切片。
dictionary MLSliceOptions : MLOperatorOptions {
  sequence<[EnforceRange] unsigned long> strides;
};

partial interface MLGraphBuilder {
  MLOperand slice(MLOperand input,
                  sequence<[EnforceRange] unsigned long> starts,
                  sequence<[EnforceRange] unsigned long> sizes,
                  optional MLSliceOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits slice;
};

MLSliceOptions 具有以下成员:

strides, 类型为 sequence<[EnforceRange] unsigned long>

沿每个轴跨越每个输入时使用的步幅。 strides 数组的长度必须等于输入张量的。 默认值是长度为且全部由 1 组成的数组。 例如,对于三维张量为 [1,1,1]。 步幅必须大于零。

参数:

返回:一个 MLOperand。 与输入张量具有相同秩的输出张量,其张量值被截取到每个维度中指定的 起始和结束索引。

slice() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input 任意 "float32", "float16", "int32", "int8", "uint8" N 0 到 5
输出 与……相同 input "float32", "float16", "int32", "int8", "uint8" 与……相同 input 0 到 5

MLOpSupportLimitsslice() 具有以下成员:

slice, 类型为 MLSingleInputSupportLimits

运算符 slice() 的支持限制。

slice(input, starts, sizes, options) 方法的步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 验证操作数,给定 thisinput 返回 false,则抛出一个 TypeError

  3. 如果 sizes 的任何为 0, 则抛出一个 TypeError

  4. 如果 starts大小sizes大小并非 都等于 input,则抛出一个 TypeError

  5. strides 为一个新的列表

  6. 如果 options.strides 存在,则:

    1. strides 设置为 options.strides

    2. 如果 strides大小 不等于 input,则抛出 一个 TypeError

  7. inputShapeinputshape,并令 inputRankinput

  8. outputShape 为一个新的列表

  9. 对于每一个 index,位于范围 0 到 inputRank 内(不含上界):

    1. inputSizeinputShape[index]。

    2. inputSliceSizesizes[index]。

    3. 如果 strides[index] 非空,则令 stride 为其值,否则为 1 :

    4. 如果 inputSliceSize 为 0,则抛出 一个 TypeError

      如果 允许大小为 0 的维度,则修订这些步骤。[问题 #391]

    5. 如果 stride 小于 1,则抛出 一个 TypeError

    6. 如果 starts[index] 大于 inputSize,则抛出一个 TypeError

    7. 如果 starts[index] + inputSliceSize 大于 inputSize,则抛出 一个 TypeError

    8. 如果 inputSliceSize % stride != 0,则令 outputSizeRoundingExcess 为 1,否则为 0。

    9. outputSize 为 floor(inputSliceSize / stride) + outputSizeRoundingExcess

    10. 附加 outputSizeoutputShape

  10. outputDesc创建 MLOperandDescriptor的结果,给定 inputdataTypeoutputShape

  11. 建立图连接:

    1. output创建一个 MLOperand的结果,给定 outputDesc

    2. operator 为一个运算符,用于 "slice" 运算,给定 startssizesoptions

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 input

    5. operator输出设置为 output

  12. 返回 output

8.9.48. softmax

沿给定轴计算 N 维输入张量的 softmax 值。
partial interface MLGraphBuilder {
  MLOperand softmax(MLOperand input,
                    [EnforceRange] unsigned long axis,
                    optional MLOperatorOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits softmax;
};
参数:

返回:

softmax() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input "float32", "float16" "float32", "float16" 1 到 N 1 到 5
输出 与……相同 input "float32", "float16" 与……相同 input 1 到 5

MLOpSupportLimitssoftmax() 具有以下成员:

softmax, 类型为 MLSingleInputSupportLimits

运算符 softmax() 的支持限制。

softmax(input, axis, options) 方法的步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 验证操作数,给定 thisinput 返回 false,则抛出一个 TypeError

  3. 如果 inputdataType 不是其允许的数据类型之一 (根据此表),则抛出一个 TypeError

  4. 如果 axis 大于或等于 input,则抛出一个 TypeError

  5. 建立图连接:

    1. output复制 MLOperand的结果,给定 input

    2. operator 为一个运算符,用于 "softmax" 运算,给定 axisoptions

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 input

    5. operator输出设置为 output

  6. 返回 output

此运算的行为可以按如下方式使用其他运算进行通用模拟, 尽管用户代理通常具有更高效的实现。在底层 平台不直接支持某项运算的情况下,可以使用此分解作为 模板来指导实现。
function softmax(builder, input, axis) {
  // 此示例采用一种广为人知的实现技巧 [1],计算到最大值距离的
  // 指数,而不是直接计算输入值本身的指数,以提高
  // 结果的数值稳定性。
  // [1]: https://cs231n.github.io/linear-classify/#softmax
  const maxX = builder.reduceMax(input, {axes: [axis], keepDimensions: true});
  const expX = builder.exp(builder.sub(input, maxX));
  return builder.div(
    expX, builder.reduceSum(expX, {axes: [axis], keepDimensions: true}));
}

8.9.49. softplus

计算输入张量的 softplus 函数 。计算遵循表达式 ln(1 + exp(x))
partial interface MLGraphBuilder {
  MLOperand softplus(MLOperand input, optional MLOperatorOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits softplus;
};
参数:

返回:

softplus() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input "float32", "float16" "float32", "float16" N 0 到 5
输出 与……相同 input "float32", "float16" 与……相同 input 0 到 5

MLOpSupportLimitssoftplus() 具有以下成员:

softplus, 类型为 MLSingleInputSupportLimits

运算符 softplus() 的支持限制。

softplus(input, options) 方法的步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 验证操作数,给定 thisinput 返回 false,则抛出一个 TypeError

  3. 如果 inputdataType 不是其允许的数据类型之一 (根据此表),则抛出一个 TypeError

  4. 建立图连接:

    1. output复制 MLOperand的结果,给定 input

    2. operator 为一个运算符,用于 "softplus" 运算和 options

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 input

    5. operator输出设置为 output

  5. 返回 output

此运算的行为可以按如下方式使用其他运算进行通用模拟, 尽管用户代理通常具有更高效的实现。在底层 平台不直接支持某项运算的情况下,可以使用此分解作为 模板来指导实现。
function softplus(builder, input) {
  return builder.log(
    builder.add(builder.exp(input), builder.constant(input.dataType, 1)));
}

8.9.50. softsign

计算输入张量的 softsign 函数。 计算遵循表达式 x / (1 + |x|)
partial interface MLGraphBuilder {
  MLOperand softsign(MLOperand input, optional MLOperatorOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits softsign;
};
此运算的行为可以按如下方式使用其他运算进行通用模拟, 尽管用户代理通常具有更高效的实现。在底层 平台不直接支持某项运算的情况下,可以使用此分解作为 模板来指导实现。
function softsign(builder, input) {
  return builder.div(
    input,
    builder.add(builder.constant(input.dataType, 1), builder.abs(input)));
}
参数:

返回:

softsign() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input "float32", "float16" "float32", "float16" N 0 到 5
输出 与……相同 input "float32", "float16" 与……相同 input 0 到 5

MLOpSupportLimitssoftsign() 具有以下成员:

softsign, 类型为 MLSingleInputSupportLimits

运算符 softsign() 的支持限制。

softsign(input, options) 方法的步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 验证操作数,给定 thisinput 返回 false,则抛出一个 TypeError

  3. 如果 inputdataType 不是其允许的数据类型之一 (根据此表),则抛出一个 TypeError

  4. 建立图连接:

    1. output复制 MLOperand的结果,给定 input

    2. operator 为一个运算符,用于 "softsign" 运算和 options

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 input

    5. operator输出设置为 output

  5. 返回 output

8.9.51. split

沿给定轴将输入张量拆分为多个子张量。
dictionary MLSplitOptions : MLOperatorOptions {
  [EnforceRange] unsigned long axis = 0;
};

partial interface MLGraphBuilder {
  sequence<MLOperand> split(
      MLOperand input,
      ([EnforceRange] unsigned long or sequence<[EnforceRange] unsigned long>) splits,
      optional MLSplitOptions options = {});
};

dictionary MLSplitSupportLimits {
  MLTensorLimits input;
  MLTensorLimits outputs;
};

partial dictionary MLOpSupportLimits {
  MLSplitSupportLimits split;
};
参数:

返回:sequence<MLOperand>。 拆分后的输出张量。如果 splits 是一个 unsigned long, 输出的大小等于 splits。 每个输出张量的形状与 input 相同,但 axis 的维度大小等于 input 沿 axis 的维度大小除以 splits 所得的商。 如果 splits 是一个 sequence<unsigned long>, 输出的大小等于 splits大小。 第 i 个输出张量的形状与 input 相同,但沿 axis 的维度大小为 splits[i]。

MLSplitOptions 具有以下成员:

axis, 类型为 unsigned long,默认为 0

进行拆分的维度。其值必须位于 [0, N-1] 范围内,其中 N 是输入 张量的

split() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input 任意 "float32", "float16", "int32" 1 到 N 1 到 5
输出 与……相同 input "float32", "float16", "int32" 与……相同 input 1 到 5

MLSplitSupportLimits 具有以下成员:

input, 类型为 MLTensorLimits

MLTensorLimits 用于 input 操作数。

outputs, 类型为 MLTensorLimits

MLTensorLimits 用于所有输出操作数。

MLOpSupportLimitssplit() 具有以下成员:

split, 类型为 MLSplitSupportLimits

运算符 split() 的支持限制。

split(input, splits, options) 方法的步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 验证操作数,给定 thisinput 返回 false,则抛出一个 TypeError

  3. axisoptions.axis

  4. 如果 axis 大于或等于 input,则抛出一个 TypeError

  5. 如果 splits 是一个 unsigned long, 则:

    1. 如果 splits 不是有效张量数量,则抛出一个 TypeError

    2. 如果 inputshape[axis] % splits 不为 0,则抛出一个 TypeError

    3. 否则,令 splitCountsplits

  6. 如果 splits 是一个sequence<unsigned long>, 则:

    1. 如果 splits大小 不是有效张量数量,则抛出一个 TypeError

    2. 如果其任何等于 0,则抛出 一个 TypeError

      如果 允许大小为 0 的维度,则修订上述步骤。[问题 #391]

    3. 如果其所有之和不等于 inputshape[axis],则抛出一个 TypeError

    4. 否则,令 splitCountsplits大小

  7. 建立图连接:

    1. operator 为一个运算符,用于 "split" 运算,给定 splitsoptions

    2. outputs 为一个新的列表

    3. 对于每一个 index,位于 范围 0 到 splitCount 内(不含上界):

      1. operand复制 MLOperand的结果,给定 input

      2. 如果 splits 是一个 unsigned long, 则令 newDimensionoperandshape[axis] / splits

      3. 否则,令 newDimensionsplits[index]。

      4. operandshape[axis] 设置为 newDimension

      5. operand.[[operator]] 设置为 operator

      6. 附加 operandoutputs

    4. operator输入设置为 input

    5. operator输出设置为 outputs

  8. 返回 outputs

此运算的行为可以按如下方式使用其他运算进行通用模拟, 尽管用户代理通常具有更高效的实现。在底层 平台不直接支持某项运算的情况下,可以使用此分解作为 模板来指导实现。
function split(builder, input, splits, options) {
  // 此示例展示 splits 参数为数组的情况。
  const outputs = [];
  const inputShape = input.shape;
  const inputRank = inputShape.length;
  let starts = Array(inputRank).fill(0);
  let sizes = inputShape;
  let start = 0;
  for (const size of splits) {
    starts[options.axis] = start;
    sizes[options.axis] = size;
    outputs.push(builder.slice(input, starts, sizes));
    start += size;
  }
  return outputs;
}

8.9.52. tanh

计算输入张量的双曲正切函数。 计算遵循表达式 (exp(2 * x) - 1) / (exp(2 * x) + 1)
partial interface MLGraphBuilder {
  MLOperand tanh(MLOperand input, optional MLOperatorOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits tanh;
};
参数:

返回:

tanh() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input "float32", "float16" "float32", "float16" N 0 到 5
输出 与……相同 input "float32", "float16" 与……相同 input 0 到 5

MLOpSupportLimitstanh() 具有以下成员:

tanh, 类型为 MLSingleInputSupportLimits

运算符 tanh() 的支持限制。

tanh(input, options) 方法的 步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 验证操作数,给定 thisinput 返回 false,则抛出一个 TypeError

  3. 如果 inputdataType 不是其允许的数据类型之一 (根据此表),则抛出一个 TypeError

  4. 建立图连接:

    1. output复制 MLOperand的结果,给定 input

    2. operator 为一个运算符,用于 "tanh" 运算,给定 options

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 input

    5. operator输出设置为 output

  5. 返回 output

此运算的行为可以按如下方式使用其他运算进行通用模拟, 尽管用户代理通常具有更高效的实现。在底层 平台不直接支持某项运算的情况下,可以使用此分解作为 模板来指导实现。
function tanh(builder, input) {
  return builder.div(
    builder.sub(
      builder.exp(builder.mul(builder.constant(input.dataType, 2), input)),
      builder.constant(input.dataType, 1)),
    builder.add(
      builder.exp(builder.mul(builder.constant(input.dataType, 2), input)),
      builder.constant(input.dataType, 1)));
}

8.9.53. tile

沿每个维度将张量重复给定次数。
partial interface MLGraphBuilder {
  MLOperand tile(MLOperand input,
                 sequence<unsigned long> repetitions,
                 optional MLOperatorOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits tile;
};
参数:

返回:一个 MLOperand。 反转后的 N 维张量。

tile() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input 任意 "float32", "float16", "int32" N 0 到 5
输出 与……相同 input "float32", "float16", "int32" 与……相同 input 0 到 5

MLOpSupportLimitstile() 具有以下成员:

tile, 类型为 MLSingleInputSupportLimits

运算符 tile() 的支持限制。

tile(input, repetitions, options) 方法的步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 验证操作数,给定 thisinput 返回 false,则抛出一个 TypeError

  3. 如果 repetitions大小不 等于 input,则抛出一个 TypeError

  4. 如果 repetitions 的值中包含 0,则抛出一个 TypeError

    如果允许大小为 0 的 维度,则修订这些步骤。[问题 #391]

  5. outputShapeinputshape 的副本。

  6. 对于每一个 index,位于范围 0 到 outputShape大小内(不含上界):

    1. outputShape[index] 设置为 outputShape[index] * repetitions[index]。

  7. outputDescriptor创建 MLOperandDescriptor的结果,给定 inputdataTypeoutputShape

  8. 建立图连接:

    1. output创建一个 MLOperand的结果,给定 outputDescriptor

    2. operator 为一个运算符,用于 "tile" 运算,给定 options

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 input

    5. operator输出设置为 output

  9. 返回 output

8.9.54. transpose

根据 permutation 置换输入张量的维度。
dictionary MLTransposeOptions : MLOperatorOptions {
  sequence<[EnforceRange] unsigned long> permutation;
};

partial interface MLGraphBuilder {
  MLOperand transpose(MLOperand input, optional MLTransposeOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits transpose;
};

MLTransposeOptions 具有以下成员:

permutation, 类型为 sequence<[EnforceRange] unsigned long>

用于置换输出形状的值。 默认值为 [N-1, ..., 0],其中 N 是输入张量的,例如三维张量为 [2,1,0]。 这些默认值会使输出成为输入的转置张量。指定时, 值的数量必须与输入张量的相同,并且值必须位于 0 到 N-1 的范围内且不得重复。

参数:

返回:一个 MLOperand。 置换或转置后的 N 维张量。

transpose() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input 任意 "float32", "float16", "int32" N 0 到 5
输出 与……相同 input "float32", "float16", "int32" 与……相同 input 0 到 5

MLOpSupportLimitstranspose() 具有以下成员:

transpose, 类型为 MLSingleInputSupportLimits

运算符 transpose() 的支持限制。

transpose(input, options) 方法的步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 验证操作数,给定 thisinput 返回 false,则抛出一个 TypeError

  3. 如果 options.permutation存在,则令 options.permutationinputshape所有索引的反转序列。

  4. 否则,如果 options.permutation 存在

    1. 如果其大小不等于 input, 则抛出一个 TypeError

    2. 如果其不在 范围 0 到 input内(不含上界),则抛出 一个 TypeError

    3. 如果它包含重复值,则抛出 一个 TypeError

  5. 建立图连接:

    1. output复制 MLOperand的结果,给定 input

    2. operator 为一个运算符,用于 "transpose" 运算,给定 options

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 input

    5. operator输出设置为 output

  6. 返回 output

8.9.55. triangular

给定一个二维张量(矩阵),返回一个包含输入张量上三角或下三角部分的二维张量。 如果输入张量具有多于 2 个维度,则将其视为一批矩阵,并且 结果具有相同的形状。
dictionary MLTriangularOptions : MLOperatorOptions {
  boolean upper = true;
  [EnforceRange] long diagonal = 0;
};

partial interface MLGraphBuilder {
  MLOperand triangular(MLOperand input, optional MLTriangularOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits triangular;
};

MLTriangularOptions 具有以下成员:

upper, 类型为 boolean,默认为 true

指示输出是保留输入矩阵的上部还是下部。True 表示保留上部。

diagonal, 类型为 long,默认为 0

指定输入矩阵主对角线上方或下方有多少条对角线被保留或 排除。值为 0 表示除主对角线之外的对角线均不受影响。

参数:

返回:一个 MLOperand。 表示三角矩阵的输出张量,或一批与输入具有相同形状的 矩阵。

triangular() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
input 任意 "float32", "float16" 2 到 N 2 到 5
输出 与……相同 input "float32", "float16" 与……相同 input 2 到 5

MLOpSupportLimitstriangular() 具有以下成员:

triangular, 类型为 MLSingleInputSupportLimits

运算符 triangular() 的支持限制。

triangular(input, options) 方法的步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 验证操作数,给定 thisinput 返回 false,则抛出一个 TypeError

  3. 如果 input不是其允许的秩之一(根据此表),则抛出一个 TypeError

  4. 建立图连接:

    1. output复制 MLOperand的结果,给定 input

    2. operator 为一个运算符,用于 "triangular" 运算,给定 options

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 input

    5. operator输出设置为 output

  5. 返回 output

triangular 在不同 diagonal 设置下如何工作的示例。
// input:
//   [[7, 1, 2],
//    [9, 4, 8],
//    [2, 6, 3]]
const input = builder.constant(
  {dataType: 'float32', shape: [3, 3]},
  new Float32Array([7, 1, 2, 9, 4, 8, 2, 6, 3]));

// 上三角矩阵:
//   [[7, 1, 2],
//    [0, 4, 8],
//    [0, 0, 3]]
const upper = builder.triangular(input);

// 额外排除一组对角线的上三角矩阵:
//   [[0, 1, 2],
//    [0, 0, 8],
//    [0, 0, 0]]
const upperPositive = builder.triangular(input, {diagonal: 1});

// 额外保留一组对角线的上三角矩阵:
//   [[7, 1, 2],
//    [9, 4, 8],
//    [0, 6, 3]]
const upperNegative = builder.triangular(input, {diagonal: -1});

// 下三角矩阵:
//   [[7, 0, 0],
//    [9, 4, 0],
//    [2, 6, 3]]
const lower = builder.triangular(input, {upper: false});

// 额外保留一组对角线的下三角矩阵:
//   [[7, 1, 0],
//    [9, 4, 8],
//    [2, 6, 3]]
const lowerPositive = builder.triangular(input, {upper: false, diagonal: 1});

// 额外排除一组对角线的下三角矩阵:
//   [[0, 0, 0],
//    [9, 0, 0],
//    [2, 6, 0]]
const lowerNegative = builder.triangular(input, {upper: false, diagonal: -1})

// 包含两个批次的下三角矩阵:
//   [[[7, 0, 0],
//     [9, 4, 0],
//     [2, 6, 3]],
//    [[1, 0, 0],
//     [4, 5, 0],
//     [7, 8, 9]]]
const lowerWithBatches = builder.triangular(input, {upper: false});

8.9.56. where

根据 condition 张量中对应的值,从 trueValuefalseValue 张量中选择值,其中非零为 true,零为 false。condition 张量通常是某个逐元素逻辑运算的输出。

该运算将根据 [numpy-broadcasting-rule] 进行广播。 输入张量必须双向可广播。输出张量的 是输入张量的最大值。 对于输出张量的每个维度,其大小是输入张量沿该维度 大小的最大值。

partial interface MLGraphBuilder {
  MLOperand where(MLOperand condition,
                  MLOperand trueValue,
                  MLOperand falseValue,
                  optional MLOperatorOptions options = {});
};

dictionary MLWhereSupportLimits {
  MLTensorLimits condition;
  MLTensorLimits trueValue;
  MLTensorLimits falseValue;
  MLTensorLimits output;
};

partial dictionary MLOpSupportLimits {
  MLWhereSupportLimits where;
};
参数:

返回:一个 MLOperand。 输出张量,其中包含逐元素从 trueValuefalseValue 张量中选择的值。

where() 的张量限制
操作数 允许的 数据类型 必需的 数据类型 允许的秩 必需的秩
condition "uint8" "uint8" N 0 到 5
trueValue 任意 "float32", "float16", "int32" N 0 到 5
falseValue 与……相同 trueValue "float32", "float16", "int32" N 0 到 5
输出 与……相同 trueValue "float32", "float16", "int32" N 0 到 5

MLWhereSupportLimits 具有以下成员:

condition, 类型为 MLTensorLimits

MLTensorLimits 用于 condition 操作数。

trueValue, 类型为 MLTensorLimits

MLTensorLimits 用于 trueValue 操作数。

falseValue, 类型为 MLTensorLimits

MLTensorLimits 用于 falseValue 操作数。

output, 类型为 MLTensorLimits

MLTensorLimits 用于 output 操作数。

MLOpSupportLimitswhere() 具有以下成员:

where, 类型为 MLWhereSupportLimits

运算符 where() 的支持限制。

where(condition, trueValue, falseValue, options) 方法的步骤为:
  1. 如果 this 无法构建,则抛出一个 "InvalidStateError" DOMException

  2. 如果使用 验证操作数,给定 this 以及 conditiontrueValuefalseValue 中任一个返回 false,则抛出一个 TypeError

  3. 如果 conditiontrueValuefalseValue 中任一个的dataType 不是其允许的数据类型之一 (根据此表),则抛出一个 TypeError

  4. outputShape双向广播 trueValueshapefalseValueshape 的结果。

    1. 如果返回失败,则抛出 一个 TypeError

  5. outputShape 设置为双向广播 conditionshapeoutputShape 的结果。

    1. 如果返回失败,则抛出 一个 TypeError

  6. descriptor创建 MLOperandDescriptor的结果,给定 trueValuedataTypeoutputShape

  7. 建立图连接:

    1. output创建一个 MLOperand的结果,给定 thisdescriptor

    2. operator 为一个运算符,用于 "where" 运算,给定 conditiontrueValuefalseValueoptions

    3. output.[[operator]] 设置为 operator

    4. operator输入设置为 conditiontrueValuefalseValue

    5. operator输出设置为 output

  8. 返回 output

此运算的行为可以按如下方式使用其他运算进行通用模拟, 尽管用户代理通常具有更高效的实现。在底层 平台不直接支持某项运算的情况下,可以使用此分解作为 模板来指导实现。
function where(builder, condition, trueValue, falseValue) {
  const c = builder.clamp(condition, {'minValue': 0, 'maxValue': 1});
  builder.add(
    builder.mul(trueValue, builder.cast(c, trueValue.dataType)),
    builder.mul(
      falseValue, builder.cast(builder.logicalNot(c), falseValue.dataType)));
}

9. 算法

9.1. 广播

广播描述了 WebNN 在图构建和计算过程中如何处理形状不同的张量。它深受 [NumPy] 的影响,并遵循 [numpy-broadcasting-rule]。宽泛地说,它允许 较小张量上的运算在较大张量的形状上进行“广播”,从而无需制作副本即可重复 应用相同的数据。

最简单的示例是将标量常量应用于 N 维张量,并使用诸如 add()mul() 这样的逐元素二元运算。无需分配和填充一个匹配的 N 维张量来包含 标量常量的多个副本,这些逐元素运算允许直接使用标量常量,并 将标量值广播到 N 维张量上。考虑以下事项后,相同的 逻辑也适用于其他维度的张量。

输入张量的形状必须兼容。如果第一个张量可以通过沿大小为 1 的轴重复第一个张量 或跨新维度重复,并从最后一个(最右侧)维度开始扩展,则一个张量可以单向 广播到另一个张量。 例如,可以将一个 [4] 张量重复 5 次广播为 [5, 4] 张量。一个 [1] 张量可以通过在最后一个维度重复 4 次并在前一个维度重复 5 次广播为 [5,4] 张量。单向广播对于诸如 expand() 这样的运算很重要,其中目标张量形状是显式给定的。

如果两个张量可以从最后一个维度开始,在各个维度上相互 “扩展”(重复),则它们可以双向广播。例如,可以将一个 [5,1] 张量与一个 [1,6] 张量进行双向广播,方法是在最后一个维度将第一个 张量重复 6 次,并在前一个维度将第二个张量重复 5 次。运算 结果将是一个 [5,6] 张量。双向广播适用于逐元素 运算。

如果所有维度都可以按整数倍上采样到目标张量的形状,则一个张量是块级可广播的。例如,一个 [4,5] 张量可以按块 广播到 [16,10] 张量,因为它是精确倍数(16 % 4 = 0,10 % 5 = 0),方法是在 第一个维度将每个元素重复 4 次,在最后一个维度将每个元素重复 2 次(例如,最后一个维度中的值 [1,2,3,4,5] 会重复为 [1,1,2,2,3,3,4,4,5,5])。但是,一个 [4,5] 张量与一个 [9,3] 张量不兼容,因为两个维度都存在 非零余数(9 % 4 = 1,3 % 5 = 3)。块级广播适用于在 较大块中共享公共值以节省内存。两个张量应具有相同的秩,输出形状 就是较小张量要上采样到的目标张量形状。

某些运算允许使用特殊语义进行广播。例如,matmul() 将输入张量的最后两个维度视为矩阵的行和列,并且第一个矩阵中的列数 必须等于第二个矩阵中的行数。矩阵 乘法会在任何附加维度上双向广播,将输入张量视为 要相乘的矩阵堆栈。

单向广播形状 shapeFromshapeTo,执行以下步骤。shapeFromshapeTo 是表示张量维度的正整数列表, 这些步骤返回一个新的正整数列表, 或失败。
  1. sizeFromshapeFrom大小

  2. sizeToshapeTo大小

  3. 如果 sizeFrom > sizeTo,则返回失败。

  4. paddedShapeFromshapeFrom 的一个克隆

  5. paddedShapeFrom大小小于 sizeTo 时,向 paddedShapeFrom 前置 1 。

  6. outputShape 为一个新的列表

  7. 对于每一个 index,位于范围 0 到 sizeTo 内(不含上界):

    1. dimFrompaddedShapeFrom[index]。

    2. dimToshapeTo[index]。

    3. 如果 dimTo 不等于 dimFromdimFrom 不 等于 1,则返回失败。

    4. 附加 dimTooutputShape

  8. 返回 outputShape

如果单向广播 shapeFromshapeTo 不会导致失败,则 shapeFrom 可以单向广播shapeTo

双向广播形状 shapeAshapeB,执行以下步骤。shapeAshapeB 是表示张量维度的正整数列表, 这些步骤返回一个新的正整数列表, 或失败。
  1. sizeAshapeA大小

  2. sizeBshapeB大小

  3. outputSizesizeAsizeB 中的最大值。

  4. paddedAshapeA 的一个克隆

  5. paddedA大小小于 outputSize 时,向 paddedA 前置 1 。

  6. paddedBshapeB 的一个克隆

  7. paddedB大小小于 outputSize 时,向 paddedB 前置 1 。

  8. outputShape 为一个新的列表

  9. 对于每一个 index,位于范围 0 到 outputSize 内(不含上界):

    1. dimApaddedA[index]。

    2. dimBpaddedB[index]。

    3. 如果 dimA 不等于 dimB,并且 dimA 不等于 1,并且 dimB 不等于 1,则返回失败。

    4. 附加 dimAdimB 中的最大值到 outputShape

  10. 返回 outputShape

如果双向广播 shapeAshapeB 不会导致失败,则 shapeA 可以双向广播shapeB

块级广播形状 shapeFromshapeTo,执行以下步骤。shapeFromshapeTo 是表示张量维度的正整数列表, 这些步骤返回 true 或 false。
  1. 如果 shapeFrom大小不等于 shapeTo大小,则返回 false。

  2. 对于每一个 index,位于范围 0 到 shapeTo大小内(不含上界):

    1. 如果 shapeFrom[index] 不能被 shapeTo[index] 整除,则返回 false。

  3. 返回 true。

如果块级 广播 shapeFromshapeTo 返回 true,则 shapeFrom 可以块级广播shapeTo

9.2. 类型转换

在算法中,当作为 MLNumberdouble 传入的参数需要转换以匹配输入或输出 MLOperandMLOperandDataType 时,会使用显式数值类型转换。

要将数字 x 转换 为给定的 MLOperandDataType dataType,执行以下步骤。它们返回一个数值。
  1. 根据 dataType 进行切换:

    "float32"

    返回 ConvertToFloat(x, 32)。

    "float16"

    返回 ConvertToFloat(x, 16)。

    "int64"

    返回 ConvertToInt(x, 64, "signed")。

    "uint64"

    返回 ConvertToInt(x, 64, "unsigned")。

    "int32"

    返回 ConvertToInt(x, 32, "signed")。

    "uint32"

    返回 ConvertToInt(x, 32, "signed")。

    "int8"

    返回 ConvertToInt(x, 8, "signed")。

    "uint8"

    返回 ConvertToInt(x, 8, "unsigned")。

注:转换的输入是一个范围和精度均不受限制的抽象数值, 包括特殊值 Infinity、-Infinity 和 NaN。输出也是一个抽象数值,但 可以由指定类型精确表示。

ConvertToFloat(x, bitLength) 的步骤为:
  1. 如果 x 为 NaN,则返回 NaN。

  2. 根据 bitLength 进行切换:

    32
    1. upperBound 为 2128

    2. lowerBound 为 -2128

    3. S[IEEE-754-2019] binary32 浮点值(-0 除外)的集合,并加入特殊值 upperBoundlowerBound

    16
    1. upperBound 为 216

    2. lowerBound 为 -216

    3. S[IEEE-754-2019] binary16 浮点值(-0 除外)的集合,并加入特殊值 upperBoundlowerBound

  3. yS 中最接近 x 的数值,如果有两个同样接近的值,则选择 有效数字为偶数的数值。为此,两个 特殊值 lowerBoundupperBound 被视为具有偶数 有效数字。

  4. 如果 yupperBound,则返回 +Infinity。

  5. 如果 ylowerBound,则返回 -Infinity。

  6. 如果 y 是 +0 且 x 为负数,则返回 -0。

  7. 返回 y

注:这基于 [WEBIDL] 中的定义,但 扩展为涵盖 16 位浮点值。

ConvertToInt(x, bitLength, signedness) 的步骤为:
  1. 如果 signedness 是 "unsigned",则:

    1. lowerBound 为 0。

    2. upperBound 为 2bitLength - 1。

  2. 否则:

    1. lowerBound 为 -(2bitLength - 1)。

    2. upperBound 为 2bitLength - 1 - 1。

  3. 如果 x 是 -0,则将 x 设置为 +0。

  4. 如果 x 是 NaN,则返回 +0。

  5. x 设置为 min(max(x, lowerBound), upperBound)。

  6. x 舍入到最近的整数,如果正好位于两个整数中间,则选择偶数整数, 并选择 +0 而不是 -0。

  7. 返回 x

注:这基于 [WEBIDL] 中的定义,但有以下 区别:不会特殊处理 64 位整数,输入 x 是一个抽象 数值,并且始终执行限制。

9.3. 其他

如果列表 A大小等于 B大小 ,并且 A 中的每个都等于 列表 B 中相同索引处的 项,则列表 A 等于该列表。

[INFRA] 中提供 定义后移除此内容。[whatwg/infra 问题 #664]

10. 示例

给定以下构建图:
constant1 ---+
             +--- Add ---> intermediateOutput1 ---+
input1    ---+                                    |
                                                  +--- Mul---> output
constant2 ---+                                    |
             +--- Add ---> intermediateOutput2 ---+
input2    ---+
以下代码实现该图:
// 使用四维张量。
const TENSOR_SHAPE = [1, 2, 2, 2];
const TENSOR_SIZE = 8;

const context = await navigator.ml.createContext();
const builder = new MLGraphBuilder(context);

// 创建 MLOperandDescriptor 对象。
const desc = {
  dataType: 'float32',
  shape: TENSOR_SHAPE
};

// constant1 是值为 0.5 的常量 MLOperand。
const constantBuffer1 = new Float32Array(TENSOR_SIZE).fill(0.5);
const constant1 = builder.constant(desc, constantBuffer1);

// input1 是输入 MLOperand 之一。其值将在
// 执行前设置。
const input1 = builder.input('input1', desc);

// constant2 是另一个值为 0.5 的常量 MLOperand。
const constantBuffer2 = new Float32Array(TENSOR_SIZE).fill(0.5);
const constant2 = builder.constant(desc, constantBuffer2);

// input2 是另一个输入 MLOperand。其值将在执行前设置。
const input2 = builder.input('input2', desc);

// intermediateOutput1 是第一个 Add 运算的输出。
const intermediateOutput1 = builder.add(constant1, input1);

// intermediateOutput2 是第二个 Add 运算的输出。
const intermediateOutput2 = builder.add(constant2, input2);

// output 是 Mul 运算的输出 MLOperand。
const output = builder.mul(intermediateOutput1, intermediateOutput2);

11. 运算符模拟

本节为非规范性内容。

其他神经网络推理 API 中存在的运算通常可以使用 WebNN 中存在的运算进行模拟。

11.1. squeeze

squeeze 运算 返回一个移除了输入中所有指定且大小为 1 的维度的张量。它可以使用 reshape() 运算按如下方式通用实现:
function squeeze(builder, input, axes) {
  if (!axes)
    axes = [];
  if (!axes.length)
    input.shape.forEach((item, i) => {
      axes.push(i);
    });
  const shape = Array.from(input.shape);
  for (let axis of axes.sort().reverse())
    if (axis < shape.length && shape[axis] == 1)
      shape.splice(axis, 1);
  return builder.reshape(input, shape);
}

11.2. unsqueeze

unsqueeze 运算 返回一个在指定位置插入大小为一的维度的新张量。它可以 使用 reshape() 运算按如下方式通用实现:
function unsqueeze(builder, input, axes) {
  const shape = Array.from(input.shape);
  for (let axis of axes.sort())
    shape.splice(axis, 0, 1);
  return builder.reshape(input, shape);
}

11.3. flatten

flatten 运算 将输入重塑为一维张量。它可以使用 reshape() 运算按如下方式通用实现:
function flatten(builder, input, axis) {
  if (axis > input.shape.length)
    return input;
  const before = axis.slice(0, axis).reduce((a, b) => a * b, 1);
  const after = axis.slice(axis, input.shape.length).reduce((a, b) => a * b, 1);
  return builder.reshape(input, [before, after]);
}

12. 附录

12.1. MLOperandDataTypeArrayBufferView 的兼容性

MLOperandDataType ArrayBufferView
float32 Float32Array
float16 Float16Array
int64 BigInt64Array
uint64 BigUint64Array
int32 Int32Array
uint32 Uint32Array
int8 Int8Array
uint8 Uint8Array

Float16Array 处于 ECMA 第 3 阶段,表明其设计已完成。 希望在原生实现之前启用此类型的实现者可以通过 Uint16Array 传递原始 位来模拟该类型。 [问题 webnn#373]

13. 致谢

本规范遵循 Android Neural Networks API C API 的概念。

感谢 Tomoyuki Shimizu、Ningxin Hu、Zhiqiang Yu 和 Belem Zhang 提供 用例。

感谢 Nikhil Thorat、Daniel Smilkov、Ganesan Ramalingam、Rafael Cintron 和 Benjamin Poulain 对 API 规范所做的贡献。

感谢 Sangwhan Moon 和 W3C Technical Architecture Group 从 Web 架构适配、设计一致性和开发者易用性方面审查本规范。

感谢 Zoltan Kis 添加算法,并使浏览本规范成为一种愉悦的体验。 感谢 Joshua Bell 使本规范与现代编辑惯例保持一致。感谢 Ningxin Hu、Lisha Guo、Shiyi Zou、Mingming Xu、Junwei Fu、Bruce Dai 和 Bin Miao 的细致审查和意见。

感谢 W3C Privacy Interest Group 提供隐私和安全审查及反馈。

感谢 Alex Gough 和 Chrome Security 团队提供安全审查和问题。

感谢 Michal Karzynski 分享来自 ONNX 的实用指南和经验。

感谢 Kaustubha Govind 和 Chrome 隐私审查人员提供反馈和隐私方面的考虑。

感谢 Jiewei Qian 提供 Chromium 实现审查和反馈。

感谢 Dwayne Robinson、Joshua Lochner 和 Wanming Lin 在研究并提供 transformer 支持建议方面所做的工作。还要特别感谢 Dwayne 和 Wanming 对 运算符合性和 web-platform-tests 实现提供审查。

感谢 Feng Dai 持续贡献,使 web-platform-tests 与 规范同步发展。

感谢 Fuqiao Xue 和 W3C Internationalization Activity 提供审查和建议。

14. 变更

本节为非规范性内容。

本节按照变更类别记录自上一次主要发布以来对本规范所做的变更。

候选推荐快照 2024 年 4 月 11 日2026 年 1 月 22 日之间的详细变更

新功能(第 4 类

不添加新功能的其他变更(第 3 类

不影响文档解释功能的变更(第 2 类

横向审查(第 2 类第 3 类

编辑性变更(第 2 类

一致性

文档 约定

一致性要求通过 描述性断言和 RFC 2119 术语的组合来表达。 本文档规范性部分中的关键词 “MUST”、“MUST NOT”、“REQUIRED”、“SHALL”、“SHALL NOT”、“SHOULD”、“SHOULD NOT”、“RECOMMENDED”、 “MAY” 和 “OPTIONAL” 应按照 RFC 2119 中的描述进行解释。 但是,为了可读性, 这些词在本规范中并非全部以大写字母出现。

本规范的所有文本均为规范性内容, 明确标记为非规范性的章节、示例和注释除外。[RFC2119]

本规范中的示例以“例如”一词引入, 或通过 class="example" 与规范性文本分隔, 如下所示:

这是一个资料性示例。

资料性注释以“注”一词开头, 并通过 class="note" 与规范性文本分隔, 如下所示:

注:这是一条资料性注释。

一致性 算法

作为算法一部分以祈使语气表述的要求 (例如“移除任何前导空格字符” 或“返回 false 并中止这些步骤”) 应按引入该算法时使用的关键词 (“must”、“should”、“may”等) 的含义进行解释。

以算法或特定步骤表述的一致性要求 可以以任何方式实现, 只要最终结果等效即可。 特别是,本规范中定义的算法 旨在易于理解, 并非旨在实现高性能。 鼓励实现者进行优化。

索引

本规范定义的 术语

由引用定义的 术语

参考文献

规范性参考文献

[ECMASCRIPT]
ECMAScript 语言规范. URL: https://tc39.es/ecma262/multipage/
[HTML]
Anne van Kesteren; et al. HTML 标准. 现行标准. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra 标准. 现行标准. URL: https://infra.spec.whatwg.org/
[NUMPY-BROADCASTING-RULE]
SciPy 社区. NumPy 的通用 广播规则. 2019 年 7 月. URL: https://numpy.org/doc/stable/user/basics.broadcasting.html#general-broadcasting-rules
[PERMISSIONS-POLICY-1]
Ian Clelland. 权限策略. 18 2026 年 6 月. WD. URL: https://www.w3.org/TR/permissions-policy-1/
[RFC2119]
S. Bradner. RFC 中用于 指示要求级别的关键词. 1997 年 3 月. 当前最佳实践. URL: https://datatracker.ietf.org/doc/html/rfc2119
[WEBGPU]
Kai Ninomiya; Brandon Jones; Jim Blandy. WebGPU. 12 2026 年 8 月. CRD. URL: https://www.w3.org/TR/webgpu/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL 标准. 现行 标准. URL: https://webidl.spec.whatwg.org/

非规范性参考文献

[Batch-Normalization]
Sergey Ioffe; Christian Szegedy. 批归一化: 通过减少内部协变量偏移加速深度网络训练. 2015 年 3 月. URL: https://arxiv.org/abs/1502.03167
[ContextualLoss]
Roey Mechrez; Itamar Talmi; Lihi Zelnik-Manor. 使用非对齐数据进行图像转换的 上下文损失. 2018 年 7 月. URL: https://arxiv.org/abs/1803.02077
[DeepLabv3+]
Liang-Chieh Chen; et al. 用于语义图像分割的空洞 可分离卷积编码器-解码器. 2018 年 8 月. URL: https://arxiv.org/abs/1802.02611
[DeepMoji]
Bjarke Felbo; et al. 使用数百万次 emoji 出现来 学习用于检测情感、情绪和讽刺的跨领域表示. 2017 年 10 月. URL: https://arxiv.org/abs/1708.00524
[ELU]
Djork-Arné Clevert; Thomas Unterthiner; Sepp Hochreiter. 通过指数 线性单元(ELU)实现快速准确的深度网络学习. 2016 年 2 月. URL: https://arxiv.org/abs/1511.07289
[Error-Function]
Larry C. Andrews. 工程师数学中的特殊 函数. 1998. URL: https://books.google.com/books?id=2CAqsF-RebgC&pg=PA110
[FaceForensics++]
Andreas Rössler; et al. FaceForensics++. 2019 年 1 月. URL: https://github.com/ondyari/FaceForensics
[FaceNet]
Florian Schroff; Dmitry Kalenichenko; James Philbin. FaceNet:用于人脸识别和 聚类的统一嵌入. 2015 年 6 月. URL: https://arxiv.org/abs/1503.03832
[FAN]
Adrian Bulat; Georgios Tzimiropoulos. 我们距离 解决二维和三维人脸对齐问题还有多远?(以及一个包含 230,000 个三维人脸 标志点的数据集). 2017 年 9 月. URL: https://arxiv.org/abs/1703.07332
[GNMT]
Minh-Thang Luong; Eugene Brevdo; Rui Zhao. 神经机器 翻译(seq2seq)教程. 2017 年 5 月. URL: https://github.com/tensorflow/nmt
[GPT2]
Alec Radford; et al. 语言 模型是无监督多任务学习器. 2019 年 2 月. URL: https://d4mucfpksywv.cloudfront.net/better-language-models/language-models.pdf
[GRU]
Kyunghyun Cho; et al. 使用 RNN 编码器-解码器学习短语表示 以进行统计机器翻译. 2014 年 9 月. URL: https://arxiv.org/pdf/1406.1078.pdf
[HR-TIME-3]
Yoav Weiss. 高精度时间. 24 2026 年 3 月. WD. URL: https://www.w3.org/TR/hr-time-3/
[IEEE-754-2019]
IEEE 浮点 算术标准. 22 2019 年 7 月. URL: https://ieeexplore.ieee.org/document/8766229
[IM2TXT]
Oriol Vinyals; et al. Show and Tell:从 2015 MSCOCO 图像描述挑战中吸取的经验. 2016 年 9 月. URL: https://arxiv.org/abs/1609.06647
[Instance-Normalization]
Dmitry Ulyanov; Andrea Vedaldi; Victor Lempitsky. 实例 归一化:快速风格化所缺失的关键要素. 2016 年 7 月. URL: https://arxiv.org/abs/1607.08022
[Layer-Normalization]
Jimmy Lei Ba; Jamie Ryan Kiros; Geoffrey E. Hinton. 层 归一化. 2016 年 7 月. URL: https://arxiv.org/abs/1607.06450
[LDM]
Robin Rombach; et al. 使用 潜在扩散模型进行高分辨率图像合成. 2022 年 4 月. URL: https://arxiv.org/abs/2112.10752
[LeakyReLU]
Andrew L. Maas; Awni Y. Hannun; Andrew Y. Ng. 修正器 非线性改进神经网络声学模型. 2013 年 6 月. URL: https://pdfs.semanticscholar.org/367f/2c63a6f6a10b3b64b8729d601e69337ee3cc.pdf
[LLAMA-2-7B]
Hugo Touvron; et al. Llama 2:开放基础模型和 微调聊天模型. 2023 年 7 月. URL: https://arxiv.org/abs/2307.09288
[LSTM]
Sepp Hochreiter; Jürgen Schmidhuber. 长 短期记忆. 1997 年 11 月. URL: https://doi.org/10.1162/neco.1997.9.8.1735
[m2m100_418M]
Angela Fan; et al. 超越以英语为中心的多语言 机器翻译. 2020 年 10 月. URL: https://arxiv.org/abs/2010.11125
[MaskR-CNN]
Kaiming He; et al. Mask R-CNN. 2018 年 1 月. URL: https://arxiv.org/abs/1703.06870
[MobileNetV3]
Andrew Howard; et al. 搜索 MobileNetV3. 2019 年 11 月. URL: https://arxiv.org/pdf/1905.02244
[MODELS]
Web 机器学习社区组. 第一 波模型. 2020. URL: https://github.com/webmachinelearning/webnn/blob/master/op_compatibility/first_wave_models.md
[NumPy]
SciPy 社区. NumPy. 2019 年 7 月. URL: https://numpy.org/doc/stable/
[OpenNMT]
Guillaume Klein; et al. OpenNMT:用于 神经机器翻译的开源工具包. 2017 年 3 月. URL: https://arxiv.org/abs/1701.02810
[PairedCycleGAN]
Huiwen Chang; et al. PairedCycleGAN: 用于添加和去除妆容的非对称风格迁移. 2018 年 6 月. URL: http://openaccess.thecvf.com/content_cvpr_2018/html/Chang_PairedCycleGAN_Asymmetric_Style_CVPR_2018_paper.html
[PoseNet]
Dan Oved. 在浏览器中使用 TensorFlow.js 进行实时 人体姿态估计. 2018 年 5 月. URL: https://medium.com/tensorflow/real-time-human-pose-estimation-in-the-browser-with-tensorflow-js-7dd0bc881cd5
[POWERFUL-FEATURES]
Mike West. 安全上下文. 10 2023 年 11 月. CRD. URL: https://www.w3.org/TR/secure-contexts/
[Prefix-sum]
Wikipedia 社区. 前缀和. 2025 年 1 月. URL: https://en.wikipedia.org/wiki/Prefix_sum
[RNNoise]
Jean-Marc Valin. 用于音频降噪的循环神经网络 . 2017 年 9 月. URL: https://github.com/xiph/rnnoise
[SECURITY-PRIVACY-QUESTIONNAIRE]
Theresa O'Connor; Peter Snyder; Simone Onofri. 自我审查问卷:安全 与隐私. 18 2025 年 4 月. NOTE. URL: https://www.w3.org/TR/security-privacy-questionnaire/
[SegAny]
Alexander Kirillov; et al. 分割一切. 2023 年 4 月. URL: https://arxiv.org/abs/2304.02643
[SRGAN]
Christian Ledig; et al. 使用生成对抗网络进行照片级真实的单幅图像 超分辨率. 2017 年 5 月. URL: https://arxiv.org/abs/1609.04802
[SSD]
Wei Liu; et al. SSD:单次多框 检测器. 2016 年 12 月. URL: https://arxiv.org/abs/1512.02325
[T5-SMALL]
Colin Raffel; et al. 探索使用统一文本到文本 Transformer 进行迁移学习的极限. 2020 年 6 月. URL: https://jmlr.org/papers/volume21/20-074/20-074.pdf
[UTR36]
Mark Davis; Michel Suignard. Unicode 安全考虑. 19 2014 年 9 月. Unicode 技术报告 #36. URL: https://www.unicode.org/reports/tr36/tr36-15.html
[UTS55]
Robin Leroy; Mark Davis. Unicode 源 代码处理. 29 2024 年 1 月. Unicode 技术标准 #55. URL: https://www.unicode.org/reports/tr55/tr55-5.html
[Video-Summarization-with-LSTM]
Ke Zhang; et al. 使用 长短期记忆进行视频摘要. 2016 年 10 月. URL: http://www-scf.usc.edu/~zhan355/ke_eccv2016.pdf
[WASM-JS-API-2]
. Ms2ger; Ryan Hunt. WebAssembly JavaScript 接口. 12 2026 年 8 月. CRD. URL: https://www.w3.org/TR/wasm-js-api-2/
[WCAG]
Michael Cooper; et al. Web 内容无障碍指南 (WCAG) 2.2. 12 2024 年 12 月. REC. URL: https://www.w3.org/TR/WCAG22/
[WEBMACHINELEARNING-ETHICS]
Anssi Kostiainen. Web 机器学习的伦理原则. 8 2024 年 1 月. DNOTE. URL: https://www.w3.org/TR/webmachinelearning-ethics/
[Whisper]
Alec Radford; et al. 通过 大规模弱监督实现鲁棒语音识别. 2022 年 12 月. URL: https://arxiv.org/abs/2212.04356
[YOLO]
Joseph Redmon; et al. 只看一次:统一的 实时目标检测. 2016 年 5 月. URL: https://arxiv.org/abs/1506.02640

IDL 索引

interface mixin NavigatorML {
  [SecureContext, SameObject] readonly attribute ML ml;
};
Navigator includes NavigatorML;
WorkerNavigator includes NavigatorML;

enum MLPowerPreference {
  "default",
  "high-performance",
  "low-power"
};

dictionary MLContextOptions {
  MLPowerPreference powerPreference = "default";
  boolean accelerated = true;
};

[SecureContext, Exposed=(Window, Worker)]
interface ML {
  Promise<MLContext> createContext(optional MLContextOptions options = {});
  Promise<MLContext> createContext(GPUDevice gpuDevice);
};

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;
};

dictionary MLOpSupportLimits {
  MLInputOperandLayout preferredInputLayout;
  [EnforceRange] unsigned long long maxTensorByteLength;
  MLTensorLimits input;
  MLTensorLimits constant;
  MLTensorLimits output;
};

dictionary MLRankRange {
  unsigned long min;
  unsigned long max;
};

typedef sequence<MLOperandDataType> MLDataTypeList;

dictionary MLTensorLimits {
  MLDataTypeList dataTypes;
  MLRankRange rankRange;
};

dictionary MLBinarySupportLimits {
  MLTensorLimits a;
  MLTensorLimits b;
  MLTensorLimits output;
};

dictionary MLSingleInputSupportLimits {
  MLTensorLimits input;
  MLTensorLimits output;
};

[SecureContext, Exposed=(Window, Worker)]
interface MLGraph {
  undefined destroy();
};

enum MLInputOperandLayout {
  "nchw",
  "nhwc"
};

enum MLOperandDataType {
  "float32",
  "float16",
  "int32",
  "uint32",
  "int64",
  "uint64",
  "int8",
  "uint8"
};

dictionary MLOperandDescriptor {
  required MLOperandDataType dataType;
  required sequence<[EnforceRange] unsigned long> shape;
};

[SecureContext, Exposed=(Window, Worker)]
interface MLOperand {
  readonly attribute MLOperandDataType dataType;
  readonly attribute FrozenArray<unsigned long> shape;
};

dictionary MLOperatorOptions {
  USVString label = "";
};

typedef (bigint or unrestricted double) MLNumber;

dictionary MLTensorDescriptor : MLOperandDescriptor {
  boolean readable = false;
  boolean writable = false;
};

[SecureContext, Exposed=(Window, Worker)]
interface MLTensor {
  readonly attribute MLOperandDataType dataType;
  readonly attribute FrozenArray<unsigned long> shape;
  readonly attribute boolean readable;
  readonly attribute boolean writable;
  readonly attribute boolean constant;

  undefined destroy();
};

typedef record<USVString, MLOperand> MLNamedOperands;

[SecureContext, Exposed=(Window, Worker)]
interface MLGraphBuilder {
  // Construct the graph builder from the context.
  constructor(MLContext context);

  // Create an operand for a graph input.
  MLOperand input(USVString name, MLOperandDescriptor descriptor);

  // Create an operand for a graph constant.
  MLOperand constant(MLOperandDescriptor descriptor,
                     AllowSharedBufferSource buffer);

  // Create a scalar operand from the specified number of the specified type.
  MLOperand constant(MLOperandDataType dataType, MLNumber value);

  // Create an operand from a specified constant tensor.
  MLOperand constant(MLTensor tensor);

  // Compile the graph up to the specified output operands asynchronously.
  Promise<MLGraph> build(MLNamedOperands outputs);
};

dictionary MLArgMinMaxOptions : MLOperatorOptions {
  boolean keepDimensions = false;
  MLOperandDataType outputDataType = "int32";
};

partial interface MLGraphBuilder {
  MLOperand argMin(MLOperand input, [EnforceRange] unsigned long axis,
                   optional MLArgMinMaxOptions options = {});
  MLOperand argMax(MLOperand input, [EnforceRange] unsigned long axis,
                   optional MLArgMinMaxOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits argMin;
  MLSingleInputSupportLimits argMax;
};

dictionary MLBatchNormalizationOptions : MLOperatorOptions {
  MLOperand scale;
  MLOperand bias;
  [EnforceRange] unsigned long axis = 1;
  double epsilon = 1e-5;
};

partial interface MLGraphBuilder {
  MLOperand batchNormalization(MLOperand input, MLOperand mean, MLOperand variance,
                               optional MLBatchNormalizationOptions options = {});
};

dictionary MLBatchNormalizationSupportLimits {
  MLTensorLimits input;
  MLTensorLimits mean;
  MLTensorLimits variance;
  MLTensorLimits scale;
  MLTensorLimits bias;
  MLTensorLimits output;
};

partial dictionary MLOpSupportLimits {
  MLBatchNormalizationSupportLimits batchNormalization;
};

partial interface MLGraphBuilder {
  MLOperand cast(MLOperand input,
                 MLOperandDataType dataType,
                 optional MLOperatorOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits cast;
};

dictionary MLClampOptions : MLOperatorOptions {
  MLNumber minValue;
  MLNumber maxValue;
};

partial interface MLGraphBuilder {
  MLOperand clamp(MLOperand input, optional MLClampOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits clamp;
};

partial interface MLGraphBuilder {
  MLOperand concat(sequence<MLOperand> inputs,
                   [EnforceRange] unsigned long axis,
                   optional MLOperatorOptions options = {});
};

dictionary MLConcatSupportLimits {
  MLTensorLimits inputs;
  MLTensorLimits output;
};

partial dictionary MLOpSupportLimits {
  MLConcatSupportLimits concat;
};

enum MLConv2dFilterOperandLayout {
  "oihw",
  "hwio",
  "ohwi",
  "ihwo"
};

dictionary MLConv2dOptions : MLOperatorOptions {
  sequence<[EnforceRange] unsigned long> padding;
  sequence<[EnforceRange] unsigned long> strides;
  sequence<[EnforceRange] unsigned long> dilations;
  [EnforceRange] unsigned long groups = 1;
  MLInputOperandLayout inputLayout = "nchw";
  MLConv2dFilterOperandLayout filterLayout = "oihw";
  MLOperand bias;
};

partial interface MLGraphBuilder {
  MLOperand conv2d(MLOperand input,
                   MLOperand filter,
                   optional MLConv2dOptions options = {});
};

dictionary MLConv2dSupportLimits {
  MLTensorLimits input;
  MLTensorLimits filter;
  MLTensorLimits bias;
  MLTensorLimits output;
};

partial dictionary MLOpSupportLimits {
  MLConv2dSupportLimits conv2d;
};

enum MLConvTranspose2dFilterOperandLayout {
  "iohw",
  "hwoi",
  "ohwi"
};

dictionary MLConvTranspose2dOptions : MLOperatorOptions {
  sequence<[EnforceRange] unsigned long> padding;
  sequence<[EnforceRange] unsigned long> strides;
  sequence<[EnforceRange] unsigned long> dilations;
  sequence<[EnforceRange] unsigned long> outputPadding;
  sequence<[EnforceRange] unsigned long> outputSizes;
  [EnforceRange] unsigned long groups = 1;
  MLInputOperandLayout inputLayout = "nchw";
  MLConvTranspose2dFilterOperandLayout filterLayout = "iohw";
  MLOperand bias;
};

partial interface MLGraphBuilder {
  MLOperand convTranspose2d(MLOperand input, MLOperand filter,
                            optional MLConvTranspose2dOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLConv2dSupportLimits convTranspose2d;
};

dictionary MLCumulativeSumOptions : MLOperatorOptions {
  boolean exclusive = false;
  boolean reversed = false;
};

partial interface MLGraphBuilder {
  MLOperand cumulativeSum(MLOperand input,
                          unsigned long axis,
                          optional MLCumulativeSumOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits cumulativeSum;
};

partial interface MLGraphBuilder {
  MLOperand add(MLOperand a, MLOperand b, optional MLOperatorOptions options = {});
  MLOperand sub(MLOperand a, MLOperand b, optional MLOperatorOptions options = {});
  MLOperand mul(MLOperand a, MLOperand b, optional MLOperatorOptions options = {});
  MLOperand div(MLOperand a, MLOperand b, optional MLOperatorOptions options = {});
  MLOperand max(MLOperand a, MLOperand b, optional MLOperatorOptions options = {});
  MLOperand min(MLOperand a, MLOperand b, optional MLOperatorOptions options = {});
  MLOperand pow(MLOperand a, MLOperand b, optional MLOperatorOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLBinarySupportLimits add;
  MLBinarySupportLimits sub;
  MLBinarySupportLimits mul;
  MLBinarySupportLimits div;
  MLBinarySupportLimits max;
  MLBinarySupportLimits min;
  MLBinarySupportLimits pow;
};

partial interface MLGraphBuilder {
  MLOperand equal(MLOperand a,
                  MLOperand b,
                  optional MLOperatorOptions options = {});
  MLOperand notEqual(MLOperand a,
                     MLOperand b,
                     optional MLOperatorOptions options = {});
  MLOperand greater(MLOperand a,
                    MLOperand b,
                    optional MLOperatorOptions options = {});
  MLOperand greaterOrEqual(MLOperand a,
                           MLOperand b,
                           optional MLOperatorOptions options = {});
  MLOperand lesser(MLOperand a,
                   MLOperand b,
                   optional MLOperatorOptions options = {});
  MLOperand lesserOrEqual(MLOperand a,
                          MLOperand b,
                          optional MLOperatorOptions options = {});
  MLOperand logicalNot(MLOperand a, optional MLOperatorOptions options = {});
  MLOperand logicalAnd(MLOperand a,
                       MLOperand b,
                       optional MLOperatorOptions options = {});
  MLOperand logicalOr(MLOperand a,
                      MLOperand b,
                      optional MLOperatorOptions options = {});
  MLOperand logicalXor(MLOperand a,
                       MLOperand b,
                       optional MLOperatorOptions options = {});
  MLOperand isNaN(MLOperand a, optional MLOperatorOptions options = {});
  MLOperand isInfinite(MLOperand a, optional MLOperatorOptions options = {});
};

dictionary MLLogicalNotSupportLimits {
  MLTensorLimits a;
  MLTensorLimits output;
};

partial dictionary MLOpSupportLimits {
  MLBinarySupportLimits equal;
  MLBinarySupportLimits notEqual;
  MLBinarySupportLimits greater;
  MLBinarySupportLimits greaterOrEqual;
  MLBinarySupportLimits lesser;
  MLBinarySupportLimits lesserOrEqual;
  MLLogicalNotSupportLimits logicalNot;
  MLBinarySupportLimits logicalAnd;
  MLBinarySupportLimits logicalOr;
  MLBinarySupportLimits logicalXor;
  MLLogicalNotSupportLimits isNaN;
  MLLogicalNotSupportLimits isInfinite;
};

partial interface MLGraphBuilder {
  MLOperand abs(MLOperand input, optional MLOperatorOptions options = {});
  MLOperand ceil(MLOperand input, optional MLOperatorOptions options = {});
  MLOperand cos(MLOperand input, optional MLOperatorOptions options = {});
  MLOperand erf(MLOperand input, optional MLOperatorOptions options = {});
  MLOperand exp(MLOperand input, optional MLOperatorOptions options = {});
  MLOperand floor(MLOperand input, optional MLOperatorOptions options = {});
  MLOperand identity(MLOperand input, optional MLOperatorOptions options = {});
  MLOperand log(MLOperand input, optional MLOperatorOptions options = {});
  MLOperand neg(MLOperand input, optional MLOperatorOptions options = {});
  MLOperand reciprocal(MLOperand input, optional MLOperatorOptions options = {});
  MLOperand roundEven(MLOperand input, optional MLOperatorOptions options = {});
  MLOperand sin(MLOperand input, optional MLOperatorOptions options = {});
  MLOperand sign(MLOperand input, optional MLOperatorOptions options = {});
  MLOperand sqrt(MLOperand input, optional MLOperatorOptions options = {});
  MLOperand tan(MLOperand input, optional MLOperatorOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits abs;
  MLSingleInputSupportLimits ceil;
  MLSingleInputSupportLimits cos;
  MLSingleInputSupportLimits erf;
  MLSingleInputSupportLimits exp;
  MLSingleInputSupportLimits floor;
  MLSingleInputSupportLimits identity;
  MLSingleInputSupportLimits log;
  MLSingleInputSupportLimits neg;
  MLSingleInputSupportLimits reciprocal;
  MLSingleInputSupportLimits roundEven;
  MLSingleInputSupportLimits sin;
  MLSingleInputSupportLimits sign;
  MLSingleInputSupportLimits sqrt;
  MLSingleInputSupportLimits tan;
};

partial interface MLGraphBuilder {
  MLOperand dequantizeLinear(MLOperand input,
                             MLOperand scale,
                             MLOperand zeroPoint,
                             optional MLOperatorOptions options = {});
};

dictionary MLQuantizeDequantizeLinearSupportLimits {
  MLTensorLimits input;
  MLTensorLimits scale;
  MLTensorLimits zeroPoint;
  MLTensorLimits output;
};

partial dictionary MLOpSupportLimits {
  MLQuantizeDequantizeLinearSupportLimits dequantizeLinear;
};

partial interface MLGraphBuilder {
  MLOperand quantizeLinear(MLOperand input,
                           MLOperand scale,
                           MLOperand zeroPoint,
                           optional MLOperatorOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLQuantizeDequantizeLinearSupportLimits quantizeLinear;
};

dictionary MLEluOptions : MLOperatorOptions {
  double alpha = 1;
};

partial interface MLGraphBuilder {
  MLOperand elu(MLOperand input, optional MLEluOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits elu;
};

partial interface MLGraphBuilder {
  MLOperand expand(MLOperand input,
                   sequence<[EnforceRange] unsigned long> newShape,
                   optional MLOperatorOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits expand;
};

dictionary MLGatherOptions : MLOperatorOptions {
  [EnforceRange] unsigned long axis = 0;
};

partial interface MLGraphBuilder {
  MLOperand gather(MLOperand input,
                   MLOperand indices,
                   optional MLGatherOptions options = {});
};

dictionary MLGatherSupportLimits {
  MLTensorLimits input;
  MLTensorLimits indices;
  MLTensorLimits output;
};

partial dictionary MLOpSupportLimits {
  MLGatherSupportLimits gather;
};

partial interface MLGraphBuilder {
  MLOperand gatherElements(MLOperand input,
                           MLOperand indices,
                           optional MLGatherOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLGatherSupportLimits gatherElements;
};

partial interface MLGraphBuilder {
  MLOperand gatherND(MLOperand input,
                     MLOperand indices,
                     optional MLOperatorOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLGatherSupportLimits gatherND;
};

partial interface MLGraphBuilder {
  MLOperand gelu(MLOperand input, optional MLOperatorOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits gelu;
};

dictionary MLGemmOptions : MLOperatorOptions {
  MLOperand c;
  double alpha = 1.0;
  double beta = 1.0;
  boolean aTranspose = false;
  boolean bTranspose = false;
};

partial interface MLGraphBuilder {
  MLOperand gemm(MLOperand a, MLOperand b, optional MLGemmOptions options = {});
};

dictionary MLGemmSupportLimits {
  MLTensorLimits a;
  MLTensorLimits b;
  MLTensorLimits c;
  MLTensorLimits output;
};

partial dictionary MLOpSupportLimits {
  MLGemmSupportLimits gemm;
};

enum MLGruWeightLayout {
  "zrn",  // update-reset-new gate ordering
  "rzn"   // reset-update-new gate ordering
};

enum MLRecurrentNetworkActivation {
  "relu",
  "sigmoid",
  "tanh"
};

enum MLRecurrentNetworkDirection {
  "forward",
  "backward",
  "both"
};

dictionary MLGruOptions : MLOperatorOptions {
  MLOperand bias;
  MLOperand recurrentBias;
  MLOperand initialHiddenState;
  boolean resetAfter = true;
  boolean returnSequence = false;
  MLRecurrentNetworkDirection direction = "forward";
  MLGruWeightLayout layout = "zrn";
  sequence<MLRecurrentNetworkActivation> activations;
};

partial interface MLGraphBuilder {
  sequence<MLOperand> gru(MLOperand input,
                          MLOperand weight,
                          MLOperand recurrentWeight,
                          [EnforceRange] unsigned long steps,
                          [EnforceRange] unsigned long hiddenSize,
                          optional MLGruOptions options = {});
};

dictionary MLGruSupportLimits {
  MLTensorLimits input;
  MLTensorLimits weight;
  MLTensorLimits recurrentWeight;
  MLTensorLimits bias;
  MLTensorLimits recurrentBias;
  MLTensorLimits initialHiddenState;
  MLTensorLimits output0;
  MLTensorLimits output1;
};

partial dictionary MLOpSupportLimits {
  MLGruSupportLimits gru;
};

dictionary MLGruCellOptions : MLOperatorOptions {
  MLOperand bias;
  MLOperand recurrentBias;
  boolean resetAfter = true;
  MLGruWeightLayout layout = "zrn";
  sequence<MLRecurrentNetworkActivation> activations;
};

partial interface MLGraphBuilder {
  MLOperand gruCell(MLOperand input,
                    MLOperand weight,
                    MLOperand recurrentWeight,
                    MLOperand hiddenState,
                    [EnforceRange] unsigned long hiddenSize,
                    optional MLGruCellOptions options = {});
};

dictionary MLGruCellSupportLimits {
  MLTensorLimits input;
  MLTensorLimits weight;
  MLTensorLimits recurrentWeight;
  MLTensorLimits hiddenState;
  MLTensorLimits bias;
  MLTensorLimits recurrentBias;
  MLTensorLimits output;
};

partial dictionary MLOpSupportLimits {
  MLGruCellSupportLimits gruCell;
};

dictionary MLHardSigmoidOptions : MLOperatorOptions {
  double alpha = 0.2;
  double beta = 0.5;
};

partial interface MLGraphBuilder {
  MLOperand hardSigmoid(MLOperand input, optional MLHardSigmoidOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits hardSigmoid;
};

partial interface MLGraphBuilder {
  MLOperand hardSwish(MLOperand input, optional MLOperatorOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits hardSwish;
};

dictionary MLInstanceNormalizationOptions : MLOperatorOptions {
  MLOperand scale;
  MLOperand bias;
  double epsilon = 1e-5;
  MLInputOperandLayout layout = "nchw";
};

partial interface MLGraphBuilder {
  MLOperand instanceNormalization(
    MLOperand input,
    optional MLInstanceNormalizationOptions options = {});
};

dictionary MLNormalizationSupportLimits {
  MLTensorLimits input;
  MLTensorLimits scale;
  MLTensorLimits bias;
  MLTensorLimits output;
};

partial dictionary MLOpSupportLimits {
  MLNormalizationSupportLimits instanceNormalization;
};

dictionary MLLayerNormalizationOptions : MLOperatorOptions {
  MLOperand scale;
  MLOperand bias;
  sequence<[EnforceRange] unsigned long> axes;
  double epsilon = 1e-5;
};

partial interface MLGraphBuilder {
  MLOperand layerNormalization(MLOperand input,
                               optional MLLayerNormalizationOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLNormalizationSupportLimits layerNormalization;
};

dictionary MLLeakyReluOptions : MLOperatorOptions {
  double alpha = 0.01;
};

partial interface MLGraphBuilder {
  MLOperand leakyRelu(MLOperand input, optional MLLeakyReluOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits leakyRelu;
};

dictionary MLLinearOptions : MLOperatorOptions {
  double alpha = 1;
  double beta = 0;
};

partial interface MLGraphBuilder {
  MLOperand linear(MLOperand input, optional MLLinearOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits linear;
};

enum MLLstmWeightLayout {
  "iofg", // input-output-forget-cell gate ordering
  "ifgo"  // input-forget-cell-output gate ordering
};

dictionary MLLstmOptions : MLOperatorOptions {
  MLOperand bias;
  MLOperand recurrentBias;
  MLOperand peepholeWeight;
  MLOperand initialHiddenState;
  MLOperand initialCellState;
  boolean returnSequence = false;
  MLRecurrentNetworkDirection direction = "forward";
  MLLstmWeightLayout layout = "iofg";
  sequence<MLRecurrentNetworkActivation> activations;
};

partial interface MLGraphBuilder {
  sequence<MLOperand> lstm(MLOperand input,
                           MLOperand weight,
                           MLOperand recurrentWeight,
                           [EnforceRange] unsigned long steps,
                           [EnforceRange] unsigned long hiddenSize,
                           optional MLLstmOptions options = {});
};

dictionary MLLstmSupportLimits {
  MLTensorLimits input;
  MLTensorLimits weight;
  MLTensorLimits recurrentWeight;
  MLTensorLimits bias;
  MLTensorLimits recurrentBias;
  MLTensorLimits peepholeWeight;
  MLTensorLimits initialHiddenState;
  MLTensorLimits initialCellState;
  MLTensorLimits output0;
  MLTensorLimits output1;
  MLTensorLimits output2;
};

partial dictionary MLOpSupportLimits {
  MLLstmSupportLimits lstm;
};


dictionary MLLstmCellOptions : MLOperatorOptions {
  MLOperand bias;
  MLOperand recurrentBias;
  MLOperand peepholeWeight;
  MLLstmWeightLayout layout = "iofg";
  sequence<MLRecurrentNetworkActivation> activations;
};

partial interface MLGraphBuilder {
  sequence<MLOperand> lstmCell(MLOperand input,
                               MLOperand weight,
                               MLOperand recurrentWeight,
                               MLOperand hiddenState,
                               MLOperand cellState,
                               [EnforceRange] unsigned long hiddenSize,
                               optional MLLstmCellOptions options = {});
};

dictionary MLLstmCellSupportLimits {
  MLTensorLimits input;
  MLTensorLimits weight;
  MLTensorLimits recurrentWeight;
  MLTensorLimits hiddenState;
  MLTensorLimits cellState;
  MLTensorLimits bias;
  MLTensorLimits recurrentBias;
  MLTensorLimits peepholeWeight;
  MLTensorLimits output0;
  MLTensorLimits output1;
};

partial dictionary MLOpSupportLimits {
  MLLstmCellSupportLimits lstmCell;
};

partial interface MLGraphBuilder {
  MLOperand matmul(MLOperand a, MLOperand b, optional MLOperatorOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLBinarySupportLimits matmul;
};

enum MLPaddingMode {
  "constant",
  "edge",
  "reflection"
};

dictionary MLPadOptions : MLOperatorOptions {
  MLPaddingMode mode = "constant";
  MLNumber value = 0;
};

partial interface MLGraphBuilder {
  MLOperand pad(MLOperand input,
                sequence<[EnforceRange] unsigned long> beginningPadding,
                sequence<[EnforceRange] unsigned long> endingPadding,
                optional MLPadOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits pad;
};

enum MLRoundingType {
  "floor",
  "ceil"
};

dictionary MLPool2dOptions : MLOperatorOptions {
  sequence<[EnforceRange] unsigned long> windowDimensions;
  sequence<[EnforceRange] unsigned long> padding;
  sequence<[EnforceRange] unsigned long> strides;
  sequence<[EnforceRange] unsigned long> dilations;
  MLInputOperandLayout layout = "nchw";
  MLRoundingType outputShapeRounding = "floor";
  sequence<[EnforceRange] unsigned long> outputSizes;
};

partial interface MLGraphBuilder {
  MLOperand averagePool2d(MLOperand input, optional MLPool2dOptions options = {});
  MLOperand l2Pool2d(MLOperand input, optional MLPool2dOptions options = {});
  MLOperand maxPool2d(MLOperand input, optional MLPool2dOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits averagePool2d;
  MLSingleInputSupportLimits l2Pool2d;
  MLSingleInputSupportLimits maxPool2d;
};

partial interface MLGraphBuilder {
  MLOperand prelu(MLOperand input,
                  MLOperand slope,
                  optional MLOperatorOptions options = {});
};

dictionary MLPreluSupportLimits {
  MLTensorLimits input;
  MLTensorLimits slope;
  MLTensorLimits output;
};

partial dictionary MLOpSupportLimits {
  MLPreluSupportLimits prelu;
};

dictionary MLReduceOptions : MLOperatorOptions {
  sequence<[EnforceRange] unsigned long> axes;
  boolean keepDimensions = false;
};

partial interface MLGraphBuilder {
  MLOperand reduceL1(MLOperand input, optional MLReduceOptions options = {});
  MLOperand reduceL2(MLOperand input, optional MLReduceOptions options = {});
  MLOperand reduceLogSum(MLOperand input, optional MLReduceOptions options = {});
  MLOperand reduceLogSumExp(MLOperand input, optional MLReduceOptions options = {});
  MLOperand reduceMax(MLOperand input, optional MLReduceOptions options = {});
  MLOperand reduceMean(MLOperand input, optional MLReduceOptions options = {});
  MLOperand reduceMin(MLOperand input, optional MLReduceOptions options = {});
  MLOperand reduceProduct(MLOperand input, optional MLReduceOptions options = {});
  MLOperand reduceSum(MLOperand input, optional MLReduceOptions options = {});
  MLOperand reduceSumSquare(MLOperand input, optional MLReduceOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits reduceL1;
  MLSingleInputSupportLimits reduceL2;
  MLSingleInputSupportLimits reduceLogSum;
  MLSingleInputSupportLimits reduceLogSumExp;
  MLSingleInputSupportLimits reduceMax;
  MLSingleInputSupportLimits reduceMean;
  MLSingleInputSupportLimits reduceMin;
  MLSingleInputSupportLimits reduceProduct;
  MLSingleInputSupportLimits reduceSum;
  MLSingleInputSupportLimits reduceSumSquare;
};

partial interface MLGraphBuilder {
  MLOperand relu(MLOperand input, optional MLOperatorOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits relu;
};

enum MLInterpolationMode {
  "nearest-neighbor",
  "linear"
};

dictionary MLResample2dOptions : MLOperatorOptions {
  MLInterpolationMode mode = "nearest-neighbor";
  sequence<float> scales;
  sequence<[EnforceRange] unsigned long> sizes;
  sequence<[EnforceRange] unsigned long> axes;
};

partial interface MLGraphBuilder {
  MLOperand resample2d(MLOperand input, optional MLResample2dOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits resample2d;
};

partial interface MLGraphBuilder {
  MLOperand reshape(MLOperand input,
                    sequence<[EnforceRange] unsigned long> newShape,
                    optional MLOperatorOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits reshape;
};

dictionary MLReverseOptions : MLOperatorOptions {
  sequence<[EnforceRange] unsigned long> axes;
};

partial interface MLGraphBuilder {
  MLOperand reverse(MLOperand input, optional MLReverseOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits reverse;
};

dictionary MLScatterOptions : MLOperatorOptions {
  [EnforceRange] unsigned long axis = 0;
};

partial interface MLGraphBuilder {
  MLOperand scatterElements(MLOperand input,
                            MLOperand indices,
                            MLOperand updates,
                            optional MLScatterOptions options = {});
};

dictionary MLScatterSupportLimits {
  MLTensorLimits input;
  MLTensorLimits indices;
  MLTensorLimits updates;
  MLTensorLimits output;
};

partial dictionary MLOpSupportLimits {
  MLScatterSupportLimits scatterElements;
};

partial interface MLGraphBuilder {
  MLOperand scatterND(MLOperand input,
                      MLOperand indices,
                      MLOperand updates,
                      optional MLOperatorOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLScatterSupportLimits scatterND;
};

partial interface MLGraphBuilder {
  MLOperand sigmoid(MLOperand input, optional MLOperatorOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits sigmoid;
};

dictionary MLSliceOptions : MLOperatorOptions {
  sequence<[EnforceRange] unsigned long> strides;
};

partial interface MLGraphBuilder {
  MLOperand slice(MLOperand input,
                  sequence<[EnforceRange] unsigned long> starts,
                  sequence<[EnforceRange] unsigned long> sizes,
                  optional MLSliceOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits slice;
};

partial interface MLGraphBuilder {
  MLOperand softmax(MLOperand input,
                    [EnforceRange] unsigned long axis,
                    optional MLOperatorOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits softmax;
};

partial interface MLGraphBuilder {
  MLOperand softplus(MLOperand input, optional MLOperatorOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits softplus;
};

partial interface MLGraphBuilder {
  MLOperand softsign(MLOperand input, optional MLOperatorOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits softsign;
};

dictionary MLSplitOptions : MLOperatorOptions {
  [EnforceRange] unsigned long axis = 0;
};

partial interface MLGraphBuilder {
  sequence<MLOperand> split(
      MLOperand input,
      ([EnforceRange] unsigned long or sequence<[EnforceRange] unsigned long>) splits,
      optional MLSplitOptions options = {});
};

dictionary MLSplitSupportLimits {
  MLTensorLimits input;
  MLTensorLimits outputs;
};

partial dictionary MLOpSupportLimits {
  MLSplitSupportLimits split;
};

partial interface MLGraphBuilder {
  MLOperand tanh(MLOperand input, optional MLOperatorOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits tanh;
};

partial interface MLGraphBuilder {
  MLOperand tile(MLOperand input,
                 sequence<unsigned long> repetitions,
                 optional MLOperatorOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits tile;
};

dictionary MLTransposeOptions : MLOperatorOptions {
  sequence<[EnforceRange] unsigned long> permutation;
};

partial interface MLGraphBuilder {
  MLOperand transpose(MLOperand input, optional MLTransposeOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits transpose;
};

dictionary MLTriangularOptions : MLOperatorOptions {
  boolean upper = true;
  [EnforceRange] long diagonal = 0;
};

partial interface MLGraphBuilder {
  MLOperand triangular(MLOperand input, optional MLTriangularOptions options = {});
};

partial dictionary MLOpSupportLimits {
  MLSingleInputSupportLimits triangular;
};

partial interface MLGraphBuilder {
  MLOperand where(MLOperand condition,
                  MLOperand trueValue,
                  MLOperand falseValue,
                  optional MLOperatorOptions options = {});
};

dictionary MLWhereSupportLimits {
  MLTensorLimits condition;
  MLTensorLimits trueValue;
  MLTensorLimits falseValue;
  MLTensorLimits output;
};

partial dictionary MLOpSupportLimits {
  MLWhereSupportLimits where;
};

问题索引

记录容易发生越界访问的运算,为实现者提供指导。
考虑到当前 CPU 在运行渲染器的进程之间共享的情况,研究侧信道攻击的可行性。
提示机制在一定程度上缓解了这一问题。研究其他缓解措施。
已提议扩展 MLGraph.devices API,以公开在图完全构建并编译后实际 选择用于执行的设备。此 API 扩展的隐私影响仍在研究中。 [问题 #836]
考虑添加一种机制,用于报告 dispatch() 期间的错误。 [问题 #778]
更严格地定义此时间线。 [问题 #529]
添加一种机制,用于报告图执行期间的错误。 [问题 #778]
添加一种机制,用于报告写入张量时的错误。 [问题 #778]
是否应支持大小为 0 的维度? [问题 #391]
操作数维度的最大数量尚未定义,但原生 ML API 通常具有 最大支持数量。 [问题 #456]
bigint数值类型的联合类型支持是 [WEBIDL] 中的新功能,并且 实现支持也有限。鼓励原型实现对此方法提供反馈。 [whatwg/webidl 问题 #1388]
如果允许大小为 0 的维度,则修订这些步骤。 [问题 #391]
如果允许大小为 0 的维度,则修订上述步骤。 [问题 #391]
如果允许大小为 0 的维度,则修订这些步骤。 [问题 #391]
[INFRA] 中提供定义后移除此内容。 [whatwg/infra 问题 #664]