CV | Segment Anything论文详解及代码实现

本文主要是详解解释了SAM的论文部分以及代码实现~

论文:2023.04.05_Segment Anything

论文地址:2304.02643.pdf (arxiv.org)

代码地址:facebookresearch/segment-anything: The repository provides code for running inference with the SegmentAnything Model (SAM), links for downloading the trained model checkpoints, and example notebooks that show how to use the model. (github.com)

1.论文详解

SAM 包括三个主要组件:图像编码器、提示编码器和掩蔽解码器。

图像编码器由基于 MAE 预训练的 ViT 构成,而掩蔽解码器如图所示

图像编码器(Image encoder)

基于ViT模型对于图像编码作为输入,利用MAE(Masked AutoEncoder)预训练的ViT模型,对每张图片只处理一次。输入(c,h,w)的图像,对图像进行缩放,按照长边缩放成1024,短边不够就填充(pad),得到(c,1024,1024)的图像,经过image encoder,得到对图像16倍下采样的feature,大小为(256,64,64)。

提示编码器(Prompt encoder )

俩种提示:

稀疏sparse

  • 点points:使用position encodings。点映射到256维的向量,代表点位置的 positional encoding,加2个代表该点是前景/背景的可学习的embedding
  • boxes:使用position encodings,俩点确定一个box,用一个embedding对表示:1) 可学习的embedding代表左上角,2) 可学习的embedding代表右下角
  • text:使用CLIP作为encoder

密集dense(mask):使用卷积作为encoder,mask 和image embedding通过element-wise相乘 (逐元素相乘,可以理解成mask的feature对image的feature进行加权)。在内部会缩小4倍,通过2个2×2,步幅为2的卷积实现,输出的通道为4和16.然后1×1卷积将这些通道映射到256个通道,每一次都通过层归一化进行增强,然后将蒙版按元素添加到图像嵌入中。在未提示掩码提示的情况下,将每个图像嵌入位置添加一个表示无掩码的学习嵌入。

掩码解码(Mask decoder)

掩码解码器有效地将图像嵌入、提示嵌入和输出令牌映射到掩码。它使用改进的 Transformer 解码器,后跟动态掩模预测头。输出token作为掩码,运行俩个块后,对图像编码和MLPt图输出到动态线性分类(dynamic linear classifier),用来计算掩码区域在每张图像的位置.

 每个解码器层执行 4 个步骤(从上图可以看出):

1)对 output tokens + prompt tokens 进行 self-attention;
2)将 1)得到的 tokens 作为 query token 与 image embedding 做 cross-attention;
3)使用 MLP 更新 2)得到的 tokens;
4)将 image embedding 作为 query token 与 3)得到的 tokens 做 cross-attention。

 最后一步使用提示信息更新图像嵌入。在交叉注意力期间,图像嵌入被视为一组 64×64×256 维向量。

推理时,sam模型是被封装在SamPredictor类中,将sam的forward预测的流程分别拆解到SamPredictor类的不同方法中、分不同阶段进行。
sam中forward函数对Image encoder、Prompt encoder和Mask decoder三个操作是连续的。

2.项目实现

docker 容器(Ubuntu20.04)+python3.8+pytorch1.14等等

2.1.SAM实现

2.1.0.环境设置

git clone https://github.com/facebookresearch/segment-anything
cd segment-anything; pip install -e .pip install  pycocotools matplotlib onnxruntime onnx# docker容器内的情况下务必安装  
pip install opencv-python-headless
pip install opencv-contrib-python pip install opencv-python==4.8.0.74# b的大小:358M
wget https://dl.fbaipublicfiles.com/segment_anything/sam_vit_b_01ec64.pth# l的大小:1.2G
wget https://dl.fbaipublicfiles.com/segment_anything/sam_vit_l_0b3195.pth# 默认为h,h的大小:2.4G
wget https://dl.fbaipublicfiles.com/segment_anything/sam_vit_h_4b8939.pth

2.1.1.分割方法实例

输入提示点分割

可以直接用官网给的,segment-anything/notebooks/predictor_example.ipynb文件。

可以用谷歌的,也也用自己的GPU。

import sys
sys.path.append("..")
from segment_anything import sam_model_registry, SamPredictor# 这里指定sam预训练模型的权重地址及类型
sam_checkpoint = "sam_vit_h_4b8939.pth"
model_type = "vit_h"# 指定cuda还是cpu
device = "cuda"sam = sam_model_registry[model_type](checkpoint=sam_checkpoint)
sam.to(device=device)#用SamPredictor进行预测。该模型返回掩码、这些掩码的质量预测和低分辨率的掩码对数,可传递给下一次迭代预测。
predictor = SamPredictor(sam)predictor.set_image(image)

输入点为提示,进行图像分割的情况

# 输入提示点的坐标[x,y]
input_point = np.array([[400,425]])
# 定义标签:[0]: 背景   [1]:前景
input_label = np.array([1])

 

masks, scores, logits = predictor.predict(point_coords=input_point,point_labels=input_label,multimask_output=True,
)

用`SamPredictor`进行预测。该模型返回掩码、这些掩码的质量预测和低分辨率的掩码对数,可传递给下一次迭代预测。

默认输出3个掩码可视化,使用multimask_output=True会生成3个mask,multimask_output=False的话只输出一个mask

全局分割

jupter lab的代码写到一起

# 导入相关库和优化可视化效果import numpy as np
import torch
import matplotlib.pyplot as plt
import cv2def show_anns(anns):if len(anns) == 0:returnsorted_anns = sorted(anns, key=(lambda x: x['area']), reverse=True)ax = plt.gca()ax.set_autoscale_on(False)polygons = []color = []for ann in sorted_anns:m = ann['segmentation']img = np.ones((m.shape[0], m.shape[1], 3))color_mask = np.random.random((1, 3)).tolist()[0]for i in range(3):img[:,:,i] = color_mask[i]ax.imshow(np.dstack((img, m*0.35)))image = cv2.imread('test.jpg')
# 还原原图像色彩
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
plt.figure(figsize=(10,10))
plt.imshow(image)
plt.axis('on')
plt.show()import sys
from segment_anything import sam_model_registry, SamAutomaticMaskGenerator, SamPredictor# 加载模型
sam_checkpoint = '/lvdongrui/segment_anything/sam_vit_h_4b8939.pth'
model_type = "vit_h"device = "cuda"sam = sam_model_registry[model_type](checkpoint=sam_checkpoint)
sam.to(device=device)# 调用全局分割模型
mask_generator = SamAutomaticMaskGenerator(sam)#预测并输出可视化结果
masks = mask_generator.generate(image)
print(len(masks)) # 产生的掩码数量
print(masks[0].keys()) # 第1个掩码内的相关属性plt.figure(figsize=(10,10))
plt.imshow(image)
show_anns(masks)
plt.axis('off')
plt.show() # 如果想要调节相关参数的话
# 配置相关参数
mask_generator_2 = SamAutomaticMaskGenerator(model=sam,points_per_side=32,pred_iou_thresh=0.86, # IOU阈值stability_score_thresh=0.92, # 稳定性得分阈值crop_n_layers=1, crop_n_points_downscale_factor=2,min_mask_region_area=100,  # Requires open-cv to run post-processing
)masks2 = mask_generator_2.generate(image)
print(len(masks2)) # 产生的掩码数量
print(masks2[0].keys())plt.figure(figsize=(10,10))
plt.imshow(image)
show_anns(masks2)
plt.axis('off')
plt.show() 

结果

2.1.2.基于SAM微调自己的数据集

 训练代码:train.py

# 导入了一些库
import warnings
warnings.filterwarnings(action='ignore')
import numpy as np
from tqdm import tqdm
import cv2
from datetime import datetime
import json
import os
import matplotlib.pyplot as plt
import argparseimport torch
from torch import nn, optim
import torch.nn.functional as F
from torch.nn.functional import threshold, normalize
from torchvision import datasets, transforms
from torch.utils.data import DataLoaderfrom segment_anything import SamAutomaticMaskGenerator, sam_model_registry
from segment_anything.utils.transforms import ResizeLongestSide
from dataloader_sam import CustomDataset# 设置了一些配置参数
beta = [0.9, 0.999]
milestone = [60000, 86666]
gamma = 0.1
use_cuda = torch.cuda.is_available()
device = torch.device("cuda" if use_cuda else "cpu")def parse_opt():parser = argparse.ArgumentParser()parser.add_argument('--batch_size', type=int, default=4, help = 'batch size')parser.add_argument('--warmup_steps', type = int, default = 250, help = ' ')parser.add_argument('--global_step', type = int, default = 0, help = ' ')parser.add_argument('--epochs', type = int, default = 10, help = 'train epcoh')parser.add_argument('--lr', type = float, default = 1e-5, help = 'learning_rate')parser.add_argument('--weight_decay', type = float, default = 0.1, help = 'weight_decay')parser.add_argument('--num_workers', type = int, default = 0, help = 'num_workers')parser.add_argument('--model_path', type = str, default = './models/', help = 'model path directory')parser.add_argument('--data_dir', type = str, default = './data_list_sam/pr_data_list.csv', help = 'data directory')parser.add_argument('--pretrained', type = str, default = False, help = 'pre trained model select')parser.add_argument('--device_id', type = int, default = 0, help = 'Cuda device Id')return parser.parse_known_args()[0]# 数据加载
def build_dataloader(data_dir, batch_size, num_workers):dataloaders = {key: DataLoader(CustomDataset(data_dir, key),batch_size = batch_size,shuffle = True if key != 'test' else False,num_workers = num_workers,pin_memory = True) for key in ['train', 'valid', 'test']}return dataloaders# 损失函数
def focal_loss(pred, target, gamma=2.0, alpha=0.25, reduction='mean'):#pred = F.sigmoid(pred)pt = torch.where(target == 1, pred, 1-pred)ce_loss = F.binary_cross_entropy(pred, target, reduction="none")focal_term = (1 - pt).pow(gamma)loss = alpha * focal_term * ce_lossreturn loss.mean()def dice_loss(pred, target, smooth=1.0):pred_flat = pred.reshape(-1)target_flat = target.reshape(-1)intersection = (pred_flat * target_flat).sum()return 1 - ((2. * intersection + smooth) /(pred_flat.sum() + target_flat.sum() + smooth))def compute_loss(pred_mask, true_mask, pred_iou, true_iou):pred_mask = F.sigmoid(pred_mask).squeeze(1).to(dtype = torch.float64)fl = focal_loss(pred_mask, true_mask)dl = dice_loss(pred_mask, true_mask)mask_loss = 20 * fl + dliou_loss = F.mse_loss(pred_iou, true_iou)total_loss = mask_loss + iou_lossreturn total_lossdef mean_iou(preds, labels, eps=1e-6):preds = normalize(threshold(preds, 0.0, 0)).squeeze(1)pred_cls = (preds == 1).float()label_cls = (labels == 1).float()intersection = (pred_cls * label_cls).sum(1).sum(1)union = (1 - (1 - pred_cls) * (1 - label_cls)).sum(1).sum(1)intersection = intersection + (union == 0)union = union + (union == 0)ious = intersection / unionreturn iousdef main(opt):use_cuda = torch.cuda.is_available()device = torch.device(f"cuda:{opt.device_id}" if use_cuda else "cpu")torch.manual_seed(234)if device == f'cuda:{opt.device_id}':torch.cuda.manual_seed_all(234)#  脚本使用预先构建的架构(sam_model_registry['vit_b'])定义了一个神经网络模型,并设置了优化器(AdamW)和学习率调度。print(device,'is available')print("Loading model...")epoch_add = 0lr = opt.lrsam = sam_model_registry['vit_b'](checkpoint='./checkpoint/sam_vit_b_01ec64.pth')if opt.pretrained:sam.load_state_dict(torch.load('./models/' + opt.pretrained))sam = sam.to(device=device)else: sam = sam.to(device=device)sam_decoder = sam.mask_decoder.to(device=device)sam_decoder = sam.mask_decoderoptimizer = optim.AdamW(sam.mask_decoder.parameters(),lr = lr, betas = beta, weight_decay = opt.weight_decay)criterion = nn.CrossEntropyLoss()scheduler = optim.lr_scheduler.MultiStepLR(optimizer, milestones=milestone, gamma=gamma)# 脚本在各个检查点保存训练模型的状态字典,如果模型在验证集上取得最佳平均IOU,则单独保存最佳模型。if len(os.listdir(opt.model_path)) == 0:save_path = os.path.join(opt.model_path, f"model_{opt.epochs}_{opt.batch_size}_0")os.makedirs(save_path)else:save_path = os.path.join(opt.model_path, f"model_{opt.epochs}_{opt.batch_size}_" + str(len(os.listdir(opt.model_path))))os.makedirs(save_path)print('Training Start')best_loss = 999999999best_mIOU = 0tr_pl_loss_list = []tr_pl_mi_list = []val_pl_loss_list = []val_pl_mi_list = []dataloaders = build_dataloader(opt.data_dir, opt.batch_size, opt.num_workers)for epoch in range(opt.epochs):train_loss_list = []train_miou_list = []sam.train()iterations = tqdm(dataloaders['train'])# 循环进行模型的多轮训练for train_data in iterations:# 将训练数据移到指定设备,这里是GPUtrain_input = train_data['image'].to(device)train_target_mask = train_data['mask'].to(device, dtype = torch.float64)# 对优化器的梯度进行归零optimizer.zero_grad()with torch.no_grad():# 使用 sam 模型的 image_encoder 提取图像特征,并使用 prompt_encoder 提取稀疏和密集的嵌入。在本代码中进行提示输入,所以都是None.train_encode_feature = sam.image_encoder(train_input)train_sparse_embeddings, train_dense_embeddings = sam.prompt_encoder(points = None, boxes = None, masks = None)#  通过 mask_decoder 解码器生成训练集的预测掩码和IOUtrain_mask, train_IOU = sam.mask_decoder(image_embeddings = train_encode_feature,image_pe = sam.prompt_encoder.get_dense_pe(),sparse_prompt_embeddings = train_sparse_embeddings,dense_prompt_embeddings = train_dense_embeddings,multimask_output = False)# 计算预测IOU和真实IOU之间的差异,并将其添加到列表中。然后计算训练损失(总损失包括mask损失和IOU损失),进行反向传播和优化器更新。train_true_iou = mean_iou(train_mask, train_target_mask, eps=1e-6)train_miou_list = train_miou_list + train_true_iou.tolist()train_loss_one = compute_loss(train_mask, train_target_mask, train_IOU, train_true_iou)train_loss_one.backward()optimizer.step()train_loss_list.append(train_loss_one.item())     # 学习率调整if epoch_add == 0:if opt.global_step < opt.warmup_steps:    lr_scale = opt.global_step / opt.warmup_stepsfor param_group in optimizer.param_groups:param_group['lr'] = 8e-4 * lr_scaleopt.global_step += 1pbar_desc = "Model train loss --- "pbar_desc += f"Total loss: {np.mean(train_loss_list):.5f}"pbar_desc += f", total mIOU: {np.mean(train_miou_list):.5f}"iterations.set_description(pbar_desc)train_loss = np.mean(train_loss_list)train_miou = np.mean(train_miou_list)torch.cuda.empty_cache()tr_pl_loss_list.append(train_loss)tr_pl_mi_list.append(train_miou)sam.eval()scheduler.step()with torch.no_grad():valid_loss_list = []valid_miou_list = []valid_true_iou = 0valid_loss = 0valid_miou = 0iterations = tqdm(dataloaders['valid'])for valid_data in iterations:   valid_input = valid_data['image'].to(device)valid_target_mask = valid_data['mask'].to(device, dtype = torch.float64)valid_encode_feature = sam.image_encoder(valid_input)valid_sparse_embeddings, valid_dense_embeddings = sam.prompt_encoder(points = None, boxes = None, masks = None)valid_mask, valid_IOU = sam.mask_decoder(image_embeddings = valid_encode_feature,image_pe = sam.prompt_encoder.get_dense_pe(),sparse_prompt_embeddings = valid_sparse_embeddings,dense_prompt_embeddings = valid_dense_embeddings,multimask_output = False)valid_true_iou = mean_iou(valid_mask, valid_target_mask, eps=1e-6)valid_miou_list = valid_miou_list + valid_true_iou.tolist()valid_loss_one = compute_loss(valid_mask, valid_target_mask, valid_IOU, valid_true_iou)valid_loss_list.append(valid_loss_one.item())pbar_desc = "Model valid loss --- "pbar_desc += f"Total loss: {np.mean(valid_loss_list):.5f}"pbar_desc += f", total mIOU: {np.mean(valid_miou_list):.5f}"iterations.set_description(pbar_desc)valid_loss = np.mean(valid_loss_list)valid_miou = np.mean(valid_miou_list)val_pl_loss_list.append(valid_loss)val_pl_mi_list.append(valid_miou)       model_path = opt.model_path + 'sam.pth'sam = sam.cpu()torch.save(sam.state_dict(), model_path)sam = sam.to(device)if best_mIOU < valid_miou:best_loss = valid_lossbest_mIOU = valid_mioumodel_path = save_path + '/sam_best.pth'sam = sam.cpu()torch.save(sam.state_dict(), model_path)sam = sam.to(device)f = open(os.path.join(save_path, 'best.txt'), 'w')f.write(f"Experimental Day: {datetime.now()}")f.write("\n")f.write(f"mIoU: {str(best_mIOU)}")f.write("\n")f.write(f"epochs:{opt.epochs}")f.write("\n")f.write(f"batch_size:{opt.batch_size}")f.write("\n")f.write(f"learning_rate:{opt.lr}")f.write("\n")f.write(f"Data_set : {opt.data_dir}")f.close()print("epoch : {:3d}, train loss : {:3.4f}, valid loss : {:3.4f}, valid mIOU : {:3.4f}\( best vaild loss : {:3.4f}, best valid mIOU : {:3.4f} )".format(epoch + 1 + epoch_add,train_loss,valid_loss,valid_miou,best_loss,best_mIOU))lr = optimizer.param_groups[0]["lr"]if (epoch + 1) % 5 == 0 or (epoch + 1) in [1, 2, 3, 4, 5]:model_path = save_path + "/"+ "sam_" + str(epoch + 1 + epoch_add) + '_' + str(round(lr, 10)) + '.pth'sam = sam.cpu()torch.save(sam.state_dict(), model_path)sam = sam.to(device)# (2, 2) 形式的图使用matplotlib可视化训练进展,生成用于训练和验证平均IOU、训练和验证损失的图表。plt_dict = {"Train_mIoU" : tr_pl_mi_list,"Val_mIoU" : val_pl_mi_list,"Train_Loss" : tr_pl_loss_list,"Val_Loss" : val_pl_loss_list}plt.figure(figsize=(15,15))for i, (key, item) in enumerate(plt_dict.items()):plt.subplot(2, 2, i+1)plt.plot(range(opt.epochs), item, label=f"{key}")plt.title(f"{key}", fontsize = 20)plt.xlabel('Epochs', fontsize = 15)plt.ylabel(f'{key.split("_")[-1]}', fontsize = 15)plt.grid(True)plt.savefig(save_path + f'/sam_{opt.epochs}_{opt.batch_size}_{opt.lr}_result.png')if __name__ == '__main__':opt = parse_opt()main(opt)

2.2.YOLO+SAM

2.2.0.环境设置及Demo

pip install ultralytics
#pip install git+https://github.com/facebookresearch/segment-anything.git
#wget https://dl.fbaipublicfiles.com/segment_anything/sam_vit_l_0b3195.pthgit clone https://github.com/poojatambe/Segmentation_models_on_custom_data
cd Segmentation_models_on_custom_data# 下载yolov8的检测权重
wget https://github.com/ultralytics/assets/releases/download/v8.1.0/yolov8x-oiv7.pt# 根据自己路径修改
python SAM_yolov8.py --img './test.png' --weights './yolov8-oiv7.pt' --model_type 'vit_l' --checkpoints './sam_vit_l_0b3195.pth'

如果出现错误,请参考【PS3】

3.2.1.微调自己的数据集

敬请期待。。。

3.相关知识点进阶

输入提示点实例

#输入1个点

input_point = np.array([[495,518]])
input_label = np.ones(input_point.shape[0])
input_box = None

#输入三个提示点

input_point = np.array([[221,482],[498,633],[750,379]])
input_label = np.ones(input_point.shape[0])
input_box = None

输入提示框box实例

#输入一个box

input_box = np.array([[4,13,1007,1023]])
input_point, input_label = None, None

#输入多个box

input_box = torch.tensor([[45,260,515,470], [310,228,424,296]],device=predictor.device)
transformed_box = predictor.transform.apply_boxes_torch(input_box, image.shape[:2])
input_point, input_label = None, None

 使用box的计算

IoU(Intersection over Union): 交并比,衡量bounding box与ground truth的重叠程度。比值越大重叠度越高,完全重合为1。如果还没有bbox回归,则bbox就是候选框/锚框。

IoU= \frac{boundingbox\cap groundtruth}{boundingbox\cup groundtruth}

通常表示边界框的位置有两种方式:

  • (x1​,y1​,x2​,y2​):其中(x1​,y1​)是矩形框左上角的坐标,(x2​,y2​)是矩形框右下角的坐标。
  • (x,y,w,h):其中(x,y)是矩形框中心点的坐标,w是矩形框的宽度,h是矩形框的高度。

在SAM中使用xywh 格式的掩模边界框

 【PS1】IndexError: boolean index did not match indexed array along dimension 0; dimension is 2 but corresponding boolean dimension is 1

错误原因:数组输入错误,少了一对中括号

将自定义的input_point = np.array([200,400])
改为input_point = np.array([[200,400]])

【PS2】RuntimeError:CUDNN_BACKEND_TENSOR_DESCRIPTOR: Check and Set the CUDNN_ATTR_TENSOR_STRIDES Correctly cudnn_status: CUDNN_STATUS_BAD_PARAM

【PS3】RuntimeError: Expected all tensors to be on the same device, but found at least two devices, cuda:0 and cpu! (when checking argument for argument mat2 in method wrapper_mm)

这是由于代码所写的某一位置的Tensor设备指代不清楚,tensors一会在cpu计算,一会在GPU计算。容易导致该错误产生的原因:

当多GPU并行训练时(我的是俩块):

这时候你的model里面传入的不只是Cuda:0,还有Cuda:1, Cuda:2等等,这个时候,你的网络模型model里面的forward函数里面就及其容易报错,因为里面存在的一些定维度的Tensor,比如权重、偏差的device容易跑偏。

当单GPU正常使用Cuda:0:

这时候按理来说,只要device代码指代正常,按理来说不会出现设备问题,如果出现了,就是tensor的格式没有传入cuda而是保留在cpu。碰巧我某天训练时候就出现了,思路跟上面一样,最容易出现错误的地方是网络模型中固定维度的tensor如权重和偏差等tensor,或者是forward函数里面的输出.

解决

指定GPU

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

 更多可参考RuntimeError: Expected all tensors to be on the same device, but found at least two devices-CSDN博客

Q&A

【Q&A1】怎么计算mask的shape?

在图像分割中,模型在训练过程中计算mask的形状(shape)取决于任务的要求。分割模型通常输出一个与输入图像相同空间分辨率的mask,该mask用于标识图像中的不同区域或对象。这个mask的形状通常与输入图像的宽度和高度相匹配。

参考文献

【1】SAM : Segment Anything - Best Simple Tutorial to master it (inside-machinelearning.com)

【2】SAM + YOLO8图像检测与分割简明教程 - 知乎 (zhihu.com)

【3】Segment Anything Model (SAM): Explained | by Utkarsh Doshi | Dec, 2023 | Medium 

【4】SAM: A Image Segmentation Foundation Model – Towards AI 

【5】SAM(segment anything model)分割一切 Demo测试及API调用_sam模型运行实例-CSDN博客 

【6】Segment Anything(SAM)的demo的简单使用_segment anything demo-CSDN博客 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

随机过程及应用学习笔记(四) 马尔可夫过程

马尔可夫过程是理论上和实际应用中都十分重要的一类随机过程。 目录 前言 一、马尔可夫过程的概念 二、离散参数马氏链 1 定义 2 齐次马尔可夫链 3 齐次马尔可夫链的性质 三、齐次马尔可夫链状态的分类 四、有限马尔可夫链 五、状态的周期性 六、极限定理 七、生灭过…

接口测试方法论

第1章 测试那点事 单元测试》接口测试》界面测试 接口就是包含特定输入和特定输出的一套逻辑处理单元&#xff0c;用户无须知晓接口的内部实现逻辑&#xff0c;这也可以称为接口的黑河处理逻辑。因为服务对象不同&#xff0c;接口又可分为两种&#xff1a;一种是系统或服务的…

K8S集群实践之十:虚拟机部署阶段性总结

目录 1. 说明&#xff1a; 2. 安装准备 2.1 每个节点设置双网卡&#xff0c;一卡做网桥&#xff08;外部访问&#xff09;&#xff0c;一卡做NAT&#xff08;集群内网访问&#xff09; 2.2 准备一个可用的代理服务器 3. 由于虚拟机崩溃&#xff08;停电&#xff0c;宿主机…

VBA技术资料MF119:数据验证的添加与删除

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。“VBA语言専攻”提供的教程一共九套&#xff0c;分为初级、中级、高级三大部分&#xff0c;教程是对VBA的系统讲解&#…

《苍穹外卖》知识梳理p7-用户下单与模拟微信支付

用户下单与微信支付 一.用户下单 1.1 订单表的设计 订单功能是一个核心的功能&#xff1b; 需要使用2张表&#xff1a; 1.订单表&#xff1a;记录一些订单信息&#xff0c;比如收货人&#xff0c;收获地址&#xff0c;支付金额&#xff0c;下单时间等&#xff1b; 2.订单详情…

GitLab配置SSHKey

段落一&#xff1a;什么是SSH密钥 SSH&#xff08;Secure Shell&#xff09;是一种网络协议&#xff0c;用于安全地远程登录和执行命令。SSH密钥是一种用于身份验证的加密文件&#xff0c;它允许您在与远程服务器通信时&#xff0c;无需输入密码即可进行认证。在GitLab中配置S…

Kibana:如何嵌入 Kibana 仪表板

作者&#xff1a;Carly Richmond 像我这样的前端工程师经常提出的要求是将 Kibana 等来源的现有仪表板嵌入到 JavaScript Web 应用程序中。 这是我必须多次执行的任务&#xff0c;因为我们希望快速部署用户生成的视图或允许用户控制给定的视图。 从我们从精彩的开发者社区收到的…

JVM(3)高级篇

1 GraalVM 1.1 什么是GraalVM GraalVM是Oracle官方推出的一款高性能JDK&#xff0c;使用它享受比OpenJDK或者OracleJDK更好的性能。 GraalVM的官方网址&#xff1a;https://www.graalvm.org/ 官方标语&#xff1a;Build faster, smaller, leaner applications。 更低的CPU、内…

Sora技术报告——Video generation models as world simulators

文章目录 1. 视频生成模型&#xff0c;可以视为一个世界模拟器2. 技术内容2.1 将可视数据转换成patches2.2 视频压缩网络2.3 Spacetime Latent Patches2.4 Scaling transformers 用于视频生成2.5 可变的持续时间&#xff0c;分辨率&#xff0c;宽高比2.6 抽样的灵活性2.7 改进框…

Invalid DataSize: cannot convert ‘30Mb‘ to Long

Invalid DataSize: cannot convert 30Mb to Long servlet:multipart:max-file-size: 30MBmax-request-size: 30MB

【研究生复试】计算机软件工程人工智能研究生复试——资料整理(速记版)——计算机网络

1、JAVA 2、计算机网络 3、计算机体系结构 4、数据库 5、计算机租场原理 6、软件工程 7、大数据 8、英文 自我介绍 2. 计算机网络 1. TCP如何解决丢包和乱序&#xff1f; 序列号&#xff1a;TCP所传送的每段数据都有标有序列号&#xff0c;避免乱序问题发送端确认应答、超时…

反向迭代器------封装的力量

目录 一、list封装中模板参数Ref和Ptr的理解 二、反向迭代器的实现 一、list封装中模板参数Ref和Ptr的理解 对于反向迭代器&#xff0c;是我们在前面STL模拟实现中留下的一个问题。在之前的文章中&#xff0c;我们极大程度上的利用了模板&#xff0c;从而减少了许多的代码&…

09、全文检索 -- Solr -- SpringBoot 整合 Spring Data Solr (生成DAO组件 和 实现自定义查询方法)

目录 SpringBoot 整合 Spring Data SolrSpring Data Solr的功能&#xff08;生成DAO组件&#xff09;&#xff1a;Spring Data Solr大致包括如下几方面功能&#xff1a;Query查询&#xff08;属于半自动&#xff09;代码演示&#xff1a;1、演示通过dao组件来保存文档1、实体类…

SpringCloud之Feign发送Http请求

文章目录 http客户端Feign使用步骤自定义Feign的配置Feign的性能优化Feign的性能优化-连接池配置 Feign的最佳实践 http客户端Feign Feign的介绍&#xff1a; Feign是一个声明式的http客户端&#xff0c;官方地址&#xff1a;https:/github.com/OpenFeign/feign 其作用就是帮助…

laravel_进程门面_简单介绍

文章目录 Facade是什么&#xff1f;Facade能干什么Facade有哪些方法&#xff1f;怎么使用Facade呢&#xff1f;详细的代码解释Symfony Process是什么&#xff1f;介绍Symfony总结 Facade是什么&#xff1f; 在 Laravel 框架中&#xff0c;Facade 是一种设计模式。 它提供了一…

476. Number Complement(数字的补数)

问题描述 对整数的二进制表示取反&#xff08;0 变 1 &#xff0c;1 变 0&#xff09;后&#xff0c;再转换为十进制表示&#xff0c;可以得到这个整数的补数。 例如&#xff0c;整数 5 的二进制表示是 “101” &#xff0c;取反后得到 “010” &#xff0c;再转回十进制表示…

java8默认方法

口什么是默认方法 口如何以一种兼容的方式改进API 口默认方法的使用模式 口 解析规则 传统上&#xff0c;Java程序的接口是将相关方法按照约定组合到一起的方式。实现接口的类必须为接口中定义的每个方法提供一个实现&#xff0c;或者从父类中继承它的实现。但是&#xff0c;一…

NLP_Transformer架构

文章目录 Transformer架构剖析编码器-解码器架构各种注意力的应用Transformer中的自注意力Transformer中的多头自注意力Transformer中的编码器-解码器注意力Transformer中的注意力掩码和因果注意力 编码器的输入和位置编码编码器的内部结构编码器的输出和编码器-解码器的连接解…

计网物理层

通信基础 基本概念 物理层解决如何在连接各种计算机的传输媒体上传输数据比特流&#xff0c;而不是指具体的传输媒体。 其主要任务是确定与传输媒体接口有关的一些特性&#xff0c;即定义标准。 机械特性&#xff1a;定义物理连接的特性&#xff0c;规定物理连接时所采用的…