6.深度学习入门:数值微分,梯度,学习算法的实现详细讲解

数值微分

导数

数值微分是一种计算数学方法,用于近似计算函数的导数。在数学中,导数是函数在某一点处的变化率。数值微分通过计算函数在该点附近的两个点的函数值来估计导数。
常见的数值微分方法包括前向差分后向差分中心差分。前向差分使用函数在当前点和稍后一点的函数值来估计导数,后向差分使用函数在当前点和稍前一点的函数值来估计导数,而中心差分使用函数在当前点和当前点左右两侧的函数值来估计导数。
数值微分方法在科学计算和工程应用中非常常见,特别是在数值求解微分方程和优化问题时。

数值微分的例子

下面是一个使用中心差分法进行数值微分的例子:
假设我们要计算函数 f ( x ) = x 2 f(x) = x^2 f(x)=x2 x = 1 x=1 x=1 处的导数。我们可以使用中心差分法来近似计算导数。具体步骤如下:

选择一个小的步长 h h h,例如 h = 0.01 h=0.01 h=0.01

计算函数在 x = 1 x=1 x=1 x = 1 + h x=1+h x=1+h 处的函数值:
f ( 1 ) = 1 2 = 1 f(1) = 1^2 = 1 f(1)=12=1
f ( 1 + h ) = ( 1 + h ) 2 = 1 + 2 h + h 2 f(1+h) = (1+h)^2 = 1 + 2h + h^2 f(1+h)=(1+h)2=1+2h+h2

使用中心差分法计算导数的近似值:
f ′ ( 1 ) ≈ f ( 1 + h ) − f ( 1 − h ) 2 h f'(1) \approx \frac{f(1+h) - f(1-h)}{2h} f(1)2hf(1+h)f(1h)
f ′ ( 1 ) ≈ ( 1 + 2 h + h 2 ) − ( 1 − 2 h + h 2 ) 2 h f'(1) \approx \frac{(1+2h+h^2) - (1-2h+h^2)}{2h} f(1)2h(1+2h+h2)(12h+h2)
f ′ ( 1 ) ≈ 4 h 2 h = 2 f'(1) \approx \frac{4h}{2h} = 2 f(1)2h4h=2

因此,我们得到了函数 f ( x ) = x 2 f(x) = x^2 f(x)=x2 x = 1 x=1 x=1 处的导数的近似值为 2 2 2

下面是一个使用前向差分法进行数值微分的例子:
假设我们要计算函数 f ( x ) = sin ⁡ ( x ) f(x) = \sin(x) f(x)=sin(x) x = 0 x=0 x=0 处的导数。我们可以使用前向差分法来近似计算导数。具体步骤如下:

选择一个小的步长 h h h,例如 h = 0.01 h=0.01 h=0.01

计算函数在 x = 0 x=0 x=0 x = h x=h x=h 处的函数值:
f ( 0 ) = sin ⁡ ( 0 ) = 0 f(0) = \sin(0) = 0 f(0)=sin(0)=0
f ( h ) = sin ⁡ ( h ) f(h) = \sin(h) f(h)=sin(h)

使用前向差分法计算导数的近似值:
f ′ ( 0 ) ≈ f ( h ) − f ( 0 ) h f'(0) \approx \frac{f(h) - f(0)}{h} f(0)hf(h)f(0)
f ′ ( 0 ) ≈ sin ⁡ ( h ) h f'(0) \approx \frac{\sin(h)}{h} f(0)hsin(h)

h h h 趋近于 0 0 0 时,我们可以使用泰勒公式将 sin ⁡ ( h ) \sin(h) sin(h) 展开成一阶泰勒多项式:
sin ⁡ ( h ) = h − h 3 3 ! + h 5 5 ! − h 7 7 ! + ⋯ \sin(h) = h - \frac{h^3}{3!} + \frac{h^5}{5!} - \frac{h^7}{7!} + \cdots sin(h)=h3!h3+5!h57!h7+
因此,
f ′ ( 0 ) ≈ sin ⁡ ( h ) h ≈ 1 − h 2 3 ! f'(0) \approx \frac{\sin(h)}{h} \approx 1 - \frac{h^2}{3!} f(0)hsin(h)13!h2
h = 0.01 h=0.01 h=0.01 时, f ′ ( 0 ) ≈ 0.999983 f'(0) \approx 0.999983 f(0)0.999983

因此,我们得到了函数 f ( x ) = sin ⁡ ( x ) f(x) = \sin(x) f(x)=sin(x) x = 0 x=0 x=0 处的导数的近似值为 0.999983 0.999983 0.999983

偏导数

偏导数是多元函数的导数的一种,它表示当函数的一个自变量变化时,函数的变化率。偏导数通常用符号 ∂ f ∂ x i \frac{\partial f}{\partial x_i} xif 表示,其中 f f f 是多元函数, x i x_i xi 是自变量中的一个。它表示在其他自变量保持不变的情况下,函数关于 x i x_i xi 的变化率。在二元函数的情况下,偏导数可以表示为 ∂ f ∂ x \frac{\partial f}{\partial x} xf ∂ f ∂ y \frac{\partial f}{\partial y} yf,分别表示函数关于 x x x y y y 的变化率。
偏导数在数学、物理、工程等领域中都有广泛的应用。例如,在物理学中,速度可以表示为位置关于时间的偏导数,加速度可以表示为速度关于时间的偏导数。在机器学习中,梯度下降算法使用偏导数来更新模型参数,以最小化损失函数。

梯度

梯度法

梯度是多元函数的偏导数向量,它表示函数在某一点上的最大变化率的方向。在二元函数的情况下,梯度可以表示为 ∇ f = ( ∂ f ∂ x , ∂ f ∂ y ) \nabla f = (\frac{\partial f}{\partial x}, \frac{\partial f}{\partial y}) f=(xf,yf),它是一个二维向量,指向函数在某一点上的最大变化率的方向。
梯度法是一种基于梯度的优化算法,它通过不断地沿着函数的梯度方向更新自变量的取值,来最小化或最大化函数。在机器学习中,梯度法通常用于更新模型参数,以最小化损失函数。
梯度法的基本思想是:

选择一个初始点 x 0 x_0 x0

计算函数在 x 0 x_0 x0 处的梯度 ∇ f ( x 0 ) \nabla f(x_0) f(x0)

沿着梯度的反方向更新自变量的取值,即
x t + 1 = x t − α ∇ f ( x t ) x_{t+1} = x_t - \alpha \nabla f(x_t) xt+1=xtαf(xt)
其中, t t t 表示迭代次数, α \alpha α 表示学习率,它控制每次更新的步长。

重复步骤 2 和 3,直到满足停止条件。

梯度法的优点是简单易懂、易于实现,但它也存在一些缺点。例如,它可能会陷入局部最优解,而无法找到全局最优解。此外,它对初始点的选择和学习率的调整非常敏感。

神经网络的梯度

  • 在神经网络中,梯度通常是指损失函数对于网络参数的偏导数向量。在反向传播算法中,我们通过计算损失函数对于每个参数的偏导数来更新网络参数。这些偏导数组成了一个梯度向量,它指示了损失函数在当前参数取值处的最大下降方向。
  • 具体来说,我们可以将神经网络看作是一个函数,它将输入映射到输出。损失函数是衡量网络输出与真实输出之间差异的度量。我们的目标是最小化损失函数,以使网络输出更接近真实输出。
  • 在反向传播算法中,我们首先计算损失函数对于网络输出的偏导数。然后,我们使用链式法则计算每个参数的偏导数,以获得整个网络的梯度向量。最后,我们使用梯度下降算法来更新参数,以最小化损失函数。
  • 由于神经网络通常具有大量的参数,计算整个网络的梯度可能非常耗时。因此,研究人员提出了许多优化技术,如随机梯度下降、批量梯度下降、动量法、自适应学习率等,以加快梯度计算和参数更新的速度。

学习算法的实现

层神经网络的类

下面是一个简单的 Python 类,用于实现具有多个隐藏层的神经网络。该类使用反向传播算法来训练网络,并使用梯度下降算法来更新网络参数。您可以根据需要对其进行修改和扩展。

import numpy as np

class NeuralNetwork:
    def __init__(self, layers, learning_rate=0.1):
        self.layers = layers
        self.learning_rate = learning_rate
        self.weights = []
        self.biases = []
        for i in range(1, len(layers)):
            w = np.random.randn(layers[i], layers[i-1])
            b = np.zeros((layers[i], 1))
            self.weights.append(w)
            self.biases.append(b)

    def sigmoid(self, z):
        return 1 / (1 + np.exp(-z))

    def sigmoid_derivative(self, z):
        return self.sigmoid(z) * (1 - self.sigmoid(z))

    def forward(self, x):
        a = x
        for w, b in zip(self.weights, self.biases):
            z = np.dot(w, a) + b
            a = self.sigmoid(z)
        return a

    def backward(self, x, y):
        a = x
        activations = [a]
        zs = []
        for w, b in zip(self.weights, self.biases):
            z = np.dot(w, a) + b
            zs.append(z)
            a = self.sigmoid(z)
            activations.append(a)

        delta = (activations[-1] - y) * self.sigmoid_derivative(zs[-1])
        for i in range(len(self.weights)-1, -1, -1):
            dw = np.dot(delta, activations[i].T)
            db = delta
            delta = np.dot(self.weights[i].T, delta) * self.sigmoid_derivative(zs[i-1])
            self.weights[i] -= self.learning_rate * dw
            self.biases[i] -= self.learning_rate * db

    def train(self, X, y, epochs):
        for i in range(epochs):
            for j in range(len(X)):
                x = X[j].reshape(-1, 1)
                y_true = y[j].reshape(-1, 1)
                self.backward(x, y_true)

    def predict(self, X):
        y_pred = []
        for x in X:
            y = self.forward(x.reshape(-1, 1))
            y_pred.append(y)
        return np.array(y_pred)

该类的构造函数接受一个列表,其中包含每个层的神经元数量。例如,如果您想创建一个具有 2 个输入、3 个隐藏神经元和 1 个输出神经元的神经网络,则可以使用以下代码:

net = NeuralNetwork([2, 3, 1])

该类包含以下方法:

  • sigmoid(z):Sigmoid 激活函数。
  • sigmoid_derivative(z):Sigmoid 函数的导数。
  • forward(x):前向传播函数,根据输入 x 计算输出。
  • backward(x, y):反向传播函数,根据输入 x 和目标输出 y 更新网络参数。
  • train(X, y, epochs):训练函数,使用输入 X 和目标输出 y 训练网络,共进行 epochs 轮训练。
  • predict(X):预测函数,根据输入 X 预测输出。

可以使用以下代码来训练和测试该神经网络:

X = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
y = np.array([0, 1, 1, 0])
net = NeuralNetwork([2, 3, 1])
net.train(X, y, epochs=10000)
y_pred = net.predict(X)
print(y_pred)

mini-batch的实现

Mini-batch 是一种在训练神经网络时使用的优化技术,它可以加速梯度下降算法的收敛速度。它的基本思想是将训练数据集分成多个小批次,每次使用一个小批次来更新网络参数,而不是使用整个数据集。这样可以减少每次参数更新的计算量,加快训练速度。

下面是一个使用 mini-batch 的神经网络类的实现示例:

import numpy as np

class NeuralNetwork:
    def __init__(self, layers, learning_rate=0.1):
        self.layers = layers
        self.learning_rate = learning_rate
        self.weights = []
        self.biases = []
        for i in range(1, len(layers)):
            w = np.random.randn(layers[i], layers[i-1])
            b = np.zeros((layers[i], 1))
            self.weights.append(w)
            self.biases.append(b)

    def sigmoid(self, z):
        return 1 / (1 + np.exp(-z))

    def sigmoid_derivative(self, z):
        return self.sigmoid(z) * (1 - self.sigmoid(z))

    def forward(self, x):
        a = x
        for w, b in zip(self.weights, self.biases):
            z = np.dot(w, a) + b
            a = self.sigmoid(z)
        return a

    def backward(self, x_batch, y_batch):
        dw = [np.zeros(w.shape) for w in self.weights]
        db = [np.zeros(b.shape) for b in self.biases]
        for x, y in zip(x_batch, y_batch):
            a = x
            activations = [a]
            zs = []
            for w, b in zip(self.weights, self.biases):
                z = np.dot(w, a) + b

基于测试数据的评价

在机器学习中,我们通常需要使用测试数据来评估模型的性能。以下是几种常见的基于测试数据的评价指标:

  • 准确率(Accuracy):准确率是分类模型中最常用的评价指标之一。它表示模型在测试数据集上正确分类的样本数占总样本数的比例。
  • 精确率(Precision):精确率是指模型在预测为正类别的样本中,实际为正类别的比例。它的计算公式为:Precision = TP /(TP + FP),其中 TP 表示真正类别的样本数,FP 表示预测为正类别但实际为负类别的样本数。
  • 召回率(Recall):召回率是指实际为正类别的样本中,被模型预测为正类别的比例。它的计算公式为:Recall = TP / (TP +FN),其中 TP 表示真正类别的样本数,FN 表示实际为正类别但被预测为负类别的样本数。
  • F1 值(F1-score):F1 值是精确率和召回率的调和平均数,它综合了两者的性能表现。它的计算公式为:F1-score = 2 *
    Precision * Recall / (Precision + Recall)。
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值