PyTorch深度学习实战 | 神经网络的优化难题

news2024/12/23 18:49:36
即使我们可以利用反向传播来进行优化,但是训练过程中仍然会出现一系列的问题,比如鞍点、病态条件、梯度消失和梯度爆炸,对此我们首先提出了小批量随机梯度下降,并且基于批量随机梯度下降的不稳定的特点,继续对其做出方向和学习率上的优化。

01、局部极小值,鞍点和非凸优化

基于梯度的一阶和二阶优化都在梯度为零的点停止迭代,梯度为零的点并非表示我们真的找到了最佳的参数,更可能是局部极小值或者鞍点,在统计学习的大部分问题中,我们似乎并不关心局部极小值和全局最小值的问题,这是因为统计学习的损失函数经过设计是一个方便优化的凸函数,会保证优化问题是一个凸优化问题。

在凸优化问题中,比如最小二乘和线性约束条件下的二次规划,参数空间的局部最小值必定是全局最小值。但对于神经网络这样复杂的参数空间,损失函数就不再是一个凸函数,如图1,非凸函数的局部极小值可能与全局极小值相去甚远,那么在理论上就无法保证一定会找到全局极小值。

■ 图1损失函数在二维参数空间的可视化,(a)为典型的凸函数,(b)为非凸函数

但是我们并不用担心这样的问题,优化停止在局部极小值也是非常困难的,因为在高维参数空间中,局部极小值的海森矩阵必须是正定的,也就是说每个维度上的二阶导数都必须为正,要陷入真正的局部极小值也是很困难的。我们可以假设某一维度的二阶导数为正的概率为s,那么在一个d维的参数空间的,找到局部极小值概率就是sd,可以看出局部极小值随着参数空间维度的增加,概率指数级下降。另一方面,目前的主流观点认为,局部极小值也具有小的损失函数,优化的目的只需要将损失函数降到足够低的水平,所以即便找到了局部极小值,但是损失函数已经降低到了足够低的水平,也是可以接受的。从理论上来说,真正容易陷入的是鞍点,鞍点的存在条件更为宽松,因为它在各个维度上二阶导数有正有负。虽然有实验表明,基于梯度下降的算法可以逃离鞍点,但在理论上并无保证,面对更广泛的场景,单纯的梯度下降对于鞍点的表现仍然是一个需要证明的问题。

02、随机梯度下降的优势

一个好的优化算法,一方面我们希望它迭代更新的次数越少越好,最好能一步到位;另一方面我们希望计算的代价要小,也就是利用的信息要少。但这两者是无法兼得的,单纯的梯度下降只需要计算梯度,但在每一步下降只局限于大小为ε的局部方向,迭代更新的次数较多,而牛顿法这样的二阶优化,利用了海森矩阵包含的曲率信息,迭代更新的每一个点都可以保证是极值点或者鞍点,使得一般情况下的迭代更新次数更少,但是每次迭代时需要计算海森矩阵的逆。

如果我们想在牛顿法的基础上进一步的减小信息量,就可以考虑BFGS(Broyden-FletcherGoldfalb-Shanno)这种著名的拟牛顿法,它并没有直接计算海森矩阵的逆,而是采取向量的乘积和矩阵的加法来代替了逆的计算,使得计算量进一步下降。但即便这样,深度学习中模型参数和训练数据的庞大,使得耗时仍然较为严重。

转向对梯度下降法的改良时,我们首先会注意到数据集内样本的冗余,很多样本太相似了,产生的损失也是相似的,对梯度估计的贡献也就是相似的,比如说我们只是采取复制的操作去扩充数据集,那么经过一次迭代,计算量翻了一番,但是得到的参数更新却是相同的。

我们完全可以采用少且有效的信息去替代冗余的信息,每次计算梯度时,我们不采用全部的样本,而是选择一个或者一批样本来进行梯度的估计,前者为随机梯度下降(Stochastic Gradient Descent),后者为小批量梯度下降(mini-batch gradient descent),在大部分情况下,人们会混用它们,统一叫作随机梯度下降。它还会带来两个好处:

(1) 小批量的梯度下降或者随机梯度下降,即便增加了迭代次数,但计算的代价少了,总体的效率仍然很高。

(2) 随机的挑选小批量的样本一定程度上增加了梯度估计的方差(噪声项),反而使得梯度下降有机会跳出局部最小值和鞍点。

那么随机梯度下降的参数更新公式就由原来的:

变为了

当使用随机梯度下降时,虽然可以更好地逃离局部最小值或者鞍点,但也会存在下降到真正的极小值(如果存在极小值的话),仍然无法停止迭代,但此时的梯度也会变得很小,所以会看到随机梯度下降并不会停留在极小值,而是在其附近微小振荡。另外实验表明,随机梯度下降会在迭代初期性能远远超越使用全部样本的梯度下降。但是,小批量随机梯度下降会引入一个额外的参数,就是批量的大小。小的批量可能会更有助于逃离梯度为零的点,但是迭代的次数却增加了,而大批量的效果恰恰相反,在应用中,它仍然是一个根据实际情况来指定的参数。

03、梯度方向优化

虽然梯度是所有方向导数变化最大的,但是因为小批量随机梯度的方差较大,表现为每一次的梯度方向都不相同,多次的迭代都被浪费在了来回移动上面;甚至只是因为初始值的选取,使得在迭代一开始所产生的梯度就无法朝向最佳的参数。如图2,初始值和小批量使得每一步的梯度下降方向并不一致,而是在损失函数的多个contour之间来回移动。本质上,这是因为梯度下降是一个贪心算法。

■ 图2 梯度下降算法的表现,(a)为理想情形,(b)为可能的实际情况

改善该问题的方法之一就是在执行梯度下降的时候,将每一步的下降都尽量积累在同一个方向上。动量(Momentum)算法引入了一个量叫作速度,用来积累以前的梯度信息:

这种做法是出于物理上的考虑,损失函数的负梯度就相当于物理上的势的负梯度,我们把它叫作力,根据矢量加法,就可以直观地看出,累加的结果就是把每一次力的相同方向加大,而相反的方向相互抵消,使得速度在每次力的相同方向都越来越大,本质上就是对梯度的方向做了归一化,然后我们选择利用速度去更新参数:

让我们来考虑极端的情况,如果每次的梯度的方向一致,那么ν就会沿着一个方向越来越大,参数μ叫作动量因子,用来调节过去积累梯度与现在梯度的比重,μ越大,那么过去的信息占的比重越大,本质上是一种加权移动平均法。参数的更新幅度不再单单只取决于当前的梯度,而是决定于当前梯度和历史梯度的加权平均。

同时,我们也可以在梯度计算之前就在参数中添加速度项:

这样就得到了Nesterov动量算法,简单来看只是添加的步骤不同,并且在循环中,标准的动量算法迟早都会执行参数更新后的梯度,事实上,两者有着根本的不同。

如果我们做一个简单的矢量加法,如图3,它们两者的主要区别在于梯度的计算,当我们需要更新θi+1时,Nesterov算法给出的更新方向是基于g(θi+1),表明在累积梯度方向信息时,会把当前参数梯度信息也计算进去,显得更为合理。

■ 图3(a)为动量法的参数更新示意,(b)为Nesterov动量的参数更新示意

04、动态调整学习率

动量算法一定程度上加速了梯度下降,但是却引入了另一个超参数μ,更重要的是,实践发现,同样作为超参数,α远远没有学习率ε重要。利用泰勒展开式对梯度下降的表现作出了评估,在曲率特别大的地方,固定学习率可能会导致损失上升,我们就无法迭代到极小值。

所以我们希望动态地调整学习率,使得学习率在梯度很大的时候,变得小一些,不至于错过极小值,在梯度很小的时候变得大一点,使得在损失平坦的地方走得更快。这就是所谓的自适应学习率算法。

首先我们可以设想,优化算法是可以良好工作的,这意味着随着迭代次数的增加,它会越来越接近极小值,学习率应该越来越小,不容易错过极小值。所以很直接想法就是直接设置学习率随迭代次数的衰减(learning rate decay),比如指数衰减:

上式的β小于1,为衰减率,t为迭代次数。此外我们也可以设置其他的函数来描述这一衰减的过程。可是,学习率的指数衰减率一般都要事先指定,我们进一步的设想,这一衰减率可否从优化过程中自动学习到,同时每个参数对于梯度下降贡献不同,很多情况下,我们希望在一个参数方向上下降得快一点,而在另一个参数方向上下降得慢一点。

一个自然的想法就是,我们想在损失陡峭的时候走得慢一点,梯度大的时候,学习率应该小一点,也即,偏导数大的参数,学习率应该小一点,同时也想在平坦的区域走得快一点,梯度小的时候,学习率应该大一些。那我们如何知道这是一个平缓或者陡峭的区域呢?AdaGrad算法使用一个量来累计历史梯度:

其中,° 是哈达玛积(Hadamard product),见定义 1,这里使用哈达玛积是因为,我们需要分开处理每个维度上的梯度信息。

定理1 (哈达玛积):哈达玛积区别于一般的矩阵乘法,假设A和B是相同维度的矩阵,A和B的哈达玛积被定义为:

接着,在学习率上乘以梯度的倒数,表示学习率与梯度的反比关系:

其中δ是非常小的常数,避免分母为零。它简单实现了动态调整学习率衰减的目标。但它积累了全部的历史梯度,使得学习率过早下降,可能永远无法迭代到最佳值。一种改进的方法就是,给不同时期的梯度赋予权重因子,使得较早的梯度不重要,而较近的梯度更重要:

参数α是权重因子,用来调节历史梯度和当前梯度的权重。这样就得到了RMSProp算法。在此基础上,我们希望将动量算法这种针对梯度方向的优化和RMSProp这种自适应调节学习率的算法结合起来,结合两者的优点,相当于对动量算法提供的“速度”提供了修正。

动量算法其实就是将每次的速度作为参数的更新,即:

我们将RMSProp对于学习率的更新加入,使得更新的速度可以动态调整:

这就得到了Adam算法的基本形式。动量使用的是梯度的一阶信息,而自适应学习率算法使用了哈达玛积,也就是梯度的二阶信息,所以有些书中会把Adam算法称为一阶矩和二阶矩结合起来的算法。

05、使用keras

我们已经详细讨论了一维参数空间中梯度下降和牛顿法的表现,并且也看到了学习率和海森矩阵对于学习算法的影响,下面来讨论基于梯度下降算法的各类优化算法。首先我们构造一个简单的损失函数:

此时的损失是两个参数的函数,并且它是一个凸函数,对其使用梯度下降算法:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import seaborn as sns

def f(x,y):
    return x**2+y**2 
def partial_x(x,y):
    return 2*x
def partial_y(y,x):
    return 2*y

def GD(lr, start, iterations):
    x, y= start[0],start[1]
    GD_x, GD_y ,GD_z= [], [],[]
    for it in range(iterations):
        GD_x.append(x)
        GD_y.append(y)
        GD_z.append(f(x,y))
        dx = partial_x(x,y)
        dy = partial_y(y,x)
        x = x - lr * dx
        y = y - lr * dy

    return(GD_x, GD_y, GD_z)

def plot_track(learning_rate, iterations):
    GD_x, GD_y, GD_z=GD(lr= learning_rate, start=[15,0.1],
        iterations = iterations)
    a = np.linspace(-20,20,100)
    b = np.linspace(-20,20,100)
    A,B = np.meshgrid(a,b)
    sns.set(style = 'white')
    plt.contourf(A, B, f(A,B), 10, alpha=0.8, cmap=cm.Greys)
    plt.scatter(GD_x, GD_y, c = 'red', alpha = 0.8, s = 20)
    u = np.array([GD_x[i+1]- GD_x[i] for i in     range(len(GD_x)-1)])
    v = np.array([GD_y[i+1]- GD_y[i] for i in range(len(GD_y)-1)])
         plt.quiver(GD_x[:len(u)], GD_y[:len(v)], u, v, angles='xy', width=0.005, \
         scale_units='xy', scale =1 ,alpha=0.9, color = 'k')
plt.xlabel('x')
    plt.ylabel('y')
    plt.title('learningrate:{}
        iterations:{}'.format(round(learning_rate,2),iterations))
    plt.show()

plot_track(learning_rate = pow(2, -7)*16, iterations = 100)

如图4,梯度下降在简单的二维参数空间上快速迭代,它说明了梯度确实是下降最快的方向,并且在这样的凸函数中,两个参数是对称的,我们无论从哪一个初始点出发,只要使用相同的学习率,那么参数更新轨迹总是从起始点直接到达目标点。

■ 图4 梯度下降在二维参数空间的表现

一个有趣的想法就是我们可以通过设置参数学习率不同来改变其运动轨迹,因为参数具有对称性,梯度必然保持一致,根据梯度下降算法的参数更新公式Δθ=-ε▽L,影响更新大小就只有学习率,所以我们将参数的学习率设置为不同:

def GD(lr, start, iterations):
x, y= start[0],start[1]
GD_x, GD_y ,GD_z= [], [],[]
for it in range(iterations):
    GD_x.append(x)
    GD_y.append(y)
    GD_z.append(f(x,y))
    dx = partial_x(x,y)
    dy = partial_y(y,x)
    x = x - lr[0] * dx
    y = y - lr[1]* dy

return(GD_x,GD_y,GD_z)

.....

plot_track(learning_rate = [pow(2,-7)*16, 0.015], iterations = 50)
plot_track(learning_rate = [0.015, pow(2,-7)*16], iterations = 50)

在上段代码中,我们将学习率设置为一个列表,以便于传入不同的数值。如图5,对于对称的参数梯度,学习率的不同会显著影响学习的过程,以(a)为例,变量x的学习率稍大一点,所以其更新幅度大于变量y的更新幅度,在x方向上会迅速迭代到损失对于x偏导为零的点,而y方向几乎没有什么变化。此时,x方向因为偏导为零,就不会发生迭代,反而在y方向上的偏导不为零,所以后期的轨迹就是沿着y方向缓慢迭代。(b)恰恰相反。

■ 图5 (a)为变量x的学习率稍大一点的结果,(b)为变量y的学习率稍大一点儿的结果

事实上,我们可以将这个例子完全反过来,在一般的损失函数中,在学习率相同的情况下,两个参数的梯度不同,造成参数的更新幅度也并不相同,所以我们经常看到使用梯度下降会在参数空间迂回前进,这也同样解释了随机梯度下降的迂回前进,因为每个batch所估计的梯度也不相同,一定程度上加剧了迂回的程度,所以会看到batch越小,损失函数会在前期振荡得越剧烈。

所以在这里,相同参数梯度下学习率的差异与相同学习率下参数梯度的差异是等价的。因为参数更新方向的差异,我们对更新轨迹的方向尝试做优化,考虑动量法,构建算法为:

def Momentum(lr, a, v_init, start, iterations):
    x, y = start[0], start[1]
    v = v_init
    M_x , M_y, M_z= [], [], []
    for it in range(iterations):
        M_x.append(x)
        M_y.append(y)
        M_z.append(f(x, y))
        v = a * v - [partial_x(x, y)*lr[0], partial_y(y, x)*lr[1]]
        x = x + v[0]
        y = y + v[1]
return(M_x, M_y, M_z)

.....

在差异化的学习率下调用动量算法,并将动量因子设置为0.9,这表明当前参数的更新会极大地依赖于历史梯度如图6,一开始的梯度较大,所以参数在逐渐靠近极小值的时候速度并不为零,所以并不会立刻停下来,在远离极小值的时候速度逐渐减小,最终停留在极小值点。所以从图中可以看出,更新轨迹就像牛顿力学下的粒子在一个碗中滑动。

■ 图6 动量因子为0.9,初始速度为(1,1),动量算法的更新轨迹

同时我们想得到用于参数更新的速度随迭代的变化,那么我们修改动量算法返回累计的速度信息,并且绘制出两个方向的速度与迭代次数的关系:

def Momentum(lr, a, v_init, start, iterations):
    x, y = start[0], start[1]
    v = v_init
    M_x , M_y, M_z, M_v = [], [], [], []
    for it in range(iterations):
        M_x.append(x)
        M_y.append(y)
        M_z.append(f(x, y))
        M_v.append(v)
        v = a * v - [partial_x(x, y)*lr[0], partial_y(y, x)*lr[1]]
        x = x + v[0]
        y = y + v[1]
return(M_x, M_y, M_z, M_v)

def plot_moment_v(learning_rate, iterations, v_init, a):
    M_x, M_y, M_z, M_v = Momentum(lr = learning_rate, a = a,
         v_init = v_init, start = [15, 15], iterations = iterations)
    plt.figure()
    plt.plot(range(iterations),[i[0] for i inM_v],label='x', linewidth
         = 3)
    plt.plot(range(iterations),[i[1] for iinM_v],label='y', linewidth
         = 3)
    plt.xlabel('iterations')
    plt.ylabel('$\nv$')
    plt.title('Momentum v at $\mu=$ {}'.format(a))
    plt.legend()
    plt.show()

plot_moment_v(learning_rate = [pow(2, -7)*16, 0.015], a = 0.9, v_init =np.array([1,1]),
    iterations = 80)

如图7,x方向和y方向的速度具有相同的初值,所以它们从同一个起点出发。由于x方向的学习率较大,所以迅速产生了较大的速度,随着迭代的进行,越来越靠近极值点,梯度逐渐减小,但速度却在不断增大,当梯度的方向变化后,速度才会逐渐减小。所以x方向上速度的变化就像牛顿力学下谐振子的速度,极值点附近的速度反而是最大的。y方向上的速度变化与x方向的道理相同,只是幅度较小。

■ 图7 动量因子为0.9,动量算法速度的变化

在这个例子中我们可以预想到,因为一开始的梯度较大,所以稍微减小动量因子,使得在靠近极小值的时候速度变小,就可以更快地到达目标点。如图3.8,设置动量因子为0.8,减弱了历史较大梯度的影响,迭代得更快。

■ 图8 动量因子为0.8,初始速度为(1,1),动量算法的更新轨迹

另一个角度就是对于学习率本身的动态调整。在使用RMSprop算法之前,因为它提供了学习率衰减,所以为了避免学习率过早地衰减到零,我们一开始就将学习率设置的大一点,使得它在初期迭代得快一点;另一方面为了很直观地看到不同参数所积累的梯度信息,我们将初始的参数点设置为(15,7.5),代码如下:

def RMSProp(lr, d, ro, start, iterations):
    x, y = start[0], start[1]
    r = np.array([0, 0])
    RMS_x, RMS_y, RMS_z, RMS_r = [], [], [], []
    for it in range(iterations):
        RMS_x.append(x)
        RMS_y.append(y)
        RMS_z.append(f(x, y))
        RMS_r.append(r)
        g = np.array([partial_x(x, y), partial_y(y, x)])
        r = ro * r + (1 - ro) * g * g
        lr[0] = lr[0] /(d + np.sqrt(r[0]))
        lr[1] = lr[1] /(d + np.sqrt(r[1]))
        x = x - lr[0] * g[0]
        y = y - lr[1] * g[1]
    return(RMS_x, RMS_y, RMS_z, RMS_r)

.....

我们在构建RMSprop算法时,使用了numpy的两个数组直接相乘来得到哈达玛积,结果如图9,即使我们设置了很大的初始学习率,RMSprop能很快地迭代,而不会产生震荡。但是它并未迭代到最低点就几乎停止了,这是因为一开始的梯度较大,学习率过早衰减,那么也就很难到达目标点。

■ 图 9 μ=0.9时,RMSprop算法的更新轨迹

我们在上述构建的算法函数中还将累计的梯度信息返回,这样是为了方便我们对累积梯度进行观察:

def plot_hadamard(learning_rate, iterations, ro, start):

    RMS_x, RMS_y,RMS_z, RMS_r = RMSProp(lr= learning_rate, d = 1e-6, ro = ro,\
        start = start, iterations = iterations)
    plt.figure()
    plt.plot(range(iterations), [i[0] for i in RMS_r], label ='x',
        linewidth = 3)
    plt.plot(range(iterations), [i[1] for i in RMS_r], label ='y',
        linewidth = 3)
    plt.xlabel('iterations')
    plt.ylabel('$g \circ g$')
    plt.title('gradient hadamard product')
    plt.legend()
    plt.show()

plot_hadamard(learning_rate = [pow(2, -2)*16, pow(2, -2)*16 ], \
            iterations = 30, ro = 0.9, start = [15, 7.5])

如图10,x方向的参数梯度的哈达玛积随着迭代先增加后减少,这说明一开始的梯度比较大,在该方向上参数产生了一个较大的更新以后,梯度迅速减小。由于使用了0.9的移动平均来积累梯度信息,后面的梯度如果小于前面梯度的,梯度就会缓慢减小。y方向的参数梯度的哈达玛积随着迭代缓慢增加,这意味着一开始的梯度较小,并且算法在该方向上迭代的次数更多。这一现象与参数初始点(15,7.5)是对应的,损失函数在初始点在x方向上具有更大的偏导。

■ 图10 μ=0.9时,RMSprop 算法下不同参数梯度累积随迭代的变化

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

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

相关文章

SpringBoot集成Dubbo启用gRPC协议

文章目录前言项目结构代码示例父工程api moduleservice module注意事项区别本文记录下SpringBoot集成Dubbo启用gRPC协议,以及与原生 gRPC 在代码编写过程中的区别。 下面还有投票,帮忙投个票👍 前言 Dubbo 在 2.7.5 版本开始支持原生 gRPC 协…

【软件测试】浅了解什么是软件测试及开发测试模型

目录 1.什么是软件测试? 2.什么是需求? 3.什么是测试用例 4.什么是软件错误(bug)? 5.开发模型和测试模型 5.1软件的生命周期 5.2瀑布模型 5.3螺旋模型 5.4软件测试V模型 5.5软件测试w模型 6.软件测试的生命周…

Redis第二十九讲 Redis集群发布订阅模式以及Redis集群事务

Redis集群状态下的发布订阅 在Redis的几个基本数据结构介绍中,有讲过List数据结构,可以使用List的阻塞特性实现订阅消费,关于Redis的底层数据结构可以参考我的这篇博客:Redis第六讲 Redis之List底层数据结构实现 底层数据结构基本操作可以看我的这篇博客,Redis第十五讲 R…

【从零到Offer】- 泛型

泛型是个啥 ​ Java 泛型是 JDK 5 中引入的一个新特性,其提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。 ​ 通过将数据类型参数化&#xf…

用opencv+playwright过滑动验证码

目录 梳理思路 编写代码 总结与提高 在本节,我们将使用opencv和playwright这两个库通过QQ空间的滑动验证码。 梳理思路 1. 使用playwright打开浏览器,访问qq空间登录页面。 2. 点击密码登录。 3. 输入账号密码并点击登录。 4. 出现滑动验证码图片后…

大型语言模型综述,非常详细,格局打开!A Survey of Large Language Models

大型语言模型综述,非常详细,格局打开!A Survey of Large Language Models 返回论文和资料目录 论文地址 项目地址 1.导读 讲得通俗易懂,且格局拉满!基本覆盖了自ChatGPT以来的AI比较火的事件,还多次提到…

AI 模型首次有了国家标准!头部大厂参与编制,辐射 AMD

3月17日,国内首个面向人工智能生成式模型的国家标准正式公开,并向社会征求意见。 该标准全称为《信息技术神经网络表示与模型压缩第一部分:卷积神经网络》 (GB/T 42382.1-2023&a…

【Linux系统下安装JDK】

一,linux下载JDK最方便快捷的方式:yum 1,执行下方命令,查看可安装java版本。 yum -y list java*2,选择一个进行安装,带-devel的安装的是jdk,而不带-devel的安装的是jre 3,安装命令…

【springBoot篇1】概念、创建和运行

目录 一、什么是springBoot?为什么要学springBoot springBoot的优点:(5点) 优点1:快速集成框架 优点2:内置了Tomcat容器 优点3:快速部署项目 优点4:少配置,多注解 优点5:支持更…

机器学习中的公平性

文章目录机器学习公平性评估指标群体公平性指标个人公平性指标引起机器学习模型不公平的潜在因素提升机器学习模型公平性的措施机器学习公平性 定义: 机器学习公平性主要研究如何通过解决或缓解“不公平”来增加模型的公平性,以及如何确保模型的输出结果…

生信刷题之ROSALIND——Part 1

目录写在前面1、Counting DNA NucleotidesProblemSample DatasetSample OutputCodeOutput2、Transcribing DNA into RNAProblemSample DatasetSample OutputCodeOutput3、Complementing a Strand of DNAProblemSample DatasetSample OutputCodeOutput4、Rabbits and Recurrence…

Android操作系统介绍

目录 Android 名词 Android LOGO 体系架构 Android系统架构 Linux 内核 硬件抽象层(HAL) Android Runtime 原生C/C库 Java API框架 系统应用 应用组件 活动 (Activity) 服务 (Service ) 广播接收器 &…

BLOOM模型结构详解

《BLOOM: A 176B-Parameter Open-Access Multilingual Language Model》 论文地址: https://arxiv.org/pdf/2211.05100.pdf 代码地址: transformers库-modeling_bloom.py BigScience 官方提供的代码链接并没有找到完整的模型实现代码,只有提示说模型结构代码是在 Megatron 的…

JS 中深拷贝的几种爱恨情仇

页面开发中,经常会碰到需要对数据进行某些处理操作,又不想影响原先的数据,所会经常将数据进行拷贝,当然这里指的是深拷贝。 深拷贝和浅拷贝的区别? 深拷贝通通俗点来讲呢,其实就是不管当前要操作的数据层级…

目标检测算法——YOLOv5/v7/v8改进结合涨点Trick之Wise-IoU(超越CIOU/SIOU)

超越CIOU/SIOU | Wise-IoU助力YOLO强势涨点!!! 论文题目:Wise-IoU: Bounding Box Regression Loss with Dynamic Focusing Mechanism 论文链接:https://arxiv.org/abs/2301.10051 ​ 近年来的研究大多假设训练数据中的…

Java实现发送邮件(定时自动发送邮件)

系列文章目录 Redis缓存穿透、击穿、雪崩问题及解决方法Spring Cache的使用–快速上手篇分页查询–Java项目实战篇全局异常处理–Java实战项目篇 该系列文章持续更新,更多的文章请点击我的主页查看哦! 文章目录 目录 系列文章目录 文章目录 前言 一…

算法训练第五十七天 | 647. 回文子串、516.最长回文子序列、动态规划总结篇

动态规划part17 647. 回文子串题目描述思路暴力解法动态规划双指针法 516.最长回文子序列题目描述思路 动态规划总结篇动划基础背包问题系列打家劫舍系列股票系列子序列系列总结 647. 回文子串 题目链接:647. 回文子串 参考:https://programmercarl.com…

hot100:数组——31、33

31. 下一个排列 思路:其实这道题的意思就是,简单地说,就是找到一个比现有的给出的数组代表的值大的最小的数 比如给出的数组是[1,2,3],它代表的数值是123,现有的元素组成的数值中,比123大的有很多&#xf…

3.6 n维随机变量

学习目标: 学习n维随机变量需要掌握一定的数学知识,包括多元微积分、线性代数和概率论等。要学习n维随机变量,我会采取以下步骤: 复习相关的数学知识:首先,我会复习多元微积分、线性代数和概率论的基本知…

OpenCV介绍与GUI特征(一)

目录0.1 OpenCV-Python教程简介OpenCVOpenCV-PythonOpenCV-Python教程OpenCV需要你!!!贡献者0.2 在Windows中安装OpenCV-Python目标从预制的二进制文件中安装OpenCV从源代码构建OpenCV练习0.3 在Ubuntu中安装OpenCV-Python目标从预制的二进制文件中安装OpenCV-Python从源码构建…