NNDL 实验八 网络优化与正则化(3)不同优化算法比较

news2024/11/16 22:00:46

文章目录

    • 7.3 不同优化算法的比较分析
      • 7.3.1 优化算法的实验设定
        • 7.3.1.1 2D可视化实验
        • 7.3.1.2 简单拟合实验
        • 7.3.1.3 与Torch API对比,验证正确性
      • 7.3.2 学习率调整
        • 7.3.2.1 AdaGrad算法
        • 7.3.2.2 RMSprop算法
      • 7.3.3 梯度估计修正
        • 7.3.3.1 动量法
        • 7.3.3.2 Adam算法
      • 7.3.4 不同优化器的3D可视化对比
    • 【选做题】
    • 总结

7.3 不同优化算法的比较分析

除了批大小对模型收敛速度的影响外,学习率和梯度估计也是影响神经网络优化的重要因素。

神经网络优化中常用的优化方法也主要是如下两方面的改进,包括:

学习率调整:通过自适应地调整学习率使得优化更稳定。AdaGrad、RMSprop、AdaDelta算法等。
梯度估计修正:通过修正每次迭代时估计的梯度方向来加快收敛速度。动量法、Nesterov加速梯度方法等。
本节还会介绍综合学习率调整和梯度估计修正的优化算法,如Adam算法。

7.3.1 优化算法的实验设定

7.3.1.1 2D可视化实验

为了更好地展示不同优化算法的能力对比,我们选择一个二维空间中的凸函数,然后用不同的优化算法来寻找最优解,并可视化梯度下降过程的轨迹。
在这里插入图片描述
将被优化函数实现为OptimizedFunction算子,其forward方法是Sphere函数的前向计算,backward方法则计算被优化函数对x的偏导。代码实现如下:

from nndl.op import Op
import torch
class OptimizedFunction(Op):
    def __init__(self, w):
        super(OptimizedFunction, self).__init__()
        self.w = torch.as_tensor(w,dtype=torch.float32)
        self.params = {'x': torch.as_tensor(0,dtype=torch.float32)}
        self.grads = {'x': torch.as_tensor(0,dtype=torch.float32)}

    def forward(self, x):
        self.params['x'] = x
        return torch.matmul(self.w.T, torch.square(self.params['x']))

    def backward(self):
        self.grads['x'] = 2 * torch.multiply(self.w.T, self.params['x'])

nndl.op.Op:

class Op(object):
    def __init__(self):
        pass

    def __call__(self, inputs):
        return self.forward(torch.as_tensor(inputs,dtype=torch.float32))

    def forward(self, inputs):
        raise NotImplementedError

    def backward(self, inputs):
        raise NotImplementedError

小批量梯度下降优化器 复用3.1.4.3节定义的梯度下降优化器SimpleBatchGD。
训练函数 定义一个简易的训练函数,记录梯度下降过程中每轮的参数x和损失。代码实现如下:

def train_f(model, optimizer, x_init, epoch):
    x = x_init
    all_x = []
    losses = []
    for i in range(epoch):
        all_x.append(copy.copy(x.numpy()))
        loss = model(x)
        losses.append(loss)
        model.backward()
        optimizer.step()
        x = model.params['x']
    return torch.as_tensor(all_x), losses

可视化函数 定义一个Visualization类,用于绘制x的更新轨迹。代码实现如下:

import numpy as np
import matplotlib.pyplot as plt
class Visualization(object):
    def __init__(self):
        x1 = np.arange(-5, 5, 0.1)
        x2 = np.arange(-5, 5, 0.1)
        x1, x2 = np.meshgrid(x1, x2)
        self.init_x = torch.as_tensor([x1, x2])

    def plot_2d(self, model, x, fig_name):
        fig, ax = plt.subplots(figsize=(10, 6))
        cp = ax.contourf(self.init_x[0], self.init_x[1], model(self.init_x.transpose(1,0)), colors=['#e4007f', '#f19ec2', '#e86096', '#eb7aaa', '#f6c8dc', '#f5f5f5', '#000000'])
        c = ax.contour(self.init_x[0], self.init_x[1], model(self.init_x.transpose(1,0)), colors='black')
        cbar = fig.colorbar(cp)
        ax.plot(x[:, 0], x[:, 1], '-o', color='#000000')
        ax.plot(0, 'r*', markersize=18, color='#fefefe')

        ax.set_xlabel('$x1$')
        ax.set_ylabel('$x2$')

        ax.set_xlim((-2, 5))
        ax.set_ylim((-2, 5))
        plt.savefig(fig_name)

定义train_and_plot_f函数,调用train_f和Visualization,训练模型并可视化参数更新轨迹。代码实现如下:

def train_and_plot_f(model, optimizer, epoch, fig_name):
    x_init = torch.as_tensor([3, 4], dtype=torch.float32)
    print('x1 initiate: {}, x2 initiate: {}'.format(x_init[0].numpy(), x_init[1].numpy()))
    x, losses = train_f(model, optimizer, x_init, epoch)
    losses = np.array(losses)

    # 展示x1、x2的更新轨迹
    vis = Visualization()
    vis.plot_2d(model, x, fig_name)

模型训练与可视化:

from nndl.op import SimpleBatchGD
# 固定随机种子
torch.seed()
w = torch.as_tensor([0.2, 2])
model = OptimizedFunction(w)
opt = SimpleBatchGD(init_lr=0.2, model=model)
# train_and_plot_f(model, opt, epoch=20, fig_name='opti-vis-para.pdf')

nndl.op.SimpleBatchGD:

class SimpleBatchGD(Optimizer):
    def __init__(self, init_lr, model):
        super(SimpleBatchGD, self).__init__(init_lr=init_lr, model=model)

    def step(self):
        #参数更新
        if isinstance(self.model.params, dict):
            for key in self.model.params.keys():
                self.model.params[key] = self.model.params[key] - self.init_lr * self.model.grads[key]

Optimizer:

# 优化器基类
class Optimizer(object):
    def __init__(self, init_lr, model):
        self.init_lr = init_lr
        #指定优化器需要优化的模型
        self.model = model

    @abstractmethod
    def step(self):
        pass

结果:
在这里插入图片描述
输出图中不同颜色代表f(x1,x2)的值,具体数值可以参考图右侧的对应表,比如深粉色区域代表f(x1,x2)在0~8之间,不同颜色间黑色的曲线是等值线,代表落在该线上的点对应的f(x1,x2)的值都相同。

7.3.1.2 简单拟合实验

这里我们随机生成一组数据作为数据样本,再构建一个简单的单层前馈神经网络,用于前向计算。

与Paddle API对比,验证正确性

分别实例化自定义SimpleBatchGD优化器和调用paddle.optimizer.SGD API, 验证自定义优化器的正确性。

# 固定随机种子
torch.seed()
# 随机生成shape为(1000,2)的训练数据
X = torch.randn([1000, 2])
w = torch.as_tensor([0.5, 0.8])
w = torch.unsqueeze(w, dim=1)
noise = 0.01 * torch.rand([1000])
noise = torch.unsqueeze(noise, dim=1)
# 计算y
y = torch.matmul(X, w) + noise
# 打印X, y样本
print('X: ', X[0].numpy())
print('y: ', y[0].numpy())

# X,y组成训练样本数据
data = torch.concat((X, y), dim=1)
print('input data shape: ', data.shape)
print('data: ', data[0].numpy())

运行结果:
在这里插入图片描述
定义Linear算子,实现一个线性层的前向和反向计算。代码实现如下:

class Linear(Op):
    def __init__(self, input_size,  weight_init=np.random.standard_normal, bias_init=torch.zeros):
        self.params = {}
        self.params['W'] = weight_init([input_size, 1])
        self.params['W'] = torch.as_tensor(self.params['W'],dtype=torch.float32)
        self.params['b'] = bias_init([1])

        self.inputs = None
        self.grads = {}

    def forward(self, inputs):
        self.inputs = inputs
        self.outputs = torch.matmul(self.inputs, self.params['W']) + self.params['b']
        return self.outputs

    def backward(self, labels):
        K = self.inputs.shape[0]
        self.grads['W'] = 1./ K*torch.matmul(self.inputs.T, (self.outputs - labels))
        self.grads['b'] = 1./K* torch.sum(self.outputs-labels, dim=0)

模型训练train函数的代码实现如下:

def train(data, num_epochs, batch_size, model, calculate_loss, optimizer, verbose=False):

    # 记录每个回合损失的变化
    epoch_loss = []
    # 记录每次迭代损失的变化
    iter_loss = []
    N = len(data)
    for epoch_id in range(num_epochs):
        # np.random.shuffle(data) #不再随机打乱数据
        # 将训练数据进行拆分,每个mini_batch包含batch_size条的数据
        mini_batches = [data[i:i+batch_size] for i in range(0, N, batch_size)]
        for iter_id, mini_batch in enumerate(mini_batches):
            # data中前两个分量为X
            inputs = mini_batch[:, :-1]
            # data中最后一个分量为y
            labels = mini_batch[:, -1:]
            # 前向计算
            outputs = model(inputs)
            # 计算损失
            loss = calculate_loss(outputs, labels).numpy()
            # 计算梯度
            model.backward(labels)
            # 梯度更新
            optimizer.step()
            iter_loss.append(loss)
        # verbose = True 则打印当前回合的损失
        if verbose:
            print('Epoch {:3d}, loss = {:.4f}'.format(epoch_id, np.mean(iter_loss)))
        epoch_loss.append(np.mean(iter_loss))
    return iter_loss, epoch_loss

优化过程可视化 定义plot_loss函数,用于绘制损失函数变化趋势。代码实现如下:

def plot_loss(iter_loss, epoch_loss, fig_name):
    """
    可视化损失函数的变化趋势
    """
    plt.figure(figsize=(10, 4))
    ax1 = plt.subplot(121)
    ax1.plot(iter_loss, color='#e4007f')
    plt.title('iteration loss')
    ax2 = plt.subplot(122)
    ax2.plot(epoch_loss, color='#f19ec2')
    plt.title('epoch loss')
    plt.savefig(fig_name)
    plt.show()


对于使用不同优化器的模型训练,保存每一个回合损失的更新情况,并绘制出损失函数的变化趋势,以此验证模型是否收敛。定义train_and_plot函数,调用train和plot_loss函数,训练并展示每个回合和每次迭代(Iteration)的损失变化情况。在模型训练时,使用torch.nn.MSELoss()计算均方误差。代码实现如下:

import torch.nn as nn
def train_and_plot(optimizer, fig_name):
    """
    训练网络并画出损失函数的变化趋势
    输入:
        - optimizer:优化器
    """
    # 定义均方差损失
    mse = nn.MSELoss()
    iter_loss, epoch_loss = train(data, num_epochs=30, batch_size=64, model=model, calculate_loss=mse, optimizer=optimizer)
    plot_loss(iter_loss, epoch_loss, fig_name)

训练网络并可视化损失函数的变化趋势。代码实现如下:

# 固定随机种子
torch.seed()
# 定义网络结构
model = Linear(2)
# 定义优化器
opt = SimpleBatchGD(init_lr=0.01, model=model)
train_and_plot(opt, 'opti-loss.pdf')

运行结果:
在这里插入图片描述
从输出结果可以看出:loss在不断减小,模型也在逐渐收敛。

7.3.1.3 与Torch API对比,验证正确性

分别实例化自定义SimpleBatchGD优化器和调用torch.optim.SGD API, 验证自定义优化器的正确性。代码如下:

# 固定随机种子
torch.seed()
# 定义网络结构
model = Linear(2)
# 定义优化器
opt = SimpleBatchGD(init_lr=0.01, model=model)

x = data[0, :-1].unsqueeze(0)
y = data[0, -1].unsqueeze(0)

model1 = Linear(2)
print('model1 parameter W: ', model1.params['W'].numpy())
opt1 = SimpleBatchGD(init_lr=0.01, model=model1)
output1 = model1(x)

model2 = nn.Linear(2, 1)
model2.weight = torch.nn.Parameter(model1.params['W'])
print('model2 parameter W: ', model2.state_dict()['weight'].numpy())
output2 = model2(x.T)

model1.backward(y)
opt1.step()
print('model1 parameter W after train step: ', model1.params['W'].numpy())

opt2 = torch.optim.SGD(lr=0.01, params=model2.parameters())
loss = torch.nn.functional.mse_loss(output2, y) / 2
loss.backward()
opt2.step()
opt2.zero_grad()
print('model2 parameter W after train step: ', model2.state_dict()['weight'].numpy())

运行结果:

model1 parameter W: [[ 0.8403961]
[-0.1934289]]
model2 parameter W: [[ 0.8403961 -0.1934289]]
model1 parameter W after train step: [[ 0.85348997 ]
[-0.18250407]]
model2 parameter W after train step: [[ 0.85317418 -0.1826655]]

7.3.2 学习率调整

学习率是神经网络优化时的重要超参数。在梯度下降法中,学习率α的取值非常关键,如果取值过大就不会收敛,如果过小则收敛速度太慢。
常用的学习率调整方法包括如下几种方法:

  • 学习率衰减:如分段常数衰减(Piecewise Constant Decay)、余弦衰减(Cosine Decay)等;
  • 学习率预热:如逐渐预热(Gradual Warmup) 等;
  • 周期性学习率调整:如循环学习率等;
  • 自适应调整学习率的方法:如AdaGrad、RMSprop、AdaDelta等。自适应学习率方法可以针对每个参数设置不同的学习率。

7.3.2.1 AdaGrad算法

构建优化器 定义Adagrad类,继承Optimizer类。定义step函数调用adagrad进行参数更新。代码实现如下:


class Adagrad(Optimizer):
    def __init__(self, init_lr, model, epsilon):
        super(Adagrad, self).__init__(init_lr=init_lr, model=model)
        self.G = {}
        for key in self.model.params.keys():
            self.G[key] = 0
        self.epsilon = epsilon

    def adagrad(self, x, gradient_x, G, init_lr):
        G += gradient_x ** 2
        x -= init_lr / torch.sqrt(G + self.epsilon) * gradient_x
        return x, G

    def step(self):
        for key in self.model.params.keys():
            self.model.params[key], self.G[key] = self.adagrad(self.model.params[key],
                                                               self.model.grads[key],
                                                               self.G[key],
                                                               self.init_lr)

2D可视化实验 使用被优化函数展示Adagrad算法的参数更新轨迹。代码实现如下:

# 固定随机种子
torch.seed()
w = torch.as_tensor([0.2, 2])
model2 = OptimizedFunction(w)
opt2 = Adagrad(init_lr=0.5, model=model2, epsilon=1e-7)
train_and_plot_f(model2, opt2, epoch=50, fig_name='opti-vis-para2.pdf')

运行结果:
在这里插入图片描述
从输出结果看,AdaGrad算法在前几个回合更新时参数更新幅度较大,随着回合数增加,学习率逐渐缩小,参数更新幅度逐渐缩小。在AdaGrad算法中,如果某个参数的偏导数累积比较大,其学习率相对较小。相反,如果其偏导数累积较小,其学习率相对较大。但整体随着迭代次数的增加,学习率逐渐缩小。该算法的缺点是在经过一定次数的迭代依然没有找到最优点时,由于这时的学习率已经非常小,很难再继续找到最优点。

简单拟合实验 训练单层线性网络,验证损失是否收敛。代码实现如下:

# 固定随机种子
torch.seed()
# 定义网络结构
model = Linear(2)
# 定义优化器
opt = Adagrad(init_lr=0.1, model=model, epsilon=1e-7)
train_and_plot(opt, 'opti-loss2.pdf')

运行结果:
在这里插入图片描述

7.3.2.2 RMSprop算法

构建优化器 定义RMSprop类,继承Optimizer类。定义step函数调用rmsprop更新参数。代码实现如下:

class RMSprop(Optimizer):
    def __init__(self, init_lr, model, beta, epsilon):
        """
        RMSprop优化器初始化
        输入:
            - init_lr:初始学习率
            - model:模型,model.params存储模型参数值
            - beta:衰减率
            - epsilon:保持数值稳定性而设置的常数
        """
        super(RMSprop, self).__init__(init_lr=init_lr, model=model)
        self.G = {}
        for key in self.model.params.keys():
            self.G[key] = 0
        self.beta = beta
        self.epsilon = epsilon
 
    def rmsprop(self, x, gradient_x, G, init_lr):
        """
        rmsprop算法更新参数,G为迭代梯度平方的加权移动平均
        """
        G = self.beta * G + (1 - self.beta) * gradient_x ** 2
        x -= init_lr / torch.sqrt(G + self.epsilon) * gradient_x
        return x, G
 
    def step(self):
        """参数更新"""
        for key in self.model.params.keys():
            self.model.params[key], self.G[key] = self.rmsprop(self.model.params[key],
                                                               self.model.grads[key],
                                                               self.G[key],
                                                               self.init_lr)

2D可视化实验 使用被优化函数展示RMSprop算法的参数更新轨迹。代码实现如下:

# 固定随机种子
torch.manual_seed(0)
w = torch.tensor([0.2, 2])
model = OptimizedFunction(w)
opt = RMSprop(init_lr=0.1, model=model, beta=0.9, epsilon=1e-7)
train_and_plot_f(model, opt, epoch=50, fig_name='opti-vis-para3.pdf')

运行结果:
在这里插入图片描述
简单拟合实验 训练单层线性网络,进行简单的拟合实验。代码实现如下:

# 固定随机种子
torch.manual_seed(0)
# 定义网络结构
model = Linear(2)
# 定义优化器
opt = RMSprop(init_lr=0.1, model=model, beta=0.9, epsilon=1e-7)
train_and_plot(opt, 'opti-loss3.pdf')

运行结果:
在这里插入图片描述

7.3.3 梯度估计修正

除了调整学习率之外,还可以进行梯度估计修正。在小批量梯度下降法中,由于每次迭代的样本具有一定的随机性,因此每次迭代的梯度估计和整个训练集上的最优梯度并不一致。如果每次选取样本数量比较小,损失会呈振荡的方式下降。
一种有效地缓解梯度估计随机性的方式是通过使用最近一段时间内的平均梯度来代替当前时刻的随机梯度来作为参数更新的方向,从而提高优化速度。

7.3.3.1 动量法

用之前积累动量来替代真正的梯度。每次迭代的梯度可以看作加速度。

class Momentum(Optimizer):
    def __init__(self, init_lr, model, rho):
        """
        Momentum优化器初始化
        输入:
            - init_lr:初始学习率
            - model:模型,model.params存储模型参数值
            - rho:动量因子
        """
        super(Momentum, self).__init__(init_lr=init_lr, model=model)
        self.delta_x = {}
        for key in self.model.params.keys():
            self.delta_x[key] = 0
        self.rho = rho
 
    def momentum(self, x, gradient_x, delta_x, init_lr):
        """
        momentum算法更新参数,delta_x为梯度的加权移动平均
        """
        delta_x = self.rho * delta_x - init_lr * gradient_x
        x += delta_x
        return x, delta_x
 
    def step(self):
        """参数更新"""
        for key in self.model.params.keys():
            self.model.params[key], self.delta_x[key] = self.momentum(self.model.params[key],
                                                                      self.model.grads[key],
                                                                      self.delta_x[key],
                                                                      self.init_lr)
# 固定随机种子
torch.manual_seed(0)
w = torch.tensor([0.2, 2])
model = OptimizedFunction(w)
opt = Momentum(init_lr=0.01, model=model, rho=0.9)
train_and_plot_f(model, opt, epoch=50, fig_name='opti-vis-para4.pdf')

运行结果:
在这里插入图片描述
从输出结果可以看出:在模型训练初期,梯度方向比较一致,参数更新幅度逐渐增大;在迭代后期,参数更新幅度减小,在收敛值附近振荡。

简单拟合实验 训练单层线性网络,进行简单的拟合实验。代码实现如下

# 固定随机种子
torch.manual_seed(0)
 
# 定义网络结构
model = Linear(2)
# 定义优化器
opt = Momentum(init_lr=0.01, model=model, rho=0.9)
train_and_plot(opt, 'opti-loss4.pdf')

运行结果:
在这里插入图片描述

7.3.3.2 Adam算法

Adam算法(自适应矩估计算法)可以看作动量法和RMSprop算法的结合,不但使用动量作为参数更新方向,而且可以自适应调整学习率。

class Adam(Optimizer):
    def __init__(self, init_lr, model, beta1, beta2, epsilon):
        """
        Adam优化器初始化
        输入:
            - init_lr:初始学习率
            - model:模型,model.params存储模型参数值
            - beta1, beta2:移动平均的衰减率
            - epsilon:保持数值稳定性而设置的常数
        """
        super(Adam, self).__init__(init_lr=init_lr, model=model)
        self.beta1 = beta1
        self.beta2 = beta2
        self.epsilon = epsilon
        self.M, self.G = {}, {}
        for key in self.model.params.keys():
            self.M[key] = 0
            self.G[key] = 0
        self.t = 1
 
    def adam(self, x, gradient_x, G, M, t, init_lr):
        """
        adam算法更新参数
        输入:
            - x:参数
            - G:梯度平方的加权移动平均
            - M:梯度的加权移动平均
            - t:迭代次数
            - init_lr:初始学习率
        """
        M = self.beta1 * M + (1 - self.beta1) * gradient_x
        G = self.beta2 * G + (1 - self.beta2) * gradient_x ** 2
        M_hat = M / (1 - self.beta1 ** t)
        G_hat = G / (1 - self.beta2 ** t)
        t += 1
        x -= init_lr / torch.sqrt(G_hat + self.epsilon) * M_hat
        return x, G, M, t
 
    def step(self):
        """参数更新"""
        for key in self.model.params.keys():
            self.model.params[key], self.G[key], self.M[key], self.t = self.adam(self.model.params[key],
                                                                                 self.model.grads[key],
                                                                                 self.G[key],
                                                                                 self.M[key],
                                                                                 self.t,
                                                                                 self.init_lr)

# 固定随机种子
torch.manual_seed(0)
w = torch.tensor([0.2, 2])
model = OptimizedFunction(w)
opt = Adam(init_lr=0.2, model=model, beta1=0.9, beta2=0.99, epsilon=1e-7)
train_and_plot_f(model, opt, epoch=20, fig_name='opti-vis-para5.pdf')

运行结果:
在这里插入图片描述
从输出结果看,Adam算法可以自适应调整学习率,参数更新更加平稳。
简单拟合实验 训练单层线性网络,进行简单的拟合实验。代码实现如下:

# 固定随机种子
torch.manual_seed(0)
# 定义网络结构
model = Linear(2)
# 定义优化器
opt = Adam(init_lr=0.1, model=model, beta1=0.9, beta2=0.99, epsilon=1e-7)
train_and_plot(opt, 'opti-loss5.pdf')

运行结果:
在这里插入图片描述

7.3.4 不同优化器的3D可视化对比

import torch
import numpy as np
import copy
from matplotlib import pyplot as plt
from matplotlib import animation
from itertools import zip_longest
 
 
class Op(object):
    def __init__(self):
        pass
 
    def __call__(self, inputs):
        return self.forward(inputs)
 
    # 输入:张量inputs
    # 输出:张量outputs
    def forward(self, inputs):
        # return outputs
        raise NotImplementedError
 
    # 输入:最终输出对outputs的梯度outputs_grads
    # 输出:最终输出对inputs的梯度inputs_grads
    def backward(self, outputs_grads):
        # return inputs_grads
        raise NotImplementedError
 
 
class Optimizer(object):  # 优化器基类
    def __init__(self, init_lr, model):
        """
        优化器类初始化
        """
        # 初始化学习率,用于参数更新的计算
        self.init_lr = init_lr
        # 指定优化器需要优化的模型
        self.model = model
 
    def step(self):
        """
        定义每次迭代如何更新参数
        """
        pass
 
 
class SimpleBatchGD(Optimizer):
    def __init__(self, init_lr, model):
        super(SimpleBatchGD, self).__init__(init_lr=init_lr, model=model)
 
    def step(self):
        # 参数更新
        if isinstance(self.model.params, dict):
            for key in self.model.params.keys():
                self.model.params[key] = self.model.params[key] - self.init_lr * self.model.grads[key]
 
 
class Adagrad(Optimizer):
    def __init__(self, init_lr, model, epsilon):
        """
        Adagrad 优化器初始化
        输入:
            - init_lr: 初始学习率 - model:模型,model.params存储模型参数值  - epsilon:保持数值稳定性而设置的非常小的常数
        """
        super(Adagrad, self).__init__(init_lr=init_lr, model=model)
        self.G = {}
        for key in self.model.params.keys():
            self.G[key] = 0
        self.epsilon = epsilon
 
    def adagrad(self, x, gradient_x, G, init_lr):
        """
        adagrad算法更新参数,G为参数梯度平方的累计值。
        """
        G += gradient_x ** 2
        x -= init_lr / torch.sqrt(G + self.epsilon) * gradient_x
        return x, G
 
    def step(self):
        """
        参数更新
        """
        for key in self.model.params.keys():
            self.model.params[key], self.G[key] = self.adagrad(self.model.params[key],
                                                               self.model.grads[key],
                                                               self.G[key],
                                                               self.init_lr)
 
 
class RMSprop(Optimizer):
    def __init__(self, init_lr, model, beta, epsilon):
        """
        RMSprop优化器初始化
        输入:
            - init_lr:初始学习率
            - model:模型,model.params存储模型参数值
            - beta:衰减率
            - epsilon:保持数值稳定性而设置的常数
        """
        super(RMSprop, self).__init__(init_lr=init_lr, model=model)
        self.G = {}
        for key in self.model.params.keys():
            self.G[key] = 0
        self.beta = beta
        self.epsilon = epsilon
 
    def rmsprop(self, x, gradient_x, G, init_lr):
        """
        rmsprop算法更新参数,G为迭代梯度平方的加权移动平均
        """
        G = self.beta * G + (1 - self.beta) * gradient_x ** 2
        x -= init_lr / torch.sqrt(G + self.epsilon) * gradient_x
        return x, G
 
    def step(self):
        """参数更新"""
        for key in self.model.params.keys():
            self.model.params[key], self.G[key] = self.rmsprop(self.model.params[key],
                                                               self.model.grads[key],
                                                               self.G[key],
                                                               self.init_lr)
 
 
class Momentum(Optimizer):
    def __init__(self, init_lr, model, rho):
        """
        Momentum优化器初始化
        输入:
            - init_lr:初始学习率
            - model:模型,model.params存储模型参数值
            - rho:动量因子
        """
        super(Momentum, self).__init__(init_lr=init_lr, model=model)
        self.delta_x = {}
        for key in self.model.params.keys():
            self.delta_x[key] = 0
        self.rho = rho
 
    def momentum(self, x, gradient_x, delta_x, init_lr):
        """
        momentum算法更新参数,delta_x为梯度的加权移动平均
        """
        delta_x = self.rho * delta_x - init_lr * gradient_x
        x += delta_x
        return x, delta_x
 
    def step(self):
        """参数更新"""
        for key in self.model.params.keys():
            self.model.params[key], self.delta_x[key] = self.momentum(self.model.params[key],
                                                                      self.model.grads[key],
                                                                      self.delta_x[key],
                                                                      self.init_lr)
 
 
class Adam(Optimizer):
    def __init__(self, init_lr, model, beta1, beta2, epsilon):
        """
        Adam优化器初始化
        输入:
            - init_lr:初始学习率
            - model:模型,model.params存储模型参数值
            - beta1, beta2:移动平均的衰减率
            - epsilon:保持数值稳定性而设置的常数
        """
        super(Adam, self).__init__(init_lr=init_lr, model=model)
        self.beta1 = beta1
        self.beta2 = beta2
        self.epsilon = epsilon
        self.M, self.G = {}, {}
        for key in self.model.params.keys():
            self.M[key] = 0
            self.G[key] = 0
        self.t = 1
 
    def adam(self, x, gradient_x, G, M, t, init_lr):
        """
        adam算法更新参数
        输入:
            - x:参数
            - G:梯度平方的加权移动平均
            - M:梯度的加权移动平均
            - t:迭代次数
            - init_lr:初始学习率
        """
        M = self.beta1 * M + (1 - self.beta1) * gradient_x
        G = self.beta2 * G + (1 - self.beta2) * gradient_x ** 2
        M_hat = M / (1 - self.beta1 ** t)
        G_hat = G / (1 - self.beta2 ** t)
        t += 1
        x -= init_lr / torch.sqrt(G_hat + self.epsilon) * M_hat
        return x, G, M, t
 
    def step(self):
        """参数更新"""
        for key in self.model.params.keys():
            self.model.params[key], self.G[key], self.M[key], self.t = self.adam(self.model.params[key],
                                                                                 self.model.grads[key],
                                                                                 self.G[key],
                                                                                 self.M[key],
                                                                                 self.t,
                                                                                 self.init_lr)
 
 
class OptimizedFunction3D(Op):
    def __init__(self):
        super(OptimizedFunction3D, self).__init__()
        self.params = {'x': 0}
        self.grads = {'x': 0}
 
    def forward(self, x):
        self.params['x'] = x
        return x[0] ** 2 + x[1] ** 2 + x[1] ** 3 + x[0] * x[1]
 
    def backward(self):
        x = self.params['x']
        gradient1 = 2 * x[0] + x[1]
        gradient2 = 2 * x[1] + 3 * x[1] ** 2 + x[0]
        grad1 = torch.Tensor([gradient1])
        grad2 = torch.Tensor([gradient2])
        self.grads['x'] = torch.cat([grad1, grad2])
 
 
class Visualization3D(animation.FuncAnimation):
    """    绘制动态图像,可视化参数更新轨迹    """
 
    def __init__(self, *xy_values, z_values, labels=[], colors=[], fig, ax, interval=600, blit=True, **kwargs):
        """
        初始化3d可视化类
        输入:
            xy_values:三维中x,y维度的值
            z_values:三维中z维度的值
            labels:每个参数更新轨迹的标签
            colors:每个轨迹的颜色
            interval:帧之间的延迟(以毫秒为单位)
            blit:是否优化绘图
        """
        self.fig = fig
        self.ax = ax
        self.xy_values = xy_values
        self.z_values = z_values
 
        frames = max(xy_value.shape[0] for xy_value in xy_values)
        self.lines = [ax.plot([], [], [], label=label, color=color, lw=2)[0]
                      for _, label, color in zip_longest(xy_values, labels, colors)]
        super(Visualization3D, self).__init__(fig, self.animate, init_func=self.init_animation, frames=frames,
                                              interval=interval, blit=blit, **kwargs)
 
    def init_animation(self):
        # 数值初始化
        for line in self.lines:
            line.set_data([], [])
            # line.set_3d_properties(np.asarray([]))  # 源程序中有这一行,加上会报错。 Edit by David 2022.12.4
        return self.lines
 
    def animate(self, i):
        # 将x,y,z三个数据传入,绘制三维图像
        for line, xy_value, z_value in zip(self.lines, self.xy_values, self.z_values):
            line.set_data(xy_value[:i, 0], xy_value[:i, 1])
            line.set_3d_properties(z_value[:i])
        return self.lines
 
 
def train_f(model, optimizer, x_init, epoch):
    x = x_init
    all_x = []
    losses = []
    for i in range(epoch):
        all_x.append(copy.deepcopy(x.numpy()))  # 浅拷贝 改为 深拷贝, 否则List的原值会被改变。 Edit by David 2022.12.4.
        loss = model(x)
        losses.append(loss)
        model.backward()
        optimizer.step()
        x = model.params['x']
    return torch.Tensor(np.array(all_x)), losses
 
 
# 构建5个模型,分别配备不同的优化器
model1 = OptimizedFunction3D()
opt_gd = SimpleBatchGD(init_lr=0.01, model=model1)
 
model2 = OptimizedFunction3D()
opt_adagrad = Adagrad(init_lr=0.5, model=model2, epsilon=1e-7)
 
model3 = OptimizedFunction3D()
opt_rmsprop = RMSprop(init_lr=0.1, model=model3, beta=0.9, epsilon=1e-7)
 
model4 = OptimizedFunction3D()
opt_momentum = Momentum(init_lr=0.01, model=model4, rho=0.9)
 
model5 = OptimizedFunction3D()
opt_adam = Adam(init_lr=0.1, model=model5, beta1=0.9, beta2=0.99, epsilon=1e-7)
 
models = [model1, model2, model3, model4, model5]
opts = [opt_gd, opt_adagrad, opt_rmsprop, opt_momentum, opt_adam]
 
x_all_opts = []
z_all_opts = []
 
# 使用不同优化器训练
 
for model, opt in zip(models, opts):
    x_init = torch.FloatTensor([2, 3])
    x_one_opt, z_one_opt = train_f(model, opt, x_init, 150)  # epoch
    # 保存参数值
    x_all_opts.append(x_one_opt.numpy())
    z_all_opts.append(np.squeeze(z_one_opt))
 
# 使用numpy.meshgrid生成x1,x2矩阵,矩阵的每一行为[-3, 3],以0.1为间隔的数值
x1 = np.arange(-3, 3, 0.1)
x2 = np.arange(-3, 3, 0.1)
x1, x2 = np.meshgrid(x1, x2)
init_x = torch.Tensor(np.array([x1, x2]))
 
model = OptimizedFunction3D()
 
# 绘制 f_3d函数 的 三维图像
fig = plt.figure()
ax = plt.axes(projection='3d')
X = init_x[0].numpy()
Y = init_x[1].numpy()
Z = model(init_x).numpy()  # 改为 model(init_x).numpy() David 2022.12.4
ax.plot_surface(X, Y, Z, cmap='rainbow')
 
ax.set_xlabel('x1')
ax.set_ylabel('x2')
ax.set_zlabel('f(x1,x2)')
 
labels = ['SGD', 'AdaGrad', 'RMSprop', 'Momentum', 'Adam']
colors = ['#f6373c', '#f6f237', '#45f637', '#37f0f6', '#000000']
 
animator = Visualization3D(*x_all_opts, z_values=z_all_opts, labels=labels, colors=colors, fig=fig, ax=ax)
ax.legend(loc='upper left')
 
plt.show()
animator.save('animation.gif')  

运行结果:
在这里插入图片描述

【选做题】

1.编程实现下面的动画

在这里插入图片描述
代码如下:

import torch
import numpy as np
import copy
from matplotlib import pyplot as plt
from matplotlib import animation
from itertools import zip_longest
from matplotlib import cm
 
 
class Op(object):
    def __init__(self):
        pass
 
    def __call__(self, inputs):
        return self.forward(inputs)
 
    # 输入:张量inputs
    # 输出:张量outputs
    def forward(self, inputs):
        # return outputs
        raise NotImplementedError
 
    # 输入:最终输出对outputs的梯度outputs_grads
    # 输出:最终输出对inputs的梯度inputs_grads
    def backward(self, outputs_grads):
        # return inputs_grads
        raise NotImplementedError
 
 
class Optimizer(object):  # 优化器基类
    def __init__(self, init_lr, model):
        """
        优化器类初始化
        """
        # 初始化学习率,用于参数更新的计算
        self.init_lr = init_lr
        # 指定优化器需要优化的模型
        self.model = model
 
    def step(self):
        """
        定义每次迭代如何更新参数
        """
        pass
 
 
class SimpleBatchGD(Optimizer):
    def __init__(self, init_lr, model):
        super(SimpleBatchGD, self).__init__(init_lr=init_lr, model=model)
 
    def step(self):
        # 参数更新
        if isinstance(self.model.params, dict):
            for key in self.model.params.keys():
                self.model.params[key] = self.model.params[key] - self.init_lr * self.model.grads[key]
 
 
class Adagrad(Optimizer):
    def __init__(self, init_lr, model, epsilon):
        """
        Adagrad 优化器初始化
        输入:
            - init_lr: 初始学习率 - model:模型,model.params存储模型参数值  - epsilon:保持数值稳定性而设置的非常小的常数
        """
        super(Adagrad, self).__init__(init_lr=init_lr, model=model)
        self.G = {}
        for key in self.model.params.keys():
            self.G[key] = 0
        self.epsilon = epsilon
 
    def adagrad(self, x, gradient_x, G, init_lr):
        """
        adagrad算法更新参数,G为参数梯度平方的累计值。
        """
        G += gradient_x ** 2
        x -= init_lr / torch.sqrt(G + self.epsilon) * gradient_x
        return x, G
 
    def step(self):
        """
        参数更新
        """
        for key in self.model.params.keys():
            self.model.params[key], self.G[key] = self.adagrad(self.model.params[key],
                                                               self.model.grads[key],
                                                               self.G[key],
                                                               self.init_lr)
 
 
class RMSprop(Optimizer):
    def __init__(self, init_lr, model, beta, epsilon):
        """
        RMSprop优化器初始化
        输入:
            - init_lr:初始学习率
            - model:模型,model.params存储模型参数值
            - beta:衰减率
            - epsilon:保持数值稳定性而设置的常数
        """
        super(RMSprop, self).__init__(init_lr=init_lr, model=model)
        self.G = {}
        for key in self.model.params.keys():
            self.G[key] = 0
        self.beta = beta
        self.epsilon = epsilon
 
    def rmsprop(self, x, gradient_x, G, init_lr):
        """
        rmsprop算法更新参数,G为迭代梯度平方的加权移动平均
        """
        G = self.beta * G + (1 - self.beta) * gradient_x ** 2
        x -= init_lr / torch.sqrt(G + self.epsilon) * gradient_x
        return x, G
 
    def step(self):
        """参数更新"""
        for key in self.model.params.keys():
            self.model.params[key], self.G[key] = self.rmsprop(self.model.params[key],
                                                               self.model.grads[key],
                                                               self.G[key],
                                                               self.init_lr)
 
 
class Momentum(Optimizer):
    def __init__(self, init_lr, model, rho):
        """
        Momentum优化器初始化
        输入:
            - init_lr:初始学习率
            - model:模型,model.params存储模型参数值
            - rho:动量因子
        """
        super(Momentum, self).__init__(init_lr=init_lr, model=model)
        self.delta_x = {}
        for key in self.model.params.keys():
            self.delta_x[key] = 0
        self.rho = rho
 
    def momentum(self, x, gradient_x, delta_x, init_lr):
        """
        momentum算法更新参数,delta_x为梯度的加权移动平均
        """
        delta_x = self.rho * delta_x - init_lr * gradient_x
        x += delta_x
        return x, delta_x
 
    def step(self):
        """参数更新"""
        for key in self.model.params.keys():
            self.model.params[key], self.delta_x[key] = self.momentum(self.model.params[key],
                                                                      self.model.grads[key],
                                                                      self.delta_x[key],
                                                                      self.init_lr)
 
 
class Adam(Optimizer):
    def __init__(self, init_lr, model, beta1, beta2, epsilon):
        """
        Adam优化器初始化
        输入:
            - init_lr:初始学习率
            - model:模型,model.params存储模型参数值
            - beta1, beta2:移动平均的衰减率
            - epsilon:保持数值稳定性而设置的常数
        """
        super(Adam, self).__init__(init_lr=init_lr, model=model)
        self.beta1 = beta1
        self.beta2 = beta2
        self.epsilon = epsilon
        self.M, self.G = {}, {}
        for key in self.model.params.keys():
            self.M[key] = 0
            self.G[key] = 0
        self.t = 1
 
    def adam(self, x, gradient_x, G, M, t, init_lr):
        """
        adam算法更新参数
        输入:
            - x:参数
            - G:梯度平方的加权移动平均
            - M:梯度的加权移动平均
            - t:迭代次数
            - init_lr:初始学习率
        """
        M = self.beta1 * M + (1 - self.beta1) * gradient_x
        G = self.beta2 * G + (1 - self.beta2) * gradient_x ** 2
        M_hat = M / (1 - self.beta1 ** t)
        G_hat = G / (1 - self.beta2 ** t)
        t += 1
        x -= init_lr / torch.sqrt(G_hat + self.epsilon) * M_hat
        return x, G, M, t
 
    def step(self):
        """参数更新"""
        for key in self.model.params.keys():
            self.model.params[key], self.G[key], self.M[key], self.t = self.adam(self.model.params[key],
                                                                                 self.model.grads[key],
                                                                                 self.G[key],
                                                                                 self.M[key],
                                                                                 self.t,
                                                                                 self.init_lr)
 
 
class OptimizedFunction3D(Op):
    def __init__(self):
        super(OptimizedFunction3D, self).__init__()
        self.params = {'x': 0}
        self.grads = {'x': 0}
 
    def forward(self, x):
        self.params['x'] = x
        return - x[0] * x[0] / 2 + x[1] * x[1] / 1  # x[0] ** 2 + x[1] ** 2 + x[1] ** 3 + x[0] * x[1]
 
    def backward(self):
        x = self.params['x']
        gradient1 = - 2 * x[0] / 2
        gradient2 = 2 * x[1] / 1
        grad1 = torch.Tensor([gradient1])
        grad2 = torch.Tensor([gradient2])
        self.grads['x'] = torch.cat([grad1, grad2])
 
 
class Visualization3D(animation.FuncAnimation):
    """    绘制动态图像,可视化参数更新轨迹    """
 
    def __init__(self, *xy_values, z_values, labels=[], colors=[], fig, ax, interval=100, blit=True, **kwargs):
        """
        初始化3d可视化类
        输入:
            xy_values:三维中x,y维度的值
            z_values:三维中z维度的值
            labels:每个参数更新轨迹的标签
            colors:每个轨迹的颜色
            interval:帧之间的延迟(以毫秒为单位)
            blit:是否优化绘图
        """
        self.fig = fig
        self.ax = ax
        self.xy_values = xy_values
        self.z_values = z_values
 
        frames = max(xy_value.shape[0] for xy_value in xy_values)
 
        self.lines = [ax.plot([], [], [], label=label, color=color, lw=2)[0]
                      for _, label, color in zip_longest(xy_values, labels, colors)]
        self.points = [ax.plot([], [], [], color=color, markeredgewidth=1, markeredgecolor='black', marker='o')[0]
                       for _, color in zip_longest(xy_values, colors)]
        # print(self.lines)
        super(Visualization3D, self).__init__(fig, self.animate, init_func=self.init_animation, frames=frames,
                                              interval=interval, blit=blit, **kwargs)
 
    def init_animation(self):
        # 数值初始化
        for line in self.lines:
            line.set_data_3d([], [], [])
        for point in self.points:
            point.set_data_3d([], [], [])
        return self.points + self.lines
 
    def animate(self, i):
        # 将x,y,z三个数据传入,绘制三维图像
        for line, xy_value, z_value in zip(self.lines, self.xy_values, self.z_values):
            line.set_data_3d(xy_value[:i, 0], xy_value[:i, 1], z_value[:i])
        for point, xy_value, z_value in zip(self.points, self.xy_values, self.z_values):
            point.set_data_3d(xy_value[i, 0], xy_value[i, 1], z_value[i])
        return self.points + self.lines
 
 
def train_f(model, optimizer, x_init, epoch):
    x = x_init
    all_x = []
    losses = []
    for i in range(epoch):
        all_x.append(copy.deepcopy(x.numpy()))  # 浅拷贝 改为 深拷贝, 否则List的原值会被改变。 Edit by David 2022.12.4.
        loss = model(x)
        losses.append(loss)
        model.backward()
        optimizer.step()
        x = model.params['x']
    return torch.Tensor(np.array(all_x)), losses
 
 
# 构建5个模型,分别配备不同的优化器
model1 = OptimizedFunction3D()
opt_gd = SimpleBatchGD(init_lr=0.05, model=model1)
 
model2 = OptimizedFunction3D()
opt_adagrad = Adagrad(init_lr=0.05, model=model2, epsilon=1e-7)
 
model3 = OptimizedFunction3D()
opt_rmsprop = RMSprop(init_lr=0.05, model=model3, beta=0.9, epsilon=1e-7)
 
model4 = OptimizedFunction3D()
opt_momentum = Momentum(init_lr=0.05, model=model4, rho=0.9)
 
model5 = OptimizedFunction3D()
opt_adam = Adam(init_lr=0.05, model=model5, beta1=0.9, beta2=0.99, epsilon=1e-7)
 
models = [model5, model2, model3, model4, model1]
opts = [opt_adam, opt_adagrad, opt_rmsprop, opt_momentum, opt_gd]
 
x_all_opts = []
z_all_opts = []
 
# 使用不同优化器训练
 
for model, opt in zip(models, opts):
    x_init = torch.FloatTensor([0.00001, 0.5])
    x_one_opt, z_one_opt = train_f(model, opt, x_init, 100)  # epoch
    # 保存参数值
    x_all_opts.append(x_one_opt.numpy())
    z_all_opts.append(np.squeeze(z_one_opt))
 
# 使用numpy.meshgrid生成x1,x2矩阵,矩阵的每一行为[-3, 3],以0.1为间隔的数值
x1 = np.arange(-1, 2, 0.01)
x2 = np.arange(-1, 1, 0.05)
x1, x2 = np.meshgrid(x1, x2)
init_x = torch.Tensor(np.array([x1, x2]))
 
model = OptimizedFunction3D()
 
# 绘制 f_3d函数 的 三维图像
fig = plt.figure()
ax = plt.axes(projection='3d')
X = init_x[0].numpy()
Y = init_x[1].numpy()
Z = model(init_x).numpy()  # 改为 model(init_x).numpy() David 2022.12.4
surf = ax.plot_surface(X, Y, Z, edgecolor='grey', cmap=cm.coolwarm)
# fig.colorbar(surf, shrink=0.5, aspect=1)
ax.set_zlim(-3, 2)
ax.set_xlabel('x1')
ax.set_ylabel('x2')
ax.set_zlabel('f(x1,x2)')
 
labels = ['Adam', 'AdaGrad', 'RMSprop', 'Momentum', 'SGD']
colors = ['#8B0000', '#0000FF', '#000000', '#008B00', '#FF0000']
 
animator = Visualization3D(*x_all_opts, z_values=z_all_opts, labels=labels, colors=colors, fig=fig, ax=ax)
ax.legend(loc='upper right')
 
plt.show()

运行结果:
在这里插入图片描述

总结

本次实验对不同优化算法比较,对上次作业各种算法的比较进行了验证。对于AdaGrad算法和RMSprop算法有了更深的了解。

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

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

相关文章

【并发】深度解析CAS原理与底层源码

【并发】深度解析CAS原理与底层源码 什么是 CAS? CAS全称是(Compare And Swap,比较并交换),通常指的是这样一种原子操作(针对一个变量,首先比较它的内存值与某个期望值是否相同,如…

不就是Redis吗?竟让我一个月拿了8个offer,其中两家都是一线大厂

在高并发的场景Redis是必须的,而 Redis非关系型内存存储不可谓不彪悍。 支持异步持久化达到容灾:速度快、并发高。官方号称支持并发11万读操作,并发8万写操作。惊了吗? 支持数据结构丰富:string(字符串&a…

盘点5种最频繁使用的检测异常值的方法(附Python代码)

本文介绍了数据科学家必备的五种检测异常值的方法。 无论是通过识别错误还是主动预防,检测异常值对任何业务都是重要的。本文将讨论五种检测异常值的方法。 文章目录什么是异常值?为什么我们要关注异常值?技术提升方法1——标准差方法2——箱…

【OpenEnergyMonitor】开源的能源监控系统--项目介绍

OpenEnergyMonitor1. 系统框架2.项目组成2.1 emonPi模块:2.1.1 emonpi的安装:2.1.2 emonTx & emonBase 安装2.1.3 emonTx Wifi 安装:2.1.4 添加额外的 emonTx 节点:2.1.5 添加额外的emonTx-节点监控三项电源2.1.6 添加 emonTH 温度节点2.…

【Vue核心】8.计算属性

1. 定义: 要用的属性不存在,要通过已有属性计算得来。 2. 原理 底层借助了objcet.defineproperty方法提供的getter fllsetter. 3. get两数什么时候执行? (1),初次读取时会执行一次。 (2),当依赖的数据发生改变时会被再次调用。 4. 优势 与methods实现相比,内部有缓存机…

进厂手册:Git 学习笔记(详解命令)

文章目录git 对象通过git对象进行文件的保存git对象的缺点树对象构建树对象提交对象高层命令工作区的文件状态git reset hard 咋用以及用错了怎么恢复git checkout vs git resetGit存储后悔药工作区暂存区版本库reset三部曲checkout深入理解tag远程上的相关操作ssh登入一些个人…

[附源码]计算机毕业设计家庭整理服务管理系统Springboot程序

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis Maven Vue 等等组成,B/S模式 M…

LeetCode刷题复盘笔记—一文搞懂动态规划之213. 打家劫舍 II问题(动态规划系列第十八篇)

今日主要总结一下动态规划完全背包的一道题目,213. 打家劫舍 II 题目:213. 打家劫舍 II Leetcode题目地址 题目描述: 你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一…

快速排序详解

快速排序,简称快排。其实看快速排序的名字就知道它肯定是一个很牛的排序,C语言中的qsort和C中的sort底层都是快排。 快速排序由于排序效率在同为O(N*logN)的几种排序方法中效率较高,因此经常被采用,再加上快速排序思想----分治法…

Opencv 基本操作五 各种连通域处理方法

在深度学习中,尤其是语义分割模型部署的结果后处理中,离不开各类形态学处理方法,其中以连通域处理为主;同时在一些传统的图像处理算法中,也需要一些形态学、连通域处理方法。为此,整理了一些常用的连通域处…

leetcode每日一题寒假版:1691. 堆叠长方体的最大高度 (hard)( 换了皮的最长递增子序列)

2022-12-10 1691. 堆叠长方体的最大高度 (hard) 🚩 学如逆水行舟,不进则退。 —— 《增广贤文》 题目描述: 给你 n 个长方体 cuboids ,其中第 i 个长方体的长宽高表示为 cuboids[i] [width(i), length(i), height(i)]&#xf…

Docker补充知识点--自定义网络实现直连容器

前面介绍docker镜像的秘密这篇知识点的时候,https://blog.csdn.net/dudadudadd/article/details/128200522,提到了docker容器也有属于自己的IP的概念,默认的Docker容器是采用的是bridge网络模式。并且提到了一嘴自定义网卡配置,本…

java基于Springboot的健身房课程预约平台-计算机毕业设计

项目介绍 开发语言:Java 框架:springboot JDK版本:JDK1.8 服务器:tomcat7 数据库:mysql 数据库工具:Navicat11 开发软件:eclipse/myeclipse/idea Maven包:Maven 本健身网站系统是针…

Unity纹理优化:缩小包体

Android打包apk大小约:475M 查看打包日志:Console→Open Editor Log; 或者依赖第三方插件:build reports tool(在unity store里可以下载); 定位问题 经过排查后,发现项目中纹理占比很高&#…

分布式能源的不确定性——风速测试(Matlab代码实现)

💥💥💥💞💞💞欢迎来到本博客❤️❤️❤️💥💥💥🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清…

(6)Pytorch数据处理

Pytorch 数据处理 要点总结 1、功能 Dataset:准备数据集,一般会针对自己的数据集格式重写Dataset,定义数据输入输出格式 Dataloader:用于加载数据,通常不用改这部分内容 2、看代码时请关注 Dataloader中collate_fn 传入…

【云原生】K8s Ingress rewrite与TCP四层转发讲解与实战操作

文章目录一、背景二、K8s Ingress安装三、K8s Ingress rewrite 讲解与使用1)配置说明2)示例演示1、部署应用2、配置ingress rewrite转发(http)3、配置ingress rewrite转发(https)【1】创建证书(…

音视频- iOS图像采集

本文主要总结一下,如何使用AVFoundation的功能来实现图像的采集,主要用到了AVFoundation中的一些类,采集的结构如下图,引用自iOS开发者官网: AVCaptureSession 采集会话,其主要功能从整体上来掌管图像采集的…

MOSFET 和 IGBT 栅极驱动器电路的基本原理学习笔记(五)交流耦合栅极驱动电路

交流耦合栅极驱动电路 1.计算耦合电容 2.耦合电容器的启动瞬变 3.总结 栅极驱动路径中的交流耦合可为栅极驱动信号提供简单的电平位移。交流耦合的主要作用是修改主MOSFET 的开通和关断栅极电压,而高侧栅极驱动则不同,它最需要关注的是缩小较大的电势差…

软件安全测试-web安全测试基础

目录 1. Web安全的测试范围 2.Web安全的四要素 3. Web安全的分类 4. Web安全的类别排名​ 5. 零时差攻击 6. Web安全的载体 7. 了解软件安全测试相关的Cooike,Session,Token 7.1 会话级鉴权及认证技术 7.2 会话安全管理需要授权和鉴权两个步骤 …