Skip to content

Latest commit

 

History

History
453 lines (329 loc) · 13.4 KB

2.3-解决回归问题.md

File metadata and controls

453 lines (329 loc) · 13.4 KB

2.3 解决回归问题

本小节主要围绕解决回归问题中的各个环节和知识点展开,包含提出问题,万能近似定力,定义神经网络结构,前向计算和反向传播等内容。

2.3.1 提出问题

前面的正弦函数,看上去是非常有规律的,也许单层神经网络很容易就做到了。如果是更复杂的曲线,单层神经网络还能轻易做到吗?比如图9-2所示的样本点和表9-2的所示的样本值,如何使用神经网络方法来拟合这条曲线?

图 2.3.1 复杂曲线样本可视化

表 2.3.1 复杂曲线样本数据

样本 x y
1 0.606 -0.113
2 0.129 -0.269
3 0.582 0.027
... ... ...
1000 0.199 -0.281

上面这条“蛇形”曲线,实际上是由下面这个公式添加噪音后生成的:

$$y=0.4x^2 + 0.3x\sin(15x) + 0.01\cos(50x)-0.3$$

我们特意把数据限制在[0,1]之间,避免做归一化的麻烦。要是觉得这个公式还不够复杂,大家可以用更复杂的公式去自己做试验。

以上问题可以叫做非线性回归,即自变量X和因变量Y之间不是线性关系。常用的传统的处理方法有线性迭代法、分段回归法、迭代最小二乘法等。在神经网络中,解决这类问题的思路非常简单,就是使用带有一个隐层的两层神经网络。

2.3.2 万能近似定理

万能近似定理(universal approximation theorem) $^{[1]}$,是深度学习最根本的理论依据。它证明了在给定网络具有足够多的隐藏单元的条件下,配备一个线性输出层和一个带有任何“挤压”性质的激活函数(如Sigmoid激活函数)的隐藏层的前馈神经网络,能够以任何想要的误差量近似任何从一个有限维度的空间映射到另一个有限维度空间的Borel可测的函数。

前馈网络的导数也可以以任意好地程度近似函数的导数。

万能近似定理其实说明了理论上神经网络可以近似任何函数。但实践上我们不能保证学习算法一定能学习到目标函数。即使网络可以表示这个函数,学习也可能因为两个不同的原因而失败:

  1. 用于训练的优化算法可能找不到用于期望函数的参数值;
  2. 训练算法可能由于过拟合而选择了错误的函数。

根据“没有免费的午餐”定理,说明了没有普遍优越的机器学习算法。前馈网络提供了表示函数的万能系统,在这种意义上,给定一个函数,存在一个前馈网络能够近似该函数。但不存在万能的过程既能够验证训练集上的特殊样本,又能够选择一个函数来扩展到训练集上没有的点。

总之,具有单层的前馈网络足以表示任何函数,但是网络层可能大得不可实现,并且可能无法正确地学习和泛化。在很多情况下,使用更深的模型能够减少表示期望函数所需的单元的数量,并且可以减少泛化误差。

2.3.3 定义神经网络结构

根据万能近似定理的要求,我们定义一个两层的神经网络,输入层不算,一个隐藏层,含3个神经元,一个输出层。图 2.3.2 显示了此次用到的神经网络结构。

图 2.3.2 单入单出的双层神经网络

为什么用3个神经元呢?这也是笔者经过多次试验的最佳结果。因为输入层只有一个特征值,我们不需要在隐层放很多的神经元,先用3个神经元试验一下。如果不够的话再增加,神经元数量是由超参控制的。

输入层

输入层就是一个标量x值,如果是成批输入,则是一个矢量或者矩阵,但是特征值数量总为1,因为只有一个横坐标值做为输入。

$$X = (x)$$

权重矩阵W1/B1

$$ W1= \begin{pmatrix} w1_{11} & w1_{12} & w1_{13} \end{pmatrix} $$

$$ B1= \begin{pmatrix} b1_{1} & b1_{2} & b1_{3} \end{pmatrix} $$

隐层

我们用3个神经元:

$$ Z1 = \begin{pmatrix} z1_1 & z1_2 & z1_3 \end{pmatrix} $$

$$ A1 = \begin{pmatrix} a1_1 & a1_2 & a1_3 \end{pmatrix} $$

权重矩阵W2/B2

W2的尺寸是3x1,B2的尺寸是1x1。

$$ W2= \begin{pmatrix} w2_{11} \\\ w2_{21} \\\ w2_{31} \end{pmatrix} $$

$$ B2= \begin{pmatrix} b2_{1} \end{pmatrix} $$

输出层

由于我们只想完成一个拟合任务,所以输出层只有一个神经元,尺寸为1x1:

$$ Z2 = \begin{pmatrix} z2_{1} \end{pmatrix} $$

2.3.4 前向计算

根据图 2.3.2 的网络结构,我们可以得到如图 2.3.3 的前向计算图。

图 2.3.3 前向计算图

隐层

  • 线性计算

$$ z1_{1} = x \cdot w1_{11} + b1_{1} $$

$$ z1_{2} = x \cdot w1_{12} + b1_{2} $$

$$ z1_{3} = x \cdot w1_{13} + b1_{3} $$

矩阵形式:

$$ \begin{aligned} Z1 &=x \cdot \begin{pmatrix} w1_{11} & w1_{12} & w1_{13} \end{pmatrix} + \begin{pmatrix} b1_{1} & b1_{2} & b1_{3} \end{pmatrix} \\\ &= X \cdot W1 + B1 \end{aligned} $$

  • 激活函数

$$ a1_{1} = Sigmoid(z1_{1}) $$

$$ a1_{2} = Sigmoid(z1_{2}) $$

$$ a1_{3} = Sigmoid(z1_{3}) $$

矩阵形式:

$$ A1 = Sigmoid(Z1) $$

输出层

由于我们只想完成一个拟合任务,所以输出层只有一个神经元:

$$ \begin{aligned} Z2&=a1_{1}w2_{11}+a1_{2}w2_{21}+a1_{3}w2_{31}+b2_{1} \\\ &= \begin{pmatrix} a1_{1} & a1_{2} & a1_{3} \end{pmatrix} \begin{pmatrix} w2_{11} \\ w2_{21} \\ w2_{31} \end{pmatrix} +b2_1 \\\ &=A1 \cdot W2+B2 \end{aligned} $$

损失函数

均方差损失函数:

$$loss(w,b) = \frac{1}{2} (z2-y)^2$$

其中,$z2$是预测值,$y$是样本的标签值。

代码

class NeuralNet(object):
    def forward(self, batch_x):
        # layer 1
        self.Z1 = np.dot(batch_x, self.wb1.W) + self.wb1.B
        self.A1 = Sigmoid().forward(self.Z1)
        # layer 2
        self.Z2 = np.dot(self.A1, self.wb2.W) + self.wb2.B
        if self.hp.net_type == NetType.BinaryClassifier:
            self.A2 = Logistic().forward(self.Z2)
        elif self.hp.net_type == NetType.MultipleClassifier:
            self.A2 = Softmax().forward(self.Z2)
        else:   # NetType.Fitting
            self.A2 = self.Z2
        #end if
        self.output = self.A2

2.3.5 反向传播

求损失函数对输出层的反向误差

根据公式4:

$$ \frac{\partial loss}{\partial z2} = z2 - y \rightarrow dZ2 $$

求W2的梯度

根据公式3和W2的矩阵形状,把标量对矩阵的求导分解到矩阵中的每一元素:

$$ \begin{aligned} \frac{\partial loss}{\partial W2} &= \begin{pmatrix} \frac{\partial loss}{\partial z2}\frac{\partial z2}{\partial w2_{11}} \\\ \frac{\partial loss}{\partial z2}\frac{\partial z2}{\partial w2_{21}} \\\ \frac{\partial loss}{\partial z2}\frac{\partial z2}{\partial w2_{31}} \end{pmatrix} \begin{pmatrix} dZ2 \cdot a1_{1} \\\ dZ2 \cdot a1_{2} \\\ dZ2 \cdot a1_{3} \end{pmatrix} \\\ &=\begin{pmatrix} a1_{1} \\ a1_{2} \\ a1_{3} \end{pmatrix} \cdot dZ2 =A1^{\top} \cdot dZ2 \rightarrow dW2 \end{aligned} $$

求B2的梯度

$$ \frac{\partial loss}{\partial B2}=dZ2 \rightarrow dB2 $$

求损失函数对隐层的反向误差

下面的内容是双层神经网络独有的内容,也是深度神经网络的基础,请大家仔细阅读体会。我们先看看正向计算和反向计算图,即图9-9。

图 2.3.4 正向计算和反向传播路径图

图 2.3.4 中:

  • 蓝色矩形表示数值或矩阵;
  • 蓝色圆形表示计算单元;
  • 蓝色的箭头表示正向计算过程;
  • 红色的箭头表示反向计算过程。

如果想计算W1和B1的反向误差,必须先得到Z1的反向误差,再向上追溯,可以看到Z1->A1->Z2->Loss这条线,Z1->A1是一个激活函数的运算,比较特殊,所以我们先看Loss->Z->A1如何解决。

根据公式3和A1矩阵的形状:

$$ \begin{aligned} \frac{\partial loss}{\partial A1}&= \begin{pmatrix} \frac{\partial loss}{\partial Z2}\frac{\partial Z2}{\partial a1_{11}} & \frac{\partial loss}{\partial Z2}\frac{\partial Z2}{\partial a1_{12}} & \frac{\partial loss}{\partial Z2}\frac{\partial Z2}{\partial a1_{13}} \end{pmatrix} \\\ &= \begin{pmatrix} dZ2 \cdot w2_{11} & dZ2 \cdot w2_{12} & dZ2 \cdot w2_{13} \end{pmatrix} \\\ &=dZ2 \cdot \begin{pmatrix} w2_{11} & w2_{21} & w2_{31} \end{pmatrix} \\\ &=dZ2 \cdot \begin{pmatrix} w2_{11} \\ w2_{21} \\ w2_{31} \end{pmatrix}^{\top}=dZ2 \cdot W2^{\top} \end{aligned} $$

现在来看激活函数的误差传播问题,由于公式2在计算时,并没有改变矩阵的形状,相当于做了一个矩阵内逐元素的计算,所以它的导数也应该是逐元素的计算,不改变误差矩阵的形状。根据 Sigmoid 激活函数的导数公式,有:

$$ \frac{\partial A1}{\partial Z1}= Sigmoid'(A1) = A1 \odot (1-A1) $$

所以最后到达Z1的误差矩阵是:

$$ \begin{aligned} \frac{\partial loss}{\partial Z1}&=\frac{\partial loss}{\partial A1}\frac{\partial A1}{\partial Z1} \\\ &=dZ2 \cdot W2^T \odot Sigmoid'(A1) \rightarrow dZ1 \end{aligned} $$

有了dZ1后,再向前求W1和B1的误差,就和第5章中一样了,我们直接列在下面:

$$ dW1=X^T \cdot dZ1 $$

$$ dB1=dZ1 $$

代码

class NeuralNet(object):
    def backward(self, batch_x, batch_y, batch_a):
        # 批量下降,需要除以样本数量,否则会造成梯度爆炸
        m = batch_x.shape[0]
        # 第二层的梯度输入 公式5
        dZ2 = self.A2 - batch_y
        # 第二层的权重和偏移 公式6
        self.wb2.dW = np.dot(self.A1.T, dZ2)/m 
        # 公式7 对于多样本计算,需要在横轴上做sum,得到平均值
        self.wb2.dB = np.sum(dZ2, axis=0, keepdims=True)/m 
        # 第一层的梯度输入 公式8
        d1 = np.dot(dZ2, self.wb2.W.T) 
        # 第一层的dZ 公式10
        dZ1,_ = Sigmoid().backward(None, self.A1, d1)
        # 第一层的权重和偏移 公式11
        self.wb1.dW = np.dot(batch_x.T, dZ1)/m
        # 公式12 对于多样本计算,需要在横轴上做sum,得到平均值
        self.wb1.dB = np.sum(dZ1, axis=0, keepdims=True)/m 

2.3.6 运行结果

图 2.3.5 为损失函数曲线和验证集精度曲线,都比较正常。图 2.3.6 是拟合效果。

图 2.3.5 三个神经元的训练过程中损失函数值和准确率的变化

图 2.3.6 三个神经元的拟合效果

再看下面的打印输出结果,最后测试集的精度为97.6%,已经令人比较满意了。如果需要精度更高的话,可以增加迭代次数。

......
epoch=4199, total_iteration=377999
loss_train=0.001152, accuracy_train=0.963756
loss_valid=0.000863, accuracy_valid=0.944908
testing...
0.9765910104463337

以下就是笔者找到的最佳组合:

  • 隐层3个神经元
  • 学习率=0.5
  • 批量=10

小结与讨论

本小节主要介绍了回归问题中的,提出问题,万能近似定力,定义神经网络结构,前向计算和反向传播。

请读者尝试用PyTorch定义网络结构并解决一个简单的回归问题的。

参考文献

  1. 《智能之门》,胡晓武等著,高等教育出版社

  2. Duchi, J., Hazan, E., & Singer, Y. (2011). Adaptive subgradient methods for online learning and stochastic optimization. Journal of Machine Learning Research, 12(Jul), 2121-2159.

  3. Zeiler, M. D. (2012). ADADELTA: an adaptive learning rate method. arXiv preprint arXiv:1212.5701.

  4. Tieleman, T., & Hinton, G. (2012). Lecture 6.5-rmsprop: Divide the gradient by a running average of its recent magnitude. COURSERA: Neural networks for machine learning, 4(2), 26-31.

  5. Kingma, D. P., & Ba, J. (2014). Adam: A method for stochastic optimization. arXiv preprint arXiv:1412.6980.

  6. 周志华老师的西瓜书《机器学习》

  7. Chawla N V, Bowyer K W, Hall L O, et al. SMOTE: synthetic minority over-sampling technique[J]. Journal of Artificial Intelligence Research, 2002, 16(1):321-357.

  8. Inoue H. Data Augmentation by Pairing Samples for Images Classification[J]. 2018.

  9. Zhang H, Cisse M, Dauphin Y N, et al. mixup: Beyond Empirical Risk Minimization[J]. 2017.

  10. 《深度学习》- 伊恩·古德费洛

  11. Shaoqing Ren, Kaiming He, Ross Girshick, and Jian Sun, Faster R-CNN: Towards Real-Time Object Detection with Region Proposal Networks. Link: https://arxiv.org/pdf/1506.01497v3.pdf