改善我们的神经网络
![改善我们的神经网络](https://pic3.zhimg.com/v2-10cd72a700dc67c0015ae5100fd51c27.png)
要记住的一件事是,大多数这些改进都具有保持低重量(接近0)的效果。出于正则化用于回归的相同原因,在神经网络中具有低权重值可以帮助它更好地推广。由于过度使用神经网络非常容易,我们会尽我们所能。
激活功能
我做的第一件事是添加两个我们可以使用的激活(传输)函数。每一个都比我们开始的逻辑sigmoid有一定的优势。最大的改进来自于将隐藏层激活函数从逻辑sigmoid更改为双曲正切。两者都被认为是sigmoid函数,但logistic是(0,1)的范围,双曲正切(tanh)的范围是(-1,1)。这里的想法是,由于tanh函数以0为中心,它产生的输出平均将更接近0.逻辑sigmoid的输出总是大于0所以输出的平均值也会更大比0。
下一个激活功能称为softmax。这个只在输出层有用,并且仅在类互斥时才有用。它强制神经网络的输出总和为1,这样它们就可以表示各类的概率分布。这样,网络“知道”它不能给出输出中的类的相同概率。很简约!
1 def softmax(w): 2 e = np.exp(w - np.amax(w)) 3 dist = e / np.sum(e) 4 return dist 5 6 def tanh(x): 7 return np.tanh(x) 8 9 def dtanh(y): 10 return 1 - y*y
最好的部分是我们可以将这些激活交换到我们的反向传播算法,只需很少的更改。为了在隐藏层中使用tanh函数,我们所要做的就是将它换成sigmoid。
1 sum = np.dot(self.wi.T, self.ai) self.ah = tanh(sum)
当我们计算tanh隐藏单位的梯度时,我们将使用我们之前定义的新tanh导数代替logistic Sigmoid导数。
1 error = np.dot(self.wo, output_deltas) hidden_deltas = dtanh(self.ah) * error
要使用softmax输出层,我们将进行最大幅度的更改。我们将从这里走出来
1 output_deltas = dsigmoid(self.ao) * -(targets - self.ao)
对此
1 output_deltas = -(targets - self.ao)
再一次,我在这些帖子中跳过数学,只关注可读的python代码和更高层次的理解。但是,基本上这就是正在发生的事情:如果你要使用softmax函数的导数计算出梯度下降算法,你将最终取消项并到达 - (t - yhat)进行误差计算,其中t是真值和yhat是预测值。真棒!
如果我没记错,只需切换出这些激活功能,我就会有一些改进。
初始化权重
在我们之前的神经网络中,我们简单地用一些随机数初始化权重。哪个好,因为它打破了对称性,但还有一个更好的方法。我们想尝试在它们的线性区域中激活sigmoid函数,以便它们的导数为我们的学习提供足够的梯度来继续。换句话说,如果单位的输出接近sigmoid函数的最小值或最大值,它的导数将是平坦的,我们的网络将学习得非常缓慢(没有梯度下降)。那么我们如何解决这个问题呢?
这部分需要在输入数据和权重之间进行一些“协调”才能使其有效。对于输入数据,我们只需要将它们缩放到平均值为0.那么我们将再次随机绘制权重,但这次我们将告诉numpy给它们一个0的平均值和负平方根的标准偏差进入节点的层的大小。
1 input_range = 1.0 / self.input ** (1/2) 2 output_range = 1.0 / self.hidden ** (1/2) 3 self.wi = np.random.normal(loc = 0, scale = input_range, size = (self.input, self.hidden)) 4 self.wo = np.random.normal(loc = 0, scale = output_range, size = (self.hidden, self.output))
洗牌训练的例子
下一个提示可能是代码中最简单有效的改进。在训练期间的每次迭代中,我们现在将在将数据馈送到网络之前对数据的顺序进行混洗。网络从最意想不到的样本中学得最快。让我们说我们所有的数据都很整洁有序。我们所有的'''','twos'和'threes'都归为一组。如果我们像这样将数据输入到网络中,那么它将非常善于对“那些”进行分类,但是一旦它获得它的第一个“两个”,它就无法接近对它进行分类。然后,网络必须开始学习'两个'并忘记'那些'。如果我们在每次迭代时随机化输入,网络将更容易创建可在所有类之间进行推广的权重。
将其添加到我们的代码就像......一样简单
1 import random 2 3 def fit(self, patterns): 4 5 for i in range(self.iterations): 6 error = 0.0 7 random.shuffle(patterns) 8 for p in patterns: 9 feed_forward(X) 10 backprop_function(y)
其中patterns是训练数据集的X和y值列表。
正则
与低重量值的整体主题保持一致另一个方便的技巧是以重量衰减的形式添加正规化。这与线性模型中使用的l2正则化非常相似。对于这个神经网络,我初始化了隐藏权重的输入和隐藏到输出权重的正则化项。这样就可以更灵活地进行微调。基本上,正则化引入了对大权重的惩罚,这反过来又将权重的值推向零。将其添加到网络非常简单直接。
1 # update the weights connecting hidden to output, change == partial derivative 2 change = output_deltas * np.reshape(self.ah, (self.ah.shape[0],1)) 3 regularization = self.l2_out * self.wo 4 self.wo -= self.learning_rate * (change + regularization) + self.co * self.momentum 5 self.co = change 6 7 # update the weights connecting input to hidden, change == partial derivative 8 change = hidden_deltas * np.reshape(self.ai, (self.ai.shape[0], 1)) 9 regularization = self.l2_in * self.wi 10 self.wi -= self.learning_rate * (change + regularization) + self.ci * self.momentum 11 self.ci = change
正如您所看到的,我们在反向传播算法中添加了一个正则化项,该算法被添加到变化(偏导数)变量中,这反过来又增加了每次迭代时每个权重减少的量。self.l2 in和self.l2 out参数是需要通过一些交叉验证进行调整的参数,因此找到最佳值会非常耗时。有了正确的数据,它可能是值得的。 没有更多的过度拟合! 因此,有四件事情大大改善了我的神经网络的性能。显然还有很多可以添加但是这些只需很少的努力就可以提供很大的改进。 就像最后一个神经网络帖子一样,我没有深入研究所有这些。如果您希望将您对神经网络的理解提升到一个新的水平,斯坦福深度学习教程是我现在最喜欢的网站。它提供了比我在这里的帖子更深入地了解神经网络的所有算法。我发现将“多层神经网络”教程中的每个方程与神经网络脚本中的每个代码片段进行匹配非常有帮助。它使基础数学更容易消化。 此外,LeCun等人在“Efficient Backprop”中概述了许多改进神经网络训练的方法。我在这里概述的大部分内容来自那篇论文。 所有这些提示中最大的一点是,接近零的平均值将使你成为英雄。 我的机器学习库 我正在创建一个面向新用户的机器学习库。它不是最快的库,但我们的想法是以非常清晰易懂的方式编写代码,以便人们可以查看每个算法的具体内容。希望它将成为一些人认为有用的资源。
翻译自克里斯托弗奥拉博客