09.4 神经网络非线性回归的实现

9.4 双层神经网络实现非线性回归⚓︎

9.4.1 万能近似定理⚓︎

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

9.4.2 定义神经网络结构⚓︎

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}

隐层⚓︎

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}

输出层⚓︎

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

9.4.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} \tag{1}
• 激活函数
a1_{1} = Sigmoid(z1_{1})
a1_{2} = Sigmoid(z1_{2})
a1_{3} = Sigmoid(z1_{3})

A1 = Sigmoid(Z1) \tag{2}

输出层⚓︎

\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} \tag{3}

损失函数⚓︎

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

9.4.4 反向传播⚓︎

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

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

求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} \tag{6}

求B2的梯度⚓︎

\frac{\partial loss}{\partial B2}=dZ2 \rightarrow dB2 \tag{7}

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

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

\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} \tag{8}

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

\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} \tag{10}

dW1=X^T \cdot dZ1 \tag{11}
dB1=dZ1 \tag{12}

9.4.5 代码实现⚓︎

前向计算⚓︎

class NeuralNet2(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

Layer2中考虑了多种网络类型，在此我们暂时只关心NetType.Fitting类型。

反向传播⚓︎

class NeuralNet2(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


保存和加载权重矩阵数据⚓︎

    def SaveResult(self):
self.wb1.SaveResultValue(self.subfolder, "wb1")
self.wb2.SaveResultValue(self.subfolder, "wb2")



辅助类⚓︎

• Activators - 激活函数类，包括Sigmoid/Tanh/Relu等激活函数的实现，以及Losistic/Softmax分类函数的实现
• DataReader - 数据操作类，读取、归一化、验证集生成、获得指定类型批量数据
• HyperParameters2 - 超参类，各层的神经元数量、学习率、批大小、网络类型、初始化方法等
class HyperParameters2(object):
def __init__(self, n_input, n_hidden, n_output,
eta=0.1, max_epoch=10000, batch_size=5, eps = 0.1,
net_type = NetType.Fitting,
init_method = InitialMethod.Xavier):

• LossFunction - 损失函数类，包含三种损失函数的代码实现
• NeuralNet2 - 神经网络类，初始化、正向、反向、更新、训练、验证、测试等一系列方法
• TrainingTrace - 训练记录类，记录训练过程中的损失函数值、验证精度
• WeightsBias - 权重矩阵类，初始化、加载数据、保存数据

代码位置⚓︎

ch09, HelperClass2

• 双层神经网络解决方案的基本代码都在HelperClass2子目录下

参考文献⚓︎

[1] Hornik, K., Stinchcombe, M., and White, H. (1989). Multilayer feedforward networks are uni-versal approximators. Neural Networks, 2, 359–366. 171