机器学习的乐趣

百度云服务器

(声明:本文内容纯属个人兴趣和观点,与本人在职公司的立场无关)

有些人曾经看过我评论人工智能的文章。他们以为我不看好人工智能,但却惊讶的发现,我现在的职位叫做 Machine Learning Engineer。

其实我并没有改变过我的立场。我从来没有否认过机器学习的价值,我只是对“人工智能”的过度浮夸表示过批评。打心眼里,我一直对机器学习感到好奇,然而却没有动力和压力来推动我去深入了解这个领域。这就是为什么面临多个工作选择的时候,我放弃了可以让我舒服吃老本的方向,而决定去探索这个相对陌生的领域。我热爱未知的事物,因为通过理解它们,我可以不断得到启发。

经过一番头痛的折腾,我亲自实现了机器学习的各种主要算法,可以说理解了机器学习的原理。虽然经验粗浅,然而我却出乎意料的把机器学习和我的老本行(编程语言 PL)给联系在了一起。它们之间的相通之处让我着迷,给我启发。

你可能没有想到,机器学习(machine learning)和逻辑编程(logic programming)有着如此美妙的联系,在我眼里她们就像一对亲姐妹。现在我就简要的讲一下我看到了什么。


逻辑编程是什么

说到逻辑编程(logic programming),人们不禁想到 Prolog 之类晦涩的逻辑式编程语言,只剩下敬畏和茫然。其实,逻辑编程的原理可以被很轻松的解释清楚,而不需要理解 Prolog。

你只需要看一个很简单的例子:

有一个未知数 X,我们不知道它是多少,但我们知道:

​ X + 2 = 5

请问 X 是几?

以上求解 X 的问题就是一个逻辑程序。像 Prolog 这样的逻辑语言系统,会给你结果:X 等于 3。可是这个问题却不能用其它几乎所有编程语言来表达(C, C++, Python, Java, Go, Scala, Haskell, Rust, Swift…)。原因在于,使用普通的编程语言,你不能把“未知数”当成一个值来进行演算。

在我们的例子里面,X 的值是未知数,所以当普通语言看到 X + 2 这样的表达式,它就无法运行。它会报错:使用未初始化的变量 X。也就是说,你必须先知道 X 的值,你才能说 X + 2

但在像 Prolog 这样的逻辑式语言里面,“未知数”是可以被作为一个正常的值来进行计算的。它们可以被传递到其它函数里,可以被放进数据结构,可以进行复杂的逻辑组合操作,就像你在操作一个普通的数字或者字符串一样。

逻辑式程序中一般会有一个(或者多个)“目标”(goal)。目标一般是一个判断表达式,也就是说它的值是布尔类型(boolean)。这里我们的例子里只有一个目标,就是“X + 2 = 5”。也就是说,我们想要 X 加上 2 等于 5。

当逻辑式语言看到了目标,就把目标记下来。最后程序员开始提问:X 是几?这时候,逻辑语言的运行系统开始进行“反向计算”,找到未知数的值,使得目标的值为“真”(true)。在我们的例子里,系统会告诉你:X 等于 3。

为什么叫做“反向计算”呢?因为

  1. 我们先声明了未知变量 X
  2. 然后我们提出目标 X + 2 = 5

对于复杂一点的程序,1 和 2 之间可能还有其它的代码。我们最后的问题,却是问最开头声明的变量 X 等于几,所以系统从最后面的目标 X + 2 = 5 出发,“反向”推导出 X 的值。

这就是为什么研究逻辑式编程的人把这种操作叫做“反向计算”。你可能注意到了,我们的代码里面只写了加法(+)操作,而系统实质上为我们做了减法:5 – 2 得到 3。

如果你想深入理解逻辑式编程,我建议你看看 Dan Friedman 的书『The Reasoned Schemer』。但目前你了解到的这些,应该足以读完这篇文章。


机器学习与逻辑编程的相似点

恭喜你,你已经明白了逻辑编程是什么。下面我们来看看它跟机器学习有什么关系。

首先我们看到逻辑编程有“目标”(goal),比如 X + 2 = 5。在机器学习中有一个对应的东西,那就是误差函数(loss function)。只不过逻辑编程的 goal 是个等式,而机器学习的 loss function 是个函数。

逻辑编程系统会为你选择未知数的值,从而精确地“满足”这个 goal。而机器学习的目标呢,是要为你选择未知数的值,最小化这个 loss function,使得误差最小。看到相似之处了吗?所以,机器学习可以被看成是“在连续空间中的近似的逻辑编程”,而逻辑编程可以被看成是“在离散空间中的精确的机器学习”。

逻辑编程有“反向计算”,机器学习有“反向传递”(back propagation),而它们的工作方式,有着惊人的相似之处。只不过机器学习因为是连续空间的,所以需要使用微积分的原理,而不只是简单的逻辑组合。

实际上逻辑编程必须先进行正向计算,构造出含有未知数的结构,然后进行所谓“resolution“,求出未知数的值。而机器学习也类似,你必须进行一遍正向计算(forward pass),然后才能进行 back propagation,求出导数,并且更新“weight”的值。

逻辑编程的“未知数”(比如 X),对应了机器学习的 weight。实际上,机器学习的 weight 本质就是“未知数”。你需要得到它们的值,使得 loss function 最小,但一开头你不知道它们是什么,所以你给它们一些随机的初始值,让系统开始正向计算。机器学习的 weight 和逻辑编程的未知数如此的相似,它们可以被作为普通的值,与输入进行计算操作(Conv 等操作),直至你遇到 goal 或者 loss function,然后你掉头回去调整未知数的值……


机器学习框架是程序语言

所以呢,你现在明白了我为什么对机器学习感兴趣了吧。作为一个 PL 专家,我看到了它与编程语言的优雅知识之间的联系,看到了它是对于“计算”概念的一种扩展。机器学习把“计算”和“微积分”奇妙地融合在了一起。

实际上,你可以把机器学习的各种框架(framework)看成是新的编程语言系统,它们的工作原理类似于 Prolog 语言的运行时系统。如果要起一个名字,你也许可以把它们叫做“可求导编程语言”(differentiable programming language)。

目前这些语言还处于初级阶段,表达力比较弱,有各种不完善的地方。由于机器学习解决的是连续的数值问题,机器学习的“模型”一般要很简单才行,否则很可能出现学习不收敛的情况。所以我还不知道编程语言的很多概念能否顺利的迁移到机器学习上面。

但目前看来有一些很明显的对应关系和发展趋势:

  1. Feed-forward 网络,比如 CNN 一类的,对应了编程语言中最简单的表达式,或者叫“纯函数”。其中没有递归,也没有副作用。它只能处理图像一类具有固定长度的数据。
  2. RNN 对应的是程序语言里含有递归(循环)的函数。由于递归函数对应的是“递归数据结构”,这就是为什么 RNN 可以处理文本一类没有固定长度的“链表”数据。
  3. Neural Turing Machine 及其后续的研究 Differentiable Neural Computer,试图把更广阔的编程概念引入到机器学习里面,实现任意复杂的数据结构和计算。

由于数值计算特有的误差问题和机器学习带来的巨大计算量,我不知道这个趋势最终可以发展到什么地步。但编程语言和机器学习的这个联系,是很优雅而且让人回味的。


机器学习的价值

机器学习比逻辑编程的应用面更广,影响力更大,对硬件性能的要求也更高。在我看来,机器学习可以被看作是统计学的延伸,它能帮助我们更好的挖掘出数据的价值,所以它的潜力是巨大的。

这是不是说我之前关于“人工智能”的言论被我自己推翻了呢?不是的。虽然机器学习价值很大,然而理解了它的人都知道,它跟“智能”相去甚远。机器学习所做的其实是统计学的工作:用大量数据拟合出一个函数。以前统计学只能拟合出简单的函数,现在机器学习能拟合出非常复杂的函数。用这种函数,我们可以实现前所未有的技术,比如识别出图片上的物体,战胜围棋冠军……

但终究我们还是在拟合函数,很难说这就是“智能”。数据再多也总有例外,这跟人类认知的工作原理是非常不同的。人类的活动是广袤无垠,难以概括的。我们最好不要做井底之蛙,因为在某些领域的成功,而对广义的智能妄下断言。有兴趣的人,可以了解一下认知科学(cognitive science)。

在我看来,机器学习的价值在于“数据”,而不是“智能”。智能仍然是虚无缥缈的,而数据却是有实在价值的。机器学习可以帮助我们史无前例的发掘数据里面隐藏的价值,这已经很让人满足了。我一点也不担心“人类级别的智能”能否实现。

(如果你觉得这篇文章有启发,可以点击这里付费


关注微信公众号

码中人 微信公众号