iOS 10和macOS中的卷积神经网络

物联网

  原文: Convolutional Neural Networks in iOS 10 and macOS

  作者: Geppy Parziale

  译者:ALEX吴浩文

  苹果在iOS 10和macOS 10.12的Metal Performance Shaders框架和Accelerate框架里,引入了新的卷积神经网络APIs。

  我在一篇之前的文章里已经介绍了 iOS上的机器学习(ML)和人工神经网络(ANN)。如果你对这些不熟悉,建议你先读读那篇文章。

  我最近参加了 CVPR 2016 ,一个计算机视觉与模式识别会议。我在那得知最近卷积神经网络被世界各地的大学和公司用于几乎所有的研究工作。卷积神经网络在计算机视觉的不同领域的流行,再加上手机上又快又强的GPU,使卷积神经网络也成为移动开发的一个极具吸引力的利器。卷积神经网络和深度学习打开了移动应用创新的大门。

  我从五年前在苹果工作时开始接触卷积神经网络(CNNs)。当时可不像今天,可用的文献和工具都极其有限。我曾用CNNs建立一个iOS和OS X上的光学字符识别(OCR)。它是iOS 5的一个实现。OCR的准确度是惊人的,即使当时设备上的实现是用的CPU。

  在这之后,我继续对其他类型的应用CNNs。最近,我用CNNs来进行人脸识别和面部表情识别。我们得到的结果是惊人的。

  卷积(Convolution)

  CNN把一种大量使用的很常见的信号处理操作称为卷积。卷积是把数组(或矩阵)的邻近元素进行加权求和。其中使用的权重是由一个输入数组定义的,它通常被称为核(kernel)、滤镜(filter)或卷积的遮罩(mask)。

  卷积是一种非常重要的数字信号(音频、视频、图像)处理,因此图形处理单元(GPU)优化了它。如果你想从事CNNs工作,GPU是最重要的实现工具。

  作为人类,我们也在我们的日常活动中使用卷积,尤其那些涉及到我们五感的活动。例如,当我们听音乐或盯着东西看时,我们的大脑对外部世界的声音和光信息在执行着每秒数百万次的卷积。

  一维卷积的例子

  让我们构建一个例子来更好地理解卷积是如何工作的。下面的图显示了一个输入数组或一维信号x[n]与一维核数组w[n]的卷积。

物联网

  在这个例子中,我任意假设输入数组的值是1、2、…7,核数组的值是1、1、2、1、1。前面的图显示了输出序列y[n]的元素(或样本)y[2]是如何被计算的。

  在一般情况下,核的个数往往是奇数,这使得在被计算元素周围的加权和计算是对称的。远小于输入序列x[n]的核也是很常见的。核的中央元素被用作我们想处理的输入信号中的元素的重量,其他元素则作为被计算元素左右两边的元素的权重。

  概括这个例子,如果x[n]是一个输入序列,w[m]是一个核序列,那么卷积操作的结果y[n]可以用以下的数学表达式表示:

  注意一下序列w[m]在操作中第一个被反转和转化。

  如果我们用以前的数学公式来计算之前例子中y[n]的每个元素,我们得到以下结果:

  既然卷积由相邻元素的顺序定义,那么靠近数组结尾的输出元素自然存在边界条件。为了避免这个问题,一钟很常见的做法是在输入序列x[n]的两端添加足够的元素(称为鬼元素)。如果你添加0,这个操作被称为零填充。其他方法也可以。在实现卷积时,你需要解决填充问题。

  Swift的卷积

  让我们来看看如何用Swift实现卷积。假设我们有以下的输入数组x和核数组w:

  let x: [Float] = [1, 2, 3, 4, 5], M = x.count

  let w: [Float] = [1, 2, 3], N = w.count

  let T = N+M-1 // 这个之后需要

  在我们开始之前,如上所述让我们添加N-1个0到序列x,和M-1个0到核来容纳计算。你可以使用以下函数:

  func pad(sequence x: [Float], other sequence: [Float]) -> [Float] {

  return x + [Float](repeatElement(0, count: sequence.count-1))

  }

  所以,填充过的新序列是:

  let paddedX: [Float] = pad(sequence: x, other: kernel)