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

本文主要是介绍结合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] = Xfor 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 - 1ints = 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 = -1Z = 0# create cost function  for i in range(nTrain):localSum = 0for 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 * localSumZ += localSum# fetch the correct classification layersbk = 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 = 0XbatchTest = 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 samplesints = 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 transformationsif opt.transform: Xbatch = transform(Xbatch)data = Variable(torch.from_numpy(Xbatch)).float().cuda()for i in range(n): data = allBlocks[i](data)# get gradientsoutput = modelTmp(data)loss = torch.exp(criterion(output, Ybatch))loss.backward()err += loss.data[0]# evaluate training accuracyoutput = 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.printEveryaccTest /= opt.printEveryerr /= opt.printEveryprinter([n, rounds, totalIterations + batch + (opt.checkEvery * tries), err, accTrain, accTest])accTrain = 0;accTest = 0; err = 0;optimizer.step()totalIterations += opt.checkEverytries += 1# get performance of new layera_current, Xoutput_previous = getPerformance(modelTmp, Xtrain, Ytrain, n + 1)gamma = (a_current - a_previous) / (1 - a_current)a_previous = a_currentprinter([n, gamma, opt.gammaThresh])if gamma < opt.gammaThresh:breakelse: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 = -1Z = 0
  • gamma = -1Z = 0 初始化了一些用于计算的变量。
  • rounds 是算法的迭代轮数。在代码中设置为25,每轮迭代都会添加一个新的残差块,并调整模型参数。
  • 每一轮次训练前将 gammaZ 置零。

2.计算代价函数

    for i in range(nTrain):localSum = 0for 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 * localSumZ += 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 = 0XbatchTest = 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.printEveryaccTest  /= opt.printEveryerr /= opt.printEveryprinter([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) / Zgamma = (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 += 1if (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_previousaccTest, _ = getPerformance(modelTmp, Xtest, Ytest, n)    printer(['t', rounds, 'numBatches:', tries * opt.checkEvery, 'test accuracy:', accTest])    gamma_previous = gamma_current

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

这篇关于结合Boosting理论与深度ResNet:ICML2018论文代码详解与实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1071293

相关文章

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

Debezium 与 Apache Kafka 的集成方式步骤详解

《Debezium与ApacheKafka的集成方式步骤详解》本文详细介绍了如何将Debezium与ApacheKafka集成,包括集成概述、步骤、注意事项等,通过KafkaConnect,D... 目录一、集成概述二、集成步骤1. 准备 Kafka 环境2. 配置 Kafka Connect3. 安装 D

Java中ArrayList和LinkedList有什么区别举例详解

《Java中ArrayList和LinkedList有什么区别举例详解》:本文主要介绍Java中ArrayList和LinkedList区别的相关资料,包括数据结构特性、核心操作性能、内存与GC影... 目录一、底层数据结构二、核心操作性能对比三、内存与 GC 影响四、扩容机制五、线程安全与并发方案六、工程

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

Java调用DeepSeek API的最佳实践及详细代码示例

《Java调用DeepSeekAPI的最佳实践及详细代码示例》:本文主要介绍如何使用Java调用DeepSeekAPI,包括获取API密钥、添加HTTP客户端依赖、创建HTTP请求、处理响应、... 目录1. 获取API密钥2. 添加HTTP客户端依赖3. 创建HTTP请求4. 处理响应5. 错误处理6.

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

Spring Cloud LoadBalancer 负载均衡详解

《SpringCloudLoadBalancer负载均衡详解》本文介绍了如何在SpringCloud中使用SpringCloudLoadBalancer实现客户端负载均衡,并详细讲解了轮询策略和... 目录1. 在 idea 上运行多个服务2. 问题引入3. 负载均衡4. Spring Cloud Load

Springboot中分析SQL性能的两种方式详解

《Springboot中分析SQL性能的两种方式详解》文章介绍了SQL性能分析的两种方式:MyBatis-Plus性能分析插件和p6spy框架,MyBatis-Plus插件配置简单,适用于开发和测试环... 目录SQL性能分析的两种方式:功能介绍实现方式:实现步骤:SQL性能分析的两种方式:功能介绍记录