- 原文地址:How to build your own Neural Network from scratch in Python
- 原文做者:James Loy
- 译文出自:掘金翻译计划
- 本文永久连接:github.com/xitu/gold-m…
- 译者:JackEggie
- 校对者:lsvih, xionglong58
一个帮助初学者理解深度神经网络内部工做机制的指南前端
写做动机: 为了使我本身能够更好地理解深度学习,我决定在没有像 TensorFlow 这样的深度学习库的状况下,从零开始构建一个神经网络。我相信,理解神经网络的内部工做原理对任何有追求的数据科学家来讲都很重要。python
这篇文章包含了我所学到的东西,但愿对大家也有用。android
大多数介绍神经网络的文章在描述它们时都会与大脑作类比。在不深刻研究与大脑相似之处的状况下,我发现将神经网络简单地描述为给定输入映射到指望输出的数学函数更容易理解一些。ios
神经网络由如下几个部分组成:git
下图展现了 2 层神经网络的架构(注:在计算神经网络中的层数时,输入层一般被排除在外)github
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 层神经网络的输出 ŷ 以下:架构
你可能注意到了,在上面的等式中,只有权重 W 和误差 b 这两个变量会对输出 ŷ 产生影响。
固然,合理的权重和误差会决定预测的准确程度。将针对输入数据的权重和误差进行微调的过程就是训练神经网络的过程。
训练过程的每次迭代包括如下步骤:
下面的序列图展现了这个过程。
正如咱们在上面的序列图中看到的,前馈只是一个简单的计算过程,对于一个基本的 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))
复制代码
可是,咱们仍然须要一种方法来评估预测的“精准程度”(即咱们的预测有多好)?而损失函数能让咱们作到这一点。
可用的损失函数有不少,而咱们对损失函数的选择应该由问题自己的性质决定。在本教程中,咱们将使用简单的平方和偏差做为咱们的损失函数。
这就是说,平方和偏差只是每一个预测值与实际值之差的总和。咱们将差值平方后再计算,以便咱们评估偏差的绝对值。
训练的目标是找到能使损失函数最小化的一组最优的权值和误差。
如今咱们已经得出了预测的偏差(损失),咱们还须要找到一种方法将偏差传播回来,并更新咱们的权重和误差。
为了得出调整权重和误差的合适的量,咱们须要计算损失函数对于权重和误差的导数。
回忆一下微积分的知识,计算函数的导数就是计算函数的斜率。
梯度降低算法
若是咱们已经算出了导数,咱们就能够简单地经过增大/减少导数来更新权重和误差(参见上图)。这就是所谓的梯度降低。
然而,咱们没法直接计算损失函数对于权重和误差的导数,由于损失函数的等式中不包含权重和误差。 所以,咱们须要链式法则来帮助咱们进行计算。
为了更新权重使用链式法则求解函数的导数。注意,为了简单起见,咱们只展现了假设为 1 层的神经网络的偏导数。
哦!这真难看,但它让咱们获得了咱们须要的东西 —— 损失函数对于权重的导数(斜率),这样咱们就能够相应地调整权重。
如今咱们知道要怎么作了,让咱们向 Pyhton 代码中添加反向传播函数。
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 代码,让咱们将神经网络应用到一个示例中,看看效果如何。
咱们的神经网络应该经过学习得出一组理想的权重来表示这个函数。请注意,仅仅是求解权重的过程对咱们来讲也并不简单。
让咱们对神经网络进行 1500 次训练迭代,看看会发生什么。观察下图中每次迭代的损失变化,咱们能够清楚地看到损失单调递减至最小值。这与咱们前面讨论的梯度降低算法是一致的。
让咱们看一下通过 1500 次迭代后神经网络的最终预测(输出)。
1500 次训练迭代后的预测结果
咱们成功了!咱们的前馈和反向传播算法成功地训练了神经网络,预测结果收敛于真实值。
请注意,预测值和实际值之间会存在细微的误差。咱们须要这种误差,由于它能够防止过拟合,并容许神经网络更好地推广至不可见数据中。
幸运的是,咱们的学习旅程还未结束。关于神经网络和深度学习,咱们还有不少内容须要学习。例如:
我将会就这些主题编写更多内容,请在 Medium 上关注我并留意更新!
固然,我也在从零开始编写我本身的神经网络的过程当中学到了不少。
虽然像 TensorFlow 和 Keras 这样的深度学习库使得构建深度神经网络变得很简单,即便你不彻底理解神经网络内部工做原理也不要紧,可是我发现对于有追求的数据科学家来讲,深刻理解神经网络是颇有好处的。
这个练习花费了我大量的时间,我但愿它对大家也有帮助!
若是发现译文存在错误或其余须要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可得到相应奖励积分。文章开头的 本文永久连接 即为本文在 GitHub 上的 MarkDown 连接。
掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 Android、iOS、前端、后端、区块链、产品、设计、人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划、官方微博、知乎专栏。