打造自己的神经网络系统之手把手教你用Python从零开始

  Seves

原文链接:打造自己的神经网络系统之手把手教你用Python从零开始

什么是神经网络?

大多数介绍神经网络的文章在描述它们时都会提出大脑类比。在不深入研究大脑类比的情况下,我发现简单地将神经网络描述为将给定输入映射到输出的数学函数更容易。

神经网络由以下几个组件组成

一个输入层,x

任意数量的隐藏层

一个输出层,ŷ

每两层之间都有一组权重和偏置,W 和 b

每个隐藏层都要选择一个激活函数 σ。在本文中,我们选用 Sigmoid 激活函数。

下图显示了2层神经网络的架构(请注意,在计算神经网络中的层数时,通常会排除输入层)

640jpg

2层神经网络的体系结构

在Python中创建神经网络类很容易。

class NeuralNetwork:

def __init__(self, x, y):

    self.input      = x

    self.weights1   = np.random.rand(self.input.shape[1],4) 

    self.weights2   = np.random.rand(4,1)                 

    self.y          = y

    self.output     = np.zeros(y.shape)

训练神经网络

简单的2层神经网络的输出是:

640png

你可能会注意到,在上面的等式中,权重W和偏差b是影响输出的唯一变量ŷ。

当然,权重和偏差的正确值决定了预测的强度。 从输入数据微调权重和偏差的过程称为训练神经网络。

培训过程的每次迭代都包含以下步骤:

计算预测输出ŷ,称为前馈

更新权重和偏差,称为反向传播

下面的顺序图说明了该过程。

640png

前馈

正如我们在上面的顺序图中看到的,前馈只是简单的计算,对于一个基本的2层神经网络,神经网络的输出是:

640png

让我们在python代码中添加一个前馈函数来做到这一点。 请注意,为简单起见,我们假设偏差为0。

class NeuralNetwork:

def __init__(self, x, y):

    self.input      = x

    self.weights1   = np.random.rand(self.input.shape[1],4) 

    self.weights2   = np.random.rand(4,1)                 

    self.y          = y

    self.output     = np.zeros(self.y.shape)



def feedforward(self):

    self.layer1 = sigmoid(np.dot(self.input, self.weights1))

    self.output = sigmoid(np.dot(self.layer1, self.weights2))

但是,我们仍然需要一种方法来评估我们预测的“好的程度”(即我们的预测有多好)? 而损失函数能让我们做到这一点。

损失函数

有许多可用的损失函数,我们的问题的性质应该决定了我们选择的损失函数。 在本教程中,我们将使用简单的平方和误差作为我们的损失函数。

640png

也就是说,平方和误差仅仅是每个预测值与实际值之差的总和。 差值是平方的,因此我们测量的是差值的绝对值。

我们的训练目标是找到最佳的权重和偏差集,以最大限度地减少损失函数。

反向传播

既然我们已经测量了预测的误差(损失),我们需要找到一种方法来传播误差,并更新权重和偏差。

为了知道调整权重和偏差的合适数量,我们需要知道损失函数相对于权重和偏差的导数。

回想一下微积分函数的导数就是函数的斜率。

640jpg


梯度下降算法

如果我们有导数,我们可以通过简单地增加/减少导数来更新权重和偏差(参见上图)。 这就是所谓的梯度下降。

但是,我们不能直接计算损失函数相对于权重和偏差的导数,因为损失函数的方程不包含权重和偏差。 因此,我们需要链规则来帮助我们计算它。

640png

用于计算损失函数相对于权重的导数的链式法则。注意,为了简单起见,我们只显示了假设1层神经网络的偏导数。

唷!这真难看,但它允许我们得到我们需要的东西——损失函数相对于权重的导数(斜率),以便我们可以相应地调整权重。

现在我们已经有了这个,让我们将反向传播函数添加到python代码中。

class NeuralNetwork:

def __init__(self, x, y):

    self.input      = x

    self.weights1   = np.random.rand(self.input.shape[1],4) 

    self.weights2   = np.random.rand(4,1)                 

    self.y          = y

    self.output     = np.zeros(self.y.shape)



def feedforward(self):

    self.layer1 = sigmoid(np.dot(self.input, self.weights1))

    self.output = sigmoid(np.dot(self.layer1, self.weights2))



def backprop(self):

    # application of the chain rule to find derivative of the loss function with respect to weights2 and weights1

    d_weights2 = np.dot(self.layer1.T, (2*(self.y - self.output) * sigmoid_derivative(self.output)))

    d_weights1 = np.dot(self.input.T,  (np.dot(2*(self.y - self.output) * sigmoid_derivative(self.output), self.weights2.T) * sigmoid_derivative(self.layer1)))



    # update the weights with the derivative (slope) of the loss function

    self.weights1 += d_weights1

    self.weights2 += d_weights2

为了更深入地理解微积分和链式法则在反向传播中的应用,我强烈推荐3 blue 1 brown的本教程。

把它们放在一起

现在我们已经有了完整的python代码来进行前馈和反向传播,让我们将神经网络应用到一个例子中,看看它表现如何。

640png

我们的神经网络应该学习理想的权重集来表示这个函数。请注意,仅仅通过检查来计算权重对我们来说并不是微不足道的。

让我们训练神经网络进行1500次迭代,看看会发生什么。观察下面的每次迭代损失图,我们可以清楚地看到损失单调递减到最小值。这与我们前面讨论的梯度下降算法是一致的。

640png

让我们看一下1500次迭代后神经网络的最终预测(输出)。

640png

1500次训练迭代后的预测

我们做到了!我们的前馈和反向传播算法成功地训练了神经网络,预测结果收敛于真实值。

请注意,预测值和实际值略有不同。这是可取的,因为它可以防止过度拟合,并允许神经网络更好地推广到看不见的数据。

转载声明:本文转载自AI时间

本文链接:https://www.fcblog.top/step-by-step-to-build-your-own-neural-network-from-scratch-in-python-b6f76ebc

fc
1,271