前言:为了更好地理解深度学习,我决定从头开始构建一个神经网络,而不需要像
TensorFlow
这样的深度学习库。我相信,了解神经网络的内部工作对任何有抱负的数据科学家来说都是非常重要的。
什么是神经网络?
大多数神经网络的介绍性文本在描述它们时都会提到它和大脑的类比。在不深入研究大脑的情况下,我发现更容易将神经网络简单地描述为一个数学函数,将给定的输入映射(计算)为一个期望的输出。
神经网络由以下这些组件组成
-
输入层,; -
任意数量的隐藏层; -
输出层,; -
在每层之间的权重(weights)和偏差(biases), 和 ; -
每个隐藏层的激活函数选择,。在本篇中,我们将使用 激活函数。
下图显示了 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)
训练神经网络
简单的两层神经网络的输出 为:
你可能会注意到,在上面的方程中,权重和偏差是唯一影响输出的变量。
当然,权重和偏差的正确值决定了预测的准确性。根据输入数据微调权重和偏差的过程称为神经网络训练。
训练过程的每次迭代都包括以下步骤:
-
计算预测输出 ,称为前馈计算(feedforward) -
更新权重和偏差,称为反向传播(backpropagation)
下面的序列图说明了这个过程。
前馈计算(Feedforward)
As we’ve seen in the sequential graph above, feedforward is just simple calculus and for a basic 2-layer neural network, the output of the Neural Network is: 正如我们在上面的序列图中看到的,前馈只是简单的计算,对于简单的的 2 层神经网络,神经网络的输出是:
让我们在 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))
然而,我们仍然需要一种方法来评估我们预测的有多 “好”(即我们的预测离我们想要的有多远)?损失函数使我们能够做到这一点。
损失函数(Loss Function)
有许多可用的损失函数,而我们问题的性质应该决定我们对损失函数的选择。在本篇中,我们将使用简单的平方和误差作为损失函数。
也就是说,平方和误差就是每个预测值和实际值之间的差值之和的平方,这样我们就可以测量出“现实和理想之间差值”的绝对值了。
我们在训练中的目标是找到一组最佳的权重和偏差,以最小化损失函数。
反向传播(Backpropagation)
现在我们已经测量了预测的误差(损失),我们需要找到一种方法将误差传播回来,并更新我们的权重和偏差。
为了知道调整权重和偏差的适当的大小,我们需要知道损失函数相对于权重和偏差的导数。
回想一下微积分,函数的导数就是函数的斜率。
如果我们能够计算出导数,我们可以简单地通过增加/减少它来更新权重和偏差(参见上图)。这就称为梯度下降。
然而,我们不能直接计算损失函数对权重和偏差的导数,因为损失函数的方程并不包含权重和偏差。因此,我们需要链式法则(Chian Rule)来帮助我们计算它。
嚯!这看起来很难看,但它可以让我们得到我们所需要的 — 损失函数相对于权重的导数(斜率),这样我们就可以相应地调整权重。
现在我们有了这些,让我们将反向传播函数添加到 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):
# 应用链式法则求损失函数关于weights2和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)))
# 用损失函数的导数(斜率)更新权重
self.weights1 += d_weights1
self.weights2 += d_weights2
为了更深入地理解微积分和链规则在反向传播中的应用,我强烈推荐3Blue1Brown编写的教程。
把上面这些放在一起
现在我们有了完整的用于进行前馈和反向传播的 python 代码,让我们把神经网络应用于一个实例,看看它的表现如何。
我们的神经网络要去学习表示这个函数的理想权重和bias。
让我们对神经网络进行 1500 次迭代的训练,看看会发生什么。查看下面的每次迭代的损失图,我们可以清楚地看到损失单调地向最小值递减。这与我们前面讨论的梯度下降算法是一致的。
让我们看看 1500 次迭代后神经网络的最终预测(输出)。
我们做到了!我们的前馈和反向传播算法成功地训练了神经网络,预测结果收敛于我们真实想要的值。
请注意,预测值和实际值之间存在细微差异。这是可取的,因为它可以防止过拟合,并允许神经网络更好地泛化到它没有见过的数据。
接下来?
我们的旅程还没有结束。关于神经网络和深度学习,还有很多东西需要学习。比如:
-
除了 Sigmoid 函数,我们还可以使用其他什么激活函数? -
在训练神经网络时使用的学习率(Learning Rate) -
更加复杂的优化技术 -
将卷积用于图像分类任务、自然语言处理任务等 -
等等等等
我会写更多关于这些主题的文章,欢迎关注这些文章的更新!
原文始发于微信公众号(春阳CYang):【超详细】用 Python 从头开始构建自己的神经网络
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/51911.html