大多数浏览器和
Developer App 均支持流媒体播放。
-
Metal 助力机器学习
Metal Performance Shaders (MPS) 包括对于机器学习和充分发挥 GPU 的强劲性能必不可少的高度优调数据并行原语库。在 iOS 13 和 macOS Catalina 中,MPS 提升了性能,搭建更多神经网络,并且比以前更加简单易用。进一步了解 MPS 中的这些改进,并从实践角度了解如何实施样式转换等创新技术。
资源
- Metal
- Metal Performance Shaders
- Training a Neural Network with Metal Performance Shaders
- 演示幻灯片 (PDF)
相关视频
Tech Talks
WWDC20
WWDC19
-
下载
下午好 我叫 Justin 我是 GPU 软件的一名工程师 我要讲的是机器学习的 Metal
今天我们要讨论的是 Metal Performance Shaders 框架 以及我们今年 增加的新的机器学习功能 Metal Performance Shaders 或 MPS 是 GPU 加速的 基元的集合 它让你可以 在 GPU 中利用 Metal 高性能的能力 MPS 为图像处理 线性代数 光线追踪 机器学习 提供内核 现在 机器学习内核 支持推理和训练 它们在 iOS macOS tvOS 中得到了优化 MPS 还通过图表 API 为构建神经网络 提供了一个便捷的方式 所以 我们现在可以看到 MPS 是如何适应更大的 Apple ML 生态系统的
你有更高等级的框架 如 Core ML 和 Create ML 它们为你实施自己的网络 提供了一个便捷的方式 但是如果你想要在自己的项目上 多一点弹性和控制 你可以使用如 MPS 的 更低等级框架
今年 我们为机器学习支持 添加了几个新功能
我们增加了内核 来支持比以往更多的网络
我们提升了 已存在的网络的性能 我们也让 MPS 更易使用 现在 随着我们检查这些新特征 知道一些关于 推理和训练如何在 机器学习中运行 是很有用的 所以 让我们简单复习一下这些概念
推理就是 将网络运用到一个输入数值的过程 在这个例子中就是个图片 产生一个输出或者猜测它是什么
现在 网络是由 如卷积和神经元激活 等多种功能组成 这些层数都依赖于一组参数 在推理中 这些参数 都是固定的 但是它们的 数值在训练过程中被确定 那么 在训练中发生了什么呢 在训练中 我们给予 网络很多已知对象的图像 训练过程包含 不断地对这些图像进行分类 正如我们所做的 我们更新了我们的参数 每一次网络的迭代 都会产生一套更好的参数 直到我们最终得到一套 让我们能最好地分类图像的参数 在我们停止 训练过程的阶段 我们的参数已经准备好 在推理中使用了 我们来看看 如何运用 MPS 来执行这些想法 我还是想说 与我们刚刚说到的相比 还有更多的推理和训练 所以如果你想要更多细节 你可以看看我们过去几年的 那些演讲
现在 我们今年增加了 几个新的功能 以此来更好地支持 广范围的推理和训练网络 首先 通过支持你推理图表 中的训练图表的隐式创建 我们让你的网络的 图表创建变得更加简单 我们为可分离损耗层 和随机数生成添加了内核 以此来运行 一系列的新网络 我们为如预测和更好地控制 MPS 使其提升性能等 增加了支持 所以 让我们先从隐式图表创作开始吧 通过隐式图表创作 我们可以隐晦地从我们的推理图表中 创建训练图表
所以 我们先来复习一下 如何为我们的网络创建一个图表 这里我们有一个简单的推理网络 它由一些卷积层组成 一些池化层 最后是一些完全相连的层 所以 我们会通过 为每个层创建节点 来为网络创建一个图表 我们将为 每个卷积层创建一个 卷积节点 每个池化层创建一个 池化节点 然后为完全连接层 创建一个完全连接节点 随着我们的推理图表被定义 我们可以将其拓展为训练图表
我们首先在推理图表的末端 增加一个损耗节点 然后我们在正向节点 增加梯度节点 方向和我们的推理图表正好相反 现在我们可以看看 这个部分的代码 正如之前所说 我们开始先是增加损耗节点 然后增加梯度节点 顺序就和我们之前提到的一样 所以我们可以看到 每一个梯度节点 都可以很容易地从前面的节点中创造出来 但是如果通过隐式 图表创造 这一切会更简单
现在 你一旦通过损耗节点 初始化你的梯度图像 我们可以自动地 创建与推理图表 相匹配的训练图表
和之前一样 我们创建了损耗节点 通过一行代码 我们可以创建我们的训练图表 在这个例子中 我们会从损耗节点创建训练图表 我们将为我们的 资源梯度使用 nil 参数 这将会告知损耗节点 使用其结果 来初始化梯度 但是如果我们想的话 可以使用另一个图像 我们为第二个参数提供零点 这个叫作节点处理器 节点处理器会让你提供一个块 你可以用它来 实施一些自定义的代码 在你的节点创建之后来配置它们
我还想要提到另一个有用的 功能 那就是停止梯度属性 所以 一般来说 当你生成你的训练序列时 你所有的可训练层都会更新它们的权重
在这例子中 这些是卷积和完全连接层 但是在一些例子中 你可能只想更新 网络中某些层的权重 比如说 在转换学习中 现在 在转换学习中 我们将要对很多层 使用事先训练好的权重 我们只想训练 某些层的权重 比如说 最终完全连接层
隐式图表创作同样支持 通过停止梯度属性 为这些网络类型创作图表 所以 为了实现这个 我们应该首先 在那些我们想要更新权重的第一层 设置停止梯度属性 在这个例子中 就是完全连接层 当图表生成时 后来的梯度节点 都不会被创建 所以正如你们所见 使用隐式图表创作 是一个从你的推理图表 生成训练图表的 非常简单的方法
让我们来看一个 我们增加的用来支持新的网络的功能 可分离损耗内核
所以 早些时候 我们看到了 如何使用损耗节点来使用 MPS CNN 损耗
MPS CNN 损耗消耗一个最终图像 这通常都是如 Softmax 层 和 Truth 数据 为了计算梯度数值 以开始反向传播过程的结果 但是还有一些为了 产生一个最终损耗 而使用多重中间损耗数值的网络 为了支持这个 我们增加了分别的正向和 梯度损耗内核 所以 在这里 我们使用了利用正向损耗节点 进行运算的两个损耗数值 然后我们得到了这些结果 将它们加在一起来产生一个最终损耗 现在 我们需要初始化 梯度值来开始反向过程
在它通过损耗节点 隐式发生前 我们需要增加一个初始的梯度内核 这将会生成 一个梯度图像 它将会变成 最终损耗计算的结果权重 所以 随着梯度数值的初始化 我们可以启动 反向过程 我们将会为 我们拥有附加梯度的 正向内核使用梯度内核 也为正向损耗内核使用梯度 现在 让我们看看 使用可分离损耗的网络 特别地 我们将看一下 风格转换 风格转换网络 产生了融合 风格和原始图像的图像
我们会看到的模型 是你可以找到并重新创作的 它可以使用 MPS 来实施
在推理中 这个网络 由转换节点构成 如卷积和 实例规范化层 它们的权重构成了 训练的参数 这就是嵌入风格的地方 它通过训练过程 学习到参数内 所以让我们来看看如何训练 现在我们有一个关于网络的概览
作为推理 我们将应用转换器 来生成一个风格化的图像 现在 在这个例子中 它将是网络目前 关于最好的风格图像的猜测 它结合了风格和内容 因为网络的目标是 匹配期望的风格 和原始图像的内容 我们将需要 两个损耗数值 所以 第一个损耗数值 是由我们称之为风格损耗网络的数据网络 进行计算的
这个损耗数值 将会保证网络收敛在一个结果上 而这个结果会与我们期望的风格很匹配 我们还想要保证 生成的图像还能保持 原始图像的特征 所以 我们要对此 使用第二个损耗网络 那就是内容损耗 我们可以为每个损耗计算
但是让我们更仔细地看看 风格损耗网络
为了计算 风格损耗 我们需要一个 测量图像风格的方法 为了实现这个 我们需要为几个 代表这个图像的中间特征 计算我们所称的格兰姆矩阵 今年 格兰姆矩阵计算 在 MPS 中 由正向和梯度内核 本机支持 所以 让我们迅速来看一下 格兰姆矩阵以及 它是如何运算的 格兰姆矩阵代表了 特征向量间的 非居中的关联 现在 每个特征向量 都是从空间扁平化一个单一的特征通道中的 一个单一的图像所获得的结果 为了创作一个格兰姆矩阵 我们在特征向量间计算点积 我们来看看这是如何使用的 在我们得到格兰姆矩阵前 我们将使用 VGG 图像分类网络 来从我们的风格和 风格化的输入图像来提取特征 现在 正如我们之前描述的 格兰姆矩阵 给了我们特征向量之间的关联
现在 当我们采用这些 从风格提取的特征 这为我们想要应用的 风格提供了真值 我们还要为 关于最好的风格图像的 现存猜测做同样的事情
现在 我们用这两个数值一起 来形成我们的风格损耗
所以 现在让我们看看 我们如何计算两个损耗中的第二个 内容损耗 和之前一样 我们将使用 VGG 提取特征 然后使用这些特征计算损耗 以及我们风格化的图像的特征 网络的最终损耗 将是内容损耗和 风格损耗的总和
所以 现在让我们看看 我们如何使用 MPS 来计算这些数值 和初始化梯度 首先 让我们假设 我们有特征代表 由 VGG 生成 首先 我们要为了 风格和风格化图像 增加格兰姆矩阵计算节点 来计算格兰姆矩阵
我们将把这些结果 注入正向损耗节点 以此来计算我们风格的损耗
这里的源图像是格兰姆矩阵 对我们的网络风格化的图像的 计算结果
对于引用风格图像的 格兰姆矩阵 将会用在标签参数中
现在它展示了一个 新的正向损耗内核的重要特征 之前 你必须使用 MPS 状态对象 来传递聚类 但是现在 你可以使用 MPS 图像 现在我们可以使用 风格化的图像和原始图像 来为内容损耗 增加损耗节点 我们可以将其相结合 来获得我们总共的损耗数值 现在我们需要初始化 最终的损耗梯度 来启动反向过程
我们使用 之前提到过的初始化梯度节点
我们之前已经看到了 损耗节点的结果是 如何用来隐式生成训练图表的 这是因为它生成了 初始的梯度 但是现在 正如我之前提到的 通过可分离损耗内核 我们可以使用初始梯度节点 来明确地完成这个 所以 这就是 我们用来生成训练图表的节点
随着图表的生成 我们可以看看 这个网络在活动中做什么
所以 我们可以看到 在 GPU 上运行的使用 MPS 的 风格转换网络 它在拥有 AMD Radeon pro 560 图表卡的 Mac Book Pro 上运行 这个展示了 风格转换训练在运行过程中 在每个迭代的结果 正如你所见 风格在日益增多地被运用 但是图像的内容 却被保留了 我还想提到的是 这些迭代在这个视频中 被加速了 以此来更好地展示 训练网络的进程
所以现在我要看一看 我们今年增加的另一个功能 随机数生成
今年 我们为 MPS 中的 两种随机数生成 增加了支持 我们有一个名为 MTGP32 的 梅森旋转算法的变量 以及一个名为 Philox 的 基于计数的生成器
这些生成器之所以被选择 是因为它们的算法 很适合 GPU 体系结构 它们也会提供 拥有很好的统计属性的 随机数的序列 现在 你们可以使用这些内核 来生成大的使用 缓冲和 GPU 内存的随机数序列 既然在 GPU 内存上 这个结果可用 你可以避免同步大的 射线和 CPU 的数字
生成像这样的随机数 对于机器学习 App 非常重要 它们被要求 比如说 初始化 你用于训练的网络的权重 同时也在训练生成 对抗的网络 或简称为 GAN 创建输入
GAN 是随机数生成器 的一个非常重要的使用例子 你需要在你的训练的 每个迭代 生成随机输入 如果你要从 CPU 同步一个阵列的数字 每一个迭代 都可以训练你的网络 过分昂贵
所以 让我们进一步看看 这些网络和 我们如何使用新的 随机数生成器
生成对抗网络或 GAN 一般都在 两个网络中构建 我们有一个生成器网络 和一个鉴别器网络 我们这里有一个生成器的例子 它只会生成 手写数字的图像 和训练中的 图像分类相似 我们将会提供有手写数字的很多例子的网络 然而 网络并没有试着对它们分类 而是试着从 从随机初始数据集 生成新的图像 以此与训练集 看起来相似 为了执行 训练过程 我们需要 一些决定这些图像 将如何相似的方法 所以 针对这第二个网络 我们将使用 我们称之为鉴别器的东西
正如这个名字所展示的 它将用来鉴别 训练图像和那些 由生成器模拟的图像 在这个例子中 它表现为图像分类网络 但是只有两个可能性 输入要么是从训练集中的真实的图像 或者它可能是生成的 是一个假图像 所以你们可以看到 这就是鉴别器 正在查询数字 是真还是假
现在 典型的生成器和鉴别器 都是一起训练的 我们训练生成器 来产生更多真实的图像 我们训练鉴别器 来更好地分辨合成的图像 与训练图像 所以 我们这里有一个 针对你的训练网络的 节点的高层次的概览 这就是我们的鉴别器训练 训练网络 它由两个损耗计算组成 所以 我们刚刚谈到的 是关于你在哪里能使用 可分离损耗节点的例子
我们还有一个损耗 通过它我们试图保证 鉴别器正确地 将模拟图像分类为假 我们还有第二个损耗 通过它我们可以训练 鉴别器将从训练集中的真实图像 分类为真 计算完这些分离损耗数值后 我们可以使用 一个初始化梯度节点 来初始化你的训练图表 第二 我们在这里有 生成器训练网络 这个稍微简单一点 它只有一个损耗数值 但是在这个例子中 我们使用一个真实的聚类数值 来保证我们的生成器可以生成 鉴别器随后会分类为 真的图像 正如我之前提到的 生成器网络开始时伴随着 一套随机的数据 我们将会使用随机数生成器 来生成这些数 所以 让我们仔细看看 随机数字生成器 现在 随机数生成器内核 属于 MPSMatrix 副框架 它们通过 MPSMatrix 随机类 被接入 所以它们在 MPSMatrix 和 MPSVector 对象上运转 这意味着它们与 metal 缓冲器一起运行 它们也与潜在的生成器一起 支持生成随机整数 或者你可以使用一个 统一的分布来生成浮点值 在这里 我们将为数值在 0 和 1 之间的 统一分配 创建一个分配描述符 然后我们将创建我们的生成器 测试合适的数据类型 然后给它一个初始种子
最后 我们为了保存结果 创建了一个矩阵 我们将运算编码至指令缓冲 所以 现在让我们回到网络 然后看看我们如何使用它 这是生成器网络的 更进一步的视图 我们有一些卷积层 一些 ReLu 层 双曲正切神经元 现在 输入图像将 成为我们随机数生成器的结果
正如我们之前看的 随机数生成器 与矩阵一起运行 但是图表 和所有神经网络内核 都需要图像 所以 我们将使用我们的 MPS 复制内核 来将矩阵上的数据复制至一个图像上
所以 首先我们要创建一个矩阵 来保存我们的随机数值
然后我们会创建一个图像 它将会作为我们网络的输入
我们将初始化一个 复制内核来运行这个复制
然后我们将编码我们的 随机数字生成器 来生成数值 我们将会编码复制 把它们复制成图像 然后我们会使用图像 编码网络 现在 想要获得关于这个网络 和使用 MPSMatrix 随机数生成器内核 的更多细节 请查看我们网上的文档 这里还有一些代码样例
现在我们增加了一些特征 来帮助提升使用 MPS 的 网络性能和效率 所以 让我们来看看 它们中的一个 预测 通过预测 你可以 有条件地实行 MPS 内核 内核的实施 在存在于 GPU 内存的 数值上被预测 它们在内核实施的时候被引用
所以 让我们看一眼网络 它说明了这个是如何使用的 这是一个图片说明 这是我们几年前 展示的一个网络 它会使用卷积神经网络 和常返的神经网络 来生成图片说明
卷积网络是 普通的分类网络 在这个例子中 我们会使用 Inception V3 它将会用来从 源图像提取特征 然后我们使用这些特征示意图 我们把它们注入一个 小的以 LSTM 为基础的网络 在那里图片说明从提取的 特征中生成 现在 我们迭代这个网络 来生成图片说明 在这个例子中 我们需要知道 在这个例子中 我们需要 运行以 LSTM 为基础的网络 以获得一些迭代 它将会被修复 我们至少需要一直这样做 直到我们相信这样 可以生成图片说明
在这个例子中 比如说 我们运行 20 次 以 LSTM 为基础的网络 每个迭代通过 在之前的迭代中 为图片说明增加一个新的词 来计算出最好的图片说明 但如果图片说明只要求 五个词 那我们需要 运行超过 我们需要的迭代
通过预测 我们可以早点 结束这个执行 在这个例子中 就是在五个单词的图片说明生成之后
所以 让我们看看 如何在 MPS 中使用它 为了实现它 我们首先需要 讨论我们是如何为 MPS 命令 提供预测数值的 针对这个 我们引入了 MPSCommandBuffer 现在 MPSCommandBuffer 是一个遵循 MTLCommandBuffer 协议的类 但是它增加了 更多一点的弹性 它可以在任何 你正在使用 metal 命令缓冲的地方被使用 正如一个 MTLCommandBuffer 它由 MTLCommandQueue 构建而成 现在 它提供了好几个重要的好处 它允许你预测 MPS 内核的实施 我们之后也会讨论到 随着你编码你的 MPS 工作 你可以简单地运行一些 中间事务 并使用一个名为 commitAndContinue 的方法 但是我们之后再谈这个 首先 让我们看看 如何使用 MPSCommandBuffers 将预测运用到 MPS 内核上 所以一个 MPS 预测对象 包含一个 metal 缓冲 这个缓冲内包含了 32 位的 整数预测数值 它们处在偏移位置
随着实施预测 我们在偏移位置 在 metal 缓冲内获得数值 数值 0 代表 我们不想实施这个内核 而非零数值则意味正常实施 在这个图表中 我们通过在偏移位置 将数值设定为 0 避开了这个内核的实施 这个偏移很重要 它让你可以在 众多 MPS 预测对象中 分享一个单一的 metal 缓冲 这样你就可以 向多个内核发送预测 每个预测数值 将会由一个不同的偏移指定 为了使用一个 预测数值 我们需要把它附加到 MPSCommandBuffer 这种情况下 我们编码到命令缓冲的任何 MPS 内核 都会获得预测数值 让我们看看 我们如何创建一个预测 并在一个 MPSCommandBuffer 上设置 首先 我们创建了 一个 MPSPredicate 对象 我们把预测附加到 我们的 MPSCommandBuffer 现在 我们会编码一个操作 来修改预测数值 因为现存的 metal 缓冲 我们需要一个内核在 metal 缓冲中产生结果 你可以使用你自己的内核 或者也可以使用 MPSMatrix 内核之一 这就是接下来我们要做的 我们首先要将预测包含在一个 MPSMatrix 对象中 然后我们将编码一个内核 以修改预测数值
所以 这里 我们将使用一个 线性神经元内核 我们将用它来做一些简单的事 我们将要缩减预测的数值 最后 我们将会 编码一个 cnnKernel 在实施之前读取预测的数值
所以 在 MPSCommandBuffers 中 使用预测是一个 消除你网络中不必要工作的简单方法 如果你有可以被避开的 内核 你可以使用预测 来利用减少的工作量 如果有应用的 多个内核 你可以使用多个预测 并只通过设定独特的偏移数 值使用一个单一的 metal 缓冲 所以 现在让我们来谈谈 MPSCommandBuffers 的其他特征 commitAndContinue 这是一个可以让你 在实施工作的时候 更简单地获得更好的 GPU 利用的方法 所以 为了看看这个是如何有益的 我们首先来复习一下 一个典型的工作量是如何实施的 一般实施 MPS 内核的方法是 将你的工作编码至一个 命令缓冲之后 交付实施 所以 这里我们有一个 关于单一命令缓冲的例子 你编码一些工作 之后我们来实施 现在 在实际中 CPU 的编码时间 将比 GPU 的实施时间 更少 但是我们想要避免 因为节流和类似这样的事情的 空闲时间
所以你可以看到 我们将在 CPU 和 GPU 之间得到一些失速 现在 解决这个问题的一个办法 是使用双重缓冲 使用双重缓冲 我们可以保持两个 命令缓冲 我们将在对一个工作进行编码的同时实施另一个 现在 这个可以 消除我们之前看到的空闲 不过这个也有一些限制 首先 正如我提到的 你需要保留两套工作 这意味着 你需要找到一个方法 将你的工作划分为两个 独立的工作量 结果 你可以有 大量增加的内存需求
然而 commitAndContinue 方法 我们可以通过将工作量 分成更小的部分 而获得这个性能的 很多好处
所以 我们将通过 在每个命令缓冲内 利用每层的独立来分解工作
然后我们会使用双重缓冲 来交付更小的组的工作 现在 commitAndContinue 将自动处理这个 内部工作分配 同时也保证 你分配在命令缓冲上的 临时对象都会 对之后编码的工作保持有效 而与双重缓冲一起 它会保证你在 GPU 实施工作同时 继续在 CPU 上编码 通过简单地允许你 分配工作量 你可以避免增加的对于 双重缓冲的内存需求 同时还可以获得 很多提升的 GPU 使用 所以让我们看看 你如何在你自己的代码中将其利用
现在我们有四个 编码于 MTLCommandBuffer 的 MPS 内核 最后 我们指派实施的工作
正如我们之前展示的 这将会给你我们 之前看到的停顿 然而 通过使用 MPSCommandBuffers 和新的 CommitAndContinue 方法 我们可以轻易地将其提升
所以 我们将创建一个 MPSCommandBuffer
我们将编码我们的 前两个内核
然后我们会调用 commitAndContinue 它会指派我们已经编码的工作 将任何分配 向前发展 并允许我们立刻继续 对另外两个内核进行编码 最后 我们可以使用一个 定期的指派来指派剩下的工作 所以你可以看到 使用 commitAndContinue 需要 你代码的变化是很少的 但是如果你要利用图表 那就更简单了
当你使用 MPSCommandBuffe 在一个图表中编码 MPS 它会自动使用 commitAndContinue 来定期在编码过程中 提交工作 不需要更深一步的变化 只是简单的使用 MPSCommandBuffer 而不是 MTLCommandBuffer 即可 最后 我想要指出 你还是可以将 commitAndContinue 与双重缓冲相结合 同时也可以得到更好的性能 所以正如你所见 它允许你缩减 我们看到的与 commitAndContinue 一起的小停顿 所以我们有很多 指派工作实施的选择 你可以使用一个 单一的命令缓冲 一次执行一个工作 为了更好的性能 潜在地与增加的 内存消耗一起 你可以使用双重缓冲
现在 通过 MPSCommandBuffer 你可以实现和使用 commitAndContinue
如果你还是想要更好的性能 你可以使用 commitAndContinue 和双重缓冲 所以让我们看看 这些途径如何在现实世界的网络上运行 针对这个例子 我们将会看看在 CIFAR-10 数据集上的 ResNet 50 网络 现在这个数据 通过使用外部 AMD Radeon Pro Vega 64 GPU 来被测算
这是一个拥有多层的 普通图像分类网络 所以这是一个 我们可以看到与 commitAndContinue 一起的好例子 我将会把单一缓冲的例子 作为我们的基线 我们在垂直轴上 有性能和内存消耗 让我们看看 双重缓冲是什么样的 现在我们提升了一点性能 但是我们同样 提升了一点 内存消耗 这是因为我们可以 通过在特定时间的飞行状态维持两倍的工作 来实现双重缓冲 所以 让我们来看看使用 CommitAndContinue
因为我们在性能上 非常接近 而却少了很多的内存负担 在这里我们可以看到 与双重缓冲一起的 CommitAndContinue 我们还有一些更好的性能 但我们也同时使用 更多的内存 所以你可以看到 使用 CommitAndContinue 是一个增加最少的内存压力 同时实现更好性能的 非常简单的方法 现在 让我们通过另一个机器学习的 App 也就是去噪 来把所有这些途径 放在一起 正如这个名字所示 去噪将去除充满噪点的图像上的噪点 并生成一个干净的图像
我们将在光线追踪的环境中 来看看这个例子 如果你看了之前的 光线追踪 metal 的视频 你会看到降噪的另一个例子 一个使用图像处理技术的例子 在这里 我们将会在机器学习基础上 看一下解决方案 针对这个例子 我们会看到三个阶段 我们将创建一个 离线训练过程 我们将运行训练网络 最后我们将 把推理图表部署至 滤波新图像
首先 我们需要创建图表 我们来仔细看看这个结构 这里我们将 开始于我们的输入图像 也就是噪点图像 是由我们的光线追踪产生的 我们将会把这个图像 注入编码的阶段 编码器是在空间上压缩图像时 提取更高等级的 特征代表的小的子网络 我们将会把这些结果 逐渐加入我们的解码器阶段 现在这些展示了反转过程 它们将重新构建 特征图中的图像 我们还将使用跳跃连接 它们将特征从 编码的图像支援到 每个解码阶段 将每个编码器的结果转发至 解码器就可以完成这个 最后 降噪的图像 是完全被重建的
所以 让我们仔细看看 编码器阶段 编码器阶段在学习 如何保留特征的同时 压缩图像 特征由三对卷积层 ReLu 层 和一个大的池化层组成 让我们来看一下这个代码 正如我们之前所见 我们可以把每个节点 按照它们在网络中的顺序进行构建 我们也会用相同的方法构建解码器 你可以先从一个不抽样层开始
之后 我们通过跳跃连接 增加对应的编码器结果 最后 我们有两对 卷积层和 ReLu 层
再次和之前一样 我们将嵌入和网络中的每层 对应的节点
现在我们可以把编码器和解码器阶段 放在一起了
所以 首先我们将 连接我们的编码器节点
在我们进一步行动 连接我们的解码节点时 我们需要加放一个编码节点 我们将这个节点称之为瓶颈节点 除了它没有 最终的大型池化层外 它与编码器是一致的 在瓶颈节点之后 我们将会连接解码器节点 通过从对应的编码器节点 传递结果图像 我们将满足跳跃连接
所以现在我们有推理图表 我们来看看训练阶段
为了开始训练阶段 我们需要计算损耗数值 我们需要与推理一起启动 我们需要与推理图表的结果 一起启动 对于训练迭代来说 现在是我们的网络 对于现有的降噪图像的最好猜测 我们还将把干净的 RGB 图像 作为真值 我们将会用它来计算一个损耗数值 我们还想要 计算第二个损耗 我们将运行一些边缘检测 我们将使用 高斯滤波器的拉普拉斯 现在 我们想这样做是因为 我们想要自己的网络 学习如何降噪图像 但与此同时我们还想要 保证它保留了 原始图像的边缘
我们将实施 高斯滤波器的拉普拉斯 或者 LoG 滤波器使用卷积
最后 我们将结合这两个损耗 第一个损耗是 RGB 损耗 第二个则是 LoG 损耗 我们将把两者 结合为一个最终的损耗
让我们来仔细看看 如何做到这一点 我们将使用 推理图表的结果 和真值 RGB 图像 来创建我们的 RGB 损耗节点 正如你之前提到的 我们将使用可分离的损耗内核 我们将传递图像至我们的 源和我们的聚类 对于我们的 LoG 损耗 我们需要将 LoG 滤波器 应用至目标 RGB 图像 和推理图表的结果
所以我们将使用卷积节点 实施 LoG 滤波器
我们将使用卷积的结果 运算 LoG 损耗 最后 随着两个损耗都被计算 我们可以加它们加在一起 并产生最后的损耗 有了最后的损耗数值 我们可以开始 反向传播阶段 同时看一看训练图表
所以 我们要通过计算初始梯度 来实现这一点 初始的梯度数值 可以让我们开启训练图表
所以这包含的几个梯度节点 首先是为了伴随着 对每个正向损耗的梯度节点的增加 然后就是编码器和 解码器阶段 现在 为了每层实施 图表节点 将需要很多代码 同时为错误 引入很多机会 然而 通过隐式图表创建 我们可以让图表 为我们做所有的工作 所以这就是所有 我们需要写来生成训练图表的东西
首先 我们使用最后损耗的结果 增加初始梯度节点
然后使用隐式图表创建 我们生成所有 剩余的梯度节点
现在我们创建了我们的图表 我们可以开始训练它了 首先 让我们讨论一下 我们的输入训练数据 输入是我们知道 期望的结果的图像 在这个情况下我们有充满噪点的图像 同时也有相对应的干净的图像 现在两个图像都是熟练掌握 MPS 使用光线追踪器生成的 我们通过让 光线追踪器只运行短时间 来生成有噪点的图像 然后通过让光线追踪器 运行更长的时间 来获得干净的图像 现在 通过训练这些图像 我们希望我们的网络 会学习如何从有噪点的图像 接近干净的图像 我们还将通过一些其他图像 增大我们的输入数据 同时通过光线追踪器 生成曲面法线和反射率
反射率图像是一个 三通道图像 它包含着 反射光数量的数值 曲面法线 是一个三通道图像 每个通道都包含了 曲面法线向量的一个成分 现在 在我们开始训练 之前 我们需要做一些 预处理的工作 正如我所提到的 这些数据都包含在三个通道中
然而 MPS 网络和 MPS cnnKernels 使用它们的图像 作为四通道纹理 所以我们要 把这些值关联起来 现在 因为每个图像是 三个通道 我们需要将它们 关联到一个 metal 纹理数组中 我们不能 使用 MPS cnn 关联法 因为它需要四个通道的特征通道 但是 我们可以编写一个 简单的内核来实现这一点 这里有一个简单的 metal 计算着色器 将这些图像关联在一起 我们将开始使用一个 线程栅格映射到结果的 每个四通道像素 我们的参数将作为 保持图像 RGB 输入 反照率输入和正常图像关联的结果 我们要让每个线程 从网格中每个输入的位置 读取一个像素 我们将把这些值关联 在一起 然后用 0 填充 剩余的未使用的通道
最后 我们要把结果写在网格中 相同的位置 现在我们有了一个着色器 它可以将这些值关联到 一个单一的 MPS 图像中 我们来看看 如何把它传递到图表上
或者 让我们先看看如何编码 下面是一个例子 它告诉我们如何 编码内核并将结果封装 到 MPS 图像中
我们的输入 是包含数据的图像 我们要 用结果作为图表的输入 因此我们需要构造一个 MPS 图像 我们将使用它的纹理 保存关联内核的结果 接下来 我们将把每个参数 绑定到适当的位置
我们将分派线程 然后最终传回 准备传递到网络中的图像 所以 现在输入已经 准备好了 我们来看看 执行训练图 在训练期间 我们将执行多次迭代的图表 我们将在每个训练集中 执行多个批次 然后每个期 执行多个批次
这里我们要对 训练图表进行一次迭代 我们将使用刚才显示的内核将 除了批处理中的每个图像外的 所有图像关联在一起
我们将把这些关联放入 阵列中 因为 这个图表需要一个图像阵列 一个用于源图像 一个用于聚类 现在我们将在这里使用 MPSCommandBuffers 因为 正如我们前面看到的 这是提高 GPU 利用率的一种 简单方法
最后 我们将 对图像进行编码 然后提交执行 现在 让我们仔细看看 每个训练期 在这个方案中 我们要 处理完整的训练 数据集 每个期 以便 更好地收敛 我们还会每隔几个期 更新一次训练集 这里是每 100 个期 此时 我们还会执行网络校验 最后 在每千分之一期 我们都要降低 优化器的学习率 这也将有助于提高收敛性 我们来看看它的代码 首先 我们要先每个期一次 处理一次 整个训练集 我们在这里看到的是每一个 第一百的期 我们要更新 我们的训练数据集 我们要运行校验
最后 每第一千的期 我们的学习率 会下降 2 倍 所以 现在我们已经训练了 这个图表 我们可以降噪新的图像 现在 由于 MPS 可以 跨多个平台使用和优化 我们可以轻松地 在不同的设备上部署训练网络 例如 你可能希望 在 Mac 上执行 计算开销较大的训练 然后使用训练网络 在 iPad 过滤图像 首先 让我们看看 MPS 中的串行化支持 现在所有 MPS 内核 以及图表都支持安全编码 这允许你轻松地 保存网络到磁盘上和从磁盘上恢复网络
对于从数据源 加载权重的网络 你必须自己在数据源上 实现安全编码支持 现在 这需要支持 SecureCoding 属性 和 init 和 encode(coder) 方法 现在 一旦你的数据源 符合安全编码 就很容易序列化和保存图表 首先 我们要创建 一个编码器编码图表 然后我们会调用 图表上的 encode(with:coder) 当这种情况发生时 它会序列化每个 单独的内核 如果这些 内核有数据源 它也会序列化它们 这样 生成的存档文件 包含恢复和初始化图表所需的所有信息
最后 我们可以将数据保存到文件夹中 现在我们来看看加载它 因此 为了确保 在未存档的内核上 初始化正确的 metal 设备 我们提供给你们 MPSKeyedUnarchiver 它就像一个常规的解压缩工具 只是你用一个 metal 设备 初始化它 然后它会在 所有内核初始化时 提供这个设备 因此 在加载数据之后 我们将使用设备创建一个 解压缩工具 我们将在新设备上 恢复该图 用现在初始化了的训练网络 可以使用该图对新图像进行降噪 那么 让我们来看看 这个网络是如何运作的 这里我们把我们的降噪器 应用了场景中 顶部区域显示了 输入噪点图像的场景 是什么样的 中心区域显示了 我们降噪的结果 你可以看到底部区域显示了 真值的图像 正如你所看到的 降噪区域 看起来几乎和干净的目标 一样好 除了我们 用更少的工作 来实现这一点 因为 我们没有运行完整的光线跟踪器 正如你所看到的 使用 MPS 我们可以很容易地实现复杂的网络 比如降噪和风格转换
今年我们将对 推理和训练的支持 扩展到一类新的网络 特征 比如可分耗损和 随机数字生成 通过 MPSCommandBuffering 我们现在支持通过预测和 commitAndContinue 改进性能和 获得更好的利用率 并且我们通过隐式图表创建 使所有这些特征 更容易使用 因此 有关 MPS 和 metal 的 更多信息 请参见 在线文档和我们的示例代码 有关 MPS 和光线跟踪的 更多信息 请参见前面的 metal 光线跟踪视频 谢谢 [掌声]
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。