神经网络回顾-反向传播算法

1. BP算法

多层感知机模型理论上有求解线性不可分的能力,但是当时并没有有效的训练方法。
 
1986年,Rumelhart和McCelland领导的科学家小组在《Parallel Distributed Processing》一书中,提出了一种前馈型神经计算模型和用于调节该模型神经元联系效率或联结强度的误差往回传播学习算法,
即著名的BP网络和BP算法。BP算法解决了感知器存在的问题,使人工神经网络有了强大的计算能力,可实现各种复杂映射。
作为一种前馈型神经计算模型,BP网络与多层感知机没有本质的区别,但是增加了重要的调节神经元联系效率的算法。
  
不过在之前用到的Theano 和 Tensorflow中,并没直接用到BP算法,而是通过工具自带的自动求导完成这个过程。如Theano 学习二 自动求导和T.grad中提到的。
BP算法是通过求解多层复合函数的所有变量的偏导数来进行计算的一种梯度下降法。
 

2.BP算法的效果

得到广泛运用的BP算法无疑是正确高效的。

如下为一个简单的例子。

仅采用三层神经网络,ReLu作为激活函数,经过反向传播训练,可以得到对训练数据集100%的正确率。

这个结果的意义是,对于5000个28*28的输入数据,神经网络可以精准的找到像素水平上的特征点,和标签一一对应。

对于测试数据集上的正确率,由于实际特征并不仅限于像素点,所以没有足够高。

# coding:utf8
import cPickle
import numpy as np

class Network(object):
    def __init__(self, sizes):
        self.num_layers = len(sizes)
        self.sizes = sizes
        self.biases = [np.random.randn(y, 1) for y in sizes[1:]]
        self.weights = [np.random.randn(y, x)/np.sqrt(x)
                        for x, y in zip(sizes[:-1], sizes[1:])]

    def feedforward(self, a):
        for b_, w_ in zip(self.biases[:-1], self.weights[:-1]):
            a = self.relu(np.dot(w_, a)+b_)
        a=self.relu(np.dot(self.weights[-1], a)+self.biases[-1])
        return a

    def SGD(self, training_data, test_data,epochs, mini_batch_size, eta=1.0, lambda_=0.1):
        n_test = len(test_data)
        n = len(training_data)
        cx=range(epochs)
        for j in cx:
            self.cost = 0.0
            np.random.shuffle(training_data)
            for k in xrange(0, n, mini_batch_size):
                mini_batch = training_data[k:k+mini_batch_size]
                self.update_mini_batch(mini_batch, eta,n)
            self.cost+=0.5*lambda_*sum(np.linalg.norm(w_)**2 for w_ in self.weights)
            print "Epoch {0}: cost={1} {2} / {3}, {4} / {5}".format(j, self.cost/n,
                    self.evaluate(training_data,1), n,self.evaluate(test_data), n_test)

    def update_mini_batch(self, mini_batch, eta, n, lambda_=0.1):
        for x, y in mini_batch:
            delta_b, delta_w = self.backprop(x, y)
            for i in range(len(self.weights)):  # L2 regularization
                self.weights[i]-=eta/len(mini_batch)*(delta_w[i] +lambda_/n*self.weights[i])
            self.biases -= eta/len(mini_batch)*delta_b
            a=self.feedforward(x)
            cost=0.5*np.sum((a-y)**2)
            self.cost += cost

    def backprop(self, x, y):
        b=np.zeros_like(self.biases)
        w=np.zeros_like(self.weights)
        a_ = x
        a = [x]
        for b_, w_ in zip(self.biases, self.weights):
            a_ = self.relu(np.dot(w_, a_)+b_)
            a.append(a_)
        for l in xrange(1, self.num_layers):
            if l==1:
                delta= a[-1]-y
            else:
                sp=self.relu_prime(a[-l])
                delta = np.dot(self.weights[-l+1].T, delta) * sp
            b[-l] = delta
            w[-l] = np.dot(delta, a[-l-1].T)
        return (b, w)

    def evaluate(self, test_data, train=0):
        test_results = [(np.argmax(self.feedforward(x)), y)
                        for (x, y) in test_data]
        if train:
            return sum(int(x == np.argmax(y)) for (x, y) in test_results)
        else:
            return sum(int(x == y) for (x, y) in test_results)

    def relu(self,z):
        return np.maximum(z, 0.0)

    def relu_prime(self,z):
        z[z>0]=1
        return z

def get_label(i):
    c=np.zeros((10,1))
    c[i]=1
    return c

if __name__ == '__main__':
        def get_data(data):
            return [np.reshape(x, (784,1)) for x in data[0]]

        f = open('mnist.pkl', 'rb')
        training_data, validation_data, test_data = cPickle.load(f)
        training_inputs = get_data(training_data)
        training_label=[get_label(y_) for y_ in training_data[1]]
        data = zip(training_inputs,training_label)
        test_inputs = training_inputs = get_data(test_data)
        test = zip(test_inputs,test_data[1])
        net = Network([784, 100, 10])
        net.SGD(data[:5000],test[:5000],epochs=30,mini_batch_size=10, eta=0.1, lambda_=1.0)

"""
Epoch 0: cost=0.0426238448876 4597 / 5000, 4296 / 5000
Epoch 1: cost=0.033819213208 4689 / 5000, 4358 / 5000
Epoch 2: cost=0.0288248484125 4813 / 5000, 4513 / 5000
Epoch 3: cost=0.0253698863098 4875 / 5000, 4547 / 5000
Epoch 4: cost=0.02279885831 4902 / 5000, 4560 / 5000
Epoch 5: cost=0.0210175139057 4918 / 5000, 4557 / 5000
Epoch 6: cost=0.0199298287658 4949 / 5000, 4604 / 5000
Epoch 7: cost=0.0189699481528 4953 / 5000, 4596 / 5000
Epoch 8: cost=0.0183841353475 4974 / 5000, 4615 / 5000
Epoch 9: cost=0.0179210678531 4980 / 5000, 4615 / 5000
Epoch 10: cost=0.0176382791906 4983 / 5000, 4619 / 5000
Epoch 11: cost=0.0173457308329 4988 / 5000, 4627 / 5000
Epoch 12: cost=0.0172302053576 4992 / 5000, 4621 / 5000
Epoch 13: cost=0.0171641452743 4993 / 5000, 4604 / 5000
Epoch 14: cost=0.0170756316506 4997 / 5000, 4619 / 5000
Epoch 15: cost=0.0170865863368 4999 / 5000, 4622 / 5000
Epoch 16: cost=0.0170484913743 4997 / 5000, 4630 / 5000
Epoch 17: cost=0.0170963866718 4999 / 5000, 4625 / 5000
Epoch 18: cost=0.0170749375354 4999 / 5000, 4619 / 5000
Epoch 19: cost=0.0170992634974 4999 / 5000, 4629 / 5000
Epoch 20: cost=0.0171174078201 5000 / 5000, 4617 / 5000
Epoch 21: cost=0.0171825465141 5000 / 5000, 4642 / 5000
Epoch 22: cost=0.0171631918395 5000 / 5000, 4620 / 5000
Epoch 23: cost=0.0172101142436 5000 / 5000, 4623 / 5000
Epoch 24: cost=0.0172301926833 5000 / 5000, 4637 / 5000
Epoch 25: cost=0.017262734852 5000 / 5000, 4620 / 5000
Epoch 26: cost=0.0172794396558 5000 / 5000, 4627 / 5000
Epoch 27: cost=0.0172999811158 5000 / 5000, 4631 / 5000
Epoch 28: cost=0.0173430208818 5000 / 5000, 4630 / 5000
Epoch 29: cost=0.0173583754701 5000 / 5000, 4629 / 5000
"""

3.生物学中的梯度下降近似

生物可能不具有计算特定函数的功能。

在上一篇随笔中,使用ReLU作为激活函数,计算梯度是比较直观、容易理解的。

复杂一点的函数,神经网络可能有许多方式,不依赖于公式推导,计算梯度并反向传播。

2016年9月发表于《HYPOTHESIS & THEORY ARTICLE》的一篇文章《Toward an Integration of Deep Learning and Neuroscience》中,提到了大脑进行神经网络优化的生物学基础。

作者认为大脑中可能用来实现对梯度下降算法近似的可能机制非常多。

以下为原文经过谷歌翻译(语句基本通顺)。

2.2.2。生物可信的梯度下降近似

现在有许多建议的“生物学上似真的”机制,通过该机制神经回路可以实现优化算法,像反向传播一样,可以有效地利用梯度。这些包括广义循环(O'Reilly,1996),对比性Hebbian学习(Xie和Seung,2003),随机反馈权重与突触体内平衡(Lillicrap等,2014; Liao等, (Bengio等人,2015a; Scellier和Bengio,2016),具有反向传播动作电位的复杂神经元(Körding和König,2000)和其他人(Balduzzi et al。,2014)。虽然这些机制在细节上不同,但是它们都调用具有相位误差的反馈连接。通过将预测与目标进行比较来进行学习,并且使用预测误差来驱动自底向上活动中的自上而下的变化。

作为一个例子,考虑O'Reilly的临时扩展对比吸引器学习(XCAL)算法(O'Reilly et al。,2012,2014b)。假设我们有一个具有输入层,输出层和一组隐藏层之间的多层神经网络。 O'Reilly表明,与反向传播相同的功能可以通过具有相同权重但对称连接的双向网络来实现。在仅使用正向连接计算输出之后,我们将输出神经元设置为它们应具有的值。网络的动态使得隐藏层的活动朝着将输入和输出链接的稳定的吸引子状态发展。 XCAL算法在该过程中在网络中的每个突触处执行一种类型的局部修改的Hebbian学习(O'Reilly等人,2012)。 XCAL Hebbian学习规则将在该沉降的早期阶段(在达到吸引子状态之前)到最终阶段(一旦达到吸引子状态)的局部突触活动(前x post)进行比较,并且调整方式,应该使早期阶段更密切地反映后期阶段。这些对比的Hebbian学习方法甚至在连接权重不是精确对称时也起作用(O'Reilly,1996)。 XCAL已经在生物似真的基于电导的神经元中实现,并且基本上实现了误差方法的反向传播。

反向传播的近似也可以通过神经活动的毫秒级定时来实现(O'Reilly等人,2014b)。例如,尖峰定时依赖性可塑性(STDP)(Markram等人,1997)是一些神经元的特征,其中突触权重变化的符号取决于突触前和突触后的精确毫秒量级相对定时,突触峰值。这通常被解释为测量突触前和突触后尖峰之间的因果关系的潜力的Hebbian可塑性:突触前尖峰可能已经有助于导致突触后尖峰,如果它不久预先发生。为了实现反向传播机制,Hinton提出了一种替代解释:神经元可以在它们的发生率的时间导数中对反向传播所需的误差导数类型进行编码(Hinton,2007,2016)。然后,STDP对应于对这些误差导数敏感的学习规则(Xie和Seung,2000; Bengio等人,2015b)。换句话说,在适当的网络环境中,STDP学习可以产生反向传播的生物实现。

生物神经网络可以近似反向传播的另一种可能机制是“反馈比对”(Lillicrap等,2014; Liao等,2015)。在那里,反向传播中的反馈通路由一组随机反馈连接代替,通过该反馈通路从后续层的误差导数计算一层处的误差导数,而不依赖于前向权重。根据前馈和反馈连接之间的突触归一化机制和近似符号一致性的存在(Liao等人,2015),计算误差导数的这种机制几乎与各种任务的反向传播一样好。实际上,前向权重能够适应使网络进入其中随机后向权重实际上携带用于近似梯度的信息的状态。这是一个引人注目和令人惊讶的发现,并且表明我们对梯度下降优化的理解,特别是反向传播本身起作用的机制仍然是不完全的。在神经科学,我们发现反馈连接几乎无论我们在哪里找到前馈连接,他们的作用是多样的理论的主题(Callaway,2004; Maass等人,2007)。应当注意,反馈对准本身并不精确地指定神经元如何表示和利用误差信号;它仅放宽对误差信号的传送的约束。因此,反馈对准更能够在完全生物学(近似)实施反向传播中使用,而不是完全生物学实施本身。因此,可以将其并入这里讨论的几个其他方案中。

上述反向传播的“生物学”实施仍然缺乏生物现实主义的一些关键方面。例如,在大脑中,神经元往往是兴奋性的或抑制性的,但不是两者,而在人工神经网络中,单个神经元可以向其下游神经元发送兴奋性和抑制性信号。幸运的是,这种约束不可能限制可以学习的功能(Parisien et al。,2008; Tripp and Eliasmith,2016)。然而,其他生物学考虑需要更详细地看待:生物神经网络的高度复发性质,其显示时间丰富的动力学,以及哺乳动物大脑中的大多数神经元通过尖峰通信的事实。

原文地址:https://www.cnblogs.com/qw12/p/6308876.html