一、感知机
在机器学习中,感知机(perceptron)是二分类的线性分类模型,属于监督学习算法。输入为实例的特征向量,输出为实例的类别(取+1和-1)。感知机对应于输入空间中将实例划分为两类的分离超平面。感知机旨在求出该超平面,为求得超平面导入了基于误分类的损失函数,利用梯度下降法
对损失函数进行最优化(最优化)。感知机的学习算法具有简单而易于实现的优点,分为原始形式和对偶形式。感知机预测是用学习得到的感知机模型对新的实例进行预测的,因此属于判别模型。感知机由Rosenblatt于1957年提出的,是神经网络和支持向量机的基础。
定义
假设输入空间(特征向量)为 X⊆Rn X ⊆ R n ,输出空间为 Y={−1,+1} Y = { − 1 , + 1 } 。输入 x∈X x ∈ X 表示实例的特征向量,对应于输入空间的点;输出 y∈Y y ∈ Y 表示示例的类别。由输入空间到输出空间的函数为
f(x)=sign(w⋅x+b) f ( x ) = s i g n ( w ⋅ x + b )
称为感知机。其中,参数w叫做权值向量weight,b称为偏置bias。
w⋅x
w
⋅
x
表示
w
w
和的点积
sign s i g n 为符号函数,即
在二分类问题中, f(x) f ( x ) 的值(+1或-1)用于分类x为正样本(+1)还是负样本(-1)。感知机是一种线性分类模型,属于判别模型。我们需要做的就是找到一个最佳的满足 w⋅x+b=0 w ⋅ x + b = 0 的w和b值,即分离超平面( separating hyperplane)。如下图,一个线性可分的感知机模型
中间的直线即 w⋅x+b=0 w ⋅ x + b = 0 这条直线。
线性分类器的几何表示有:直线、平面、超平面。
感知机算法强迫函数输出值为{-1,1}离散值而不是概率值。
感知机算法是人工神经网络的基础,在后面的学习理论中,将把它作为分析的起点。
有关感知机的具体介绍,可以查看 李航《统计学习方法》 第二章 感知机。
参考链接:机器学习-感知机perceptron
二、从多层感知器到卷积网络(一)
写在最前面
本系列文章试图以直观的思维讲解神经网络的两个种类——多层感知器(Muti-Layer Percetron)和卷积网络(Convolutional Neural Network)。这两种网络都属于前馈型网络(Feedforward network),其中多层感知器(MLP)是最简单也是最常见的一种神经网络结构,它是所有其他神经网络结构的基础,所以不出意外,在介绍卷积网络前,不得不提一提它。卷积网络则是另外一种特殊结构的前馈网络,在图像识别方面有着惊人的效果,了解它也是百益无害。作为普通的程序员,直接看成熟的开源代码学习神经网络是一个很大挑战。Andrew Ng的Ufldl教程是一个不错的入门材料,但是课后作业用Matlab实现神经网络始终对于我们奋战在一线的程序员觉得浑身不自在,没有彻头彻尾掌握的快感。斯坦福的cs231n课程是目前我看到的最容易懂的神经网络教程,本文实现卷积网络大部分知识都是从该课程的notes上得的,然而里面(暂时?)并没有详细介绍backprop(这是训练神经网络的一种有效的方法,后面我会提到)在卷积网络中的实现细节(这也是我卡得比较久的地方)。但是在我翻遍材料,读遍教程,看遍coursera所有神经网络相关的课程之后,最终我还是能够不借助任何现有的机器学习API(除了Numpy的矩阵向量的基本操作),实现MLP和CNN,欣喜之余,决定总结一番,这也是本系列的来由。由于MLP是CNN的基础,文章第一篇先介绍MLP,第二篇进阶到CNN。文章尽量以浅显语言表达,对于一些生物知识也是如此,错误在所难免,希望读者指正,不胜感激。好了,有台阶可以下了,现在开始进入正题。
神经网络的由来
人是万物之灵,而大脑便是灵中灵,自从人类发现自己是通过大脑思考后,研究大脑的人便不计其数,而计算机研究者也不甘示弱,于是神经网络的计算模型便应运而生了。现在我们先回顾一下高中的生物知识,我们知道,生物神经系统的基本结构和功能单位是神经元(神经细胞),而神经元的活动和信息在神经系统中的传输则表现为一定的生物电变化及其传播。大量生物学家对大脑进行研究,并提出了神经系统工作原理的许多假设。计算机科学家估计在某天晚上读了相关的理论,然后给出了自己的理解:大体就是神经元通过树突接收到外部信号,然后对信号作出反应,要么通过轴突激发一个冲动,向回路中相邻的神经元发出信号,要么相反,不发出信号。于是计算机科学家利用这点理解,研发出了一种类似这种工作方式的模型——人工神经网络,读者可以相信,生物学家专注研究大脑那么多年的东西,岂是程序员可以简单模拟的,所以有些计算机学者为了造成不必要的误解,就刻意避开神经网络神经元这样的说法,取而代之就是简单的以“网络”(Network)“处理单位”(Unit)称呼,其实背后还隐藏着雄心壮志——人工神经网络目标并不只是简单地模仿大脑,是要变成一种超越大脑的智能。总之,我们大概是知道神经网络的由来了。
深入了解神经网络
基本结构
首先欢迎我们生物学家的神经网络登场:
没错,这就是生物学家心目中的神经网络结构的一小部分,信号首先通过树突接收,然后经过细胞体中处理,再通过轴突传输信号给另外的神经元。 读者此时可能对计算机科学家发明的神经网络产生兴趣了,现在我们就来揭开它的庐山真面目。
看到吧,就是这货(图是从Michael Nielsen 的书中借用的,详见http://neuralnetworksanddeeplearning.com/,该书确实很不错,图文并茂,并且有很多JS例子),图中圆圈就是大名鼎鼎的神经元,而那些带有箭头的实线可以理解为轴突和树突。这就是我们将要介绍的MLP,从左到右分别是:输入层,隐藏层,输出层。就是这样,计算机科学家认为,这样的结构可以作出图像识别,语音识别等等传统计算算法很难做出的东西。至于如何做到,读者可以先喝杯水,理清思路,听我道来。
我们不得不佩服,计算机学家实践模仿能力确实超强。首先,生物学家认为神经元会对信号进行处理,然后要么向下一个神经元传递冲动,要么抑制传递,计算机学家当然数字化一点,既然神经元会对信号处理,然后做出传递或抑制的动作,那么可以传递的时候就圆圈就输出1,抑制的时候就输出-1,太棒了,这样应该就可以模仿神经元的一部分动作了。但是这个传递过程要怎么弄呢?我们知道信号还要经过轴突的,直接将-1或1的信号作为下一个神经元的输入,就不能很好的体现这一点了。通过轴突这个通道,信号总该有所变化吧,于是模拟经过轴突的过程就是给传输信号的通道加上一些权重,就是说对于-1/1信号x,进入下一个神经元前,必须乘上权重w,因此下一个神经元收到的输入信号就是 w∗x w ∗ x ,而不是简单的x了。这样,很好地模拟了神经元与神经元沟通的过程,so far so good。
神经元
有了上述的总体设计之后,现在计算机科学家要进入详细设计阶段了。对于计算机而言,输入信号便是01所表达的任意数字,那么神经元接收到信号之后,如何将其输出为-1/1信号呢,一个方式就是将神经元作为一个阶跃函数,当信号 w∗x w ∗ x 经过神经元时,简单对其做如下处理:
if w*x > 0:
output = 1
else:
output = -1
这种以阶跃函数模拟神经元的方式在早期被称为”感知器”(Percetron),然后多个感知器相连便是一个网络结构,然而计算机学家深入研究后,发现这种生硬地让神经元输出-1或1的方式对于以后的优化工作极其困难。那么是不是没办法了呢?我们知道凡事不要太绝对,既然直接输出-1或1对于以后的工作太困难,那么算了,我们不要那么生硬了,放宽一下限制,输出是一个-1到1的平滑范围好了,计算机学家冥思苦想,其中数学功底不错的人就联想到数学中双曲函数中的正切函数
tanh
t
a
n
h
不就满足这样的要求吗?!于是就有了大名鼎鼎的
tanh
t
a
n
h
君就登场了。
tanh
t
a
n
h
君对于我们理解神经网络有重大意义,现在我们稍微介绍一下他:
其导数形式为:
这就是tanh君的数学形象,乍看起来挺神秘的,不过不用担心,该君还是挺容易相处的,并且可以看出,其导数形式还很友好。接下来还有该君的相册,且看:
大家看到了吧, tanh t a n h 君有明显的 S S 曲线,受到广大程序员的喜爱。在的时候输出1,在 x<<0 x << 0 的时候,输出-1,而在其他时候便输出一个-1到1之间的数,刚好符合计算机学家的需求。所以在接下来的节目中, tanh t a n h 君将会是我们的领衔主演之一。读者可能发现,既然我们用 tanh t a n h 模拟神经元而不是阶跃函数模拟,就不是Percetron了,那么为什么还是叫MLP呢?呵呵,回答是历史遗留问题,就像历史遗留代码,我们为了节约成本,只能忍受了。
问题的提出环节
目前为止,我们已经知道计算机科学家精心发明的神经网络是如何模拟生物的神经网络系统了。然而The more we know, the more we know don’t know,仅仅知道模拟的对应过程是远远不能满足我们如黑洞般的好奇心,这里我仅代表广大群众提出几大疑惑:
- 具体如何获得输出呢?
- 怎么掌控我们的神经网络是一个识别猪的程序而不是一个识别狗的程序呢?
- 模拟神经元之间传递信号的权重W是如何炼成的?
- ……..(此处省去问题1万个,欢迎读者踊跃提出)
问题解答环节
网络的输出
回答第一个问题之前,请读者假设我们已经解决第三个问题了,也就是我们已经获得了神经元间的权重了(不要慌张,我们一定会得到权重的!)。 好,我们正式一点介绍网络的具体形象:
咋眼一看,信息量好大,别慌,看我慢慢将其拆解:
- 最左边就是我们的输入层了,算是网络的第0层,通常是一个向量 x:={x1,x2,x3...xn} x := { x 1 , x 2 , x 3 . . . x n } (x是一个行向量,本文对于输入都是以行向量处理)就可以搞定
- 对于权重 W W 的记法,我们是这样处理的:对于第0层的第1个神经元与第1层第1个神经元的权重,我们记为,第0层第1个神经元与第1层第2个神经元的的权重,我们记为 w11,2 w 1 , 2 1 。一般的,我们将权重记为 wli,j w i , j l , 其中 l l 代表层数,当前第层,i为第l层的第i个神经元,j为第 l+1 l + 1 层的第j个神经元,学习过线性代数的话,我们就可以用矩阵来容纳权重了,第0层和第1层之间的权重可记为: W1 W 1 ,第1层与第2层之间则记为 W2 W 2 ,这样依此类推。
- 至于一个个圈圈,不是我们的神经元大哥还能是谁呢?由tanh君掌控着,靠得住
- 那几个格格不入的圈圈,里面只有一个+1,是我们的bias神经元(别着急,后面会提到)
现在可以来说说神经网络如何计算输出了,我们先聚焦下图:
上图说明了网络中两个特定神经元的计算过程。如果你重新回顾一下线性代数的基础知识,你会发现有如下结论:
对于一个输入向量x(这里x是一个行向量),第0层与第1层的权重矩阵为
W1
W
1
,在输入第1层之前,我们知道信号将经过“轴突”这样的通道进行转换,变成一个加权分数:
def forward_prop(self,x,weights,biases):
a = x
for w,b in zip(weights,biases):
a = vec_tanh(a.dot(w) + b)
return a
好了算是解决第一问了,深呼一口气。