《Python神经网络编程》里有一个例子,也算是在讲解神经网络之前引入的一个例子,是用线性分类方式来进行毛虫和瓢虫的分类。可以说例子是很简单的,但是提到的理论却并不算简单,而是很有意思。
不过我早期在读这一段的时候,其实算是跳跃式的过了。第一,我之前已经接触了神经网络,觉得这个只是在讲线性分类,算是基础中的基础;第二,也不知道为什么,看这一段比起后面的神经网络,好像更看不进去。不过今天我翻了翻,发现还是遗漏了一些里面想要表达的,为神经网络铺垫的一些知识。
虽然简书的读者们未必对AI编程感兴趣,但是AI本身来说还是挺有意思的一门科学。所以呢,掌握一些基础知识,也是挺好的。如果因此而感兴趣甚至入门,那就更好啦。
首先先观察一下上面那个图(源自这本书),毛虫是长条形的,瓢虫是椭圆形的,所以上述打点我们很清晰的可以看到两团,一团是毛虫,一团是瓢虫。但是,对于机器,该如何有效的将其分离开呢?
机器并没有直觉,所以它需要计算。那么该怎么计算呢?
我们可以用一条直线来进行分割。直线的方程是y = ax + b,不过为了简化问题,我们其实可以先别管b,只用考虑y = ax(简化问题也是科学研究中经常干的事儿,比如我们在参考生物神经元的时候,就不用把神经元的所有生物和化学行为都模拟出来,而是提炼出了最关键的特性,如输入输出,激活门限等,但这样的话我们也别自豪的说它是生物神经元的“模拟”,可以说已经是两个完全不同的东西,它成了一个数学模型)。
如果仅凭直觉,那么我们很容易给出一个挺不错的直线斜率结果。比如y = x,就不错。但是这样做是有问题的,因为这又是你这个“人”在识别出结果,而人的大脑里的算法是不透明的,无法直接复制给机器。
那么我们停下来思考一下,在没有这些辅助的情况下,该怎么办?我们手上有什么?我们手上有一堆虫子的数据,也就是它们的长和宽。比如说有一只瓢虫,它的宽是3,长是1(有点奇怪,好扁啊),即x=3, y=1。
于是我们可以用一条直线 y = 1/3 x来进行区分,但这合适吗?也不合适,因为这条直线正好通过了这个点,等于是没有做区分。我们可以用如下的方式来进行尝试:
假设有一个初始位置的分类直线,如 y = 0.25 x (初始位置并不重要),我们看下对于这个样本,分类是否OK。
当x=3代入时,y=0.75,但实际上采样点y=1。倒不是说这样分就不对,只是“误差”(其实一个样本还看不出啥,不能说这个“误差”就会带来多大的影响)大了点,达到了0.25。这个误差,应该是多大呢?似乎应该是1-0.75=0.25,但实际上并不是,因为如果我们选择的正确位置如果正好是1,那就是直线正好通过这个采样点,而我们想要的是毛虫尽可能在直线的上方,瓢虫在下方。所以,设置一个比1大一点的值比如说1.1,如果按这样的话算出来误差应该是0.35。
这个时候,我们就要思考下,我要尽可能靠近采样值,x要调整多少呢?学过线性方程的人当然可以自己直接得出答案,但是为了考虑普遍性(谁说下回就一定用线性方程呢?),就要用那么一点高等数学的知识。现在有关导数等其实也已经是中学课本内容了,所以这块理解起来应该也没有大的难度。为了方便书写,我把代尔塔简单的用d表示,大家能看懂就行。
当 a' = a + da(斜率增加了一点点),那么 y' = a'x = (a + da)x 。如果用新式子左右减去 y = ax,就得出:dy = da x,即 da = dy/x。在刚才的例子里,dy = 0.35,而 x = 3,则da = 0.35 / 3 = 0.1167。所以,只要原本假设的斜率0.25,加上0.1167,得出0.3667即可。这个时候可以验算下,y = 0.3667 x, 当 x = 3 时,y 接近 1.1,成功。
刚才是一个样本,如果有很多样本呢?还能这样来操作吗?你可以试一试,是不是会发现一个问题?你的每一次操作,都跟新导入的这个样本强相关,而之前的“训练”数据似乎都丢失了。那么,有没有更好的办法来改善这个问题呢?可以先消化消化,下回咱们再来讨论如何改善的问题。
上述的方式,其实带入几个关键点:
1)从机器计算(算法)的角度,来思考如何基于样本进行训练;
2)使用输出的变化来反推调整输入的变化,这个理念非常重要,因为神经网络的反向传播也是基于这样的理念来实现的。通过输出与输入的关联(即偏导数),通过梯度下降法向梯度方向走出一小步,反算出权重参数(此时有多个,往往是很多个)应该变化多大,从而不断的进行调优。虽然有时也会出现不小心进入局部最小值的窘境,但总体来说,反向传播大大降低了计算难度,使得深度计算最终进入AI专家的视野。这些看不懂是很正常的,即便是多次学过梯度算法、反向传播,有的概念还是含含糊糊,不过没关系,今天的一小步,就是明天的一大步。