Python编写神经网络的代码之前,让我们开始实验吧!

神经网络可以说是目前深度学习的主力军。有了足够的数据(大数据)和计算能力,神经网络可以用来解决深度学习中的大部分问题。您可以在 Python 或 R 中创建神经网络,并方便地在任何数据集上以高精度对其进行训练。

为了便于理解,我们可以把神经网络想象成一个黑盒子,即给定输入数据,它可以给你一个你想失去的答案。然而,虽然这听起来很容易,但了解这些算法背后的内容以及它们的工作原理却更加困难。

程序员正在训练神经网络

在本文中,我们将探讨构建神经网络的一些细节。本文将使用 Python 对神经网络进行编码。数值计算也将使用 Python 的 numpy 库执行。本文将尽量避免一些复杂的数学算法细节(微积分),但如果您想进一步了解它们,您可能会在以后的文章中继续详述!

sa,让我们开始实验吧!

什么是神经网络?

在我们开始编写自己的神经网络代码之前,我们必须首先了解什么是神经网络。

神经网络

在上图中,我们可以看到一个非常随机的神经网络图。它具有相互连接的彩色圆圈,箭头指向特定方向,这些彩色圆圈有时被称为神经元。

这些神经元只不过是数学函数,当给定一些输入时,会产生输出,神经元的输出取决于神经元的输入及其参数。我们可以通过更新这些参数从神经网络中得到我们需要的值。

这些神经元中的每一个都是使用 sigmoid 函数定义的,这是一个 sigmoid 函数,它为每个输入提供 0 到 1 之间的输出。这些 sigmoid 函数相互连接,形成一个完整的神经网络。

而这里的连接是指将一层sigmoid单元的输出作为输入提供给下一层的sigmoid函数。通过这种方式,我们的神经网络可以为任何给定的输入生成输出。这个转移过程一直持续到最后一层。最后一层生成输出。神经网络为给定输入产生输出的过程是前向传播。最后一层的输出也称为神经网络的预测。在本文后面,我们将讨论如何评估预测。这些评估可以用来判断我们的神经网络是否需要改进。

在生成最后一层的输出后,我们立即计算其成本函数。成本函数计算神经网络与做出所需预测的距离。成本函数的值表示预测值与真实值之间的差异。

我们的目标是最小化代价函数的值神经网络 参数初始化,而最小化代价函数的过程也需要算法不断更新网络中的参数值,使代价函数达到最小值。

使用梯度下降和随机梯度下降等算法来更新神经网络的参数。这些算法将更新网络中每一层的权重和偏差值,这取决于它将如何影响成本函数的最小化。网络中每个输入神经元的权重和偏差对最小化成本函数的影响可以通过反向传播来计算。

神经元

开始写代码

介绍了神经网络的主要基本思想之后,我们开始把我们的思想转化为具体的代码。第一步自然是导入我们需要的库。

import numpy as np
import matplotlib.pyplot as plt

需要说明的是,本文不会使用任何深度学习库。因此,本文将主要使用 numpy 库来高效地执行数学运算。

构建神经网络的第一步是初始化参数。我们需要为每一层的每个神经元初始化两个参数:

权重偏差

这些权重和偏差将以矢量化形式声明。这意味着我们不是为每一层中的每个神经元初始化权重和偏置,而是为每一层创建一个权重向量(或矩阵)神经网络 参数初始化,并为偏置创建另一个向量。

这些权重和偏置向量将与层的输入相结合。然后我们对这个组合应用一个 sigmoid 函数并将其作为输入传输到下一层。

layer_dims 保存每一层的尺寸。我们将这些层的维度传递给 init_parms 函数,该函数将使用它们来初始化参数。这些参数将存储在一个名为 params 的字典中。因此,在 params 字典中,params[‘W1’] 将表示第 1 层的权重矩阵。

def init_params(layer_dims):
 np.random.seed(3)
 params = {}
 L = len(layer_dims)
 
 for l in range(1, L):
 params['W'+str(l)] = np.random.randn(layer_dims[l], layer_dims[l-1])*0.01
 params['b'+str(l)] = np.zeros((layer_dims[l], 1))
 
 return params

注意沸腾!我们现在已经初始化了权重和偏差,现在我们将定义 sigmoid 函数。它将计算任何给定 Z 值的 sigmoid 函数的值,并将该值存储为缓存。我们存储缓存的值是因为我们需要它们进行反向传播。Z 这里指的是线性假设。

请注意,sigmoid 函数属于神经网络术语中的激活函数类别。激活函数的作用是塑造神经元的输出。

例如,一个 sigmoid 函数接受一个具有离散值的输入,并给出一个介于 0 和 1 之间的值。其目的是将线性输出转换为非线性输出。使用不同类型的激活函数当然可以获得更好的性能,但为简单起见,本文将坚持仅使用 sigmoid 函数。

#Z(线性假设)- Z=W*X+B
#W-权重矩阵,b-偏差向量,X-输入
def sigmoid(Z):
	A = 1/(1+np.exp(np.dot(-1, Z)))
 cache = (Z)
 
 return A, cache

现在,让我们开始编写前向传播代码。正如本文前面所讨论的,前向传播将采用前一层的值并将其用作下一层的输入。下面的函数将训练数据和参数作为输入,会生成一层的输出,然后继续到下一层,以此类推。

A_prev 是第一层的输入。我们将遍历网络的所有层并计算线性假设。之后,它将取 Z 值(线性假设)并将其提供给 sigmoid 激活函数。缓存的值沿途存储并累积在缓存中,最后,函数返回生成的值和存储的缓存。

现在让我们开始定义成本函数。

def cost_function(A, Y):
 m = Y.shape[1]
 
 cost = (-1/m)*(np.dot(np.log(A), Y.T) + np.dot(log(1-A), 1-Y.T)) 
 
 return cost

随着成本函数的值不断下降,我们的模型性能变得更好。可以通过不断更新神经网络中每一层的参数值来最小化代价函数的值。可以使用梯度下降等算法以最小化成本函数的方式更新这些值。

梯度下降通过一些更新项更新值。这些称为梯度的更新项是使用反向传播计算的,其中为网络中的每个神经元计算梯度值,这将表示最终输出相对于特定神经元参数的变化。

def one_layer_backward(dA, cache):
 linear_cache, activation_cache = cache
 
 Z = activation_cache
 dZ = dA*sigmoid(Z)*(1-sigmoid(Z)) # S型函数的导数
 
 A_prev, W, b = linear_cache
 m = A_prev.shape[1]
 
 dW = (1/m)*np.dot(dZ, A_prev.T)
 db = (1/m)*np.sum(dZ, axis=1, keepdims=True)
 dA_prev = np.dot(W.T, dZ)
 
 return dA_prev, dW, db

上面的代码对单层执行反向传播操作。它使用我们之前存储的缓存值来计算一层 sigmoid 单元格的梯度值。在激活缓存中,我们存储该层的 Z 值。使用这个值,我们将计算 dZ,它是成本函数相对于给定神经元的线性输出的导数。

一旦计算了所有这些,就可以计算 dW、db 和 dA_prev,它们分别是成本函数关于权重、偏差和先前激活的导数。本文直接在代码中使用公式。如果你不熟悉微积分,乍一看它可能看起来太复杂了。但是现在,试着像任何其他普通的数学公式一样思考它。

之后,我们将使用这段代码对整个神经网络进行反向传播。函数 backprop 用于完成此操作。在这里,我们创建了一个字典,用于将其梯度映射到每一层。我们将向后遍历整个模型并计算梯度。

def backprop(AL, Y, caches):
 grads = {}
 L = len(caches)
 m = AL.shape[1]
 Y = Y.reshape(AL.shape)
 
 dAL = -(np.divide(Y, AL) - np.divide(1-Y, 1-AL))
 
 current_cache = caches[L-1]
 grads['dA'+str(L-1)], grads['dW'+str(L-1)], grads['db'+str(L-1)] = one_layer_backward(dAL, current_cache)
 
 for l in reversed(range(L-1)):
 
 current_cache = caches[l]
 dA_prev_temp, dW_temp, db_temp = one_layer_backward(grads["dA" + str(l+1)], current_cache)
 grads["dA" + str(l)] = dA_prev_temp
 grads["dW" + str(l + 1)] = dW_temp
 grads["db" + str(l + 1)] = db_temp
 
 return grads

一旦我们遍历了所有层并计算了梯度,我们将这些值存储在梯度字典中并返回。

最后,使用这些梯度值,将更新每一层的参数。函数 update_parameters 遍历所有层并在更新参数后返回它们。

def update_parameters(parameters, grads, learning_rate):
 L = len(parameters) // 2
 
 for l in range(L):
 parameters['W'+str(l+1)] = parameters['W'+str(l+1)] -learning_rate*grads['W'+str(l+1)]
 parameters['b'+str(l+1)] = parameters['b'+str(l+1)] - learning_rate*grads['b'+str(l+1)]
 
 return parameters

最后,是时候将所有功能放在一起了。我们现在将创建一个名为 train 的函数来训练我们的神经网络。

def train(X, Y, layer_dims, epochs, lr):
 params = init_params(layer_dims)
 cost_history = []
 
 for i in range(epochs):
 Y_hat, caches = forward_prop(X, params)
 cost = cost_function(Y_hat, Y)
 cost_history.append(cost)
 grads = backprop(Y_hat, Y, caches)
 
 params = update_parameters(params, grads, lr)
 
 
 return params, cost_history

此函数将在给定时间段内逐步执行上述所有函数。完成后,它将返回最终更新的参数和成本历史记录。成本历史(cost_history)可用于评估网络架构的整体性能。

神经网络

写在最后

非常感谢您阅读本文。由于这篇文章看起来比较复杂,建议大家尝试打开Pycharm或者其他编译软件进行实战。你可能会获得更有意义的想法。转发和点赞是对作者最大的帮助!

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发

请登录后发表评论