
6.1 盘古人工智能框架性能测试及问题剖析
参照TensorFlow的可视化图,我们在前面的章节中已经实现了盘古人工智能框架的架构,包括输入层、隐藏层及输出层;对权重的赋值;前向传播、反向传播算法。
在如图6-2所示的TensorFlow可视化图中,如果选择Sigmoid激活函数进行分类操作,单击运行按钮,会发现损失度的收敛非常缓慢;如果选择ReLU激活函数进行分类操作,单击运行按钮,会发现损失度的收敛速度非常快。也可以选择激活函数Tanh,收敛速度也很快。
接下来一个很重要的内容是优化,损失度收敛的问题包括多个方面:可能是激活函数的问题;可能是输入数据的问题;也可能是运行过程中反向传播算法收敛时学习率的问题。由于整个梯度下降的过程中有很多个局部最低点,也可能梯度下降时达到的是局部极低点,而不一定是整个梯度下降的最低点。针对这些内容会有很多优化的方法,本节讲解的是自适应学习和特征归一化优化。

图6-2 Sigmoid、ReLU激活函数损失度比较
在进行收敛的过程中,先不考虑多个极值点的情况,只考虑有1个最低点的情况。如果步伐太大,收敛时就会出现“荡秋千”现象,永远到不了最低点,因为每次的步伐太大,从左侧的一个点跃过了最低点,到达右侧的一个点,然后下一次收敛的时候,步伐又太大,从右侧的点跃过了最低点,到达左侧的一个点,如此反复,导致的结果是测试了很多次,而精确度或损失度并没有太大的变化:损失度没有极大地降低,精确度也没有极大地提升。此时,使用自适应学习进行优化,将上次损失度的减少和这次损失度的减少做一个比较,如果步伐太大,就进行调整,将步伐变得小一点。
在进行收敛的过程中,如果步伐太小,会导致收敛速度太慢,而做人工智能的核心追求是在最短的时间内,最快、最准确地接近实际情况。无论步伐是太大还是太小,都要进行相应的调整。
关于数据源的问题。在做实际项目的时候,一般拿到的数据不可以直接使用,要进行预处理,但有时候即使数据进行了预处理,符合初步要求,也还是不够的。例如,使用Sigmoid激活函数对数据进行初步处理,在用反向传播算法对激活函数进行求导的时候,数据如果只有0和1,或者包含100、1000、–80……等,那么这些数据是否是一种最理想的状态?
如图6-3所示,Sigmoid函数的纵轴(f(x))值区间是0~1,横轴(x)值在[–4,4]变化非常明显,如果x小于–4或者大于4,则数据不是有效的数据,f(x)的变化就不明显了,因此推出了正则化的过程。案例中实例集数据中只包含了0和1,有没有必要进行正则化?有必要,因为要么是0,要么是1,数据的变化不太明显,正则化后,将0~1代表的数据变成[–4,4]的数据,理论上讲会带来更明显的训练效果。之所以是从理论上讲,是因为人工智能的优化涉及很多因素,研发人工智能的专家和人工智能应用程序的开发者,大多数时间都是在反复优化参数。
之所以自适应学习能进行性能优化是因为有时候学习率不太恰当,无法达到最低值。

图6-3 Sigmoid()函数
之所以使用特征归一化进行正则化优化是因为数据超过了有效处理的范围,或者有效处理的范围太小,不能很好发挥你算法的作用。Sigmoid函数x轴值的有效区间是[–4,4]也可以使用Softmax函数,其有效区间是[–2,2]。如图6-4所示,TensorFlow的可视化图中,有很多的激活函数:ReLU、Tanh、Sigmoid、Linear等,不同激活函数的正则化区间不太一样,也可能是高斯分布等。如果使用Tanh激活函数,运行的效果也是不错的。

图6-4 TensorFlow的激活函数
我们回顾一下5.3节的内容,再次运行chapter5_Create_AI_Framework的Neuron_Network_Entry.py程序,运行结果如下:



如图6-5所示,代码运行的结果显示收敛度是一条横线,下面通过修改代码让收敛度尽快达到最低损失度。

图6-5 代码调整前的运行结果
(1)在代码中进行一个很小的调整,将chapter5_Create_AI_Framework版本的BackPropagation.py中的代码:

修改成如下代码。

这里“==”是比较符号而不是赋值符号,如果用比较符号,反向传播就没有进行更新。只有对权重进行更新才能够把Neuron Network的训练成果作用于下一次的训练。
重新运行Neuron_Network_Entry.py,结果如图6-6所示。

图6-6 调整代码以后的运行结果
这个时候,收敛度曲线已经从横线变成了一条下降的曲线,说明修改的代码已经起作用了。但是训练的时候,发现在运行结果图中有一个不太规则的地方(图中圆圈),接下来可以修改另外一个地方。
(2)计算方法优化,根据公式修改均方误差值(Mean Square Error, MSE),MSE是反映预测值与实际值之间差异程度的一种度量。求方差的时候可以将公式稍微改一下,除以2,再次运行看一下效果。

将chapter5_Create_AI_Framework版本的LossComputation.py中的代码:

修改成如下代码。

修改以后,再次运行Neuron_Network_Entry.py,运行结果如图6-7所示。
将修改以后的代码运行结果跟之前的运行结果对比,发现将平方差除以2后的运行结果曲线左侧不规则现象更明显了,这跟实际数据的不同情况有关系。除以2是一个求方差的正常的公式,在写代码的时候要不断地调整公式。

图6-7 代码优化后运行结果