模型训练过程的显存占用实测

依赖项说明

pip install nvitop
pip install timm
pip install peft

后续的显存占用数据截图,均基于nvitop命令实现

1、模型显存占用说明

1.1 理论占用值

在 一文讲明白大模型显存占用(只考虑单卡)与大模型显存占用分析都对模型训练过程中的显存占用进行了分析。但存在一定的出入。为此编写代码进行实测。

但基本上,都是确认训练显存消耗(可估算部分)主要为:模型参数(Model)+ 优化器状态(Optimizer status)+梯度值(Gradient)+激活值(Activation),只是对这个具体为多少存在差异。

大模型显存占用分析观点 ?
占用显存的大头主要分为四部分:模型参数、前向计算过程中产生的中间激活、后向传递计算得到的梯度、优化器状态。这里着重分析参数、梯度和优化器状态的显存占用。训练大模型时通常会采用AdamW优化器,并用混合精度训练来加速训练,基于这个前提分析显存占用。

使用AdamW优化器和混合精度训练来训练参数量为 A的大模型,模型参数、梯度和优化器状态占用的显存大小为 20A。Adam需要一阶参数与二阶参数,所以需要2个float32,8个字节;博主认为但在混合精度训练中,一阶参数是fp16,二阶参数才是float32,应该是6个字节
在这里插入图片描述
根据其观点,在不考虑激活值的情况下,模型混合精度训练显存占用为模型参数的20倍。如果是fp32训练显存占用则为24倍。与其他观点相比是多了一个梯度值的参数

一文讲明白大模型显存占用(只考虑单卡) 观点 ?
在这里插入图片描述
按照训练运行的逻辑来讲:

Step1:优化器会先备份一份FP32精度的模型权重,初始化好FP32精度的一阶和二阶动量(用于更新权重)。
Step2:开辟一块新的存储空间,将FP32精度的模型权重转换为FP16精度的模型权重。
Step3:运行forward和backward,产生的梯度和激活值都用FP16精度存储。
Step4:优化器利用FP16的梯度和FP32精度的一阶和二阶动量去更新备份的FP32的模型权重。
Step5:重复Step2到Step4训练,直到模型收敛。

我们可以看到训练过程中显存主要被用在四个模块上:
模型权重本身(FP32+FP16)
梯度(FP16)
优化器(FP32),Adam优化器为(FP32+FP32)
激活值(FP16)

根据其观点,在不考虑激活值的情况下,模型混合训练显存占用为模型参数的16倍。如果是fp32训练则为20倍
在这里插入图片描述

1.2 显存实测

模型参数量与激活值占用情况。因为考虑了较大的激活值,无法准确衡量显存占用。


import timm
import torch
import ipdb
def print_memory_use(tag):useed=torch.cuda.memory_reserved() / (1024 ** 3)torch.cuda.empty_cache()print(f"{tag}: {useed:.4f}G")if __name__=="__main__":device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")print_memory_use("import时显存占用:")x = torch.randn(90, 3, 224, 224).to(device) print_memory_use("torch.randn(90, 3, 224, 224) 显存占用:")model = timm.create_model('resnet50', pretrained=False, num_classes=102).to(device)print_memory_use("模型1初始化显存占用:")model2 = timm.create_model('resnet50', pretrained=False, num_classes=102).to(device)print_memory_use("模型2初始化显存占用:")del model2torch.cuda.empty_cache()optimizer = torch.optim.Adam(model.parameters())print_memory_use("adam优化器显存占用:")from fvcore.nn import flop_count_table, FlopCountAnalysis, ActivationCountAnalysis   print(f'params: {sum(map(lambda x: x.numel(), model.parameters()))/1024/1024:.3f}M')st=flop_count_table(FlopCountAnalysis(model, x), activations=ActivationCountAnalysis(model, x))st=str(st).split('\n')[:3]st="\n".join(st)print(st)print_memory_use("FlopCountAnalysis显存占用:")with torch.no_grad():model(x)print_memory_use("torch.no_grad()推理时显存占用:")out=model(x)print_memory_use("模型+激活值显存占用:")loss_func = torch.nn.CrossEntropyLoss()label=torch.randn(90, 102).to(device) loss = loss_func(out, label)  # 计算loss print_memory_use("loss计算显存占用:")loss.backward()print_memory_use("反向传播显存占用:")optimizer.step()print_memory_use("优化时显存占用:")optimizer.zero_grad()  # 梯度置0print_memory_use("zero_grad显存占用:")

输出结果如下,可以发现模型参数量为23M约对应90M显存,激活值为1G约对应4G显存。

在执行import时,显存占用是0,基于导入torch并不会引起显存变化

初始化x时,显存占用为0.05g

基于模型1与模型2初始化的显存差异,可以确认resnet50的显存占用为0.2441-0.1680=0.0761G=76.1M ,根据参数量22.618M推算理论显存占用应当为90.472M。

同时,基于此,可以推测torch.cuda环境显存占用为0.1680G-0.0761G-0.05G=0.0419G=50M

import时显存占用:: 0.0000G
torch.randn(90, 3, 224, 224) 显存占用:: 0.0508G
模型1初始化显存占用:: 0.1680G
模型2初始化显存占用:: 0.2441G
adam优化器显存占用:: 0.1680G
params: 22.618M
| module                 | #parameters or shape   | #flops     | #activations   |
|:-----------------------|:-----------------------|:-----------|:---------------|
| model                  | 23.717M                | 0.373T     | 1G             |
FlopCountAnalysis显存占用:: 7.4160G
torch.no_grad()推理时显存占用:: 1.5469G
模型+激活值显存占用:: 7.4336G
loss计算显存占用:: 7.4043G
反向传播显存占用:: 8.5820G
优化时显存占用:: 0.7520G
zero_grad显存占用:: 0.7344G

将模型修改为resnet101后,输出的信息如下所示。在同等的输入添加下,resnet101的激活值比resnet50多0.461g(约2g的显存),参数多20m(约80m的显存)。
在这里,基于模型1与模型2初始化的显存差异,可以确认resnet101的显存占用为0.3887-0.2207=0.1679G=167.9M ,根据参数量40.731M推算理论显存占用应当为162.924M。这里的差异比resnet要少很多;同时,可以确定resnet101比resnet50要多占用80m的显存
基于此,可以推测torch.cuda环境显存占用为0.2207-0.1679-0.05=0.00280G=2M,这应该是不正确的。

adam优化器,在没有计算时是不占用显存的。
对比两次torch.no_grad()推理时显存占用差异,可以确定torch.cuda环境是在推理或训练时占用1.4G左右的显存。

对比两次模型+激活值显存占用,可以发现差值为3.5G,

import时显存占用:: 0.0000G
torch.randn(90, 3, 224, 224) 显存占用:: 0.0508G
模型1初始化显存占用:: 0.2207G
模型2初始化显存占用:: 0.3887G
adam优化器显存占用:: 0.2207G
params: 40.731M
| module                 | #parameters or shape   | #flops     | #activations   |
|:-----------------------|:-----------------------|:-----------|:---------------|
| model                  | 42.709M                | 0.709T     | 1.461G         |
FlopCountAnalysis显存占用:: 11.0273G
torch.no_grad()推理时显存占用:: 1.5996G
模型+激活值显存占用:: 11.0352G
loss计算显存占用:: 11.0332G
反向传播显存占用:: 7.4043G
优化时显存占用:: 1.3809G
zero_grad显存占用:: 1.3301G

#---------------------------------------------------------------------------------------------------
当将模型修改为resnet18时,显存信息如下所示

import时显存占用:: 0.0000G
torch.randn(90, 3, 224, 224) 显存占用:: 0.0508G
模型1初始化显存占用:: 0.1133G
模型2初始化显存占用:: 0.1562G
adam优化器显存占用:: 0.1133G
params: 10.709M
| module                 | #parameters or shape   | #flops     | #activations   |
|:-----------------------|:-----------------------|:-----------|:---------------|
| model                  | 11.229M                | 0.164T     | 0.224G         |
FlopCountAnalysis显存占用:: 1.9863G
torch.no_grad()推理时显存占用:: 0.6816G
模型+激活值显存占用:: 1.9961G
loss计算显存占用:: 1.9668G
反向传播显存占用:: 3.6562G
优化时显存占用:: 0.2969G
zero_grad显存占用:: 0.2754G

1.3 无激活值下测试

这里修改代码,将尽可能将激活值设置为0。这里可以发现,fp32下,显存占用大约是模型的24倍左右。


import timm
import torch
import ipdb
def print_memory_use(tag):useed=torch.cuda.memory_reserved() / (1024 ** 3)torch.cuda.empty_cache()print(f"{tag}: {useed:.4f}G")if __name__=="__main__":device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")print_memory_use("import时显存占用:")x = torch.randn(1, 3, 64, 64).to(device) print_memory_use("torch.randn(1, 3, 64, 64) 显存占用:")model = timm.create_model('resnet18', pretrained=False, num_classes=102).to(device)print_memory_use("模型1初始化显存占用:")model2 = timm.create_model('resnet18', pretrained=False, num_classes=102).to(device)print_memory_use("模型2初始化显存占用:")del model2torch.cuda.empty_cache()optimizer = torch.optim.Adam(model.parameters())print_memory_use("adam优化器显存占用:")from fvcore.nn import flop_count_table, FlopCountAnalysis, ActivationCountAnalysis    print(f'params: {sum(map(lambda x: x.numel(), model.parameters()))/1024/1024:.3f}M')st=flop_count_table(FlopCountAnalysis(model, x), activations=ActivationCountAnalysis(model, x))st=str(st).split('\n')[:3]st="\n".join(st)print(st)print_memory_use("FlopCountAnalysis显存占用:")with torch.no_grad():model(x)print_memory_use("torch.no_grad()推理时显存占用:")out=model(x)print_memory_use("模型+激活值显存占用:")loss_func = torch.nn.CrossEntropyLoss()label=torch.randn(1, 102).to(device) loss = loss_func(out, label)  # 计算loss print_memory_use("loss计算显存占用:")loss.backward()print_memory_use("反向传播显存占用:")optimizer.step()print_memory_use("优化时显存占用:")optimizer.zero_grad()  # 梯度置0print_memory_use("zero_grad显存占用:")

当设置为resnet18模型时,输入如下

import时显存占用:: 0.0000G
torch.randn(1, 3, 64, 64) 显存占用:: 0.0020G
模型1初始化显存占用:: 0.0625G
模型2初始化显存占用:: 0.1055G
adam优化器显存占用:: 0.0625G
params: 10.709M
| module                 | #parameters or shape   | #flops    | #activations   |
|:-----------------------|:-----------------------|:----------|:---------------|
| model                  | 11.229M                | 0.149G    | 0.203M         |
FlopCountAnalysis显存占用:: 0.0859G
torch.no_grad()推理时显存占用:: 0.0840G
模型+激活值显存占用:: 0.0859G
loss计算显存占用:: 0.0645G
反向传播显存占用:: 0.1074G
优化时显存占用:: 0.2520G
zero_grad显存占用:: 0.2109G

当设置为resnet50模型时,输入如下

import时显存占用:: 0.0000G
torch.randn(1, 3, 64, 64) 显存占用:: 0.0020G
模型1初始化显存占用:: 0.1172G
模型2初始化显存占用:: 0.1934G
adam优化器显存占用:: 0.1172G
params: 22.618M
| module                 | #parameters or shape   | #flops    | #activations   |
|:-----------------------|:-----------------------|:----------|:---------------|
| model                  | 23.717M                | 0.338G    | 0.907M         |
FlopCountAnalysis显存占用:: 0.1445G
torch.no_grad()推理时显存占用:: 0.1367G
模型+激活值显存占用:: 0.1445G
loss计算显存占用:: 0.1230G
反向传播显存占用:: 0.2168G
优化时显存占用:: 0.4805G
zero_grad显存占用:: 0.4043G

当设置为resnet101模型时,输入如下

import时显存占用:: 0.0000G
torch.randn(1, 3, 64, 64) 显存占用:: 0.0020G
模型1初始化显存占用:: 0.1699G
模型2初始化显存占用:: 0.3379G
adam优化器显存占用:: 0.1699G
params: 40.731M
| module                 | #parameters or shape   | #flops    | #activations   |
|:-----------------------|:-----------------------|:----------|:---------------|
| model                  | 42.709M                | 0.644G    | 1.325M         |
FlopCountAnalysis显存占用:: 0.1992G
torch.no_grad()推理时显存占用:: 0.1914G
模型+激活值显存占用:: 0.1992G
loss计算显存占用:: 0.1992G
反向传播显存占用:: 0.3613G
优化时显存占用:: 0.8418G
zero_grad显存占用:: 0.6738G

2、实际占用测试

这里通过对比,发现对于小模型而言,全量训练与冻结部分参数、lora训练差异不大(lora训练反而更消耗资源),影响模型训练显存需求的反而是fp32与混合精度的差异。

2.1 完整代码

基于以下代码实现模型不同情况下的训练

import torch
import torchvision
from torch.autograd import Variable
import torch.utils.data.dataloader as Data
import timm
from torchvision.transforms import transforms
from tqdm import tqdm
from torch.amp import GradScaler
from torch.amp import autocast
from peft import LoraConfig,get_peft_model
from peft.tuners import lora
def freeze_model(model,freeze_rate=0.95):if True:#对layer进行冻结---------layers=[]for name, module in model.named_modules():layers.append(name)freeze_index=int(len(layers)*freeze_rate)freeze_layers=layers[:freeze_index]for name, module in model.named_modules():if name in freeze_layers:module.requires_grad=Falsemodule.requires_grad_=False
def set_lora_model(model):# 可以按照个人需求冻结模型# for name, param in model.named_parameters():#     param.requires_grad = Falsetarget_modules=[]# 获取要继续lora操作的层for name,module in model.named_modules():if isinstance(module,torch.nn.Conv2d):target_modules.append(name)# 实例化LoraConfigconfig = LoraConfig(r=4,   lora_alpha=32, lora_dropout=0.1,bias="none",task_type=None,init_lora_weights="gaussian", target_modules=target_modules,)lora_model = get_peft_model(model, config)for name,module in model.named_modules():if isinstance(module,lora.layer.Conv2d):for name, param in module.base_layer.named_parameters():param.requires_grad = Falsemodule.in_channels=module.base_layer.in_channelsmodule.out_channels=module.base_layer.out_channelsmodule.kernel_size=module.base_layer.kernel_sizemodule.stride=module.base_layer.stridemodule.padding=module.base_layer.paddingmodule.dilation=module.base_layer.dilationmodule.groups=module.base_layer.groupsreturn lora_modeldef train():train_loss = 0.train_acc = 0.n=0d_len=0pbar= tqdm(total=len(train_loader),desc='Train: ')for batch_x, batch_y in train_loader:batch_x, batch_y = Variable(batch_x).to(device), Variable(batch_y).to(device)# print(batch_x.shape,batch_y.shape)optimizer.zero_grad()  # 梯度置0d_len+=batch_x.shape[0]if train_amp:#混合精度运算作用域with autocast(device_type='cuda'):out = model(batch_x)  # 前向传播loss = loss_func(out, batch_y)  # 计算loss#将梯度进行相应的缩放scaler.scale(loss).backward() # 返向传播#设置优化器计步scaler.step(optimizer)#更新尺度scaler.update()else:out = model(batch_x)  # 前向传播loss = loss_func(out, batch_y)  # 计算loss loss.backward()optimizer.step()# ------计算loss,acctrain_loss += loss.item()# torch.max(out, 1) 指第一维最大值,返回[最大值,最大值索引]pred = torch.max(out, 1)[1]train_correct = (pred == batch_y).sum()train_acc += train_correct.item()n += batch_y.shape[0]pbar.update(1)pbar.set_postfix({'loss': '%.4f' % (train_loss / n),'train acc': '%.3f' % (train_acc / n),'dlen':d_len})pbar.close()print('Train Loss: {:.6f}, Acc: {:.6f}'.format(train_loss / (len(train_data)), train_acc / (len(train_data)))  ,batch_x.shape)def eval():model.eval()eval_loss = 0.eval_acc = 0.n=0d_len=0pbar= tqdm(total=len(test_loader),desc='Test: ')for batch_x, batch_y in test_loader:# 测试阶段不需要保存梯度信息with torch.no_grad():batch_x, batch_y = Variable(batch_x).to(device), Variable(batch_y).to(device)if train_amp:with autocast(device_type='cuda'):out = model(batch_x)loss = loss_func(out, batch_y)else:out = model(batch_x)loss = loss_func(out, batch_y)eval_loss += loss.item()pred = torch.max(out, 1)[1]num_correct = (pred == batch_y).sum()eval_acc += num_correct.item()d_len+=batch_x.shape[0]n+=1pbar.update(1)pbar.set_postfix({'loss': '%.4f' % (eval_loss / n),'eval acc': '%.3f' % (eval_acc / d_len),'dlen':d_len})pbar.close()print('Test Loss: {:.6f}, Acc: {:.6f}'.format(eval_loss / (len(test_data)), eval_acc / (len(test_data))))transform=transforms.Compose([transforms.RandomResizedCrop(224),transforms.RandomHorizontalFlip(),transforms.ToTensor(),transforms.Normalize(mean=(0.485, 0.456, 0.406),std=(0.229, 0.224, 0.225))])
train_data = torchvision.datasets.Flowers102('./data', split = "train", transform=transform, download=True
)
test_data = torchvision.datasets.Flowers102('./data',  split = "test", transform=transform
)train_loader = Data.DataLoader(dataset=train_data, batch_size=90, shuffle=True)
test_loader = Data.DataLoader(dataset=test_data, batch_size=90)device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
model = timm.create_model('resnet18', pretrained=True, num_classes=102).to(device)if __name__=="__main__":train_amp = True # freeze_95 = Truetrain_lora = Trueif freeze_95:freeze_model(model,freeze_rate=0.95)if train_lora:set_lora_model(model)print(model)optimizer = torch.optim.Adam(filter(lambda p : p.requires_grad, model.parameters()))loss_func = torch.nn.CrossEntropyLoss()scaler = GradScaler()for epoch in range(10):print('epoch {}'.format(epoch + 1))# training-----------------------------train()# evaluation--------------------------------eval()

2.2 全量训练-fp32

设置三个关键参数为False,可以使模型在fp32下进行训练

if __name__=="__main__":train_amp = False # Truefreeze_95 = Falsetrain_lora = Falseif freeze_95:freeze_model(model,freeze_rate=0.95)if train_lora:set_lora_model(model)print(model)optimizer = torch.optim.Adam(filter(lambda p : p.requires_grad, model.parameters()))loss_func = torch.nn.CrossEntropyLoss()scaler = GradScaler()for epoch in range(10):print('epoch {}'.format(epoch + 1))# training-----------------------------train()# evaluation--------------------------------eval()

当进行全量训练时的显存占用如下,为5G
在这里插入图片描述
同时训练速度为2.3it/s
在这里插入图片描述

2.3 冻结部分参数-fp32

仅设置freeze_95=True,可以使模型在冻结95%的参数下,采用fp32进行训练

if __name__=="__main__":train_amp = False # Truefreeze_95 = Truetrain_lora = Falseif freeze_95:freeze_model(model,freeze_rate=0.95)if train_lora:set_lora_model(model)print(model)optimizer = torch.optim.Adam(filter(lambda p : p.requires_grad, model.parameters()))loss_func = torch.nn.CrossEntropyLoss()scaler = GradScaler()for epoch in range(10):print('epoch {}'.format(epoch + 1))# training-----------------------------train()# evaluation--------------------------------eval()

冻结部分参数训练时的显存占用如下,为5G
在这里插入图片描述
同时训练速度也为2.3it/s
在这里插入图片描述

2.4 lora训练-fp32

基于以下代码进行lora训练模型

if __name__=="__main__":train_amp = False # Truefreeze_95 = Falsetrain_lora = Trueif freeze_95:freeze_model(model,freeze_rate=0.95)if train_lora:set_lora_model(model)print(model)optimizer = torch.optim.Adam(filter(lambda p : p.requires_grad, model.parameters()))loss_func = torch.nn.CrossEntropyLoss()scaler = GradScaler()for epoch in range(10):print('epoch {}'.format(epoch + 1))# training-----------------------------train()# evaluation--------------------------------eval()

冻结部分参数训练时的显存占用如下,为5.1G,比正常训练要高
在这里插入图片描述
同时模型参数迭代速度下降
在这里插入图片描述

2.5 全量训练-fp16

基于以下代码进行模型训练

if __name__=="__main__":train_amp = True # freeze_95 = Falsetrain_lora = Falseif freeze_95:freeze_model(model,freeze_rate=0.95)if train_lora:set_lora_model(model)print(model)optimizer = torch.optim.Adam(filter(lambda p : p.requires_grad, model.parameters()))loss_func = torch.nn.CrossEntropyLoss()scaler = GradScaler()for epoch in range(10):print('epoch {}'.format(epoch + 1))# training-----------------------------train()# evaluation--------------------------------eval()

可以发现模型显存占用为3G
在这里插入图片描述
同时,参数迭代速度为2.6it/s
在这里插入图片描述

2.6 冻结部分参数-fp16

基于以下代码进行模型训练

if __name__=="__main__":train_amp = True # freeze_95 = Truetrain_lora = Falseif freeze_95:freeze_model(model,freeze_rate=0.95)if train_lora:set_lora_model(model)print(model)optimizer = torch.optim.Adam(filter(lambda p : p.requires_grad, model.parameters()))loss_func = torch.nn.CrossEntropyLoss()scaler = GradScaler()for epoch in range(10):print('epoch {}'.format(epoch + 1))# training-----------------------------train()# evaluation--------------------------------eval()

可以发现模型显存占用为3G
在这里插入图片描述
同时,参数迭代速度为2.6it/s
在这里插入图片描述

2.7 lora训练-fp16

基于以下代码进行模型训练

if __name__=="__main__":train_amp = True # freeze_95 = Falsetrain_lora = Trueif freeze_95:freeze_model(model,freeze_rate=0.95)if train_lora:set_lora_model(model)print(model)optimizer = torch.optim.Adam(filter(lambda p : p.requires_grad, model.parameters()))loss_func = torch.nn.CrossEntropyLoss()scaler = GradScaler()for epoch in range(10):print('epoch {}'.format(epoch + 1))# training-----------------------------train()# evaluation--------------------------------eval()

可以发现模型显存占用为3.2G
在这里插入图片描述

同时,参数迭代速度为2.5it/s
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

后端分层解耦

引入 在上篇所举的例子中,我们将所有的代码均放在HelloControl方法之中,这样会导致代码的复用性、可读性较差,难以维护。因此我们需 三层架构 在之前的代码中,代码大体可以分为三部分:数据访问、数据逻辑处理、响应数…

AIGC 入门全攻略:开启智能创作新时代

一、AIGC 初印象 AIGC,即人工智能生成内容,是继专业生产内容(PGC)、用户生产内容(UGC)之后的新型内容创作方式。它涵盖了文本生成、图像与视频创作、音频生成等多个领域,正在以惊人的速度改变着…

约克VRF地暖中央空调,让你舒适过冬

想要冬季过得舒服,采暖必须要到位!对于没有集中供暖的南方地区来说,冬季室内阴冷刺骨。 选购地暖中央空调时,强效制热的能力必不可少,让我们可以享受温暖的室内温度,有效减少室内忽冷忽热的温度变化。 约克…

基于Java Springboot宠物领养救助平台

一、作品包含 源码数据库设计文档万字PPT全套环境和工具资源部署教程 二、项目技术 前端技术:Html、Css、Js、Vue、Element-ui 数据库:MySQL 后端技术:Java、Spring Boot、MyBatis 三、运行环境 开发工具:IDEA/eclipse 数据…

使用原生 OpenTelemetry 解锁各种可能性:优先考虑可靠性,而不是专有限制

作者:来自 Elastic Bahubali Shetti•Miguel Luna Elastic 现在支持使用 OTel Operator 在 Kubernetes 上部署和管理 Elastic Distributions of OpenTelemetry (EDOT)。SRE 现在可以访问开箱即用的配置和仪表板,这些配置和仪表板旨在通过 Elastic Observ…

基于python Django的boss直聘数据采集与分析预测系统,爬虫可以在线采集,实时动态显示爬取数据,预测基于技能匹配的预测模型

本系统是基于Python Django框架构建的“Boss直聘”数据采集与分析预测系统,旨在通过技能匹配的方式对招聘信息进行分析与预测,帮助求职者根据自身技能找到最合适的职位,同时为招聘方提供更精准的候选人推荐。系统的核心预测模型基于职位需求技…

安装 python-pcl 遇到的问题

安装python-pcl 成功安装错误尝试尝试一尝试二尝试三 本人环境 Ubuntu 22.04.4LTS ros2-humble cpython 3.0.11 python 3.10.12 libpcl-dev 1.12.1dfsg-3build1 pcl-tools 1.12.1dfsg-3build1 代码摘抄来源:Breadcrumbsouster-ros-extras/scripts/ros2_pcl_filters.…

【C++进阶篇】——string类的使用

文章目录 前言:1. string的介绍2. string类对象的常见构造3. string类对象的容量操作4. string类对象的访问5. 迭代器6. string类对象的修改操作7. string类对象的字符串运算8.string类成员函数9.string类非成员函数10.string类常量成员 前言: std::str…

vmware虚拟机给创建的centos扩展磁盘步骤

1.先看看原来的磁盘信息,目前磁盘是20g的,重点关注红色箭头指向的地方,一个17g 可用11g,接下来要对其进行扩展 df -h2.关闭当前虚拟机,先进行磁盘扩展,目前我扩展到了50g。 3.重新开启虚拟机,…

开源物业管理系统助力智能社区提升服务效率与用户体验

内容概要 开源物业管理系统是一种灵活、智能的解决方案,专为社区物业管理而生。随着智能社区的发展,这种系统变得越来越重要。它不仅帮助物业管理者高效地处理日常事务,还提升了居民的生活体验。在这个日新月异的时代,开源物业管…

深入理解 Redis跳跃表 Skip List 原理|图解查询、插入

1. 简介 跳跃表 ( skip list ) 是一种有序数据结构,通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的。 在 Redis 中,跳跃表是有序集合键的底层实现之一,那么这篇文章我们就来讲讲跳跃表的实现原理。 2. …

【数据库】mysql数据库迁移前应如何备份数据?

MySQL 数据库的备份是确保数据安全的重要措施之一。在进行数据库迁移之前,备份现有数据可以防止数据丢失或损坏。以下是一套详细的 MySQL 数据库备份步骤,适用于大多数情况。请注意,具体的命令和工具可能因 MySQL 版本的不同而有所差异。整个…

AWTK-WIDGET-WEB-VIEW 实现笔记 (4) - Ubuntu

Ubuntu 上实现 AWTK-WIDGET-WEB-VIEW 开始以为很简单,后来发现是最麻烦的。因为 Ubuntu 上的 webview 库是 基于 GTK 的,而 AWTK 是基于 X11 的,两者的窗口系统不同,所以期间踩了几个大坑。 1. 编译 AWTK 在使用 Linux 的输入法时…

Rocket入门练习

搭建部署: 1. 部署平台和部署方式: Ubuntu:22.10 部署方式:源码安装部署 a. 下载源码到本地:rocketmq-all-5.3.1-source-release.zip $ unzip rocketmq-all-5.3.1-source-release.zip // 解压缩 $ cd rocketmq-all…

视觉SLAM相机——单目相机、双目相机、深度相机

一、单目相机 只使用一个摄像头进行SLAM的做法称为单目SLAM,这种传感器的结构特别简单,成本特别低,单目相机的数据:照片。照片本质上是拍摄某个场景在相机的成像平面上留下的一个投影。它以二维的形式记录了三维的世界。这个过程中…

EM算法与高斯混合聚类:理解与实践

💗💗💗欢迎来到我的博客,你将找到有关如何使用技术解决问题的文章,也会找到某个技术的学习路线。无论你是何种职业,我都希望我的博客对你有所帮助。最后不要忘记订阅我的博客以获取最新文章,也欢…

悬浮窗,ViewPager2内嵌套RecyclerView,RecyclerView高度异常的问题分析

1 背景 在一个Adnroid项目中,使用到了悬浮窗,其中有一个需求是以分页的显示显示媒体item,每一页中展示的媒体item是一个网格列表的形式显示的。 原型图如下: 2 实现方案 上述需求实现分页采用ViewPager2,在xml中的…

wordpress使用相关

这里写目录标题 遇到的相关问题WordPress安装插件过程中遇到需要ftp出现确实XMLReader 插件的提示cURL Support Missing(curl 缺失) 遇到的相关问题 WordPress安装插件过程中遇到需要ftp 一般在这个位置 出现确实XMLReader 插件的提示 解决&#xff1a…

安卓手机root+magisk安装证书+抓取https请求

先讲一下有这篇文章的背景吧,在使用安卓手机fiddler抓包时,即使信任了证书,并且手机也安装了证书,但是还是无法捕获https请求的问题,最开始不知道原因,后来慢慢了解到现在有的app为了防止抓包,把…

本草云端:中药实验管理的云服务

3系统分析 3.1可行性分析 通过对本中药实验管理系统实行的目的初步调查和分析,提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本中药实验管理系统采用SSM框架,JAVA作为开发语…