训练的目的
在更新边权之前,必须先要清晰地知道我们的目的是什么。
显然,我们想要神经网络的结果与正确的结果一模一样(虽然真不太现实,但是理想总是要有的)。
如果用严格一点的数学语言来表达,就是想办法使得输出层的损失函数趋于0!
修改边权的核心思想——随机梯度下降法
先来观察一下输出层的损失函数与什么有关,最直接的就是与输出层结点的权值有关,再进一步输出层的权值又与连接隐藏层和输出层直接的边权、隐藏层结点的权值有关。接着往下寻找,不难看出来,输出层的损失函数是一个关于所有边权的多元函数,其涉及到元的个数是非常多的。
现在我们是想要这个函数趋于0,也就是最小值,但是对于这个函数而言,十分复杂,此时我们就有它的极小值近似它的最小值。
因此就可以使用随机梯度下降法了!
求偏导数
根据随机梯度下降法,就可以知道,对一条边边权的修改值是与函数关于其的偏导数相关的,因此,需要我们快速地找到偏导数。
这里就需要用到计算图了。
不难发现,计算图中有不少地方是与误差的反向传播相似的,此时就应该明白为什么误差是这样反向传播的,因为这样反向传播能帮助我们快速地求出偏导数!!
另外
y
=
1
1
+
e
−
x
y=\frac{1}{1+e^{-x}}
y=1+e−x1的导数就是
y
×
(
1
−
y
)
y\times(1-y)
y×(1−y)。
求出了偏导数,更新一下边权,那么这一次训练也就完成了。
Python实现
结合之前的误差的反向传播和询问操作,其实这一段代码也非常简单。
def train(self , input_value , output_value):
inputs = np.array(input_value , ndmin=2).T
results = np.array(output_value , ndmin=2).T
hidden_inputs = np.dot(self.weight_input_hidden , inputs)
hidden_outputs = self.active_function(hidden_inputs)
outputs = np.dot(self.weight_hidden_output , hidden_outputs)
outputs = self.active_function(outputs)
#计算当前神经网络的输出
output_error = results - outputs
hidden_error = np.dot(self.weight_hidden_output.T , output_error)
self.weight_hidden_output += np.dot(output_error * outputs * (1.0 - outputs) , np.array(hidden_outputs , ndmin=2).T) * self.rate
self.weight_input_hidden += np.dot(hidden_error * hidden_outputs * (1.0 - hidden_outputs) , np.array(inputs , ndmin=2).T) * self.rate
pass