下一站,人工智能



- 作者: SwiftCafe


关于人工智能,是近一两年非常火的一个话题。你可能会想到 AlphaGo,无人车,这些即便是对我们开发者来说都很高深莫测的产品。但有没有可能用最简单的方式来说明它的基本原理呢,我们就一起来进行一次初体验。

深度学习与神经网络

说起现在的人工智能,其实大多都是对神经网络技术的应用。比如刚刚结束不久的 WWDC 发布会中 iOS 10 的智能相册,就是对神经网络的一个应用。那么什么是神经网络呢。可以理解为用计算机来模拟我们大脑的思维方式。

在展开之前,咱们先看两个例子, 第一个问题:

1
4847320 次方等于几?

相信如果用我们大脑计算这个问题可能要花很多时间,而且还有可能会出差错。但这种规则确定的数值运算,对机器来说简直小菜一碟,以现在计算机的性能,任何一个只要还算正常的电脑,都能在毫秒级的速度内解决这个问题,并且基本不会出差错。

从这个角度看,机器是不是很厉害? 别着急, 咱们再来看第二个问题:

这张图中有几只猫? 3只嘛,这还用问? 对我们人来说,这个太直观了,如果我们大脑的思维速度可以计算的话,我想我们也是在毫秒级别内就解决这个问题了。

现在,这个问题如果让机器来回答,嘿嘿,貌似就没那么容易了。 在机器的世界里,这张图片就是一个像素阵列而已。它不会认识猫,它只知道颜色和坐标。

这么一比,是不是发现原来我们的思维速度是这么的快吧。我们大脑的强大之处就在于这里,比如这张图片中的猫,我们可以在很快的速度里将我们看到的图片中的内容和我们大脑记忆中猫的形态做对比,然后计算出这张图片中和我们记忆中猫的形象的匹配程度,最后得出这张图片中是否有猫,有几只。这种运算是我们大脑最擅长的,我们大多数时候甚至都没意识到我们这个强大的能力。

另外, 我们还有推理能力,一旦我们建立了对猫的形象的认识,我们就可以对任何照片中是否有猫这个问题作出判断了。而这一切,都发生在我们大脑的神经网络中。

模拟我们思维方式的神经网络

神经网络正是模仿我们大脑思维方式。每一个神经元都会对某个因素做出决策,这些神经元组合在一起,就成了一个网络,它们共同作用后就会得到最终的决策。

枯燥的理论咱们不多说了(PS:因为我也研究很不深~),但是咱们可以用一个最好理解的实例来亲身体验一下神经网络的妙处。

假如我们有这样一个 UI, 上面红色区域和下面蓝色区域,我们可以在任何地方点击,然后用神经网络的方式来判断我们点击的是红色区域还是蓝色区域。

当然,这个问题比较原始,我们用普通的坐标判断其实就可以解决了,但用这个最简单的例子会帮助我们更容易理解神经网络的工作方式。

开始进行

首先,说到神经网络,他们肯定是一个个神经元组成的网络,构造这个网络的一个基本单位可以是感知器(Perceptron), 它可以接受一组输入,然后它对每一个输入都进行加权求和,最终得出它的判断结果。

比如我们上图中的感知器有三个输入,x 坐标为 30, y 坐标为 124,另外还有一个 bias 偏差值。 我们的感知器会对每一个输入都有一个权重,比如分别对x, y 和 bias 的权重为 1,-1,3。 那么感知器得到输入后,会根据权重进行加权求和,就是 (30 1) + (124 -1) + (1 * 3) = - 91。 感知器会根据这个结果对最终的输出进行归类,如果结果大于 0 那么就返回 1, 否则就返回 -1。

这就是我们这个感知器的基本运作原理了。 那么我们现在可以开始构建代码了, 首先定义 Perception 结构:

1
2
3
4
struct Perceptron {
var weights: [Float]
}

首先, 它包含一个数组 weights, 这个数组就代表这个感知器对它的所有输入的权重。 然后我们在声明一个构造器,初始化权重列表:

1
2
3
4
5
6
7
8
9
10
11
12
init() {
weights = [Float]()
for _ in 0...2 {
let randWeight = random() % 3 - 1
weights.append(Float(randWeight))
}
}

这里我们只是简单的给每一个权重一个随机值而已,为什么要这样做呢? 原因是这样,一般来说感知器的每一个权重不是靠我们程序员算出来的,而是通过不断的数据输入自动进行调整的。比如说我们给这个感知器输入大量的数据,并且预先告诉感知器,这些输入的正确结果是什么。,那么感知器会先根据他初始的权重值对输入的数据计算结果,如果它的得出的结果和我们告知他的正确结果不一样,那么他就会调整它的各个权重去接近最终的正确结果。

这个过程,就是我们所常说的数据训练了,也是深度学习的一个基础,虽然有了感知器,但这些感知器需要经过大量的数据训练后才会变得足够聪明。

那么我们的初始化工作完成了,接下来我们定义一个方法,用于根据输入,然后加权求和得出输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
func feedForward(inputs: [Float]) -> Int {
var sum: Float = 0.0
for index in 0..<weights.count {
sum += inputs[index] * weights[index]
}
return active(sum)
}

这段代码就是我们刚才说的加权求和并输出结果的代码实现, 首先计算出 sum, 然后调用 active 根据 sum 的值对结果进行归类, active 函数定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
func active(sum: Float) -> Int {
if sum > 0 {
return 1
} else {
return -1
}
}

这样我们的感知器的整个逻辑判断流程就完成了。 接下来我们还需要做一件重要的事情, 就是如何训练我们的感知器调整他们的权重值,让他们能够做出准确的判断。说起来好像挺高深的,其实如果只是简单实现的话,也并不复杂。

看看这段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var c:Float = 0.01
mutating func train(inputs: [Float], desired: Int) {
let guess = feedForward(inputs)
let error = desired - guess
for index in 0..<weights.count {
weights[index] += c * Float(error) * inputs[index]
}
}

这个 train 函数接受一组输入,然后还有这组输入预知的结果。 这就是我们刚才提到的训练过程,我们先给感知器提供很多数据,他们的结果已知,用这些已有的数据来训练我们的感知器去调整他们的权重值。

train 函数,首先会调用感知器的 feedForward 函数得出这个感知器对这组输入的判断结果 - guess。 然后用我们已知的答案 desired 减去这个 guess 值。 得到一个 error。 这个 error 其实就是我们感知器与正确结果之间的偏差。 然后我们在用这个偏差,和另外一个偏移速度 c 值,将他们与 input 中相应的输入值相乘,最后得出新的权重值。

这个 c 值其实就是我们每次训练对权重值调整的幅度,这个幅度如果太小,就会导致我们接近最佳方案的速度变慢, 而过大又会导致结果不准。 所以 c 值也需要权衡考虑得出。

因为这篇文章定位于帮助大家对神经网络的初步理解,至于这个公式的原理就不多深入讨论了。

训练感知器

现在感知器的基本逻辑,以及如何训练他的逻辑都定义好了。 那么接下来我们就需要用大量的数据来训练他了~

一般情况下这些大量数据是需要我们积累和采集的,比如我们文章开头说的识别图片中猫的例子,如果要训练这个神经网络,就需要采集很多猫的图片。但是我们这个案例的情况就相对简单一些了, 我们只需要给出在屏幕中红区和蓝区的坐标值, 换句话说,假如红区的最下方 y 坐标是 200, 那么我们只需要判断坐标值,如果小于 200 ,肯定在红区, 大于 200 就在蓝区。

所以我们不需要去采集大量数据, 只需要根据这个规则生成训练数据即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
func trainPerceptron() {
for _ in 0...5000000 {
let x = random() % Int(self.view.frame.size.width)
let y = random() % Int(self.view.frame.size.height)
if y >= 200 {
perceptron.train([Float(x),Float(y),Float(1.0)], desired: -1)
} else {
perceptron.train([Float(x),Float(y),Float(1.0)], desired: 1)
}
}
}

怎么样, 其实这个训练的逻辑也并不复杂, 只是会消耗一些计算资源。 我们这里随机生成了 500 万个坐标,然后根据 y 的值将这些坐标规则,如果 y 小于 200, 那么 descired 的结果就是 1, 代表红区, 否则 desired 就是 -1, 代表蓝区。

让感知器运转吧

到此为止,我们对感知器的训练逻辑也完成了,当然我们只是一个最简单的案例,真正的神经网络训练要比这个复杂很多。 现在你可以跑起来这个程序,这个训练计算可能会消耗几秒钟, 在这之后,你的感知器就已经足够智能了。 当然,只是针对我们这个特定的问题~

然后,可以在 touch 方法中进行验证:

1
2
3
4
5
6
7
8
9
10
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
if let point = touches.first?.locationInView(self.view) {
let result = perceptron.feedForward([Float(point.x), Float(point.y), 1])
print("\(point.x),\(point.y) \(result)")
}
}

现在,只要我们在屏幕任意区域点击, 感知器就可以判断出我们是点在了那个区域。 不知你的感觉如何, 我第一次完成这个程序后的感觉还是有些小兴奋的~

这个小例子,只相当于神经网络的 Hello World, 但它为我们开启了一扇新领域的门。

完整代码大家可以到 Github 中找到:https://github.com/swiftcafex/hello-ai

结尾

人工智能的应用近几年越来越广泛,虽然我们可能并没有太多感知到他们,但他们确实已经存在我们的日常生活中了。而且,说到人工智能,大家可能会想到无人车, AlphaGo 这些超大规模的系统。往往会觉得他们特别高深,不可接近。但其实未必只有这些所谓高大上的东西才算人工智能,比如苹果发布会提到的 iOS 10 中的智能相册。再比如根据你的喜好给你推荐一些商品,这些弱人工智能的应用离我们并不遥远。所以嘛,没准下一个机会就把握在你手中。

如果你觉得这篇文章有帮助,还可以关注微信公众号 swift-cafe,会有更多我的原创内容分享给你~

本站文章均为原创内容,如需转载请注明出处,谢谢。




微信公众平台
更多精彩内容,请关注微信公众号


公众号:swift-cafe
邮件订阅
请输入您的邮箱,我们会把最新的内容推送给您: