Run this notebook online:\ |Binder| or Colab: |Colab| .. |Binder| image:: https://mybinder.org/badge_logo.svg :target: https://mybinder.org/v2/gh/deepjavalibrary/d2l-java/master?filepath=chapter_convolutional-neural-networks/why-conv.ipynb .. |Colab| image:: https://colab.research.google.com/assets/colab-badge.svg :target: https://colab.research.google.com/github/deepjavalibrary/d2l-java/blob/colab/chapter_convolutional-neural-networks/why-conv.ipynb 从密集层到卷积层 ================ 到目前为止我们讨论过的模型 (直到今天)保留适当的选择 当我们处理表格数据时。 我们所说的\ *表格*\ 是指数据由 对应于示例的行数 和对应于特征的列。 有了表格数据,我们可以预测 我们寻求的模式可能涉及 功能之间的交互, 但我们并没有假定任何结构是\ *先验的* 关于功能如何交互。 有时,我们真的缺乏知识来指导 建造更精巧的建筑。 在这些情况下,多层感知器 也许是我们能做的最好的了。 然而,对于高维感知数据, 这些\ *无结构*\ 的网络可能会变得笨拙。 例如,让我们回到我们正在运行的示例 区分猫和狗的方法。 说我们在数据收集方面做得很彻底, 收集100万像素照片的注释数据集。 这意味着网络的每个输入都有\ *100万个维度*\ 。 甚至大幅减少到\ *1000个隐藏维度* 需要一个\ *密集*\ (完全连接)层 以 :math:`10^9` 参数为特征。 除非我们有很多GPU,一个天才 对于分布式优化, 还有非凡的耐心, 学习这个网络的参数 结果可能是不可行的。 细心的读者可能会反对这一论点 基于100万像素的分辨率可能不是必需的。 然而,尽管我们可能 为了避开10万像素, 我们的隐藏层尺寸\ :math:`1000`\ 被严重低估了 所需的隐藏节点数 为了学习图像的良好表现, 因此,一个实用的系统仍然需要数十亿个参数。 此外,通过拟合如此多的参数来学习分类器 可能需要收集大量数据集。 然而今天,人类和计算机都能 为了更好地区分猫和狗, 似乎与这些直觉相矛盾。 这是因为图像具有丰富的结构 可以被人类利用 以及机器学习模型。 卷积神经网络是一种创造性的方法 机器学习已经被用来开发 自然图像中的一些已知结构。 .. _img_waldo: 不变性 ------ 假设你想检测图像中的一个物体。无论我们使用什么方法来识别物体,都不应该过分关注物体在图像中的精确\ *位置*\ ,这似乎是合理的。理想情况下,我们的系统应该利用这些知识。猪通常不会飞,飞机通常不会游泳。尽管如此,我们仍然应该认识到,一头猪出现在图片的顶部。我们可以从儿童游戏《Waldo在哪里》(如 :numref:`img_waldo`\ )中得到一些启发。该游戏由许多充满活动的混乱场景组成。Waldo出现在每个城市的某个地方,通常潜伏在某个不太可能的地方。读者的目标是找到他。尽管他有着独特的着装,但由于有大量的干扰,这可能会出人意料地困难。然而,\ *Waldo的长相*\ 并不取决于\ *Waldo的位置*\ 。我们可以用Waldo探测器扫描图像,该探测器可以为每个斑块指定一个分数,表明斑块中含有Waldo的可能性。CNN系统化了这种空间不变性的概念,利用它来学习具有少量参数的有用表示。 我们现在可以让这些直觉更加具体 通过列举一些需求来指导我们的设计 适用于计算机视觉的神经网络体系结构: 1. 在最早的层次中,我们的网络 应该对同一个补丁有类似的反应, 无论它出现在图像中的什么位置(平移不变性)。 2. 网络的最早层次应该集中在本地区, 不考虑遥远地区(地区)图像的内容。 最终,这些局部表示可以聚合 在整个图像级别进行预测。 让我们看看这是如何转化为数学的。 约束MLP ------- 首先,我们可以考虑一个MLP。 以 :math:`h \times w` 图像作为输入 (在数学中表示为矩阵,在代码中表示为2D数组), 以及\ **类似组织的隐藏表示 为 :math:`h \times w` **\ matrices / 2D arrays\*\*。 让这一点深入人心,我们现在不仅想到了输入,而且想到了 同时,隐藏的表征也具有空间结构。 让 :math:`x[i, j]` 和 :math:`h[i, j]` 表示像素位置 :math:`(i, j)` 分别在输入图像和隐藏表示中。 因此,每个 :math:`h \times w` 隐藏节点 从每个 :math:`h \times w` 输入接收输入, 我们将不再使用权重矩阵 (正如我们之前在MLPs中所做的那样) 来表示我们的参数 作为四维重量张量。 我们可以将这个密集层正式表示为: .. math:: h[i, j] = u[i, j] + \sum_{k, l} W[i, j, k, l] \cdot x[k, l] = u[i, j] + \sum_{a, b} V[i, j, a, b] \cdot x[i+a, j+b]. 从\ :math:`W`\ 到\ :math:`V`\ 的转换完全是装饰性的(目前) 因为有一对一的通信 在两个张量的系数之间。 我们只需重新索引下标 :math:`(k, l)` 比如 :math:`k = i+a` 和 :math:`l = j+b`\ 。 换句话说,我们设置 :math:`V[i, j, a, b] = W[i, j, i+a, j+b]`\ 。 指数\ :math:`a,b`\ 既有正的抵消,也有负的抵消, 覆盖整个图像。 对于隐藏层 :math:`(i, j)` 中的任何给定位置 :math:`h[i, j]` 我们通过对像素求和来计算其值,单位为 :math:`x`\ , 以 :math:`(i, j)` 为中心,加权为 :math:`V[i, j, a, b]`\ 。 现在让我们引用第一条原则 如上所述:\ *平移不变性*\ 。 这意味着投入的变化是\ :math:`x` 只会导致激活\ :math:`h`\ 发生变化。 只有当\ :math:`V`\ 和\ :math:`u`\ 实际上不依赖于 :math:`(i, j)`\ 时,这才是可能的, 我们有\ :math:`V[i, j, a, b] = V[a, b]`\ ,而 :math:`u` 是一个常数。 因此,我们可以简化 :math:`h`\ 的定义。 .. math:: h[i, j] = u + \sum_{a, b} V[a, b] \cdot x[i+a, j+b]. 这是一个复杂的问题! 我们有效地对像素进行加权 :math:`(i+a, j+b)` 在 :math:`(i, j)` 附近,系数为 :math:`V[a, b]` 获取值 :math:`h[i, j]`\ 。 请注意, :math:`V[a, b]` 需要的系数比 :math:`V[i, j, a, b]`\ 少得多。 对于100万像素的图像,它最多有100万个系数。 这比以前少了100万个参数 不再依赖于图像中的位置。 我们取得了重大进展! 现在让我们来引用第二个原则---\ *局部性*\ 。 如上所述,我们认为我们不应该 远离\ :math:`(i, j)` 为了搜集相关信息 评估在 :math:`h[i, j]`\ 下发生了什么。 这意味着在某个范围之外 :math:`|a|, |b| > \Delta`\ , 我们应该设置 :math:`V[a, b] = 0`\ 。 等价地,我们可以将 :math:`h[i, j]` 重写为 .. math:: h[i, j] = u + \sum_{a = -\Delta}^{\Delta} \sum_{b = -\Delta}^{\Delta} V[a, b] \cdot x[i+a, j+b]. 简而言之,这是一个回旋层。 当局部区域(也称为\ *接受域*\ )很小时, 与完全连接的网络相比,差异可能是巨大的。 而之前,我们可能需要数十亿个参数 为了仅表示图像处理网络中的一个层, 我们现在通常只需要几百个,没有 改变两者的维度 输入或隐藏的表示。 为参数的大幅减少付出的代价 我们的特征现在是平移不变的 我们的层只能包含本地信息, 确定每个隐藏激活的值时。 所有的学习都依赖于施加归纳偏见。 当这种偏见与现实相符时, 我们得到了有效的模型样本 这很好地概括了看不见的数据。 但当然,如果这些偏见与现实不符, 例如,如果图像不是平移不变的, 我们的模型甚至可能难以适应我们的训练数据。 卷积 ---- 在继续之前,我们应该简要回顾一下 为什么上述操作被称为\ *卷积*\ 。 在数学中,两个函数之间的卷积, 假设\ :math:`f, g: \mathbb{R}^d \to R`\ 被定义为 .. math:: [f \circledast g](x) = \int_{\mathbb{R}^d} f(z) g(x-z) dz. 也就是说,我们测量了\ :math:`f`\ 和\ :math:`g`\ 之间的重叠 当一个函数被"翻转"并移位 :math:`x` 。 每当我们有离散对象时,积分就变成和。 例如,对于\ :math:`\ell_2`\ 上定义的向量,即: 可平方和的无限维向量集 当索引运行在 :math:`\mathbb{Z}` 上时,我们得到以下定义。 .. math:: [f \circledast g](i) = \sum_a f(a) g(i-a). 对于二维数组,我们有一个对应的和 指数 :math:`(i, j)` 分别代表 :math:`f` 和 :math:`(i-a, j-b)` 代表 :math:`g` 。 这看起来与上面的定义相似,但有一个主要区别。 我们不是使用 :math:`(i+a, j+b)`\ ,而是使用差异。 不过,请注意,这种区别主要是表面上的 因为我们总是可以通过使用 :math:`\tilde{V}[a, b] = V[-a, -b]` 获取 :math:`h = x \circledast \tilde{V}`\ 。 我们最初的定义更恰当 描述了一种\ *互相关*\ 。 我们将在下一节回到这一点。 .. _fig_waldo_mask: Waldo 回顾 ---------- 回到我们的沃尔多探测器,让我们看看这是什么样子。 卷积层选择给定大小的窗口 并根据掩码 :math:`V`\ 对强度进行加权,如 :numref:`fig_waldo_mask`\ 。 我们的目标可能是学习一个模型 无论什么地方的"waldoness"最高, 我们应该在隐藏层激活中找到一个峰值。 这种方法只有一个问题。 到目前为止,我们幸福地忽略了图像是由 共有3个通道:红色、绿色和蓝色。 实际上,图像不是二维对象 而是 :math:`3^{\mathrm{rd}}` 阶张量, 以高度、宽度和\ *通道*\ 为特征, 例如,形状为 :math:`1024 \times 1024 \times 3` 像素。 虽然前两个轴涉及空间关系, :math:`3^{\mathrm{rd}}` 可以被视为赋值 *每个像素位置*\ 的多维表示。 因此,我们将 :math:`\mathbf{x}` 索引为 :math:`x[i, j, k]`\ 。 卷积掩模必须进行相应的调整。 我们现在有了 :math:`V[a, b, c]`\ ,而不是 :math:`V[a, b]`\ 。 此外,正如我们的输入由 :math:`3^{\mathrm{rd}}` 阶张量组成, 事实证明,采用类似的公式是一个好主意 我们的隐藏表示为 :math:`3^{\mathrm{rd}}` 阶张量。 换句话说,不是只有一次激活 对应于每个空间位置, 我们需要一个完整的隐藏激活向量 对应于每个空间位置。 我们可以把隐藏的表象想象为 许多二维网格相互叠加。 在输入中,这些有时被称为\ *通道*\ 。 它们有时也被称为“要素地图”, 因为每一个都提供了一组空间化的集合 将学习到的特征添加到后续层。 直觉上,你可能会想象在较低的层, 一些通道可以专门识别边缘, 其他人需要识别纹理等。 为了在输入和隐藏激活中支持多个通道, 我们可以将第四个坐标添加到 :math:`V`: :math:`V[a, b, c, d]`\ 。 把我们所有的东西放在一起: .. math:: h[i, j, k] = \sum_{a = -\Delta}^{\Delta} \sum_{b = -\Delta}^{\Delta} \sum_c V[a, b, c, k] \cdot x[i+a, j+b, c]. 这是卷积神经网络层的定义。 我们仍有许多行动需要解决。 例如,我们需要弄清楚如何组合所有激活 到单个输出(例如,图像中是否有Waldo\ *anywhere*\ )。 我们还需要决定如何高效地计算, 如何组合多层, 适当的激活功能, 以及如何做出合理的设计选择 产生在实践中有效的网络。 我们将在本章剩余部分讨论这些问题。 总结 ---- - 图像中的平移不变性意味着图像的所有面片将以相同的方式处理。 - 局部性意味着只有一小部分像素将用于计算相应的隐藏激活。 - 输入和输出通道允许我们的模型在每个空间位置捕捉图像的多个方面。 练习 ---- 1. 假设卷积掩码的大小为 :math:`\Delta = 0`\ 。 显示在这种情况下卷积掩饰 为每组通道独立实现MLP。 2. 为什么翻译不变性毕竟不是个好主意? 什么时候允许猪飞可能没有意义? 3. 在决定如何做时,我们必须处理哪些问题 处理对应于像素位置的激活 在图像的边界处? 4. 描述一个类似的音频卷积层。 5. 你认为卷积层也适用于文本数据吗? 为什么? 6. 证明 :math:`f \circledast g = g \circledast f`\ 。