当前位置: 首页 > news >正文

从零开始实现大语言模型(十四):高阶训练技巧

1. 前言

预训练大语言模型的流程与训练普通神经深度网络模型本质上并没有任何不同。可以使用深度学习实践中已经被证明非常有效的高阶训练技巧,优化大语言模型预训练流程,使大语言模型预训练效率更高,训练过程更稳定。

本文介绍深度学习领域优化训练学习率的两种方法Learning Rate Warmup和Cosine Decay,优化深度神经网络模型参数梯度的方法Gradient Clipping,以及优化训练超参数的方法Hyperparameters Search,并实现预训练大语言模型的函数hyper_pretrain_model

2. 优化训练学习率

2.1 Learning Rate Warmup

学习率是训练深度学习模型过程中最关键的超参数,没有之一。学习率可以控制深度神经网络模型参数的迭代更新速度,学习率越大,则参数的迭代更新速度越快,学习率越小,则参数的更新速度越慢。但是过大的学习率会导致损失函数在Error Surface上发生跳跃,使训练过程不稳定,模型难以收敛。如果如学习率太小,则会导致参数深度神经网络参数每次更新的幅度很小,使神经网络模型的训练效率很低,而且容易使损失函数陷入Error Surface中的局部最优解。

Learning Rate Warmup(学习率预热)是一种经过深度学习实践证明非常有效的优化深度神经网络前几次迭代训练学习率,以降低深度神经网络参数随机初始化带来的不确定性风险,从而提升训练过程稳定性的方法。Learning Rate Warmup会指定一个非常小的初始学习率initial_lr,以及预热步骤warmup_steps,并在训练深度神经网络的前warmup_steps次迭代流程中,将学习率逐步从initial_lr提升至不使用Learning Rate Warmup时设定的值peak_lr。在深度学习实践中,预热步骤warmup_steps一般会占总训练次数的 0.1 % 0.1\% 0.1% 10 % 10\% 10%

如下面的代码所示,假设学习率的设定值peak_lr为0.01,Learning Rate Warmup指定的初始学习率initial_lr为0.0001,warmup_steps为15。使用Learning Rate Warmup优化训练学习率,需要在训练深度神经网络的前warmup_steps次迭代流程中,计算当次迭代训练使用的学习率大小,并修改优化器中使用学习率的值:

import os
import torch
import random
import tiktoken
from torch.utils.data import Dataset, DataLoader# from [从零开始实现大语言模型(二):文本数据处理] import LLMDataset
# from [从零开始实现大语言模型(七):多头注意力机制] import MultiHeadAttention
# from [从零开始实现大语言模型(八):Layer Normalization] import LayerNorm
# from [从零开始实现大语言模型(九):前馈神经网络与GELU激活函数] import GELU, FeedForward
# from [从零开始实现大语言模型(十一):构建大语言模型GPTModel] import TransformerBlock, GPTModeltorch.manual_seed(123)train_data_path = "train_data"
vocabulary = "gpt2"
special_token_id = 50256
context_len = 1024
stride = 1024
batch_size = 2embedding_dim = 768
num_layers = 12
num_heads = 12
context_len = 1024
vocabulary_size = 50257
dropout = 0.1
qkv_bias = Falsenum_epochs = 15
initial_lr = 0.0001
peak_lr = 0.01
warmup_steps = 15train_dataset = LLMDataset(train_data_path, vocabulary, special_token_id, context_len, stride)
train_loader = DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=True, drop_last=True)gpt2_small = GPTModel(embedding_dim=embedding_dim,num_layers=num_layers,num_heads=num_heads,context_len=context_len,vocabulary_size=vocabulary_size,dropout=dropout,qkv_bias=qkv_bias
)optimizer = torch.optim.AdamW(gpt2_small.parameters(), weight_decay=0.1)
lr_increment = (peak_lr - initial_lr) / warmup_stepsglobal_step = -1
track_lrs = []for epoch in range(num_epochs):for input_batch, target_batch in train_loader:optimizer.zero_grad()global_step += 1if global_step < warmup_steps:lr = initial_lr + global_step * lr_incrementelse:lr = peak_lrfor param_group in optimizer.param_groups:param_group["lr"] = lrtrack_lrs.append(optimizer.param_groups[0]["lr"])

可以使用如下代码绘制大语言模型gpt2_small的每轮迭代训练过程所使用的学习率:

import matplotlib.pyplot as pltplt.ylabel("Learning rate")
plt.xlabel("Step")
total_training_steps = len(train_loader) * num_epochs
plt.plot(range(total_training_steps), track_lrs)
plt.show()

执行上面代码,生成大语言模型gpt2_small的整个迭代训练流程中的学习率变化情况图像如下:

图一

2.2 Cosine Decay

在深度学习实践中,一般会将Learning Rate Warmup与Cosine Decay结合起来,共同优化训练学习率。Learning Rate Warmup只作用于深度神经网络的前warmup_steps轮迭代训练过程(预热阶段),使训练学习率从一个很小的initial_lr逐步提升至peak_lr。Cosine Decay会在预热阶段之后的全部迭代训练过程中,以余弦曲线的方式逐步减小训练学习率,以降低模型参数的更新速度,减少损失函数越过Error Surface上极小值的概率,提升训练过程稳定性。

如下面的代码所示,在预热阶段之后使用Cosine Decay策略调整训练学习率,需要使用global_step - warmup_steps得到预热阶段后的迭代训练步数,以及使用total_training_steps - warmup_steps计算出预热阶段后的总迭代训练次数,并通过(global_step - warmup_steps) / (total_training_steps - warmup_steps)计算出去掉预热阶段后的训练进度百分比progress。使用math.cos(math.pi * progress)可以计算得到一个介于1到-1之间的余弦值,当progress为0时,余弦值为1,当progress为1时,余弦值为-1,余弦值的变化速率曲线为余弦曲线。使用0.5 * (1 + math.cos(math.pi * progress))对余弦值做变换,使余弦值的取值范围由[1, -1]变换到[1, 0],最后使用 min_lr + (peak_lr - min_lr) * 0.5 * (1 + math.cos(math.pi * progress))计算得到当前迭代训练步数对应的训练学习率:

import mathmin_lr = 0.1 * initial_lrtrack_lrs = []
global_step = -1for epoch in range(num_epochs):for input_batch, target_batch in train_loader:optimizer.zero_grad()global_step += 1if global_step < warmup_steps:lr = initial_lr + global_step * lr_increment  else:progress = (global_step - warmup_steps) / (total_training_steps - warmup_steps)lr = min_lr + (peak_lr - min_lr) * 0.5 * (1 + math.cos(math.pi * progress))for param_group in optimizer.param_groups:param_group["lr"] = lrtrack_lrs.append(optimizer.param_groups[0]["lr"])

可以使用如下代码绘制使用Learning Rate Warmup与Cosine Decay策略后的学习率变化情况图像:

plt.ylabel("Learning rate")
plt.xlabel("Step")
plt.plot(range(total_training_steps), track_lrs)
plt.show()

执行上面代码,生成的学习率变化情况图像如下:

图二

3. 优化模型参数梯度

3.1 Gradient Clipping

Gradient Clipping(梯度裁剪)是一种通过限制参数梯度大小,以解决深度神经网络训练过程中的梯度爆炸问题,从而提升训练过程稳定性的模型参数梯度优化方法。深度学习实践中常用的Gradient Clipping方法有两种:基于梯度值的裁剪和基于梯度范数的裁剪。

基于梯度值的裁剪方法的原理非常简单,其会直接将深度神经网络参数的梯度中大于clip_value的梯度设置成clip_value,并将小于-clip_value的梯度设置成-clip_value,使深度神经网络参数梯度的绝对值值不超过clip_value。基于梯度范数的裁剪方法首先会计算神经网络参数梯度的p-范数,如果p-范数大于max_norm,则会将每个梯度值均乘以 max_norm p_norm \frac{\text{max\_norm}}{\text{p\_norm}} p_normmax_norm,使神经网络参数梯度的p-范数等于max_norm

假设深度神经网络共包含4个参数,后向传播流程计算出的参数梯度 G = [ 1 2 2 4 ] G=\begin{bmatrix}1&2\\2&4\end{bmatrix} G=[1224],使用基于梯度范数的裁剪方法优化模型参数梯度,设置参数梯度2-范数的最大值max_norm为2.0,首先需要计算神经网络参数梯度的2-范数 ∥ G ∥ 2 = 1 2 + 2 2 + 2 2 + 4 2 = 25 = 5 \|G\|_2=\sqrt{1^2+2^2+2^2+4^2}=\sqrt{25}=5 G2=12+22+22+42 =25 =5。因为 ∥ G ∥ 2 > 2 \|G\|_2>2 G2>2,因此会将每个梯度值均乘以 max_norm p_norm = 2 5 \frac{\text{max\_norm}}{\text{p\_norm}}=\frac{2}{5} p_normmax_norm=52,即将神经网络参数梯度裁剪成 G ′ = 2 5 × G = [ 2 5 4 5 4 5 8 5 ] G'=\frac{2}{5}\times G=\begin{bmatrix}\frac{2}{5}&\frac{4}{5}\\ \frac{4}{5}&\frac{8}{5}\end{bmatrix} G=52×G=[52545458]

如下面的代码所示,定义计算深度神经网络参数梯度最大值的函数find_largest_gradient,并使用torch.tensor函数创建训练样本input_batch及训练样本标签target_batch。将训练样本input_batch输入大语言模型gpt2_small,使用calc_loss_batch函数计算大语言模型的预测输出与训练样本标签之间的交叉熵损失loss,并通过loss.backward()计算大语言模型参数梯度。最后使用find_largest_gradient函数打印输入大语言模型参数梯度的最大值:

# from [从零开始实现大语言模型(十三):预训练大语言模型GPTModel] import calc_loss_batchdef find_largest_gradient(model):max_grad = Nonefor param in model.parameters():if param.grad is not None:grad_values = param.grad.data.flatten()max_grad_param = grad_values.max()if max_grad is None or max_grad_param > max_grad:max_grad = max_grad_paramreturn max_graddevice = torch.device("cpu")
input_batch = torch.tensor([[16833, 3626, 6100],  # [["every effort moves"],[40, 1107, 588]]      # ["I really like"]]
)
target_batch = torch.tensor([[3626, 6100, 345],  # [[" effort moves you"],[588, 428, 11311]]  # [" really like chocolate"]]
)loss = calc_loss_batch(input_batch, target_batch, gpt2_small, device)
loss.backward()
print(find_largest_gradient(gpt2_small))

执行上面代码,打印结果如下:

tensor(0.6413)

使用上述基于梯度范数的裁剪方法优化模型参数梯度,设置大语言模型参数梯度的2-范数最大值max_norm为1.0,并打印经过Gradient Clipping优化之后的大语言模型参数梯度的最大值:

torch.nn.utils.clip_grad_norm_(gpt2_small.parameters(), max_norm=1.0)print(find_largest_gradient(gpt2_small))

执行上面代码,打印结果如下:

tensor(0.0348)

4. 实现高阶预训练函数

可以结合上述3种高阶训练技巧实现预训练大语言模型的函数hyper_pretrain_model。修改前文从零开始实现大语言模型(十三):预训练大语言模型GPTModel中实现的预训练大语言模型的函数pretrain_model,在每轮for循环使用calc_loss_batch函数计算大语言模型的预测输出与训练样本标签之间的交叉熵损失之前,先使用2中所述优化训练学习率的两种方法Learning Rate Warmup和Cosine Decay,计算当次迭代训练使用的学习率大小,并修改训练优化器中使用学习率的值。在使用optimizer.step()方法更新大语言模型参数之前,先使用3中所述优化模型参数梯度的方法Gradient Clipping,优化模型参数梯度。具体代码如下所示:

# from [从零开始实现大语言模型(十三):预训练大语言模型GPTModel] import calc_loss_loader, val_and_savedef hyper_pretrain_model(model, optimizer, train_loader, num_epochs, device, eval_freq, eval_iter, tokenizer, start_context, save_freq, checkpoint_dir, warmup_steps=10, initial_lr=3e-05, min_lr=1e-6, max_norm=1.0,checkpoint=None, val_loader=None
):if not os.path.exists(checkpoint_dir):os.makedirs(checkpoint_dir, exist_ok=True)if checkpoint is not None:model_checkpoint_path = os.path.join(checkpoint_dir, f"model_{checkpoint:06d}.pth")optimizer_checkpoint_path = os.path.join(checkpoint_dir, f"optimizer_{checkpoint:06d}.pth")model.load_state_dict(torch.load(model_checkpoint_path))optimizer.load_state_dict(torch.load(optimizer_checkpoint_path))else:checkpoint = -1train_losses, val_losses, track_tokens_seen, track_lrs = [], [], [], []tokens_seen, global_step = 0, -1peak_lr = optimizer.param_groups[0]["lr"]total_training_steps = len(train_loader) * num_epochslr_increment = (peak_lr - initial_lr) / warmup_stepsfor epoch in range(num_epochs):model.train()for i, (input_batch, target_batch) in enumerate(train_loader):if global_step % eval_freq == 0:model.train()optimizer.zero_grad()global_step += 1if global_step < warmup_steps:lr = initial_lr + global_step * lr_increment  else:progress = (global_step - warmup_steps) / (total_training_steps - warmup_steps)lr = min_lr + (peak_lr - min_lr) * 0.5 * (1 + math.cos(math.pi * progress))for param_group in optimizer.param_groups:param_group["lr"] = lrtrack_lrs.append(lr)loss = calc_loss_batch(input_batch, target_batch, model, device)loss.backward()if global_step > warmup_steps:torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=max_norm)optimizer.step()tokens_seen += input_batch.numel()print(f"Epoch {epoch + 1} (Batch {i:06d}): Train loss {loss.item():.3f}")checkpoint, train_loss, val_loss = val_and_save(model, optimizer, train_loader, val_loader, epoch, global_step, eval_freq,eval_iter, start_context, tokenizer, save_freq, checkpoint_dir, checkpoint, device)if train_loss is not None:train_losses.append(train_loss)val_losses.append(val_loss)track_tokens_seen.append(tokens_seen)checkpoint, _, _ = val_and_save(model, optimizer, train_loader, val_loader, epoch, global_step, 1,eval_iter, start_context, tokenizer, 1, checkpoint_dir, checkpoint, device)print(f"Epoch {epoch + 1} finished, checkpoint: {checkpoint:06d}")return train_losses, val_losses, track_tokens_seen, track_lrs

5. 优化训练超参数

5.1 Hyper-parameters Search

超参数(hyper-parameters)是指需要在搭建和训练深度神经网络之前手动设置的一些参数。在深度学习中,有两类超参数,一类超参数是深度神经网络结构超参数,比如深度神经网络的层数,Embedding向量的维度等等。另一类超参数是训练超参数,例如训练深度神经网络使用的学习率,每个batch中训练样本数量等等。

优化深度神经网络结构超参数的方法被统称为神经网络结构搜索(NAS, neural architecture search)。神经网络结构搜索方法大致可分类3类,其中一类被称为“大海捞针”,即根据实践经验定义一个有限的超参数搜索空间,逐一使用搜索空间中的超参数组合构建并训练深度神经网络直至收敛,取验证集上测试指标最高的超参数组合作为搜索结果。另一类是不可微方法,其一般会将验证集上的测试指标作为环境给的奖励,使用强化学习算法搜索出较优的超参数组合。还有一类是可微方法,其核心思想是定义一个神经网络结构超参数的可微函数作为目标函数,基于Super-net对目标函数关于超参数求梯度,直接使用梯度更新超参数。

本文不会详细介绍深度神经网络结构超参数优化方法,不同大语言模型的结构基本相同,Embedding向量维度等结构超参数一般会取决于可用的计算资源,工业界实践中一般不会使用神经网络架构搜索方法确定大语言模型的结构超参数。《从零开始实现大语言模型》系列专栏全部完成之后,我应该会写几篇博客详细神经网络结构搜索,感兴趣的读者可以关注我的个人博客。

预训练大语言模型的时间成本及计算成本都非常高,例如训练大语言模型Llama 2的数据共包含2T(万亿)个tokens,花费184320 A100 GPU时,换算成云计算资源价值,大约需要690000美元。在预训练大语言模型的工业界实践中,一般会在正式开始训预训练大语言模型之前,在相对小的数据集上,使用Hyper-parameters Search得到一个比较好的训练超参数组合。Hyper-parameters Search的核心思想就是“大海捞针”,即定义一个有限的超参数搜索空间HPARAM_GRID,逐一使用搜索空间中的超参数组合训练大语言模型,取验证集上交叉熵损失最小的超参数组合作为正式预训练大语言模型时所用的训练超参数。具体代码如下所示:

import itertoolsdef hparams_search_train(model, optimizer, train_loader, val_loader, num_epochs, device,eval_iter, warmup_steps, initial_lr, min_lr, max_norm
):global_step = -1peak_lr = optimizer.param_groups[0]["lr"]total_training_steps = len(train_loader) * num_epochslr_increment = (peak_lr - initial_lr) / warmup_stepsfor epoch in range(num_epochs):model.train()for input_batch, target_batch in train_loader:optimizer.zero_grad()global_step += 1if global_step < warmup_steps:lr = initial_lr + global_step * lr_incrementelse:progress = (global_step - warmup_steps) / (total_training_steps - warmup_steps)lr = min_lr + (peak_lr - min_lr) * 0.5 * (1 + math.cos(math.pi * progress))for param_group in optimizer.param_groups:param_group["lr"] = lrloss = calc_loss_batch(input_batch, target_batch, model, device)loss.backward()if global_step > warmup_steps:torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=max_norm)optimizer.step()train_loss = calc_loss_loader(train_loader, model, device, eval_iter)val_loss = calc_loss_loader(val_loader, model, device, eval_iter)return train_loss, val_lossHPARAM_GRID = {"batch_size": [2, 4, 8, 16],"dropout": [0.0, 0.1, 0.2],"warmup_steps": [10, 20, 30],"weight_decay": [0.1, 0.01, 0.0],"max_norm": [1.0, 0.5, 2.0],"peak_lr": [0.0001, 0.0005, 0.001, 0.005],"initial_lr": [0.00005, 0.0001],"min_lr": [0.00005, 0.00001, 0.0001],"num_epochs": [5, 10, 15, 20, 25],
}
hyperparameter_combinations = list(itertools.product(*HPARAM_GRID.values()))
print(f"Total hyperparameter configurations: {len(hyperparameter_combinations)}")device = torch.device("cpu")
val_data_path = "val_data"
train_dataset = LLMDataset(train_data_path, vocabulary, special_token_id, context_len, stride)
val_dataset = LLMDataset(val_data_path, vocabulary, special_token_id, context_len, stride)best_val_loss, best_train_loss = float("inf"), float("inf")
best_hparams = {}for i, combination in enumerate(hyperparameter_combinations):print(f"Evaluating configuration {i + 1} of {len(hyperparameter_combinations)}")HPARAM_CONFIG = dict(zip(HPARAM_GRID.keys(), combination))torch.manual_seed(123)train_loader = DataLoader(dataset=train_dataset, batch_size=HPARAM_CONFIG["batch_size"], shuffle=True, drop_last=True)val_loader = DataLoader(dataset=val_dataset, batch_size=HPARAM_CONFIG["batch_size"], shuffle=False, drop_last=False)model = GPTModel(embedding_dim=embedding_dim, num_layers=num_layers, num_heads=num_heads, context_len=context_len,vocabulary_size=vocabulary_size, dropout=HPARAM_CONFIG["dropout"], qkv_bias=qkv_bias)model.to(device)optimizer = torch.optim.AdamW(model.parameters(), lr=HPARAM_CONFIG["peak_lr"],weight_decay=HPARAM_CONFIG["weight_decay"])train_loss, val_loss = hparams_search_train(model, optimizer, train_loader, val_loader, HPARAM_CONFIG["num_epochs"], device, eval_iter=1,warmup_steps=HPARAM_CONFIG["warmup_steps"], initial_lr=HPARAM_CONFIG["initial_lr"],min_lr=HPARAM_CONFIG["min_lr"], max_norm=HPARAM_CONFIG["max_norm"])if val_loss < best_val_loss:best_val_loss = val_lossbest_train_loss = train_lossbest_hparams = HPARAM_CONFIGprint(f"Evaluating configuration {i + 1} completed.")print(f"Current best hyper-parameters: {best_hparams}")print(f"Current best Val loss: {best_val_loss} | Training loss {best_train_loss}")print("============================================================================")print("Hyper-parameter search completed.")
print(f"Best hyper-parameters: {best_hparams}")
print(f"Best Val loss: {best_val_loss} | Training loss {best_train_loss}")

神经网络结构搜索领域的不可微方法并不适用于大语言模型训练超参数搜索,训练大语言模型所需的计算量太大,且使用强化学习算法搜索超参数,需要从头开始完整训练一次大语言模型才能获得1个奖励,强化学习算法一般至少需要上万至数十万次奖励反馈才能收敛。

神经网络结构搜索领域的可微方法同样不适用于大语言模型训练超参数搜索,所有可微方法的核心思想都是定义一个神经网络结构超参数的可微函数作为目标函数,然而基本没有办法找到一个神经网络训练超参数的可微函数。

6. 结束语

预训练大语言模型的流程与训练普通神经深度网络模型本质上并没有任何不同,其难度不在于算法,而在于数据,更在于算力。绝大部分企业都没有预训练大语言模型的算力资源,因此如何利用开源大语言模型成了大语言模型工业实践中的重中之重,接下来一起看看如何加载开源大语言模型参数吧!

相关文章:

从零开始实现大语言模型(十四):高阶训练技巧

1. 前言 预训练大语言模型的流程与训练普通神经深度网络模型本质上并没有任何不同。可以使用深度学习实践中已经被证明非常有效的高阶训练技巧&#xff0c;优化大语言模型预训练流程&#xff0c;使大语言模型预训练效率更高&#xff0c;训练过程更稳定。 本文介绍深度学习领域…...

Spring-framework源码编译

版本统一&#xff08;搭配其他版本会遇到不可知错误&#xff09;&#xff1a; 1&#xff09;spring 5.2.X&#xff08;5.5.26&#xff09; 2&#xff09;JDK8 3&#xff09;Gradle:5.6.4 可以在gradle-wrapper.properties中修改 https\://services.gradle.org/distribution…...

分布式系统的核心挑战与解决方案

1、分布式系统的引入 在移动互联网、云计算和物联网的推动下&#xff0c;现代软件系统需要处理亿级用户请求、PB级数据存储和毫秒级响应需求。传统的单体架构受限于单机性能瓶颈和容灾能力&#xff0c;逐渐被分布式系统取代。例如&#xff0c;电商平台在“双十一”期间需应对每…...

fastjson漏洞

fastjson漏洞 fastjson工作原理攻击原理补充 例子 fastjson工作原理 fastjson的作用是将JAVA对象转换成对应的json表示形式&#xff0c;也可以反过来将json转化为对应的Java对象。fastjson使用AutoType功能进行反序列化&#xff0c;AutoType使用type标记字符的原始类型&#x…...

upload-labs详解(13-20)文件上传分析

目录 upload-labs-env upload-labs-env第十三关 文件包含漏洞 代码 测试 上传一个.jpg图片 上传一个.png文件 上传一个.gif图片 upload-labs-env第十四关 代码 思路 upload-labs-env第十五关 代码 思路 upload-labs-env第十六关 代码 思路 测试 上传gif格式…...

HTML第四节

一.复合选择器 1.后代选择器 注&#xff1a;1.后代选择器会选中后代所有的要选择的标签 2.儿子选择器 3.并集选择器 注&#xff1a;1.注意换行&#xff0c;同时选中多种标签 4.交集选择器 注&#xff1a;1.标签选择器放在最前面&#xff0c;例如放在类选择器的前面 2.两个选择…...

基于 LeNet 网络的 MNIST 数据集图像分类

1.LeNet的原始实验数据集MNIST 名称&#xff1a;MNIST手写数字数据集 数据类型&#xff1a;灰度图 &#xff08;一通道&#xff09; 图像大小&#xff1a;28*28 类别数&#xff1a;10类&#xff08;数字0-9&#xff09; 1.通过torchvision.datasets.MNIST下载并保存到本地…...

win11编译llama_cpp_python cuda128 RTX30/40/50版本

Geforce 50xx系显卡最低支持cuda128&#xff0c;llama_cpp_python官方源只有cpu版本&#xff0c;没有cuda版本&#xff0c;所以自己基于0.3.5版本源码编译一个RTX 30xx/40xx/50xx版本。 1. 前置条件 1. 访问https://developer.download.nvidia.cn/compute/cuda/12.8.0/local_…...

Spring Boot静态资源访问顺序

在 Spring Boot 中&#xff0c;static 和 public 目录都用于存放静态资源&#xff08;如 HTML、CSS、JavaScript、图片等文件&#xff09;&#xff0c;但它们在使用上有一些细微的区别。以下是它们的详细对比&#xff1a; 1. 默认优先级 Spring Boot 会按照以下优先级加载静态…...

电脑总显示串口正在被占用处理方法

1.现象 在嵌入式开发过程中&#xff0c;有很多情况下要使用串口调试&#xff0c;其中485/422/232转usb串口是非常常见的做法。 根据协议&#xff0c;接口芯片不同&#xff0c;需要安装对应的驱动程序&#xff0c;比如ch340&#xff0c;cp2102&#xff0c;CDM212364等驱动。可…...

工具介绍《HACKBAR V2》

HackBar V2 是一款功能强大的浏览器渗透测试工具&#xff0c;主要用于测试 SQL 注入、XSS 漏洞、POST 传参等安全场景。以下是其核心功能、用法及实际案例操作的综合介绍&#xff1a; 一、核心功能与用法详解 1. 基础操作 Load URL 功能&#xff1a;将当前浏览器地址栏的 URL …...

Java算法语法学习 美丽子集的数目 - 力扣 Map接口

文章目录 题目解题思路题解统计数组中每个数字按模k分组的出现次数&#xff0c;并保持数值有序作用 **merge(x, 1, Integer::sum)**解释**检查键是否存在**:**合并现有值**: 示例在代码中的应用**计算余数**:**存储余数及其出现次数**: merge 的常见用法统计频率合并字符串合并…...

Vue项目通过内嵌iframe访问另一个vue页面,获取token适配后端鉴权(以内嵌若依项目举例)

1. 改造子Vue项目进行适配(ruoyi举例) (1) 在路由文件添加需要被外链的vue页面配置 // 若依项目的话是 router/index.js文件 {path: /contrast,component: () > import(/views/contrast/index),hidden: true },(2) 开放白名单 // 若依项目的话是 permission.js 文件 cons…...

梯度本质论:从黎曼流形到神经网络的拓扑寻优

一、微分几何框架下的梯度再诠释 在标准数学分析中&#xff0c;梯度被定义为标量场 f : R n → R f:\mathbb{R}^n→\mathbb{R} f:Rn→R的导数张量 ∇ f ( ∂ f ∂ x 1 , . . . , ∂ f ∂ x n ) \nabla f(\frac{\partial f}{\partial x_1},...,\frac{\partial f}{\partial x_n…...

计算机毕业设计SpringBoot+Vue.js网络海鲜市场系统(源码+文档+PPT+讲解)

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…...

一文对比RAGFLOW和Open WebUI【使用场景参考】

一、RAGFLOW与Open WebUI RAGFLOW是一款基于深度文档理解构建的开源 RAG&#xff08;Retrieval-Augmented Generation&#xff09;引擎。RAGFlow 可以为各种规模的企业及个人提供一套精简的 RAG 工作流程&#xff0c;结合大语言模型&#xff08;LLM&#xff09;针对用户各类不…...

2025年03月07日Github流行趋势

项目名称&#xff1a;ai-hedge-fund 项目地址url&#xff1a;https://github.com/virattt/ai-hedge-fund项目语言&#xff1a;Python历史star数&#xff1a;12788今日star数&#xff1a;975项目维护者&#xff1a;virattt, seungwonme, KittatamSaisaard, andorsk, arsaboo项目…...

实训任务2.2 使用Wireshark捕获数据包并分析

目录 【实训目标】 【实训环境】 【实训内容】 【实训步骤】 1.启动WireShark 2. 使用Wireshark捕获数据包 &#xff08;1&#xff09;选择网络接口 &#xff08;2&#xff09;捕获数据包 &#xff08;1&#xff09;设置Wireshark过滤器并捕获数据包 &#xff08;2&…...

C# Lambda 表达式 详解

总目录 前言 在C#编程中&#xff0c;Lambda表达式是一种简洁而强大的语法特性&#xff0c;它提供了一种更加灵活和直观的方式来编写匿名函数。无论是在LINQ查询、事件处理还是异步编程中&#xff0c;Lambda表达式都扮演着重要角色。本文将详细介绍Lambda&#xff0c;帮助您更好…...

wordpress自定the_category的输出结构

通过WordPress的过滤器the_category来自定义输出内容。方法很简单&#xff0c;但是很实用。以下是一个示例代码&#xff1a; function custom_the_category($thelist, $separator , $parents ) {// 获取当前文章的所有分类$categories get_the_category();if (empty($categ…...

Docker 离线安装指南

参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性&#xff0c;不同版本的Docker对内核版本有不同要求。例如&#xff0c;Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本&#xff0c;Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...

装饰模式(Decorator Pattern)重构java邮件发奖系统实战

前言 现在我们有个如下的需求&#xff0c;设计一个邮件发奖的小系统&#xff0c; 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式&#xff08;Decorator Pattern&#xff09;允许向一个现有的对象添加新的功能&#xff0c;同时又不改变其…...

【磁盘】每天掌握一个Linux命令 - iostat

目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat&#xff08;I/O Statistics&#xff09;是Linux系统下用于监视系统输入输出设备和CPU使…...

【Java学习笔记】BigInteger 和 BigDecimal 类

BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点&#xff1a;传参类型必须是类对象 一、BigInteger 1. 作用&#xff1a;适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...

Chrome 浏览器前端与客户端双向通信实战

Chrome 前端&#xff08;即页面 JS / Web UI&#xff09;与客户端&#xff08;C 后端&#xff09;的交互机制&#xff0c;是 Chromium 架构中非常核心的一环。下面我将按常见场景&#xff0c;从通道、流程、技术栈几个角度做一套完整的分析&#xff0c;特别适合你这种在分析和改…...

Unity中的transform.up

2025年6月8日&#xff0c;周日下午 在Unity中&#xff0c;transform.up是Transform组件的一个属性&#xff0c;表示游戏对象在世界空间中的“上”方向&#xff08;Y轴正方向&#xff09;&#xff0c;且会随对象旋转动态变化。以下是关键点解析&#xff1a; 基本定义 transfor…...

Python训练营-Day26-函数专题1:函数定义与参数

题目1&#xff1a;计算圆的面积 任务&#xff1a; 编写一个名为 calculate_circle_area 的函数&#xff0c;该函数接收圆的半径 radius 作为参数&#xff0c;并返回圆的面积。圆的面积 π * radius (可以使用 math.pi 作为 π 的值)要求&#xff1a;函数接收一个位置参数 radi…...

【iOS】 Block再学习

iOS Block再学习 文章目录 iOS Block再学习前言Block的三种类型__ NSGlobalBlock____ NSMallocBlock____ NSStackBlock__小结 Block底层分析Block的结构捕获自由变量捕获全局(静态)变量捕获静态变量__block修饰符forwarding指针 Block的copy时机block作为函数返回值将block赋给…...

学习 Hooks【Plan - June - Week 2】

一、React API React 提供了丰富的核心 API&#xff0c;用于创建组件、管理状态、处理副作用、优化性能等。本文档总结 React 常用的 API 方法和组件。 1. React 核心 API React.createElement(type, props, …children) 用于创建 React 元素&#xff0c;JSX 会被编译成该函数…...

AWSLambda之设置时区

目标 希望Lambda运行的时区是东八区。 解决 只需要设置lambda的环境变量TZ为东八区时区即可&#xff0c;即Asia/Shanghai。 参考 使用 Lambda 环境变量...