查看原文
其他

神经网络的学习之旅:探索“梯度下降”

王海华 模型视角 2023-09-05

想象一下,你正在爬山。你的目标是从山顶到达山脚,但你的眼睛被蒙住了,你只能感觉到脚下的坡度。每一步,你都会选择坡度最陡的方向前进,直到你觉得自己已经到达了山脚这个过程就像神经网络中的一个关键算法——梯度下降。

1. 神经网络: 一个数字的大脑

首先,让我们理解什么是神经网络。想象一个机器的大脑,它由成千上万个小单元组成,就 像我们大脑中的神经元。这些单元互相连接,传递和处理信息,帮助机器做出决策。

为了让机器学会做某件事情(比如识别手写数字),我们需要调整这些连接的强度,让它更 好地理解我们给它的数据。这就像是调整音响的各种旋钮,让音乐听起来更和谐。

2. 为什么要下山?

回到我们的爬山比喻,为什么我们要从山顶走到山脚呢? 在神经网络的世界里,山顶代表的是一个很差的网络,而山脚代表的是一个很好的网络。我们的目标是找到一种方法,让网络 从“差”变为“好”




但是,我们如何衡量“差”和“好”呢? 这就是成本函数的作用。它像一个指南针,告诉我们网络 现在的位置,以及我们应该朝哪个方向前进。数学上,我们可以描述这个差异为:

其中, 是网络的预测输出,而 是真实的标签。

3. 找到下山的最佳路径

我们知道要走到山脚,但具体怎么走呢? 这就需要了解坡度或斜率。在每一步,我们都会选 择坡度最陡的方向前进

在神经网络中,我们使用数学工具 (称为“稊度”) 来帮助我们找到这个方向。梯度就像一个指示器,告诉我们应该如何调整网络的连接,使其更好。数学上,梯度是这样计算的:

其中, 是成本函数的梯度,而 是网络的权重。

4.走一步,再走一步

每一次调整都是一个小步䅇。我们不想走得太快,否则可能会错过最佳路径。这就像是调整 音响的旋钮,我们不想转得太快,否则可能会错过最佳音效。




在神经网络中,这个步骤的大小称为“学习率”。学习率决定了我们每次调整的幅度。数学上, 我们的更新规则是:

其中, 是学习率,决定了每一步的大小。

5. 重复直到完美

我们会反复调整,直到网络变得足够好。这个过程可能需要很长时间,因为我们正在处理大 量的数据和复杂的模型。

但是,最终的结果是值得的。通过梯度下降,我们可以训练出强大的神经网络,帮助机器做出准确的决策。

6. 总结

神经网络的梯度下降就像是一个盲人爬山的故事。通过感觉脚下的坡度,我们可以找到下山 的最佳路径。同样,通过调整网络的连接,我们可以让机器更好地理解数据,做出更准确的 决策。

这个过程可能听起来很复杂,但其实它只是一个简单的迭代过程,目标是找到最佳的网络。通过梯度下降,我们可以训练出强大的机器学习模型,为人工智能的未来铺路。

附录-从头设计梯度下降算法

import numpy as np

def sigmoid(x):
    """Sigmoid activation function."""
    return 1 / (1 + np.exp(-x))

def sigmoid_derivative(x):
    """Derivative of the sigmoid function."""
    return x * (1 - x)

class NeuralNetwork:
    def __init__(self, input_size, hidden_size, output_size):
        # Initialize weights and biases
        self.weights_input_to_hidden = np.random.rand(input_size, hidden_size)
        self.weights_hidden_to_output = np.random.rand(hidden_size, output_size)
        self.bias_hidden = np.random.rand(1, hidden_size)
        self.bias_output = np.random.rand(1, output_size)
        self.learning_rate = 0.01
        
    def forward(self, x):
        """Forward pass through the network."""
        self.input = np.array([x])
        self.hidden_activation = sigmoid(np.dot(self.input, self.weights_input_to_hidden) + self.bias_hidden)
        self.output = sigmoid(np.dot(self.hidden_activation, self.weights_hidden_to_output) + self.bias_output)
        return self.output
    
    def backward(self, y):
        """Backward pass to update weights and biases."""
        output_error = y - self.output
        output_delta = output_error * sigmoid_derivative(self.output)
        
        hidden_error = output_delta.dot(self.weights_hidden_to_output.T)
        hidden_delta = hidden_error * sigmoid_derivative(self.hidden_activation)
        
        # Update weights
        self.weights_input_to_hidden += self.input.T.dot(hidden_delta) * self.learning_rate
        self.weights_hidden_to_output += self.hidden_activation.T.dot(output_delta) * self.learning_rate
        
        # Update biases
        self.bias_hidden += np.sum(hidden_delta, axis=0) * self.learning_rate
        self.bias_output += np.sum(output_delta, axis=0) * self.learning_rate
        
    def train(self, X, y, epochs):
        """Training the neural network using gradient descent."""
        for epoch in range(epochs):
            for xi, yi in zip(X, y):
                self.forward(xi)
                self.backward(yi)
                
    def predict(self, x):
        """Predicting using the trained neural network."""
        return self.forward(x)

# Sample usage with dummy data
X = np.array([[00],
              [01],
              [10],
              [11]])

y = np.array([[0], [1], [1], [0]])

nn = NeuralNetwork(input_size=2, hidden_size=4, output_size=1)
nn.train(X, y, epochs=10000)

predictions = nn.predict(X)
predictions

结果为

array([[[0.48384395],
        [0.50758334],
        [0.50515687],
        [0.51856939]]])

下面让我们解释代码的设计及其功能。

  1. 激活函数与其导数

我们首先定义了 sigmoid 函数和它的导数 sigmoid_derivative。sigmoid 函数是神经网络中常用的激活函数,它将任何输入转换为0到1之间的值。其导数用于反向传播过程中。

  1. 神经网络类定义

这个类表示了一个简单的三层神经网络:输入层、一个隐藏层和输出层。

初始化方法 init

  • 初始化权重和偏置:我们使用随机值初始化权重和偏置。这确保了网络开始时是随机的,随后的训练会调整这些值。

  • 设置学习率:学习率决定了在梯度下降过程中权重更新的步长。

  • 前向传播方法 forward

  • 输入数据通过网络进行传播,生成输出。

  • 输入数据乘以输入层到隐藏层的权重,然后加上偏置,并应用 sigmoid 激活函数。

  • 隐藏层的激活值乘以隐藏层到输出层的权重,然后加上偏置,并应用 sigmoid 激活函数,产生最终输出。

  • 反向传播方法 backward:这是训练神经网络的核心部分。首先,计算输出层的误差(实际输出与预期输出之间的差异)。然后,计算隐藏层的误差。使用这些误差值和 sigmoid_derivative 函数,计算权重和偏置的更新量。最后,根据学习率和更新量调整权重和偏置。

  • 训练方法 train 这个方法使用给定的数据和标签训练网络。对于每个纪元,它都会执行前向和反向传播。

  • 预测方法 predict 一旦网络被训练,这个方法可以用来根据给定的输入数据进行预测。

  1. 示例使用 我们使用了一个简单的数据集,包括四个输入和对应的输出。这实际上是一个逻辑异或(XOR)问题,经常用来作为神经网络的一个简单示例。

我们创建了一个神经网络实例,然后使用数据进行了10000个纪元的训练,并最终预测了输入数据的输出。


最后说一下封面的那幅图,我是采用AI生成的,关键词是“gradient decent” 结果就是这样了😂看上去蛮有意思的就留下了哈哈。

参考资料:
Sanderson, G. (2017, October 16). Gradient descent, how neural networks learn. Adapted by J. Pullen. 3Blue1Brown. Updated 2023, August 30. https://www.3blue1brown.com/lessons/gradient-descent

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存