A visual proof that neural nets can compute any function

说在前面的话:前方多图预警🤪。原文在这里

神经网络的训练其实是可以看成一个学习函数的过程,以手写数字的识别为例,网络学习的函数可能有数字轮廓信息、颜色信息、布局等等,之前说了那么多神经网络训练的方法、改进措施,但这些都是建立在网络可以学习那些函数的前提下,本章将证明neural network可以算出任何函数,用的方法主要是函数图像分析,并没有严格的算数证明(那玩意太TM难了)。
在一大波图袭来之际,简单提一下,证明的大策略其实类似于“微元法”,将所给函数切分成许多竖直矩形,需要证明neural network可以学习出那些矩形。

One input(Two-dimensional function)

以这样的一个函数为例:

function

神经网络采用最简单的单隐藏层网络,activation function采用sigmoid函数。首先考虑只有单个输入x的情况,且hidden layer只有两个neuron,目前我们只考虑第一个neuron,假设input neuron和它之间的权重为w,偏移为b,画出不同的weight和bias对hidden layer输出(并不是output层的输出)的影响:

weights bias

可以想象,随着weights越来越大,output函数将趋于“阶跃函数”,这也很好理解——随着自变量“步幅”的增大,sigmoid函数图像中间的“上升段”会变得越来越陡峭,将weight改为999后证明的确如此。
另外可以发现,阶跃点的横坐标与bias的绝对值正相关,与weight负相关(这个我看了好一会儿,需要注意bias我们取负,weight取正),实际上,阶跃点s=-b/w。这是我们造“箱子”的第一步,之后我们weight都取1000,只要给出某个hidden layer神经元的s,bias就可以由b=-s·w计算得出。
至于“阶跃”的幅度,由sigmoid函数可以显然看出是1,假设这个neuron与output neuron之间的权重为w1,则阶跃高度为w1(可正可负)。
说明一下,之后的讨论中,我们默认一对neurons中的第一个“阶跃值”s小于第二个,下面加上hidden layer中未被考虑的另一个神经元,这个图就不上了,呈现的效果其实是在之前的阶跃点s1后的s2处又“阶跃”了一次,且阶跃幅度为该neuron与output neuron之间的权重w2。那么,当w2=-w1时,呈现的图像就是一个“箱子”了,这也就是我们构造“箱子”的方法。
对此,作者还把上面的阶跃现象用伪代码呈现出来:

if input > step point:

​ add 1 to the weighted output

else:

​ add 0 to the weighted output

直接上五对神经元的效果图和对应的“阶跃函数”图(标注的h对应的都是第一个neuron):

5 5

现在已经可以构造许多“箱子”了,对于刚开始的函数,期望output层输出f(x),所以output的输入,即weighted output from hidden layer,就是σ-1(f(x)),仿照上面的过程,可以用五对neuron画出其粗略图像(可以通过不断增加“箱子”的个数不断逼近函数图像):

bump

说明一下,output层的bias取的是0。

Many input variables(Three-dimensional function)

上面的二维情况弄清楚后,之后就很好理解了,多了一个输入y,就相当于增加了一个维度,我们研究的函数也到了三维。
首先为简化我们的分析,仍然只考虑hidden layer中的一个neuron,并且y与该neuron连接的权重w2设为0,画出w1对该neuron的output的影响:

影响

同之前一样,x方向的阶跃点sx=-b/w1。模仿上一节的行为开始造“箱子”,考虑隐藏层的另外一个neuron,将两个neuron的阶跃点分别设置为0.3和0.7,阶跃幅度互为相反数,得到如下的图:

bump2

刚刚我们把另一个输入y对应的所有权重都设置为0,假如把输入x对应的所有权重设为0,将之前的参数给y,所得的图像其实就是把上面的“箱子”绕中轴旋转了90°。然而,这怎么看也不像我们期望的“箱子”——在三维下准确来说是“塔”(tower)。
这也好办,现在同时考虑输入x和输入y,都做上面的“箱子”处理(注意x,y的阶跃幅度都取相同的值,阶跃点任意,我取的是s1x=0.4,s2x=0.6,s1y=0.3,s22=0.7),得到下图(很容易想象出来,就是简单的叠加):

11

中间高出的那一块就是需要的tower了,然而它周围还多了四块“箱子”,我期望得到更标准的tower(在z>0的范围内),这是output层的bias作用就体现出来了,事实上,选择bias b≈-3h/2(更一般情况是b=(-m+1/2)h,m为输入个数),即将整个图像向下移动3h/2的长度,得到如下图像:

22

只看z>0的部分,确实得到了期望的tower,并且tower的高度很容易算出是h/2,所以可以通过改变h的值改变tower的高度,通过缩小s2-s1使tower更“细”。至此,成功造出了三维函数的微元。
之后就可以开始构造多个tower了,以如下network为例:

33

书上说通过组合上面两个network可以得到两个tower,如下:

book

单独画两个tower确实没什么问题(自动忽略z<0的部分):

但是根据网络结构,将两个network合并到一个输出,我写出如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def sigma(z):
return 1.0/(1.0+np.exp(-z))

def sigmoid(w,b,x):
return 1.0/(1.0+np.exp(-(w*x+b)))

def reversed_sigmoid(z):
return np.log(z/(1-z))

x=np.linspace(0,1,1000)
y=np.linspace(0,1,1000)

fig=plt.figure()
ax3=plt.axes(projection='3d')
X,Y=np.meshgrid(x,y)
Z1=0.8*(sigmoid(1000,-100,X)-sigmoid(1000,-200,X)\
+sigmoid(1000,-800,Y)-sigmoid(1000,-900,Y))-1.2
Z2=0.5*(sigmoid(1000,-700,X)-sigmoid(1000,-800,X)\
+sigmoid(1000,-200,Y)-sigmoid(1000,-300,Y))-0.75
Z3=0.8*sigma(Z1)+0.5*sigma(Z2)

ax3.plot_surface(X,Y,Z3,color="red")
ax3.set_xlabel('X')
ax3.set_ylabel('Y')
ax3.set_zlabel('output')

plt.show()

却得到下图:

会多出两个tower,简单分析一下这是合并的必然结果,然而我并不知道如何修正。

所以要得到最终的函数f(x),仍然只需output层的输入为σ-1(f)即可,这与之前的情况一样。更高维的情况同理可证,只不过不能可视化了。

Extension beyond sigmoid neurons

至此,已经证明了activation function为sigmoid的神经网络可以学习出任何函数,但对于其他激励函数呢?事实上,对于以上证明,我们期望当w很大时,hidden layer的输出趋于阶跃函数,显然,activation function要满足如下条件:①当x→-∞和x→∞是,函数是收敛的;②上述两个极限值不同。举个例子,如下函数满足条件:

显然,y=x不满足上述条件,并且它不适合作为激励函数,书上说

such neurons can’t be used to do universal computation.

同理,ReLU也不满足上述条件,但是

The rectified linear units are universal for computation.

所以对于ReLU的普适性需要其他证明方法。

Fixing up the step functions

然而,不要忽略一个事实:无论weights取得多大,hidden layer的输出始终只能趋于阶跃函数,即之前图片的阶跃过程并不是完全“竖直”的,中间那段斜率仍是一个非常大的有限值
对此,可以将weights设置地足够大,或者将“箱子”设置地足够narrow来减少误差,但这些方法总感觉很粗暴,这是作者就想出了一个很漂亮的方法。

如下图,用“箱子”计算一个函数时,将上述误差更明显地表现如下:

error

“箱子”交界处即误差出现处,假设要学习的函数是σ-1(f(x)),可以分别学习两次σ-1(f(x))/2,第一次仍像上图一样学习,只不过“箱子”高度都减半了。但第二次学习时,先将“箱子”统一右移其宽度的一般长度,再根据实际函数调整“箱子”高度。两次结果相加,显然原来的误差会减半。同样,可以右移更小的长度,比如“箱子”宽度的1/10,右移9次,这样误差就变成了原来的十分之一。其实这与“多次测量取平均”的思想相同。

Others

证明了单隐藏层network可以学习任何函数,那实际操作为什么常常是多隐藏层呢?这在前面提过,以图像识别为例,需要学习的图片轮廓、颜色以及其他无法描述的特征可能并不是一个函数就可以精确表示出来的,常常需要学习多个函数。

0%