结合Boosting理论与深度ResNet:ICML2018论文代码详解与实现

代码见:JordanAsh/boostresnet: A PyTorch implementation of BoostResNet

原始论文:Huang F, Ash J, Langford J, et al. Learning deep resnet blocks sequentially using boosting theory[C]//International Conference on Machine Learning. PMLR, 2018: 2058-2067.

代码解析及功能实现

这段代码实现了一个训练ResNet块并逐层训练的过程。这个过程结合了Boosting理论,通过逐层训练ResNet块来提高分类性能。

代码前期准备

import torch
import sys
import pickle
import os
import numpy as np
import torchfile
from torch import nn
from torch.autograd import Variable
import argparse

导入了必要的库,包括PyTorch、NumPy、系统操作、数据加载和命令行参数解析库。

解析命令行参数

parser = argparse.ArgumentParser()
parser.add_argument('--gammaFirst', default=0.5, help='initial gamma')
parser.add_argument('--checkEvery', default=10000, help='how frequently to check gamma requirement - 10k for cifar, 5k for svhn')
parser.add_argument('--data', default='SVHN.t7', help='load data')
parser.add_argument('--gammaThresh',default=-0.0001, help='gamma threshold to stop training layer')
parser.add_argument('--lr', default=0.001, help='learning rate')
parser.add_argument('--maxIters', default=10000, help='maximum iterations before stopping train layer')
parser.add_argument("--transform", help='do CIFAR-style image transformations?', action="store_true")
parser.add_argument('--printEvery', default=100, help='how frequently to print')
parser.add_argument('--batchSize', default=100, help='batch size')
parser.add_argument('--modelPath', default='model.pt', help='output model')
opt = parser.parse_args()

解析命令行参数,设置各种训练超参数,包括初始gamma值、检查gamma的频率、数据集路径、gamma阈值、学习率、最大迭代次数、是否进行数据变换、打印频率、批处理大小和模型保存路径。

加载数据

data = torchfile.load(opt.data) # load dataset in torch format (assuming already normalized)
Xtrain = data.Xtrain
Ytrain = data.Ytrain - 1
Xtest = data.Xtest
Ytest = data.Ytest - 1
cut = int(np.shape(Xtrain)[0] / opt.batchSize * opt.batchSize) # cut off a few samples for simplicity
nTrain = cut
Xtrain = Xtrain[:cut]
Ytrain = Ytrain[:cut]
cut = int(np.shape(Xtest)[0] / opt.batchSize * opt.batchSize)
Xtest = Xtest[:cut]
Ytest = Ytest[:cut]
nTest = cut
numClasses = 10

加载并处理数据集,将训练集和测试集按批处理大小进行裁剪,以确保数据大小是批处理大小的整数倍。

定义打印函数

def printer(print_arr):
    for v in print_arr: sys.stdout.write(str(v) + '\t')
    sys.stdout.write('\n')
    sys.stdout.flush()

定义一个打印函数,用于在控制台打印输出。

加载ResNet模型并构建块

import fbrn
model = fbrn.tmp
model.load_state_dict(torch.load('fbrn.pth'))
allBlocks = {}
allBlocks[0] = nn.Sequential(model[0], model[1], model[2])
for i in range(8): allBlocks[1 + i] = model[3][i] 
for i in range(8): allBlocks[9 + i] = model[4][i] 
for i in range(8): allBlocks[17+ i] = model[5][i]
criterion = nn.CrossEntropyLoss().cuda()
nFilters = 15; rounds = 25

加载预训练的ResNet模型,并将其分成多个块,分别存储在allBlocks字典中。定义损失函数为交叉熵损失。

  1. allBlocks[0] 包含 model 的第 0 层到第 2 层,使用 nn.Sequential 将这些层组合在一起。
  2. for i in range(8): allBlocks[1 + i] = model[3][i]model 的第 3 层的 8 个子层分别存储在 allBlocks 的索引 1 到 8。
  3. for i in range(8): allBlocks[9 + i] = model[4][i]model 的第 4 层的 8 个子层分别存储在 allBlocks 的索引 9 到 16。
  4. for i in range(8): allBlocks[17 + i] = model[5][i]model 的第 5 层的 8 个子层分别存储在 allBlocks 的索引 17 到 24。

这样,allBlocks 字典就包含了模型的所有层,并且这些层被分割成了不同的块,每个块对应一个连续的索引范围。

分块的意义

这段代码将模型分为了三个主要的残差块(residual blocks):

  1. 第 0 块:allBlocks[0] 包含模型的第 0 层到第 2 层。
  2. 第 1 块:allBlocks[1]allBlocks[8] 包含模型的第 3 层的 8 个子层。
  3. 第 2 块:allBlocks[9]allBlocks[16] 包含模型的第 4 层的 8 个子层。
  4. 第 3 块:allBlocks[17]allBlocks[24] 包含模型的第 5 层的 8 个子层。

数据增强函数

def transform(X):
    tmp = np.zeros((np.shape(X)[0], 3, 38, 38))
    tmp[:, :, 2:34, 2:34] = X
    for i in range(np.shape(X)[0]):
        r1 = np.random.randint(4)
        r2 = np.random.randint(4)
        X[i] = tmp[i, :, r1 : r1 + 32, r2 : r2 + 32]
        if np.random.uniform() > .5:
            X[i] = X[i, :, :, ::-1]
    return X

定义数据增强函数,用于CIFAR样式的图像变换,进行随机裁剪和水平翻转。

模型评估函数

def getPerformance(net, X, Y, n):
    acc = 0.
    model.eval()
    Xoutput = np.zeros((X.shape[0], 10))
    for batch in range(int(X.shape[0] / opt.batchSize)):
        start = batch * opt.batchSize; stop = (batch + 1) * opt.batchSize - 1
        ints = np.linspace(start, stop, opt.batchSize).astype(int)
        data = Variable(torch.from_numpy(X[ints])).float().cuda()
        for i in range(n): data = allBlocks[i](data)
        output = net(data)
        acc += np.mean(torch.max(output, 1)[1].cpu().data.numpy() == Y[ints])
        Xoutput[ints] = output.cpu().data.numpy()
    acc /= (X.shape[0] / opt.batchSize)
    model.train()
    return acc, Xoutput

定义模型评估函数,用于在训练和测试数据集上计算模型的准确率。

初始化模型统计数据

a_previous = 0.0
a_current = -1.0
s = np.zeros((nTrain, numClasses))
cost = np.zeros((nTrain, numClasses))
Xoutput_previous = np.zeros((nTrain, numClasses))
Ybatch = np.zeros((opt.batchSize))
YbatchTest = np.zeros((opt.batchSize))
gamma_previous = opt.gammaFirst
totalIterations = 0; tries = 0

初始化一些变量,用于存储模型统计数据、损失、输出和其他辅助数据。

逐层训练模型

for n in range(rounds):
    gamma = -1
    Z = 0

    # create cost function  
    for i in range(nTrain):
        localSum = 0
        for l in range(numClasses):
            if l != Ytrain[i]:
                cost[i][l] = np.exp(s[i][l] - s[i][int(Ytrain[i])])
                localSum += cost[i][l]
        cost[i][int(Ytrain[i])] = -1 * localSum
        Z += localSum

    # fetch the correct classification layers
    bk = allBlocks[n]
    ci = nn.Sequential(model[6], model[7], model[8])
    if n < 17: ci = nn.Sequential(allBlocks[17], ci)
    if n < 9: ci = nn.Sequential(allBlocks[9], ci)
    modelTmp = nn.Sequential(bk, ci, nn.Softmax(dim=0))
    modelTmp = modelTmp.cuda()
    optimizer = torch.optim.Adam(modelTmp.parameters(), lr=opt.lr) 
    tries = 0
    XbatchTest = torch.zeros(opt.batchSize, nFilters, 32, 32)
    while (gamma < opt.gammaThresh and ((opt.checkEvery * tries) < opt.maxIters)):
        accTrain = 0; 
        accTest = 0; 
        err = 0;
        for batch in range(1, opt.checkEvery + 1):
            optimizer.zero_grad()

            # get batch of training samples
            ints = np.random.random_integers(np.shape(Xtrain)[0] - 1, size=(opt.batchSize))
            Xbatch = Xtrain[ints]
            Ybatch = Variable(torch.from_numpy(Ytrain[ints])).cuda().long()

            # do transformations
            if opt.transform: Xbatch = transform(Xbatch)
            data = Variable(torch.from_numpy(Xbatch)).float().cuda()
            for i in range(n): data = allBlocks[i](data)

            # get gradients
            output = modelTmp(data)
            loss = torch.exp(criterion(output, Ybatch))
            loss.backward()
            err += loss.data[0]

            # evaluate training accuracy
            output = modelTmp(data)
            accTrain += np.mean(torch.max(output, 1)[1].cpu().data.numpy() == Ytrain[ints])

            # get test accuracy 
            model.eval()
            ints = np.random.random_integers(np.shape(Xtest)[0] - 1, size=(opt.batchSize))
            Xbatch = Xtest[ints]
            data = Variable(torch.from_numpy(Xbatch)).float().cuda()
            for i in range(n): data = allBlocks[i](data)
            output = modelTmp(data)
            accTest += np.mean(torch.max(output, 1)[1].cpu().data.numpy() == Ytest[ints])
            model.train()

            if batch % opt.printEvery == 0:
                accTrain /= opt.printEvery
                accTest /= opt.printEvery
                err /= opt.printEvery
                printer([n, rounds, totalIterations + batch + (opt.checkEvery * tries), err, accTrain, accTest])
                accTrain = 0;

 
                accTest = 0; 
                err = 0;
            
            optimizer.step()

        totalIterations += opt.checkEvery
        tries += 1

        # get performance of new layer
        a_current, Xoutput_previous = getPerformance(modelTmp, Xtrain, Ytrain, n + 1)
        gamma = (a_current - a_previous) / (1 - a_current)
        a_previous = a_current

    printer([n, gamma, opt.gammaThresh])
    if gamma < opt.gammaThresh:
        break
    else:
        s += np.log(Xoutput_previous)

逐层训练ResNet块,使用Boosting理论来增强分类性能。具体步骤包括:

  1. 计算损失函数。
  2. 加载对应的ResNet块。
  3. 进行优化迭代,计算训练和测试集上的准确率,并根据gamma值判断是否继续训练。

保存模型

torch.save(model.state_dict(), opt.modelPath)

保存训练好的模型。

核心代码

初始化部分

a_previous = 0.0
a_current = -1.0
s = np.zeros((nTrain, numClasses))
cost = np.zeros((nTrain, numClasses))
Xoutput_previous = np.zeros((nTrain, numClasses))
Ybatch = np.zeros((opt.batchSize))
YbatchTest = np.zeros((opt.batchSize))
gamma_previous = opt.gammaFirst
totalIterations = 0; tries = 0

这里初始化了一些变量,包括模型的统计信息、训练样本的预测输出、批量训练样本等。其中:

  • a_previousa_current 是当前和之前的提升系数。
  • s 是一个保存每个训练样本在每个类别上的累积输出。
  • cost 是样本的代价矩阵。
  • Xoutput_previous 是上一轮的输出。
  • gamma_previous 是上一轮的提升系数。
  • totalIterationstries 用于记录总迭代次数和尝试次数。

1.初始化阶段(主循环)

for n in range(rounds):
    gamma = -1
    Z = 0
  • gamma = -1Z = 0 初始化了一些用于计算的变量。
  • rounds 是算法的迭代轮数。在代码中设置为25,每轮迭代都会添加一个新的残差块,并调整模型参数。
  • 每一轮次训练前将 gammaZ 置零。

2.计算代价函数

    for i in range(nTrain):
        localSum = 0
        for l in range(numClasses):
            if l != Ytrain[i]:
                cost[i][l] = np.exp(s[i][l] - s[i][int(Ytrain[i])])
                localSum += cost[i][l]
        cost[i][int(Ytrain[i])] = -1 * localSum
        Z += localSum

这个部分计算每个训练样本的代价函数,并将计算所有错误分类的代价,存储在 cost 数组中。

每个训练样本的成本通过计算每个类别的错误分类代价 cost。如果样本 i 的真实类别为 Ytrain[i],则计算该样本在其他类别上的成本之和 localSum,并将 localSum 赋值给该样本真实类别的成本(取负值)。

  • cost[i][l] = np.exp(s[i][l] - s[i][int(Ytrain[i])]) 计算样本 i i i 在类别 l l l 上的代价。
  • cost[i][int(Ytrain[i])] = -1 * localSum 将当前样本在其真实类别上的代价设为负的 localSum
  • Z 是所有样本的总代价。

3. 获取当前分类层

    bk = allBlocks[n]
    ci = nn.Sequential(model[6], model[7], model[8])
    if n < 17: ci = nn.Sequential(allBlocks[17], ci)
    if n < 9:  ci = nn.Sequential(allBlocks[9], ci)
    modelTmp = nn.Sequential(bk, ci, nn.Softmax(dim=0))
    modelTmp = modelTmp.cuda()

这段代码的作用是创建一个临时模型 modelTmp,它由当前迭代的残差块 bk 和一些预定义的分类层 ci 组成。以下是具体解释:

  • bk 表示当前迭代的残差块。
  • ci 表示后续的分类层,包含模型中的一些层。

根据当前迭代次数 n,动态调整 ci 的层次结构:

  • 如果当前迭代次数 n 小于 17,那么在分类层 ci 前面加上第 17 块的残差块。
  • 如果当前迭代次数 n 小于 9,那么在分类层 ci 前面加上第 9 块的残差块。

这样的设计使得模型在不同的迭代次数 n 下,分类层 ci 的结构不同,逐步增加模型的复杂性。这个结构确保了模型能够逐层学习和调整,从而提高分类性能。

4.1 优化器设置

    optimizer = torch.optim.Adam(modelTmp.parameters(), lr=opt.lr) 
    tries = 0
    XbatchTest = torch.zeros(opt.batchSize, nFilters, 32, 32)

使用 Adam 优化器,并初始化 XbatchTest 用于测试。

Adam 优化器是什么?大概的数学原理是什么?是对应的梯度下降算法吗?还是什么其他的算法?

Adam(Adaptive Moment Estimation)是一种基于一阶梯度的优化算法,结合了动量和自适应学习率两个方法。它在深度学习中被广泛使用,因为它在处理稀疏梯度和非平稳目标上表现良好。
Adam的数学原理:

  • 计算每个参数的梯度的一阶动量(平均值)和二阶动量(方差)。
  • 对每个参数的更新使用动量和学习率的校正。

具体来说,Adam 优化器的步骤如下:

  1. 初始化动量和二阶动量。
  2. 在每次迭代中更新动量和二阶动量。
  3. 根据动量和二阶动量校正参数更新步长。

具体的更新公式为:

  1. m t = β 1 m t − 1 + ( 1 − β 1 ) g t m_t = \beta_1 m_{t-1} + (1 - \beta_1) g_t mt=β1mt1+(1β1)gt
  2. v t = β 2 v t − 1 + ( 1 − β 2 ) g t 2 v_t = \beta_2 v_{t-1} + (1 - \beta_2) g_t^2 vt=β2vt1+(1β2)gt2
  3. m ^ t = m t 1 − β 1 t \hat{m}_t = \frac{m_t}{1 - \beta_1^t} m^t=1β1tmt
  4. v ^ t = v t 1 − β 2 t \hat{v}_t = \frac{v_t}{1 - \beta_2^t} v^t=1β2tvt
  5. θ t = θ t − 1 − η m ^ t v ^ t + ϵ \theta_t = \theta_{t-1} - \eta \frac{\hat{m}_t}{\sqrt{\hat{v}_t} + \epsilon} θt=θt1ηv^t +ϵm^t
    其中:
  • g t g_t gt 是梯度。
  • m t m_t mt v t v_t vt 分别是一阶和二阶动量。
  • β 1 \beta_1 β1 β 2 \beta_2 β2 是动量系数,通常取 0.9 0.9 0.9 0.999 0.999 0.999
  • ϵ \epsilon ϵ 是一个小常数,用于防止分母为零,通常取 1 0 − 8 10^{-8} 108
  • η \eta η 是学习率。

4.2 训练循环

在这里插入图片描述

while (gamma < opt.gammaThresh and ((opt.checkEvery * tries) < opt.maxIters)):
    accTrain = 0; 
    accTest = 0; 
    err = 0;
    for batch in range(1, opt.checkEvery+1):
        optimizer.zero_grad()

zero_grad():在每次反向传播之前,将所有参数的梯度缓存清零。在 PyTorch 中,梯度是累加的,所以需要在每次迭代开始前清零。

在满足 gamma 小于阈值和最大迭代次数限制的条件下,开始训练循环。

关于 opt.gammaThreshgamma

这里的 opt.gammaThresh 对应算法中的 γ t \gamma_t γt,而 gamma 对应的是 γ \gamma γ。代码中的逻辑 while (gamma < opt.gammaThresh) 实际上在检查当前的 γ \gamma γ 是否小于阈值。这是因为我们希望 γ \gamma γ 足够大,代表模型的分类能力足够强,当 γ \gamma γ 小于阈值时,停止训练。

具体来说:

  • gamma 初始化为 -1,表示最差情况。
  • 当训练时,更新 gamma 的值,如果 gamma 达到或超过 opt.gammaThresh,则说明模型性能达到预期,可以停止训练。
获取训练样本批次
            ints = np.random.random_integers(np.shape(Xtrain)[0] - 1, size=(opt.batchSize))
            Xbatch = Xtrain[ints]
            Ybatch = Variable(torch.from_numpy(Ytrain[ints])).cuda().long()

从训练集中随机选择一个批次样本,并将其转换为 PyTorch 变量。

数据变换和前向传播
            if opt.transform: Xbatch = transform(Xbatch)
            data = Variable(torch.from_numpy(Xbatch)).float().cuda()
            for i in range(n): data = allBlocks[i](data)

如果指定了数据变换,则对数据进行变换,然后进行前向传播,通过前 n 个残差块处理数据。

获取梯度和更新权重
            output = modelTmp(data)
            loss = torch.exp(criterion(output, Ybatch))
            loss.backward()
            err += loss.data[0]

            output = modelTmp(data)
            accTrain += np.mean(torch.max(output,1)[1].cpu().data.numpy() == Ytrain[ints])

            model.eval()
            ints = np.random.random_integers(np.shape(Xtest)[0] - 1, size=(opt.batchSize))
            Xbatch = Xtest[ints]
            data = Variable(torch.from_numpy(Xbatch)).float().cuda()
            for i in range(n): data = allBlocks[i](data)
            output = modelTmp(data)
            accTest += np.mean(torch.max(output,1)[1].cpu().data.numpy() == Ytest[ints])
            model.train()

在训练过程中,通过交叉熵+指数损失函数计算并累积损失 err

criterion = nn.CrossEntropyLoss().cuda()

计算训练准确率 accTrain 和测试准确率 accTest

打印和梯度裁剪
            if batch % opt.printEvery == 0:
                accTrain /= opt.printEvery
                accTest  /= opt.printEvery
                err /= opt.printEvery
                printer([n, rounds, totalIterations + batch + (opt.checkEvery * tries), err, accTrain, accTest])
                accTrain = 0; accTest = 0; err = 0;

            for p in modelTmp.parameters(): p.grad.data.clamp_(-.1, .1)            
            optimizer.step()
  • 每隔 opt.printEvery 次批量训练输出一次当前状态,包括错误率、训练准确率和测试准确率。
  • 进行梯度裁剪,防止梯度爆炸。
  • step():执行一步优化算法,即根据当前的梯度和动量更新模型参数。

梯度裁剪对应的代码是:

for p in modelTmp.parameters(): 
    p.grad.data.clamp_(-.1, .1)

这段代码通过限制梯度的值在 [-0.1, 0.1] 范围内,防止梯度爆炸。这对于稳定训练过程非常重要,尤其是深度神经网络,梯度可能会在反向传播过程中变得非常大。

计算 gamma

        accTrain, Xoutput = getPerformance(modelTmp, Xtrain, Ytrain, n)
        gamma_current = -1 * np.sum(Xoutput * cost) / Z
        gamma = (gamma_current ** 2 - gamma_previous ** 2)/(1 - gamma_previous ** 2) 
        if gamma > 0: 
            gamma = np.sqrt(gamma)
        else: 
            gamma = -1 * np.sqrt(-1 * gamma)
        a_current = 0.5 * np.log((1 + gamma_current) / (1 - gamma_current))

更新模型参数,并在每个批量训练后进行一次梯度下降。

  1. 计算当前模型的性能,得到 gamma_current
  2. 根据 gamma_currentgamma_previous 更新 gamma

公式:
γ t ← γ ~ t + 1 2 − γ ~ t 2 1 − γ ~ t 2 \gamma_t \leftarrow \sqrt{\frac{\tilde{\gamma}_{t+1}^2 - \tilde{\gamma}_t^2}{1 - \tilde{\gamma}_t^2}} γt1γ~t2γ~t+12γ~t2

其中:

  • gamma_current 是当前训练轮次的 γ t \gamma_t γt
  • gamma_previous 是前一次训练轮次的 γ t \gamma_t γt
为什么这样处理负数的情况?

γ \gamma γ 为负数时,我们使用以下公式进行更新:
γ = − 1 ∗ − 1 ∗ γ ~ t + 1 2 − γ ~ t 2 1 − γ ~ t 2 \gamma = -1 * \sqrt{-1 * \frac{\tilde{\gamma}_{t+1}^2 - \tilde{\gamma}_t^2}{1 - \tilde{\gamma}_t^2}} γ=111γ~t2γ~t+12γ~t2

这样处理的原因是:

  • γ \gamma γ 的计算可能会因为 γ ~ t + 1 2 − γ ~ t 2 \tilde{\gamma}_{t+1}^2 - \tilde{\gamma}_t^2 γ~t+12γ~t2 的值而导致根号内出现负数。这在数学上是不可接受的。
  • 为了避免这种情况,取负号后再开方,以确保 γ \gamma γ 的计算结果是一个实数。负号处理后仍然保持 γ \gamma γ 的真实值,因为 γ \gamma γ 本身可以是负数或正数。

这个处理方式巧妙地避免了计算过程中根号内为负数的问题,同时保持了 γ \gamma γ 的实际含义。

尝试次数和权重更新

        tries += 1
        if (gamma > opt.gammaThresh or ((opt.checkEvery * tries) >= opt.maxIters)):
            totalIterations = totalIterations + (tries * opt.checkEvery)
            printer([gamma, gamma_current, gamma_previous])
            printer(['a_{t+1}:', a_current, 'gamma_t:', gamma])    

    s += Xoutput * a_current - Xoutput_previous * a_previous
    accTest, _ = getPerformance(modelTmp, Xtest, Ytest, n)    
    printer(['t', rounds, 'numBatches:', tries * opt.checkEvery, 'test accuracy:', accTest])    
    gamma_previous = gamma_current

更新 tries,如果 gamma 达到阈值或尝试次数达到最大迭代次数,则记录迭代次数并打印信息。最后更新累积输出 sgamma_previous

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

英特尔 “AI” 科通:英特尔AI大模型应用前瞻

亲爱的科技探险家、前沿探索者、对未来深具好奇心的您&#xff0c; 身处人工智能引领的时代&#xff0c;我们目睹着行业的革命性变革。技术的创新不仅改变着我们的日常&#xff0c;更重新定义着我们对未来的期许。今天&#xff0c;怀着无限激情和期待&#xff0c;我们邀请您参…

国际数字影像产业园:建设与推动企业孵化与梯次培育

国际数字影像产业园在建设与推动企业孵化及梯次培育方面取得了显著成效。未来&#xff0c;随着技术的不断进步和市场的不断扩大&#xff0c;园区将继续发挥其在数字经济产业中的引领作用&#xff0c;为文化产业的发展贡献更多力量。 一、企业孵化与入驻 企业入驻情况&#xff…

物联边缘网关如何助力工厂实现智能化生产?以某智能制造工厂为例-天拓四方

随着工业4.0的深入推进&#xff0c;智能制造工厂成为了工业发展的重要方向。在这个背景下&#xff0c;物联边缘网关以其独特的优势在智能制造工厂中发挥着越来越重要的作用。以下将通过一个具体的智能制造工厂应用案例&#xff0c;来阐述物联边缘网关如何助力工厂实现智能化生产…

Milvus跨集群数据迁移

将 Milvus 数据从 A 集群&#xff08;K8S集群&#xff09;迁到 B 集群&#xff08;K8S集群&#xff09;&#xff0c;解决方案很多&#xff0c;这里提供一个使用官方 milvus-backup 工具进行数据迁移的方案。 注意&#xff1a;此方案为非实时同步方案&#xff0c;但借助 MinIO 客…

在3D视觉技术的帮助下,轻松实现纸箱拆码垛

在繁忙的物流仓库中&#xff0c;纸箱的拆码垛工作常常让人头疼不已。但是&#xff0c;现在有了富唯智能的3D视觉引导纸箱拆码垛解决方案&#xff0c;这一切都变得轻松简单&#xff01; 想象一下&#xff0c;那些堆积如山的纸箱&#xff0c;在3D视觉技术的帮助下&#xff0c;仿…

黄仁勋:下一波AI的浪潮是物理AI

B站&#xff1a;啥都会一点的研究生公众号&#xff1a;啥都会一点的研究生 最近AI圈又发生了啥&#xff1f; 快手视频生成大模型“可灵”开放邀测&#xff0c;效果对标 Sora 在OpenAl文生视频大模型Sora发布后&#xff0c;国内企业争相入局&#xff0c;快手视频生成大模型可…

Confluence安装

Confluence安装 1.安装 #下载confluence版本&#xff08;8.5.11&#xff09; https://www.atlassian.com/software/confluence/download-archives #修改权限 chmod x atlassian-confluence-8.5.11-x64.bin #执行安装 ./atlassian-confluence-8.5.11-x64.bin按照以下提示输入&…

NettyのEventLoopChannel

Netty的重要组件&#xff1a;EventLoop、Channel、Future & Promise、Handler & Pipeline、ByteBuf 本篇主要介绍Netty的EventLoop和Channel组件。 1、Netty入门案例 服务器端的创建&#xff0c;主要分为以下步骤&#xff1a; 创建serverBootstrap对象。配置服务器的…

Avalonia for VSCode

1、在VSCode中编辑AvaloniaUI界面&#xff0c;在VSCode中搜索Avalonia&#xff0c;并安装。如下图&#xff0c;可以发现Avalonia for VSCode还是预览版。 2、 创建一个Avalonia 项目。 选择项目类型 输入项目名称 选择项目所在文件夹 打开项目 3、项目架构如下图。 4、builde…

基于jeecgboot-vue3的Flowable流程-所有任务

因为这个项目license问题无法开源&#xff0c;更多技术支持与服务请加入我的知识星球。 这个部分主要讲所有任务的功能 1、主要列表界面如下&#xff1a; <template><div class"p-2"><!--查询区域--><div class"jeecg-basic-table-form-…

创建型模式--抽象工厂模式

产品族创建–抽象工厂模式 工厂方法模式通过引入工厂等级结构,解决了简单工厂模式中工厂类职责太重的问题。 但由于工厂方法模式中的每个工厂只生产一类产品,可能会导致系统中存在大量的工厂类,势必会增加系统的开销。此时,可以考虑将一些相关的产品组成一个“产品族”,…

什么是Vue开发技术

概述 Vue.js 是一个用于构建用户界面的渐进式框架&#xff0c;它设计得非常灵活&#xff0c;可以轻松地被集成到任何项目中。 vue是视图的发音&#xff0c;其目的是帮助开发者易于上手&#xff0c;提供强大的功能构建复杂的应用程序 示例 以下是vue基本的语法概述 声明式渲…

示例:WPF中TreeView自定义TreeNode泛型绑定对象来实现级联勾选

一、目的&#xff1a;在绑定TreeView的功能中经常会遇到需要在树节点前增加勾选CheckBox框&#xff0c;勾选本节点的同时也要同步显示父节点和子节点状态 二、实现 三、环境 VS2022 四、示例 定义如下节点类 public partial class TreeNodeBase<T> : SelectBindable<…

探秘提交任务到线程池后源码的执行流程

探秘提交任务到线程池后源码的执行流程 1、背景2、任务提交2、Worker线程获取任务执行流程3、Worker线程的退出时机1、背景 2、任务提交 线程池任务提交有两种方式,execute()和submit()。首先看一下execute方法的源码。我们发现它接收的入参是一个Runnable类型。我们按照代码…

常见的Redis使用问题及解决方案

目录 1. 缓存穿透 1.1 解决方案 2. 缓存击穿 2.1 解决方案 3. 缓存雪崩 3.1 概念图及问题描述 ​编辑3.2 解决方案 4. 分布式锁 4.1 概念 4.2 基于redis来实现分布式锁 4.3 用idea来操作一遍redis分布式锁 4.4 分布式上锁的情况下&#xff0c;锁释放了服务器b中的锁…

水滴式粉碎机:粉碎干性物料的理想选择

在工业生产中&#xff0c;干性物料的粉碎是一个重要的环节&#xff0c;其对于提升产品质量、优化生产流程和提高生产效率具有显著的影响。近年来&#xff0c;水滴式粉碎机因其粉碎原理和工作性能&#xff0c;逐渐在干性物料粉碎领域崭露头角&#xff0c;成为众多企业的理想选择…

《Java2实用教程》 期末考试整理

作用域 当前类 当前包 子类 其他包 public √ √ √ √ protected √ √ √ default √ √ private √ 三、问答题&#xff08;每小题4分&#xff0c;共8分&#xff09; 1.类与对象的关系 对象&#xff1a;对象是类的一个实例&#xff0c;有状…

JavaScript事件类型和事件处理程序

● 之前我们用过了很多此的点击事件&#xff0c;这次让我们来学习另一种事件类型 mouseenter “mouseenter” 是一个鼠标事件类型&#xff0c;它在鼠标指针进入指定元素时触发。 const h1 document.querySelector(h1); h1.addEventListener(mouseenter, function (e) {aler…

【将xml文件转yolov5训练数据txt标签文件】连classes.txt都可以生成

将xml文件转yolov5训练数据txt标签文件 前言一、代码解析 二、使用方法总结 前言 找遍全网&#xff0c;我觉得写得最详细的就是这个博文⇨将xml文件转yolov5训练数据txt标签文件 虽然我还是没有跑成功。那个正则表达式我不会改QWQ&#xff0c;但是不妨碍我会训练ai。 最终成功…

关于从大平台跳转各个应用,更新应用前端包后,显示的仍是旧的内容,刷新应用页面后方才显示新的内容的问题的排查和解决

我们从绿洲物联平台跳转智能锁应用&#xff0c; 如下&#xff0c;我们可以看到&#xff0c;我们是通过a标签去跳转应用的。但是我们打开控制台的话&#xff0c;因为a标签是另外新开一个页面&#xff0c;我们看不到新页面的html文档的加载情况。 我们可以临时把_blank改成_sel…