【深度学习笔记】优化算法——梯度下降

news2025/1/24 14:34:40

梯度下降

🏷sec_gd

尽管梯度下降(gradient descent)很少直接用于深度学习,
但了解它是理解下一节随机梯度下降算法的关键。
例如,由于学习率过大,优化问题可能会发散,这种现象早已在梯度下降中出现。
同样地,预处理(preconditioning)是梯度下降中的一种常用技术,
还被沿用到更高级的算法中。
让我们从简单的一维梯度下降开始。

一维梯度下降

为什么梯度下降算法可以优化目标函数?
一维中的梯度下降给我们很好的启发。
考虑一类连续可微实值函数 f : R → R f: \mathbb{R} \rightarrow \mathbb{R} f:RR
利用泰勒展开,我们可以得到

f ( x + ϵ ) = f ( x ) + ϵ f ′ ( x ) + O ( ϵ 2 ) . f(x + \epsilon) = f(x) + \epsilon f'(x) + \mathcal{O}(\epsilon^2). f(x+ϵ)=f(x)+ϵf(x)+O(ϵ2).
:eqlabel:gd-taylor

即在一阶近似中, f ( x + ϵ ) f(x+\epsilon) f(x+ϵ)可通过 x x x处的函数值 f ( x ) f(x) f(x)和一阶导数 f ′ ( x ) f'(x) f(x)得出。
我们可以假设在负梯度方向上移动的 ϵ \epsilon ϵ会减少 f f f
为了简单起见,我们选择固定步长 η > 0 \eta > 0 η>0,然后取 ϵ = − η f ′ ( x ) \epsilon = -\eta f'(x) ϵ=ηf(x)
将其代入泰勒展开式我们可以得到

f ( x − η f ′ ( x ) ) = f ( x ) − η f ′ 2 ( x ) + O ( η 2 f ′ 2 ( x ) ) . f(x - \eta f'(x)) = f(x) - \eta f'^2(x) + \mathcal{O}(\eta^2 f'^2(x)). f(xηf(x))=f(x)ηf′2(x)+O(η2f′2(x)).
:eqlabel:gd-taylor-2

如果其导数 f ′ ( x ) ≠ 0 f'(x) \neq 0 f(x)=0没有消失,我们就能继续展开,这是因为 η f ′ 2 ( x ) > 0 \eta f'^2(x)>0 ηf′2(x)>0
此外,我们总是可以令 η \eta η小到足以使高阶项变得不相关。
因此,

f ( x − η f ′ ( x ) ) ⪅ f ( x ) . f(x - \eta f'(x)) \lessapprox f(x). f(xηf(x))f(x).

这意味着,如果我们使用

x ← x − η f ′ ( x ) x \leftarrow x - \eta f'(x) xxηf(x)

来迭代 x x x,函数 f ( x ) f(x) f(x)的值可能会下降。
因此,在梯度下降中,我们首先选择初始值 x x x和常数 η > 0 \eta > 0 η>0
然后使用它们连续迭代 x x x,直到停止条件达成。
例如,当梯度 ∣ f ′ ( x ) ∣ |f'(x)| f(x)的幅度足够小或迭代次数达到某个值时。

下面我们来展示如何实现梯度下降。为了简单起见,我们选用目标函数 f ( x ) = x 2 f(x)=x^2 f(x)=x2
尽管我们知道 x = 0 x=0 x=0 f ( x ) f(x) f(x)能取得最小值,
但我们仍然使用这个简单的函数来观察 x x x的变化。

%matplotlib inline
import numpy as np
import torch
from d2l import torch as d2l
def f(x):  # 目标函数
    return x ** 2

def f_grad(x):  # 目标函数的梯度(导数)
    return 2 * x

接下来,我们使用 x = 10 x=10 x=10作为初始值,并假设 η = 0.2 \eta=0.2 η=0.2
使用梯度下降法迭代 x x x共10次,我们可以看到, x x x的值最终将接近最优解。

def gd(eta, f_grad):
    x = 10.0
    results = [x]
    for i in range(10):
        x -= eta * f_grad(x)
        results.append(float(x))
    print(f'epoch 10, x: {x:f}')
    return results

results = gd(0.2, f_grad)
epoch 10, x: 0.060466

对进行 x x x优化的过程可以绘制如下。

def show_trace(results, f):
    n = max(abs(min(results)), abs(max(results)))
    f_line = torch.arange(-n, n, 0.01)
    d2l.set_figsize()
    d2l.plot([f_line, results], [[f(x) for x in f_line], [
        f(x) for x in results]], 'x', 'f(x)', fmts=['-', '-o'])

show_trace(results, f)


在这里插入图片描述

学习率

🏷subsec_gd-learningrate

学习率(learning rate)决定目标函数能否收敛到局部最小值,以及何时收敛到最小值。
学习率 η \eta η可由算法设计者设置。
请注意,如果我们使用的学习率太小,将导致 x x x的更新非常缓慢,需要更多的迭代。
例如,考虑同一优化问题中 η = 0.05 \eta = 0.05 η=0.05的进度。
如下所示,尽管经过了10个步骤,我们仍然离最优解很远。

show_trace(gd(0.05, f_grad), f)
epoch 10, x: 3.486784

在这里插入图片描述

相反,如果我们使用过高的学习率, ∣ η f ′ ( x ) ∣ \left|\eta f'(x)\right| ηf(x)对于一阶泰勒展开式可能太大。
也就是说, :eqref:gd-taylor中的 O ( η 2 f ′ 2 ( x ) ) \mathcal{O}(\eta^2 f'^2(x)) O(η2f′2(x))可能变得显著了。
在这种情况下, x x x的迭代不能保证降低 f ( x ) f(x) f(x)的值。
例如,当学习率为 η = 1.1 \eta=1.1 η=1.1时, x x x超出了最优解 x = 0 x=0 x=0并逐渐发散。

show_trace(gd(1.1, f_grad), f)
epoch 10, x: 61.917364

在这里插入图片描述

局部最小值

为了演示非凸函数的梯度下降,考虑函数 f ( x ) = x ⋅ cos ⁡ ( c x ) f(x) = x \cdot \cos(cx) f(x)=xcos(cx),其中 c c c为某常数。
这个函数有无穷多个局部最小值。
根据我们选择的学习率,我们最终可能只会得到许多解的一个。
下面的例子说明了(不切实际的)高学习率如何导致较差的局部最小值。

c = torch.tensor(0.15 * np.pi)

def f(x):  # 目标函数
    return x * torch.cos(c * x)

def f_grad(x):  # 目标函数的梯度
    return torch.cos(c * x) - c * x * torch.sin(c * x)

show_trace(gd(2, f_grad), f)
epoch 10, x: -1.528166

在这里插入图片描述

多元梯度下降

现在我们对单变量的情况有了更好的理解,让我们考虑一下 x = [ x 1 , x 2 , … , x d ] ⊤ \mathbf{x} = [x_1, x_2, \ldots, x_d]^\top x=[x1,x2,,xd]的情况。
即目标函数 f : R d → R f: \mathbb{R}^d \to \mathbb{R} f:RdR将向量映射成标量。
相应地,它的梯度也是多元的,它是一个由 d d d个偏导数组成的向量:

∇ f ( x ) = [ ∂ f ( x ) ∂ x 1 , ∂ f ( x ) ∂ x 2 , … , ∂ f ( x ) ∂ x d ] ⊤ . \nabla f(\mathbf{x}) = \bigg[\frac{\partial f(\mathbf{x})}{\partial x_1}, \frac{\partial f(\mathbf{x})}{\partial x_2}, \ldots, \frac{\partial f(\mathbf{x})}{\partial x_d}\bigg]^\top. f(x)=[x1f(x),x2f(x),,xdf(x)].

梯度中的每个偏导数元素 ∂ f ( x ) / ∂ x i \partial f(\mathbf{x})/\partial x_i f(x)/xi代表了当输入 x i x_i xi f f f x \mathbf{x} x处的变化率。
和先前单变量的情况一样,我们可以对多变量函数使用相应的泰勒近似来思考。
具体来说,

f ( x + ϵ ) = f ( x ) + ϵ ⊤ ∇ f ( x ) + O ( ∥ ϵ ∥ 2 ) . f(\mathbf{x} + \boldsymbol{\epsilon}) = f(\mathbf{x}) + \mathbf{\boldsymbol{\epsilon}}^\top \nabla f(\mathbf{x}) + \mathcal{O}(\|\boldsymbol{\epsilon}\|^2). f(x+ϵ)=f(x)+ϵf(x)+O(ϵ2).
:eqlabel:gd-multi-taylor

换句话说,在 ϵ \boldsymbol{\epsilon} ϵ的二阶项中,
最陡下降的方向由负梯度 − ∇ f ( x ) -\nabla f(\mathbf{x}) f(x)得出。
选择合适的学习率 η > 0 \eta > 0 η>0来生成典型的梯度下降算法:

x ← x − η ∇ f ( x ) . \mathbf{x} \leftarrow \mathbf{x} - \eta \nabla f(\mathbf{x}). xxηf(x).

这个算法在实践中的表现如何呢?
我们构造一个目标函数 f ( x ) = x 1 2 + 2 x 2 2 f(\mathbf{x})=x_1^2+2x_2^2 f(x)=x12+2x22
并有二维向量 x = [ x 1 , x 2 ] ⊤ \mathbf{x} = [x_1, x_2]^\top x=[x1,x2]作为输入,
标量作为输出。
梯度由 ∇ f ( x ) = [ 2 x 1 , 4 x 2 ] ⊤ \nabla f(\mathbf{x}) = [2x_1, 4x_2]^\top f(x)=[2x1,4x2]给出。
我们将从初始位置 [ − 5 , − 2 ] [-5, -2] [5,2]通过梯度下降观察 x \mathbf{x} x的轨迹。

我们还需要两个辅助函数:
第一个是update函数,并将其应用于初始值20次;
第二个函数会显示 x \mathbf{x} x的轨迹。

def train_2d(trainer, steps=20, f_grad=None):  #@save
    """用定制的训练机优化2D目标函数"""
    # s1和s2是稍后将使用的内部状态变量
    x1, x2, s1, s2 = -5, -2, 0, 0
    results = [(x1, x2)]
    for i in range(steps):
        if f_grad:
            x1, x2, s1, s2 = trainer(x1, x2, s1, s2, f_grad)
        else:
            x1, x2, s1, s2 = trainer(x1, x2, s1, s2)
        results.append((x1, x2))
    print(f'epoch {i + 1}, x1: {float(x1):f}, x2: {float(x2):f}')
    return results
def show_trace_2d(f, results):  #@save
    """显示优化过程中2D变量的轨迹"""
    d2l.set_figsize()
    d2l.plt.plot(*zip(*results), '-o', color='#ff7f0e')
    x1, x2 = torch.meshgrid(torch.arange(-5.5, 1.0, 0.1),
                          torch.arange(-3.0, 1.0, 0.1), indexing='ij')
    d2l.plt.contour(x1, x2, f(x1, x2), colors='#1f77b4')
    d2l.plt.xlabel('x1')
    d2l.plt.ylabel('x2')

接下来,我们观察学习率 η = 0.1 \eta = 0.1 η=0.1时优化变量 x \mathbf{x} x的轨迹。
可以看到,经过20步之后, x \mathbf{x} x的值接近其位于 [ 0 , 0 ] [0, 0] [0,0]的最小值。
虽然进展相当顺利,但相当缓慢。

def f_2d(x1, x2):  # 目标函数
    return x1 ** 2 + 2 * x2 ** 2

def f_2d_grad(x1, x2):  # 目标函数的梯度
    return (2 * x1, 4 * x2)

def gd_2d(x1, x2, s1, s2, f_grad):
    g1, g2 = f_grad(x1, x2)
    return (x1 - eta * g1, x2 - eta * g2, 0, 0)

eta = 0.1
show_trace_2d(f_2d, train_2d(gd_2d, f_grad=f_2d_grad))
epoch 20, x1: -0.057646, x2: -0.000073

在这里插入图片描述

自适应方法

正如我们在 :numref:subsec_gd-learningrate中所看到的,选择“恰到好处”的学习率 η \eta η是很棘手的。
如果我们把它选得太小,就没有什么进展;如果太大,得到的解就会振荡,甚至可能发散。
如果我们可以自动确定 η \eta η,或者完全不必选择学习率,会怎么样?
除了考虑目标函数的值和梯度、还考虑它的曲率的二阶方法可以帮我们解决这个问题。
虽然由于计算代价的原因,这些方法不能直接应用于深度学习,但它们为如何设计高级优化算法提供了有用的思维直觉,这些算法可以模拟下面概述的算法的许多理想特性。

牛顿法

回顾一些函数 f : R d → R f: \mathbb{R}^d \rightarrow \mathbb{R} f:RdR的泰勒展开式,事实上我们可以把它写成

f ( x + ϵ ) = f ( x ) + ϵ ⊤ ∇ f ( x ) + 1 2 ϵ ⊤ ∇ 2 f ( x ) ϵ + O ( ∥ ϵ ∥ 3 ) . f(\mathbf{x} + \boldsymbol{\epsilon}) = f(\mathbf{x}) + \boldsymbol{\epsilon}^\top \nabla f(\mathbf{x}) + \frac{1}{2} \boldsymbol{\epsilon}^\top \nabla^2 f(\mathbf{x}) \boldsymbol{\epsilon} + \mathcal{O}(\|\boldsymbol{\epsilon}\|^3). f(x+ϵ)=f(x)+ϵf(x)+21ϵ2f(x)ϵ+O(ϵ3).
:eqlabel:gd-hot-taylor

为了避免繁琐的符号,我们将 H = d e f ∇ 2 f ( x ) \mathbf{H} \stackrel{\mathrm{def}}{=} \nabla^2 f(\mathbf{x}) H=def2f(x)定义为 f f f的Hessian,是 d × d d \times d d×d矩阵。
d d d的值很小且问题很简单时, H \mathbf{H} H很容易计算。
但是对于深度神经网络而言,考虑到 H \mathbf{H} H可能非常大,
O ( d 2 ) \mathcal{O}(d^2) O(d2)个条目的存储代价会很高,
此外通过反向传播进行计算可能雪上加霜。
然而,我们姑且先忽略这些考量,看看会得到什么算法。

毕竟, f f f的最小值满足 ∇ f = 0 \nabla f = 0 f=0
遵循 :numref:sec_calculus中的微积分规则,
通过取 ϵ \boldsymbol{\epsilon} ϵ对 :eqref:gd-hot-taylor的导数,
再忽略不重要的高阶项,我们便得到

∇ f ( x ) + H ϵ = 0  and hence  ϵ = − H − 1 ∇ f ( x ) . \nabla f(\mathbf{x}) + \mathbf{H} \boldsymbol{\epsilon} = 0 \text{ and hence } \boldsymbol{\epsilon} = -\mathbf{H}^{-1} \nabla f(\mathbf{x}). f(x)+Hϵ=0 and hence ϵ=H1f(x).

也就是说,作为优化问题的一部分,我们需要将Hessian矩阵 H \mathbf{H} H求逆。

举一个简单的例子,对于 f ( x ) = 1 2 x 2 f(x) = \frac{1}{2} x^2 f(x)=21x2,我们有 ∇ f ( x ) = x \nabla f(x) = x f(x)=x H = 1 \mathbf{H} = 1 H=1
因此,对于任何 x x x,我们可以获得 ϵ = − x \epsilon = -x ϵ=x
换言之,单单一步就足以完美地收敛,而无须任何调整。
我们在这里比较幸运:泰勒展开式是确切的,因为 f ( x + ϵ ) = 1 2 x 2 + ϵ x + 1 2 ϵ 2 f(x+\epsilon)= \frac{1}{2} x^2 + \epsilon x + \frac{1}{2} \epsilon^2 f(x+ϵ)=21x2+ϵx+21ϵ2

让我们看看其他问题。
给定一个凸双曲余弦函数 c c c,其中 c c c为某些常数,
我们可以看到经过几次迭代后,得到了 x = 0 x=0 x=0处的全局最小值。

c = torch.tensor(0.5)

def f(x):  # O目标函数
    return torch.cosh(c * x)

def f_grad(x):  # 目标函数的梯度
    return c * torch.sinh(c * x)

def f_hess(x):  # 目标函数的Hessian
    return c**2 * torch.cosh(c * x)

def newton(eta=1):
    x = 10.0
    results = [x]
    for i in range(10):
        x -= eta * f_grad(x) / f_hess(x)
        results.append(float(x))
    print('epoch 10, x:', x)
    return results

show_trace(newton(), f)
epoch 10, x: tensor(0.)

在这里插入图片描述

现在让我们考虑一个非凸函数,比如 f ( x ) = x cos ⁡ ( c x ) f(x) = x \cos(c x) f(x)=xcos(cx) c c c为某些常数。
请注意在牛顿法中,我们最终将除以Hessian。
这意味着如果二阶导数是负的, f f f的值可能会趋于增加。
这是这个算法的致命缺陷!
让我们看看实践中会发生什么。

c = torch.tensor(0.15 * np.pi)

def f(x):  # 目标函数
    return x * torch.cos(c * x)

def f_grad(x):  # 目标函数的梯度
    return torch.cos(c * x) - c * x * torch.sin(c * x)

def f_hess(x):  # 目标函数的Hessian
    return - 2 * c * torch.sin(c * x) - x * c**2 * torch.cos(c * x)

show_trace(newton(), f)
epoch 10, x: tensor(26.8341)

在这里插入图片描述

这发生了惊人的错误。我们怎样才能修正它?
一种方法是用取Hessian的绝对值来修正,另一个策略是重新引入学习率。
这似乎违背了初衷,但不完全是——拥有二阶信息可以使我们在曲率较大时保持谨慎,而在目标函数较平坦时则采用较大的学习率。
让我们看看在学习率稍小的情况下它是如何生效的,比如 η = 0.5 \eta = 0.5 η=0.5
如我们所见,我们有了一个相当高效的算法。

show_trace(newton(0.5), f)
epoch 10, x: tensor(7.2699)

在这里插入图片描述

收敛性分析

在此,我们以部分目标凸函数 f f f为例,分析它们的牛顿法收敛速度。
这些目标凸函数三次可微,而且二阶导数不为零,即 f ′ ′ > 0 f'' > 0 f′′>0
由于多变量情况下的证明是对以下一维参数情况证明的直接拓展,对我们理解这个问题不能提供更多帮助,因此我们省略了多变量情况的证明。

x ( k ) x^{(k)} x(k)表示 x x x在第 k t h k^\mathrm{th} kth次迭代时的值,
e ( k ) = d e f x ( k ) − x ∗ e^{(k)} \stackrel{\mathrm{def}}{=} x^{(k)} - x^* e(k)=defx(k)x表示 k t h k^\mathrm{th} kth迭代时与最优性的距离。
通过泰勒展开,我们得到条件 f ′ ( x ∗ ) = 0 f'(x^*) = 0 f(x)=0可以写成

0 = f ′ ( x ( k ) − e ( k ) ) = f ′ ( x ( k ) ) − e ( k ) f ′ ′ ( x ( k ) ) + 1 2 ( e ( k ) ) 2 f ′ ′ ′ ( ξ ( k ) ) , 0 = f'(x^{(k)} - e^{(k)}) = f'(x^{(k)}) - e^{(k)} f''(x^{(k)}) + \frac{1}{2} (e^{(k)})^2 f'''(\xi^{(k)}), 0=f(x(k)e(k))=f(x(k))e(k)f′′(x(k))+21(e(k))2f′′′(ξ(k)),

这对某些 ξ ( k ) ∈ [ x ( k ) − e ( k ) , x ( k ) ] \xi^{(k)} \in [x^{(k)} - e^{(k)}, x^{(k)}] ξ(k)[x(k)e(k),x(k)]成立。
将上述展开除以 f ′ ′ ( x ( k ) ) f''(x^{(k)}) f′′(x(k))得到

e ( k ) − f ′ ( x ( k ) ) f ′ ′ ( x ( k ) ) = 1 2 ( e ( k ) ) 2 f ′ ′ ′ ( ξ ( k ) ) f ′ ′ ( x ( k ) ) . e^{(k)} - \frac{f'(x^{(k)})}{f''(x^{(k)})} = \frac{1}{2} (e^{(k)})^2 \frac{f'''(\xi^{(k)})}{f''(x^{(k)})}. e(k)f′′(x(k))f(x(k))=21(e(k))2f′′(x(k))f′′′(ξ(k)).

回想之前的方程 x ( k + 1 ) = x ( k ) − f ′ ( x ( k ) ) / f ′ ′ ( x ( k ) ) x^{(k+1)} = x^{(k)} - f'(x^{(k)}) / f''(x^{(k)}) x(k+1)=x(k)f(x(k))/f′′(x(k))
代入这个更新方程,取两边的绝对值,我们得到

∣ e ( k + 1 ) ∣ = 1 2 ( e ( k ) ) 2 ∣ f ′ ′ ′ ( ξ ( k ) ) ∣ f ′ ′ ( x ( k ) ) . \left|e^{(k+1)}\right| = \frac{1}{2}(e^{(k)})^2 \frac{\left|f'''(\xi^{(k)})\right|}{f''(x^{(k)})}. e(k+1) =21(e(k))2f′′(x(k)) f′′′(ξ(k)) .

因此,每当我们处于有界区域 ∣ f ′ ′ ′ ( ξ ( k ) ) ∣ / ( 2 f ′ ′ ( x ( k ) ) ) ≤ c \left|f'''(\xi^{(k)})\right| / (2f''(x^{(k)})) \leq c f′′′(ξ(k)) /(2f′′(x(k)))c
我们就有一个二次递减误差

∣ e ( k + 1 ) ∣ ≤ c ( e ( k ) ) 2 . \left|e^{(k+1)}\right| \leq c (e^{(k)})^2. e(k+1) c(e(k))2.

另一方面,优化研究人员称之为“线性”收敛,而将 ∣ e ( k + 1 ) ∣ ≤ α ∣ e ( k ) ∣ \left|e^{(k+1)}\right| \leq \alpha \left|e^{(k)}\right| e(k+1) α e(k) 这样的条件称为“恒定”收敛速度。
请注意,我们无法估计整体收敛的速度,但是一旦我们接近极小值,收敛将变得非常快。
另外,这种分析要求 f f f在高阶导数上表现良好,即确保 f f f在如何变化它的值方面没有任何“超常”的特性。

预处理

计算和存储完整的Hessian非常昂贵,而改善这个问题的一种方法是“预处理”。
它回避了计算整个Hessian,而只计算“对角线”项,即如下的算法更新:

x ← x − η d i a g ( H ) − 1 ∇ f ( x ) . \mathbf{x} \leftarrow \mathbf{x} - \eta \mathrm{diag}(\mathbf{H})^{-1} \nabla f(\mathbf{x}). xxηdiag(H)1f(x).

虽然这不如完整的牛顿法精确,但它仍然比不使用要好得多。
为什么预处理有效呢?
假设一个变量以毫米表示高度,另一个变量以公里表示高度的情况。
假设这两种自然尺度都以米为单位,那么我们的参数化就出现了严重的不匹配。
幸运的是,使用预处理可以消除这种情况。
梯度下降的有效预处理相当于为每个变量选择不同的学习率(矢量 x \mathbf{x} x的坐标)。
我们将在后面一节看到,预处理推动了随机梯度下降优化算法的一些创新。

梯度下降和线搜索

梯度下降的一个关键问题是我们可能会超过目标或进展不足,
解决这一问题的简单方法是结合使用线搜索和梯度下降。
也就是说,我们使用 ∇ f ( x ) \nabla f(\mathbf{x}) f(x)给出的方向,
然后进行二分搜索,以确定哪个学习率 η \eta η使 f ( x − η ∇ f ( x ) ) f(\mathbf{x} - \eta \nabla f(\mathbf{x})) f(xηf(x))取最小值。

有关分析和证明,此算法收敛迅速(请参见 :cite:Boyd.Vandenberghe.2004)。
然而,对深度学习而言,这不太可行。
因为线搜索的每一步都需要评估整个数据集上的目标函数,实现它的方式太昂贵了。

小结

  • 学习率的大小很重要:学习率太大会使模型发散,学习率太小会没有进展。
  • 梯度下降会可能陷入局部极小值,而得不到全局最小值。
  • 在高维模型中,调整学习率是很复杂的。
  • 预处理有助于调节比例。
  • 牛顿法在凸问题中一旦开始正常工作,速度就会快得多。
  • 对于非凸问题,不要不作任何调整就使用牛顿法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1495170.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

@RequestBody

目录 概述 深入细节 案例 RequestBody与前端传过来的json数据的匹配规则 指定模型中的属性对应什么key 用Valid校验RequestBody的参数 根据RequestBody的内容来区分使用哪个资源 概述 RequestBody主要用来接收前端传递给后端的json字符串中的数据(请求体中的数据)而最常…

AI资讯2024-03-06|超越GPT-4的大模型一夜来袭,霸主之位花落谁家

全球最强大模型一夜易主,GPT-4时代终结 新一代AI模型Claude 3系列的登场,如同一场风暴席卷AI领域。GPT-4被迅速抛入阴影,Anthropic的最新力作完胜天下,以无可匹敌之势彰显其强大。Claude 3在多模态和语言能力方面都凌驾于GPT-4之上…

Mendix 开发实践指南|Mendix 环境搭建

在上一篇文章中,我们深入探讨了Mendix平台的一些关键概念,从而获得了对Mendix模型驱动设计哲学的清晰理解。随着这些核心理念的掌握,我们自然会产生一系列实践层面的问题,尤其是关于如何开始使用Mendix进行开发的细节。 本篇文章…

【力扣 - 无重复字符的最长字符串】

题目描述 给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。 示例 1: 输入: s "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。 示例 2: 输入: s "bbbbb" 输出: 1 …

2024年最新Android面试精讲,面试题附答案

一. 开发背景 想要成为一名优秀的Android开发,你需要一份完备的知识体系,在这里,让我们一起成长为自己所想的那样。 Android 相关 1. Android 之 SharedPreferences 内部原理浅析 2. Android 源码分析-消息队列和 Looper 3. Android 源码分析…

状压dp详解,棋盘式、集合型,OJ详解

文章目录 零、引例-小国王1.问题描述2.暴力枚举3.多维dp4.维度压缩 一、状压dp1.认识状压dp2.棋盘式(基于连通性)2.1小国王2.1.1题目链接2.1.2思路分析2.1.3AC代码 2.2玉米田2.2.1题目链接2.2.2思路分析2.2.3AC代码 2.3炮兵阵地2.3.1题目链接2.3.2思路分析2.3.3AC代码 2.4蒙德里…

网络、网络协议模型、UDP编程——计算机网络——day01

今天来到了网络编程,主要讲了网络、网络协议模型以及UDP编程 网络 网络主要是进行:数据传输和数据共享 网络协议模型 OSI协议模型应用层 实际发送的数据表示层 发送的数据是否加密会话层 是否建立会话连接传…

激光炸弹c++

题目 输入样例: 2 1 0 0 1 1 1 1输出样例: 1 思路 由题知本题要求某个区间内数的和,联想到二维前缀和。我们可以先使用二维前缀和模板计算各区间的价值。然后枚举以某点为右下角,大小为R*R的正方形价值,取最大值。 …

06 - 镜像管理之:基础知识

1 了解镜像 Docker镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。 但注意, 镜像不包含任何动态数据&#…

【DataRoom】- 基于VUE的开源的大屏可视化设计器

【DataRoom】- 基于VUE的开源的大屏可视化设计器 DataRoom是一款基于SpringBoot、MyBatisPlus、Vue、ElementUI、G2Plot、Echarts等技术栈的大屏设计器,具备大屏设计、预览能力,支持MySQL、Oracle、PostgreSQL、SQLServer、ElasticSearch、JSON、JS、HTT…

springcloud:3.7测试线程池服务隔离

服务提供者【test-provider8001】 Openfeign远程调用服务提供者搭建 文章地址http://t.csdnimg.cn/06iz8 相关接口 测试远程调用:http://localhost:8001/payment/index 服务消费者【test-consumer-resilience4j8004】 Openfeign远程调用消费者搭建 文章地址http://t…

Dubbo的线程模型

1 线程模型概述 Dubbo默认的底层网络通信使用的是Netty。 服务提供方NettyServer使用两级线程池,其EventLoopGroup(boss)主要用来接收客户端的连接请求,并把完成TCP三次握手的连接分发给EventLoopGroup(worker&#…

【音视频开发好书推荐】RTC程序设计:实时音视频权威指南

目录 1、WebRTC概述2、好书推荐3、本书内容4、本书特色5、作者简介6、谁适合看这本书 1、WebRTC概述 WebRTC(Web Real-Time Communication)是一个由Google发起的实时音视频通讯C开源库,其提供了音视频采集、编码、网络传输,解码显…

【考研数学】基础660太难了?一个办法搞定660

觉得题目太难,大概率是题目超出了自己当前的水平 题型没见过,或者太复杂,属于跳级学习了,正确的思路就是回归到自己的水平线,题目略难即可。 这样做题的话,大部分题目涉及的点不会超出自己的能力范围&…

【详识JAVA语言】String 类1

String类的重要性 在C语言中已经涉及到字符串了,但是在C语言中要表示字符串只能使用字符数组或者字符指针,可以使用标准库提 供的字符串系列函数完成大部分操作,但是这种将数据和操作数据方法分离开的方式不符合面相对象的思想,而…

python之海龟绘图

海龟绘图(turtle)是一个Python内置的绘图库,也被称为“Turtle Graphics”或简称“Turtles”。它采用了一种有趣的绘图方式,模拟一只小海龟在屏幕上爬行,而小海龟爬行的路径就形成了绘制的图形。这种绘图方式最初源自20…

考研数学——高数:多元函数微分法及其应用

因为复习阶段全篇很细节的写下来一来比较费时间,二容易导致为了记笔记而记。 接下来的内容只会保留上课中比较有意义的地方,以及有自己助于理解的想法 全微分 助记: 证明是否可微,首先判断两个偏导数是否存在,不存在则…

插入排序和归并排序

插入排序&#xff0c;Insertion Sort. 给出伪代码 for i 1,2,...,n-1Insert A[i] into Sorted array A[0:i-1]by swaping down to the correct position. 冒泡排序 冒泡排序就是一种插入排序算法。 i ← 1 while i < length(A)j ← iwhile j > 0 and A[j-1] > A…

FlyClient SPV client轻量化

这篇文章主要是为了构建一种轻客户端的算法。 如果使用SPV 的方式验证交易&#xff0c;每个client上面需要存储非常多的header。使用 proofs of proof-of-work 的方式&#xff0c;使得请客户端仅仅下载少量的区块头就能验证这一条链的安全性&#xff0c;然后再对包含交易的区块…