使用Sum计算Loss和解决梯度累积(Gradient Accumulation)的Bug
使用Sum计算Loss和解决梯度累积的Bug
学习 https://unsloth.ai/blog/gradient:Bugs in LLM Training - Gradient Accumulation Fix 这篇文章的记录。
在深度学习训练过程中,尤其是在大批量(large batch)训练中,如何高效地计算损失函数(Loss)并避免内存溢出一直是一个重要的问题。在许多情况下,为了实现更高效的训练,我们会使用梯度累积(Gradient Accumulation)策略,模拟大批量训练的效果,但同时减少对显存的需求。然而,这种方法可能引发一些数值计算错误,尤其是在损失计算时。因此,理解如何处理梯度累积并确保计算正确性就变得尤为重要。
为什么要使用Sum计算Loss?
在代码中,reduce_loss="sum"
表示我们选择将每个小批量(mini-batch)的损失值相加而不是取平均。这种做法有几个目的:
-
权重每个样本的影响一致:在使用大批量训练时,如果直接取每个样本的平均损失,可能会使得样本数目更多的批次对模型训练产生不必要的影响。而通过将损失值求和,确保了每个样本都以相同的权重参与模型更新。
-
避免梯度累积带来的偏差:如果只使用小批量训练的平均损失进行梯度累积(梯度更新),随着梯度累积步骤(gradient accumulation step)增多,损失的计算会变得不准确。因为简单地将每个小批量的损失相加,可能会导致最终计算的损失过大。
梯度累积中的问题
当我们使用梯度累积时,目的是在显存限制下模拟大批量训练的效果。假设每个小批量的计算后,我们累积其梯度,最后再进行一次反向传播。这种方法可以显著减少显存的使用,但也带来了数值误差,特别是在损失的计算上。
梯度累积是否和完整批次训练数学上等价?
答案是: 不完全等价,尤其是如果我们不正确地处理梯度的累积。在数学上,损失函数通常表示为:
L = 1 n ∑ i = 1 n L i L = \frac{1}{n} \sum_{i=1}^{n} L_i L=n1i=1∑nLi
其中,( L i L_i Li ) 是每个样本的损失,( n n n ) 是样本总数。我们通常会对每个小批量的损失进行归一化处理,使得每个样本对损失的贡献相等。
但是,在梯度累积时,我们的处理方法是:
L accumulated = ∑ k = 1 G L k L_{\text{accumulated}} = \sum_{k=1}^{G} L_k Laccumulated=k=1∑GLk
其中,( G G G ) 是梯度累积的步数, ( L k L_k Lk ) 是每个小批量的损失值。这导致累积损失和原本应计算的损失不同,因为没有进行正确的归一化操作。
为什么梯度累积会导致问题?
由于梯度累积在计算损失时没有正确处理每个小批量的权重,最终的损失值可能比实际应有的损失大,特别是当批次大小不一致时。此时,我们需要对每个小批量的损失进行缩放,以使最终的结果与大批量训练的损失相符。
解决方案:修正梯度累积中的损失计算
为了解决梯度累积中损失计算的偏差,我们可以在每次计算梯度时对每个小批量的损失进行缩放。具体来说,假设我们在训练过程中设置了 ( G G G ) 步梯度累积,那么每次计算损失时,我们需要将每个小批量的损失除以 ( G G G ),以确保最终的梯度更新符合预期。
数学公式如下:
L final = 1 G ∑ k = 1 G L k m k L_{\text{final}} = \frac{1}{G} \sum_{k=1}^{G} \frac{L_k}{m_k} Lfinal=G1k=1∑GmkLk
其中,( m k m_k mk ) 是第 ( k k k ) 个小批量的有效样本数(即去除填充后的token数),( G G G ) 是梯度累积的步数。这样做的目的是在每次梯度累积时确保损失值的加权平均,而不是简单地将其相加。
小结
-
Loss的求和:在使用
reduce_loss="sum"
时,我们通过求和的方式来计算损失,而不是简单的平均。这可以确保每个样本对损失计算的贡献一致,避免了梯度累积中的偏差。 -
梯度累积的误差:梯度累积在计算损失时容易出现数值误差,尤其是在不同小批量的损失和有效样本数不一致的情况下。为了解决这一问题,我们需要对每个小批量的损失进行缩放处理,使得最终的损失与大批量训练的结果一致。
通过正确处理梯度累积的损失计算,我们可以在不增加内存消耗的情况下模拟大批量训练的效果,从而提高训练效率。
附录:代码解析
下面是open instruct框架提供的sum计算 loss的代码,它实现了一个损失计算和梯度累积的处理流程。
if args.reduce_loss == "mean":loss = outputs.loss
else:# reduce loss is sum# this ensures that we weight all tokens in the dataset equally,# rather than weighting each overall example equally when# using high amounts of gradient accumulation.# this can result in > 5 point improvements in AlpacaEval# see https://github.com/huggingface/transformers/issues/24725 for# more discussion and details.logits = outputs.logitslabels = batch["labels"]# Shift so that tokens < n predict nshift_logits = logits[..., :-1, :].contiguous()shift_labels = labels[..., 1:].contiguous()# Flatten the tokensloss_fct = torch.nn.CrossEntropyLoss(reduction="sum")shift_logits = shift_logits.view(-1, embedding_size)shift_labels = shift_labels.view(-1)# Enable model parallelismshift_labels = shift_labels.to(shift_logits.device)loss = loss_fct(shift_logits, shift_labels)if args.load_balancing_loss:aux_loss = args.load_balancing_weight * outputs.aux_lossloss += aux_loss
# We keep track of the loss at each logged step
total_loss += loss.detach().float()
accelerator.backward(loss)
1. Loss的选择(reduce_loss
参数)
代码的第一部分是根据args.reduce_loss
参数来决定使用平均(mean
)还是求和(sum
)的损失计算方式。如果选择的是mean
,则直接使用outputs.loss
(通常是模型自带的损失)。如果选择的是sum
,则使用后续的处理方式。
2. 处理sum
模式的损失计算
如果reduce_loss
为sum
,代码会进入求和模式,具体步骤如下:
-
获取logits和labels:从模型的输出
outputs
中获取logits
(预测值)和batch["labels"]
(标签)。 -
Shift操作:为了计算交叉熵损失,模型的输出logits会做一个移位操作,使得每个token的预测目标对应下一个token。这是因为语言模型任务中的预测是基于上一个token的。
shift_logits = logits[..., :-1, :].contiguous() shift_labels = labels[..., 1:].contiguous()
这里
logits[..., :-1, :]
表示除去最后一个token的logits,而labels[..., 1:]
表示去掉第一个token的标签。这样处理的目的是确保每个token的预测值与下一个token的真实标签进行比较。 -
Flatten操作:对处理后的
logits
和labels
进行flatten,展平为一维向量,以便交叉熵损失函数计算。shift_logits = shift_logits.view(-1, embedding_size) shift_labels = shift_labels.view(-1)
这一步是为了将
shift_logits
和shift_labels
转换为适合交叉熵损失函数的格式,即shift_logits
变为[batch_size * sequence_length, embedding_size]
维度,shift_labels
变为[batch_size * sequence_length]
维度。 -
交叉熵损失函数:使用
torch.nn.CrossEntropyLoss
计算损失。这里的reduction="sum"
表示对所有token的损失求和,而不是取平均。这样可以确保每个token的贡献相同。loss_fct = torch.nn.CrossEntropyLoss(reduction="sum") loss = loss_fct(shift_logits, shift_labels)
这段代码计算了每个token的损失,并将所有token的损失求和。对于大批量训练,这样做是为了避免梯度累积时损失计算的不准确。
-
负载平衡损失(Load Balancing Loss):如果
args.load_balancing_loss
为True,表示需要加上额外的负载平衡损失。负载平衡损失用于调整模型在不同任务之间的负载,以确保训练过程的稳定性。if args.load_balancing_loss:aux_loss = args.load_balancing_weight * outputs.aux_lossloss += aux_loss
这部分代码将
outputs.aux_loss
(辅助损失)加权后加到主损失上,以增强训练的稳定性。
3. 反向传播
最后,代码通过accelerator.backward(loss)
执行反向传播,计算梯度。total_loss
用于累积损失,以便记录每个步骤的损失值。
total_loss += loss.detach().float()
accelerator.backward(loss)
loss.detach().float()
表示将损失从计算图中分离,并将其转换为浮动类型,以便进行记录和后续的反向传播。
小结
-
Loss的计算方式:如果选择
sum
,会对每个token的损失进行求和,而不是取平均,以确保每个token对梯度更新的贡献相等。这对于大批量训练非常重要,能够避免在梯度累积过程中出现误差。 -
梯度累积:通过逐步计算小批量的梯度并累积,能够在不增加显存使用的情况下模拟大批量训练。需要确保在每次累积时正确计算损失,并进行适当的缩放。
这段代码的设计和实现考虑到了梯度累积和损失计算中的数值稳定性问题,确保在大批量训练时能够正确更新模型参数。
梯度累积公式解释
在梯度累积的公式中,( L k L_k Lk ) 表示第 ( k k k ) 步梯度累积中的损失值。具体来说,假设我们将训练数据分成 ( G G G ) 个小批量(mini-batches)进行梯度累积,那么在第 ( k k k ) 步(即第 ( k k k ) 个小批量)中,我们计算的损失为 ( L k L_k Lk )。这些损失是通过模型对第 ( k k k ) 小批量的数据进行前向传播后得到的。
梯度累积的背景
梯度累积的目的是在多个小批量上计算梯度,并在一定数量的步数之后再进行一次反向传播。这样,我们可以模拟更大的批量大小,从而提高训练稳定性和效率,而不需要一次性处理过大的批量数据。
解释公式
公式中的 ( L k L_k Lk ) 是每个小批量的损失值,具体来说,它是模型在第 ( k k k ) 步计算得到的损失。公式可以表示为:
L accumulated = ∑ k = 1 G L k L_{\text{accumulated}} = \sum_{k=1}^{G} L_k Laccumulated=k=1∑GLk
这意味着我们将每个小批量(即每个 ( k k k ))的损失 ( L k L_k Lk ) 累积起来,直到进行 ( G G G ) 步梯度累积。此时的累积损失 ( L accumulated L_{\text{accumulated}} Laccumulated ) 还没有进行任何梯度更新操作,它只是所有小批量损失的总和。
举个例子
假设我们有一个批量大小为 8 的训练数据集,并且我们希望模拟一个批量大小为 32 的训练过程,但由于显存限制,我们决定使用梯度累积来分 4 个小批量来计算梯度:
- 第一步:计算第一小批量(batch 1)的损失 ( L 1 L_1 L1 )。
- 第二步:计算第二小批量(batch 2)的损失 ( L 2 L_2 L2 )。
- 第三步:计算第三小批量(batch 3)的损失 ( L 3 L_3 L3 )。
- 第四步:计算第四小批量(batch 4)的损失 ( L 4 L_4 L4 )。
此时,梯度累积的总损失就可以表示为:
L accumulated = L 1 + L 2 + L 3 + L 4 L_{\text{accumulated}} = L_1 + L_2 + L_3 + L_4 Laccumulated=L1+L2+L3+L4
然后,我们对这个累积损失进行一次反向传播,更新模型参数。
为什么要进行梯度累积?
梯度累积的好处是可以模拟大批量训练而不会导致显存溢出。通过分步计算损失并累积梯度,我们可以在不增加显存需求的情况下使用较大的有效批量大小进行训练。
损失值求和的例子
通过一个简单的例子来说明为什么在使用大批量训练时,损失值求和的方式比损失值平均更能保证每个样本都以相同的权重参与模型更新。
背景说明
在大批量训练中,损失的平均和损失的求和对训练结果的影响不同。具体来说,如果我们采用损失平均,每个样本对模型更新的贡献会受到批次大小的影响;而如果我们采用损失求和,每个样本对更新的贡献将保持一致,无论批次大小有多大。
例子解释
假设我们有两个不同的批次,分别包含不同数量的样本:
- 批次 A:包含 4 个样本。
- 批次 B:包含 8 个样本。
假设这两个批次的损失分别如下:
- 批次 A 的损失:每个样本的损失分别为 2, 3, 1, 4,合计为 10。
- 批次 B 的损失:每个样本的损失分别为 1, 2, 3, 1, 2, 1, 4, 3,合计为 20。
如果使用损失的平均值:
- 批次 A:平均损失为 ( 10 4 = 2.5 \frac{10}{4} = 2.5 410=2.5 )。
- 批次 B:平均损失为 ( 20 8 = 2.5 \frac{20}{8} = 2.5 820=2.5 )。
如果我们采用损失平均的方式,两个批次的损失平均值相同。因此,无论是批次 A 还是批次 B,它们对模型的训练贡献是相同的。也就是说,每个样本的贡献被"平等化"了。这个方法在样本数差异较大的情况下可能会引入偏差,特别是当批次大小不一致时,每个批次的影响可能不成比例。
如果使用损失的求和:
- 批次 A:总损失为 10。
- 批次 B:总损失为 20。
在这种情况下,两个批次的损失对模型的影响是按批次大小加权的。具体来说,批次 B 有更多的样本,所以它的损失总和较大,也就意味着在梯度更新时,批次 B 会对模型参数的更新产生更大的影响。如果我们将这两个批次的损失求和,最终总损失会更大,这会导致模型在训练过程中更多地"倾向"于批次 B,尤其是当使用梯度累积时,批次 B 会对模型产生更大的影响。
为什么损失求和更有意义?
通过将损失求和,确保每个样本的损失对模型更新的影响是等量的。而使用平均损失时,由于批次大小不同,可能会出现以下问题:
- 样本较多的批次(如批次 B)会占据更大的权重,因为平均值会"稀释"了损失的影响,使得较大的批次对整体损失的影响降低。
- 样本较少的批次(如批次 A)则可能被忽视,因为它对最终的损失计算贡献较小。
梯度累积中的影响
在梯度累积中,采用损失求和可以避免不同批次间因批次大小差异而导致的权重不均的问题。这样,在进行多次梯度累积时,不同批次的损失就可以通过求和确保每个样本都具有相同的影响力。
举个具体的例子:
假设我们有 2 个小批量,分别为 A 和 B,批次大小分别为 4 和 8。每个批次的总损失分别为 10 和 20。如果我们直接进行平均计算,得到的损失为:
L avg = 10 + 20 4 + 8 = 30 12 = 2.5 L_{\text{avg}} = \frac{10 + 20}{4 + 8} = \frac{30}{12} = 2.5 Lavg=4+810+20=1230=2.5
而如果我们进行损失求和,得到的损失为:
L sum = 10 + 20 = 30 L_{\text{sum}} = 10 + 20 = 30 Lsum=10+20=30
在进行梯度累积时,采用求和损失可以避免批次大小的不均衡影响,从而确保每个样本的贡献是等量的。
如果batch size都一样的话,还会有这个问题吗?
如果batch size都一样的话,还会有这个问题吗?现实中大规模训练batch size开的不一样吗?
如果batch size 都一样,那么使用损失的平均或者求和不会产生太大的差异。因为在这种情况下,每个批次对模型更新的贡献是均衡的,无论是计算损失的平均值还是求和,最终对模型参数的影响都是一致的。
如果 batch size 相同:
- 平均损失:损失的平均值会反映每个样本的损失在训练中的影响,因为每个样本的数量是相同的,计算平均值不会受到影响。
- 求和损失:由于批次大小相同,求和后的损失也是一个与批次大小成正比的值,而在训练过程中,更新的频率和力度会基于每个批次的大小,进而影响训练的稳定性和收敛性。
所以,如果每个批次大小一样,通常来说,不会出现上述问题。两种方式(平均损失和求和损失)的训练效果是差不多的。
现实中大规模训练的情况
在现实中的大规模训练中,批次大小(batch size)通常是不同的,这取决于多个因素,例如:
-
显存限制:不同的硬件设备(如 GPU、TPU)的显存大小不同,支持的 batch size 也不同。在显存较小的设备上,可能无法一次性处理大量数据,因此会选择较小的批次进行梯度累积,而在显存较大的设备上则可以使用较大的 batch size 进行训练。
-
分布式训练:在多卡(multi-GPU)或者多机(multi-node)训练的情况下,为了充分利用计算资源,批次大小也会有所不同。通常,跨多个 GPU 或节点分布训练时,为了平衡每个卡上的负载,可能会采用不同的 batch size。
-
性能优化:有时为了加速训练,训练人员可能会根据任务的需要进行优化,选择不同的批次大小以提升计算效率。例如,在某些任务中,增大批次大小可以提高并行效率,但也可能导致训练不稳定,这时就需要进行梯度累积。
-
模型设计:有些模型架构(比如需要大输入的模型)可能会需要更大的批次以适应内存需求,或者为了更高效地训练。
在大批量训练中为什么使用梯度累积?
即便batch size 不同,如果我们需要模拟一个大批量训练的效果,并且又不能增加显存的使用,我们通常会使用梯度累积来应对这个问题。
-
模拟大批量效果:如果显存不足,无法处理大批量数据,我们可以通过梯度累积模拟大批量训练的效果。通过在多个小批量上计算损失并累积梯度,我们可以达到类似大批量训练的效果,避免了显存溢出的问题。
-
不同批次大小的影响:如果不同批次的大小不一致,在计算损失时,损失求和的方式就显得更为重要了,因为批次大小较大的批次对总损失的贡献较大,而平均损失的计算可能会削弱这种差异。而损失求和方式则会保证较大批次对总损失的影响相对较大,从而保持了每个样本的影响力一致。
总结
- 如果batch size 相同,那么损失求和和损失平均不会有太大差异。
- 现实中大规模训练确实可能会有不同的 batch size,尤其是在显存和计算资源有限的情况下。为了应对这个问题,梯度累积是一个常见的解决方案,用于模拟大批量训练的效果。
- 在使用不同大小的 batch size 时,损失求和能够保证每个样本在训练中的权重一致,而损失平均可能会导致较大批次的样本对训练过程的影响被稀释。
为什么要除以 m k m_k mk,直接用 L k L_k Lk不行吗?
这是一个非常重要且值得深入思考的问题。让我们仔细分析为什么在梯度累积时,每个小批量的损失还需要除以 ( m k m_k mk),即有效token数。
1. 有效token数量的影响
首先需要理解的是,损失 ( L k L_k Lk) 通常是基于每个小批量的有效token计算的。每个小批量中的有效token数量(去除填充后的部分)可能是不同的,因此直接用 ( L k L_k Lk) 来累积会导致训练时某些小批量(包含更多有效token的批次)对最终梯度的贡献过大。
例如:
- 假设有两个批次:批次 1 包含 3 个有效token,批次 2 包含 6 个有效token。
- 如果我们直接将 ( L 1 L_1 L1) 和 ( L 2 L_2 L2) 累加,批次 2 的损失会占据过多权重,因为它包含更多有效token。这样会导致更新偏向于包含更多token的批次,从而影响梯度的准确性。
2. 损失求和和平均的逻辑
不除以 ( m k m_k mk) 的问题:
- 如果不除以有效token数 ( m k m_k mk),我们实际上在做损失累积时忽视了每个批次的“贡献”大小,特别是当批次中有效token数目差异较大时。这样会使得损失计算不均衡。
- 损失 ( L k L_k Lk) 并不直接等于每个token的损失之和,它实际上是对有效token的损失的总和。所以,批次大小的不同会导致损失的总和不成比例地反映出每个批次的有效学习。
除以 ( m k m_k mk) 的好处:
- 通过除以每个小批量的有效token数 ( m k m_k mk),我们实际上是在对每个小批量的损失进行“加权平均”。
- 这样做的目的就是保证每个token对最终梯度更新的贡献是公平的,不会因为某个小批量有更多有效token而对最终梯度产生不合理的影响。
3. 为什么需要这样做?
假设你使用了梯度累积,目标是模拟大批量训练的效果。如果每个小批量中有效token的数量不同,直接将损失加起来会导致更大的小批量(含有更多有效token的批次)对最终梯度更新产生更大的影响。为了避免这种偏差,我们需要对每个小批量的损失进行标准化处理,即通过除以 ( m k m_k mk) 来校准每个小批量的损失。
数学解释
-
公式:
L final = 1 G ∑ k = 1 G L k m k L_{\text{final}} = \frac{1}{G} \sum_{k=1}^{G} \frac{L_k}{m_k} Lfinal=G1k=1∑GmkLk
这里的 ( L k m k \frac{L_k}{m_k} mkLk ) 表示对每个小批量的损失进行标准化,确保每个小批量在梯度累积中的贡献是按有效token数量来加权的,而不是简单地将所有小批量的损失直接加起来。
举个简单的例子
假设我们有两个批次:
- 批次 1:3 个有效token,损失 ( L 1 = 1.5 L_1 = 1.5 L1=1.5)
- 批次 2:6 个有效token,损失 ( L 2 = 3.0 L_2 = 3.0 L2=3.0)
如果我们直接累加损失:
L accumulated = L 1 + L 2 = 1.5 + 3.0 = 4.5 L_{\text{accumulated}} = L_1 + L_2 = 1.5 + 3.0 = 4.5 Laccumulated=L1+L2=1.5+3.0=4.5
但是,如果我们将损失除以每个批次的有效token数:
L final = 1 2 ( L 1 m 1 + L 2 m 2 ) = 1 2 ( 1.5 3 + 3.0 6 ) = 1 2 ( 0.5 + 0.5 ) = 0.5 L_{\text{final}} = \frac{1}{2} \left( \frac{L_1}{m_1} + \frac{L_2}{m_2} \right) = \frac{1}{2} \left( \frac{1.5}{3} + \frac{3.0}{6} \right) = \frac{1}{2} \left( 0.5 + 0.5 \right) = 0.5 Lfinal=21(m1L1+m2L2)=21(31.5+63.0)=21(0.5+0.5)=0.5
这样做的目的是确保每个批次(无论它的大小如何)对损失的贡献是公平的。如果没有这种标准化,损失较大的批次会导致最终的梯度更新不准确,偏向于更多token的批次。
总结:
除以 ( m k m_k mk) 的目的是为了对损失进行标准化处理,确保每个小批量的有效token对梯度累积的贡献是均衡的,特别是在批次之间有效token数量差异较大的情况下。这样可以避免某些批次因包含更多有效token而在梯度更新中占据不成比例的权重,从而使训练更加稳定和准确。
后记
2025年1月18日15点45分于上海, 在OpenAI o1大模型辅助下完成。
相关文章:
使用Sum计算Loss和解决梯度累积(Gradient Accumulation)的Bug
使用Sum计算Loss和解决梯度累积的Bug 学习 https://unsloth.ai/blog/gradient:Bugs in LLM Training - Gradient Accumulation Fix 这篇文章的记录。 在深度学习训练过程中,尤其是在大批量(large batch)训练中,如何高…...
mfc操作json示例
首先下载cJSON,加入项目; 构建工程,如果出现, fatal error C1010: unexpected end of file while looking for precompiled head 在cJSON.c文件的头部加入#include "stdafx.h"; 看情况,可能是加到.h或者是.cpp文件的头部,它如果有包含头文件, #include &…...
C语言练习(18)
一个班10个学生的成绩,存放在一个一维数组中,要求找出其中成绩最高的学生成绩和该生的序号。 #include <stdio.h>#define STUDENT_NUM 10 // 定义学生数量int main() {int scores[STUDENT_NUM]; // 定义存储学生成绩的一维数组int i;// 输入10个…...
LeetCode 热题 100_全排列(55_46_中等_C++)(递归(回溯))
LeetCode 热题 100_两数之和(55_46) 题目描述:输入输出样例:题解:解题思路:思路一(递归(回溯)): 代码实现代码实现(思路一(…...
编译chromium笔记
编译环境: windows10 powershell7.2.24 git 2.47.1 https://storage.googleapis.com/chrome-infra/depot_tools.zip 配置git git config --global user.name "John Doe" git config --global user.email "jdoegmail.com" git config --global …...
PHP语言的数据库编程
PHP语言的数据库编程 引言 随着互联网的发展,动态网站已成为主流,而动态网站的核心就是与数据库进行交互。PHP(超文本预处理器)是一种流行的开源服务器端脚本语言,被广泛用于Web开发。它以其简单易学和功能强大而受到…...
【PGCCC】PostgreSQL 中表级锁的剖析
本博客解释了 PostgreSQL 中的锁定机制,重点关注数据定义语言 (DDL) 操作所需的表级锁定。 锁定还是解锁的艺术? 人们通常将数据库锁与物理锁进行比较,这甚至可能导致您订购有关锁的历史、波斯锁和撬锁技术的书籍。我们大多数人可能都是通过…...
1.10 自洽性(Self-Consistency):多路径推理的核心力量
自洽性(Self-Consistency):多路径推理的核心力量 随着人工智能尤其是大规模语言模型的不断进化,如何提升其推理能力和决策准确性成为了研究的重点。在这一背景下,**自洽性(Self-Consistency)**作为一种新的推理方法,逐渐展现出其强大的潜力。自洽性方法通过多路径推理…...
【24】Word:小郑-准考证❗
目录 题目 准考证.docx 邮件合并-指定考生生成准考证 Word.docx 表格内容居中表格整体相较于页面居中 考试时一定要做一问保存一问❗ 题目 准考证.docx 插入→表格→将文本转换成表格→✔制表符→确定选中第一列→单击右键→在第一列的右侧插入列→布局→合并单元格&#…...
Linux 信号(Signal)详解
信号(Signal)是 Linux 系统中用于进程间通信的一种机制。它是一种异步通知,用于通知进程发生了某个事件。信号可以来自内核、其他进程或进程自身。 信号的基本概念 信号的作用: 通知进程发生了某个事件(如用户按下 Ct…...
【数据分享】1929-2024年全球站点的逐年最低气温数据(Shp\Excel\免费获取)
气象数据是在各项研究中都经常使用的数据,气象指标包括气温、风速、降水、湿度等指标!说到气象数据,最详细的气象数据是具体到气象监测站点的数据! 有关气象指标的监测站点数据,之前我们分享过1929-2024年全球气象站点…...
app版本控制java后端接口版本管理
java api version 版本控制 java接口版本管理 1 自定义 AppVersionHandleMapping 自定义AppVersionHandleMapping实现RequestMappingHandlerMapping里面的方法 public class AppVersionHandleMapping extends RequestMappingHandlerMapping {Overrideprotected RequestCondit…...
2024年度总结-CSDN
2024年CSDN年度总结 Author:OnceDay Date:2025年1月21日 一位热衷于Linux学习和开发的菜鸟,试图谱写一场冒险之旅,也许终点只是一场白日梦… 漫漫长路,有人对你微笑过嘛… 文章目录 2024年CSDN年度总结1. 整体回顾2…...
基于python的博客系统设计与实现
摘要:目前,对于信息的获取是十分的重要,我们要做到的不是裹足不前,而是应该主动获取和共享给所有人。博客系统就能够实现信息获取与分享的功能,博主在发表文章后,互联网上的其他用户便可以看到,…...
服务器日志自动上传到阿里云OSS备份
背景 公司服务器磁盘空间有限,只能存近15天日志,但是有时需要查看几个月前的日志,需要将服务器日志定时备份到某个地方,需要查询的时候有地方可查。 针对这个问题,想到3个解决方法: 1、买一个配置比较低…...
优化使用 Flask 构建视频转 GIF 工具
优化使用 Flask 构建视频转 GIF 工具 优化后的项目概述 在优化后的版本中,我们将实现以下功能: 可设置每个 GIF 的帧率和大小:用户可以选择 GIF 的帧率和输出大小。改进的用户界面:使用更现代的设计使界面更美观、整洁。自定义…...
leetcode:511. 游戏玩法分析 I
难度:简单 SQL Schema > Pandas Schema > 活动表 Activity: ----------------------- | Column Name | Type | ----------------------- | player_id | int | | device_id | int | | event_date | date | | games_playe…...
windows git bash 使用zsh 并集成 oh my zsh
参考了 这篇文章 进行配置,记录了自己的踩坑过程,并增加了 zsh-autosuggestions 插件的集成。 主要步骤: 1. git bash 这个就不说了,自己去网上下,windows 使用git时候 命令行基本都有它。 主要也是用它不方便&…...
【Python运维】Python与网络监控:如何编写网络探测与流量分析工具
《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 随着互联网技术的快速发展,网络性能的监控与分析成为保障信息系统稳定运行的关键环节。本文深入探讨了如何利用Python语言构建高效的网络探…...
OpenCV相机标定与3D重建(61)处理未校准的立体图像对函数stereoRectifyUncalibrated()的使用
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 为未校准的立体相机计算一个校正变换。 cv::stereoRectifyUncalibrated 是 OpenCV 库中的一个函数,用于处理未校准的立体图像对。该函…...
字玩FontPlayer开发笔记12 Vue3撤销重做功能
字玩FontPlayer开发笔记12 Vue3撤销重做功能 字玩FontPlayer是笔者开源的一款字体设计工具,使用Vue3 ElementUI开发,源代码:github | gitee 笔记 撤销重做功能是设计工具必不可少的模块,以前尝试使用成熟的库实现撤销重做功能…...
无人机图传模块:深入理解其工作原理与实际效用
无人机图传模块作为无人机系统的关键组成部分,承担着将无人机拍摄的图像和视频实时传输至地面控制站或接收设备的重任。本文将深入探讨无人机图传模块的工作原理及其在实际应用中的效用,帮助读者更好地理解这一技术的奥秘。 一、无人机图传模块的工作原…...
PDF文件提取开源工具调研总结
概述 PDF是一种日常工作中广泛使用的跨平台文档格式,常常包含丰富的内容:包括文本、图表、表格、公式、图像。在现代信息处理工作流中发挥了重要的作用,尤其是RAG项目中,通过将非结构化数据转化为结构化和可访问的信息࿰…...
Linux(Centos 7.6)命令详解:dos2unix
1.命令作用 将Windows格式文件件转换为Unix、Linux格式的文件(也可以转换成其他格式的) 2.命令语法 Usage: dos2unix [options] [file ...] [-n infile outfile ...] 3.参数详解 options: -c, --convmode,转换方式,支持ascii, 7bit, iso, mac,默认…...
梯度提升决策树树(GBDT)公式推导
### 逻辑回归的损失函数 逻辑回归模型用于分类问题,其输出是一个概率值。对于二分类问题,逻辑回归模型的输出可以表示为: \[ P(y 1 | x) \frac{1}{1 e^{-F(x)}} \] 其中 \( F(x) \) 是一个线性组合函数,通常表示为ÿ…...
跨域问题分析及解决方案
1、跨域 指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对javascript施加的安全限制。 2、同源策略:是指协议,域名,端口都要相同,其中有一个不同都会产生跨域; 3、跨域流程…...
【三国游戏——贪心、排序】
题目 代码 #include <bits/stdc.h> using namespace std; using ll long long; const int N 1e510; int a[N], b[N], c[N]; int w[4][N]; int main() {int n;cin >> n;for(int i 1; i < n; i)cin >> a[i];for(int i 1; i < n; i)cin >> b[i…...
深入理解 Java 的数据类型与运算符
Java学习资料 Java学习资料 Java学习资料 在 Java 编程中,数据类型与运算符是构建程序的基础元素。它们决定了数据在程序中的存储方式以及如何对数据进行各种操作。 一、数据类型 (一)基本数据类型 整型: 用于存储整数数值&…...
WOA-CNN-GRU-Attention、CNN-GRU-Attention、WOA-CNN-GRU、CNN-GRU四模型对比多变量时序预测
WOA-CNN-GRU-Attention、CNN-GRU-Attention、WOA-CNN-GRU、CNN-GRU四模型对比多变量时序预测 目录 WOA-CNN-GRU-Attention、CNN-GRU-Attention、WOA-CNN-GRU、CNN-GRU四模型对比多变量时序预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 基于WOA-CNN-GRU-Attention、…...
(二叉树)
我们今天就开始引进一个新的数据结构了:我们所熟知的:二叉树; 但是我们在引进二叉树之前我们先了解一下树; 树 树的概念和结构: 树是⼀种⾮线性的数据结构,它是由 n ( n>0 ) …...
Linux shell 批量验证端口连通性
脚本 #!/bin/bash # #database check #set -o nounset LOCALIPifconfig | grep inet | head -1 | awk {print $2} | sed s/addr\:// IPLIST192.168.1.99 192.168.1.98 192.168.1.97 PORTLIST81 82 83 84 85 86 check_nc(){ for CHECK_IP in $IPLIST dofor CHECK_PORT in $PORT…...
Java 中实体类与操作类分离
目录 一、为啥要把实体类和操作类分开 二、实体类长啥样,怎么用 三、操作类的使命与实现 四、实战演练:实体类与操作类协同工作 五、拓展思考:这种分离带来的好处与进一步优化 六、总结与展望 家人们,今天我想跟你们唠唠我在…...
创建 pdf 合同模板
创建 pdf 合同模板 一、前言二、模板展示三、制作过程 一、前言 前段时间要求创建“pdf”模板,学会了后感觉虽然简单,但开始也折腾了好久,这里做个记录。 二、模板展示 要创建这样的模板 三、制作过程 新建一个“Word”,这里命…...
【Prometheus】PromQL进阶用法
✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,…...
BOBO小火炬全套源码XE修复版2025(火炬天花板二次开发版)
《小火炬全套源码 传奇游戏源码讲解》 小火炬全套源码是一种用于开发经典传奇类游戏的源码包。传奇游戏作为一款经典的多人在线角色扮演游戏(MMORPG),有着庞大的用户基础和强大的游戏生态。小火炬全套源码主要提供了从基础架构到核心功能的完…...
【基于无线电的数据通信链】Link 11 仿真测试
〇、废话 Link 11 仿真测试 涉及多个方面,包括信号仿真、协议模拟、数据链路层的仿真以及网络性能评估等。Link 11 是一种基于 HF(高频) 或 UHF(超高频) 波段的无线通信协议,主要用于军事通信系统中。为了…...
WPF实战案例 | C# WPF实现计算器源码
WPF实战案例 | C# WPF实现计算器源码 一、设计来源计算器应用程序讲解1.1 主界面1.2 计算界面 二、效果和源码2.1 界面设计(XAML)2.2 代码逻辑(C#)2.3 实现步骤总结 源码下载更多优质源码分享 作者:xcLeigh 文章地址&a…...
WebSocket 和 Socket 的区别
一、协议层次和工作方式 1.1 )Socket 1.1.1)Socket位于传输层,通常使用TCP或UDP协议 1.1.2)提供了一个通用的网络编程接口,允许应用程序通过它发送和接收数据 1.1.3)一般需要手动管理连接,错…...
Matlab自学笔记四十五:日期时间型和字符、字符串以及double型的相互转换方法
1.说明 在Matlab中,大多数函数都有这样的功能:创建函数本身具有转换的功能,例如double函数,可以创建双精度浮点数,也可以把输入参数转换成双精度浮点数,再例如string,可以创建字符串࿰…...
Python基础学习(六)unittest 框架
1.介绍 是 Python自带的单元测试框架 - 自带的, 可以直接使用, 不需要单外安装 - 测试人员,用来做自动化测试, 作为自动化测试的执行框架,即管理和执行用例的 核心要素: TestCase 测试用例, 这个测试用例是 unittest 的组成部分,作用是用来书写真正的…...
Python数据可视化(够用版):懂基础 + 专业的图表抛给Tableau等专业绘图工具
我先说说文章标题中的“够用版”啥意思,为什么这么写。 按照我个人观点,在使用Python进行数据分析时,我们有时候肯定要结合到图表去进行分析,去直观展现数据的规律和特定,那么我们肯定要做一些简单的可视化࿰…...
麒麟操作系统服务架构保姆级教程(十三)tomcat环境安装以及LNMT架构
如果你想拥有你从未拥有过的东西,那么你必须去做你从未做过的事情 之前咱们学习了LNMP架构,但是PHP对于技术来说确实是老掉牙了,PHP的市场占有量越来越少了,我认识一个10年的PHP开发工程师,十年工资从15k到今天的6k&am…...
Docker集成onlyoffice实现预览功能
1.拉取镜像 docker pull onlyoffice/documentserver 2. 数据卷挂载 mkdir -p app/onlyoffice/DocumentServer/logs mkdir -p app/onlyoffice/DocumentServer/data mkdir -p app/onlyoffice/DocumentServer/lib mkdir -p app/onlyoffice/DocumentServer/db 3.运行容器 docker ru…...
Flowable 管理各业务流程:流程设计器 (获取流程模型 XML)、流程部署、启动流程、流程审批、流程挂起和激活、任务分配
文章目录 引言I 表结构主要表前缀及其用途核心表II 流程设计器(Flowable BPMN模型编辑器插件)Flowable-UIvue插件III 流程部署部署步骤例子:根据流程模型ID部署IV 启动流程启动步骤ACT_RE_PROCDEF:流程定义相关信息例子:根据流程 ID 启动流程V 流程审批审批步骤Flowable 审…...
BladeDISC++:Dynamic Shape AI 编译器下的显存优化技术
近年来,随着深度学习技术的迅猛发展,越来越多的模型展现出动态特性,这引发了对动态形状深度学习编译器(Dynamic Shape AI Compiler)的广泛关注。本文将介绍阿里云 PAI 团队近期发布的 BladeDISC项目,探讨在动态场景下如何优化深度…...
FFmpeg常用命令
文章目录 一、 FFmpeg 音视频的处理流程二、FFmpeg 常用命令2.1、查看本机支持的采集设备2.2、 录制视频2.2.1、原始视频2.2.2、编码的视频 2.3、录制音频:2.3.1、原始音频2.3.2、编码的音频 2.4、录制音视频:2.5、文件格式转换:2.6、提取音频…...
http请求开启长连接导致请求偶发失败
问题描述: http长连接的意思是服务器为了调用时减少TCP三次握手开销,会复用之前已经发起的请求,比较适合频繁交互(比如数据推送、流水线操作等)的场景,但是如果超过服务器配置的连接最大空闲时间࿰…...
JUnit单元测试
单元测试 就是针对最小的功能单元(方法),编写测试代码对其正确性进行测试 JUnit 最流行的java测试框架之一,方柏霓进行单元测试 入门程序 使用Junit,对UserService的方法进行单元测试 1.在pom.xml中,…...
智慧公安(实景三维公安基层基础平台)建设方案——第4章
4 建设内容 4.1 标准规范体系 在国家和地方公安基层信息化标准规范的基础上,结合项目实际情况,制定标准规范及管理制度,构建统一的标准规范体系,以便更好地实现公安基层基础信息的高度共享、平台运行的统一协调、业务流程最优化。主要包括以下内容: 1. 业务标准规范 (…...
LLMs(大型语言模型)的多智能体:Auto-GPT
LLMs(大型语言模型)的多智能体:Auto-GPT 是指在一个系统中集成多个具有不同能力、角色和任务的智能体,这些智能体能够相互协作、沟通和交互,以共同完成复杂的任务或解决复杂的问题。每个智能体都可以被视为一个独立的实体,具有自己的策略、目标和知识库,通过相互之间的…...