从零开始实现大语言模型(十四):高阶训练技巧
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 ∥G∥2=12+22+22+42=25=5。因为 ∥ G ∥ 2 > 2 \|G\|_2>2 ∥G∥2>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. 前言 预训练大语言模型的流程与训练普通神经深度网络模型本质上并没有任何不同。可以使用深度学习实践中已经被证明非常有效的高阶训练技巧,优化大语言模型预训练流程,使大语言模型预训练效率更高,训练过程更稳定。 本文介绍深度学习领域…...

Spring-framework源码编译
版本统一(搭配其他版本会遇到不可知错误): 1)spring 5.2.X(5.5.26) 2)JDK8 3)Gradle:5.6.4 可以在gradle-wrapper.properties中修改 https\://services.gradle.org/distribution…...
分布式系统的核心挑战与解决方案
1、分布式系统的引入 在移动互联网、云计算和物联网的推动下,现代软件系统需要处理亿级用户请求、PB级数据存储和毫秒级响应需求。传统的单体架构受限于单机性能瓶颈和容灾能力,逐渐被分布式系统取代。例如,电商平台在“双十一”期间需应对每…...
fastjson漏洞
fastjson漏洞 fastjson工作原理攻击原理补充 例子 fastjson工作原理 fastjson的作用是将JAVA对象转换成对应的json表示形式,也可以反过来将json转化为对应的Java对象。fastjson使用AutoType功能进行反序列化,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.后代选择器 注:1.后代选择器会选中后代所有的要选择的标签 2.儿子选择器 3.并集选择器 注:1.注意换行,同时选中多种标签 4.交集选择器 注:1.标签选择器放在最前面,例如放在类选择器的前面 2.两个选择…...

基于 LeNet 网络的 MNIST 数据集图像分类
1.LeNet的原始实验数据集MNIST 名称:MNIST手写数字数据集 数据类型:灰度图 (一通道) 图像大小:28*28 类别数:10类(数字0-9) 1.通过torchvision.datasets.MNIST下载并保存到本地…...

win11编译llama_cpp_python cuda128 RTX30/40/50版本
Geforce 50xx系显卡最低支持cuda128,llama_cpp_python官方源只有cpu版本,没有cuda版本,所以自己基于0.3.5版本源码编译一个RTX 30xx/40xx/50xx版本。 1. 前置条件 1. 访问https://developer.download.nvidia.cn/compute/cuda/12.8.0/local_…...
Spring Boot静态资源访问顺序
在 Spring Boot 中,static 和 public 目录都用于存放静态资源(如 HTML、CSS、JavaScript、图片等文件),但它们在使用上有一些细微的区别。以下是它们的详细对比: 1. 默认优先级 Spring Boot 会按照以下优先级加载静态…...

电脑总显示串口正在被占用处理方法
1.现象 在嵌入式开发过程中,有很多情况下要使用串口调试,其中485/422/232转usb串口是非常常见的做法。 根据协议,接口芯片不同,需要安装对应的驱动程序,比如ch340,cp2102,CDM212364等驱动。可…...
工具介绍《HACKBAR V2》
HackBar V2 是一款功能强大的浏览器渗透测试工具,主要用于测试 SQL 注入、XSS 漏洞、POST 传参等安全场景。以下是其核心功能、用法及实际案例操作的综合介绍: 一、核心功能与用法详解 1. 基础操作 Load URL 功能:将当前浏览器地址栏的 URL …...
Java算法语法学习 美丽子集的数目 - 力扣 Map接口
文章目录 题目解题思路题解统计数组中每个数字按模k分组的出现次数,并保持数值有序作用 **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…...
梯度本质论:从黎曼流形到神经网络的拓扑寻优
一、微分几何框架下的梯度再诠释 在标准数学分析中,梯度被定义为标量场 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+讲解)
温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…...

一文对比RAGFLOW和Open WebUI【使用场景参考】
一、RAGFLOW与Open WebUI RAGFLOW是一款基于深度文档理解构建的开源 RAG(Retrieval-Augmented Generation)引擎。RAGFlow 可以为各种规模的企业及个人提供一套精简的 RAG 工作流程,结合大语言模型(LLM)针对用户各类不…...

2025年03月07日Github流行趋势
项目名称:ai-hedge-fund 项目地址url:https://github.com/virattt/ai-hedge-fund项目语言:Python历史star数:12788今日star数:975项目维护者:virattt, seungwonme, KittatamSaisaard, andorsk, arsaboo项目…...

实训任务2.2 使用Wireshark捕获数据包并分析
目录 【实训目标】 【实训环境】 【实训内容】 【实训步骤】 1.启动WireShark 2. 使用Wireshark捕获数据包 (1)选择网络接口 (2)捕获数据包 (1)设置Wireshark过滤器并捕获数据包 (2&…...
C# Lambda 表达式 详解
总目录 前言 在C#编程中,Lambda表达式是一种简洁而强大的语法特性,它提供了一种更加灵活和直观的方式来编写匿名函数。无论是在LINQ查询、事件处理还是异步编程中,Lambda表达式都扮演着重要角色。本文将详细介绍Lambda,帮助您更好…...
wordpress自定the_category的输出结构
通过WordPress的过滤器the_category来自定义输出内容。方法很简单,但是很实用。以下是一个示例代码: function custom_the_category($thelist, $separator , $parents ) {// 获取当前文章的所有分类$categories get_the_category();if (empty($categ…...

.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...
【磁盘】每天掌握一个Linux命令 - iostat
目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...
什么是EULA和DPA
文章目录 EULA(End User License Agreement)DPA(Data Protection Agreement)一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA(End User License Agreement) 定义: EULA即…...

(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文全面剖析RNN核心原理,深入讲解梯度消失/爆炸问题,并通过LSTM/GRU结构实现解决方案,提供时间序列预测和文本生成…...

【网络安全】开源系统getshell漏洞挖掘
审计过程: 在入口文件admin/index.php中: 用户可以通过m,c,a等参数控制加载的文件和方法,在app/system/entrance.php中存在重点代码: 当M_TYPE system并且M_MODULE include时,会设置常量PATH_OWN_FILE为PATH_APP.M_T…...
WEB3全栈开发——面试专业技能点P4数据库
一、mysql2 原生驱动及其连接机制 概念介绍 mysql2 是 Node.js 环境中广泛使用的 MySQL 客户端库,基于 mysql 库改进而来,具有更好的性能、Promise 支持、流式查询、二进制数据处理能力等。 主要特点: 支持 Promise / async-await…...

EEG-fNIRS联合成像在跨频率耦合研究中的创新应用
摘要 神经影像技术对医学科学产生了深远的影响,推动了许多神经系统疾病研究的进展并改善了其诊断方法。在此背景下,基于神经血管耦合现象的多模态神经影像方法,通过融合各自优势来提供有关大脑皮层神经活动的互补信息。在这里,本研…...

解密鸿蒙系统的隐私护城河:从权限动态管控到生物数据加密的全链路防护
摘要 本文以健康管理应用为例,展示鸿蒙系统如何通过细粒度权限控制、动态权限授予、数据隔离和加密存储四大核心机制,实现复杂场景下的用户隐私保护。我们将通过完整的权限请求流程和敏感数据处理代码,演示鸿蒙系统如何平衡功能需求与隐私安…...