从DETR到Mask2Former(1):DETR-segmentation结构全解析

网上关于DETR做的detection的解析很多,但是DETR做Segmentation的几乎没有,本文结合DETR的论文与代码,对DETR做一个详细的拆解。理解DETR是理解Mask2Former的基础。

首先得把DETR-segmentation给run起来。Github上DETR的repository,下载了也只能run起来detection,run不起来segmentation功能,但还是下载下来,后面留着有用。我们用torch的hub里集成的DETR segmentation模型,运行下面的代码

import torch
models_list = torch.hub.list('facebookresearch/detr', force_reload=True)
print(models_list)

你可以看到torch.hub中所有关于detr的模型。我们选择  detr_resnet50_panoptic

model = torch.hub.load('facebookresearch/detr', 'detr_resnet50_panoptic', pretrained=True)

再新建一个py文件,把以下代码放进去:

import math
from PIL import Image
import requests
import matplotlib.pyplot as pltimport ipywidgets as widgets
from IPython.display import display, clear_outputimport torch
from torch import nn
import torch.nn.functional as F
from torchvision.models import resnet50
import torchvision.transforms as T
torch.set_grad_enabled(False)import matplotlib.pyplot as plt# COCO classes
CLASSES = ['N/A', 'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus','train', 'truck', 'boat', 'traffic light', 'fire hydrant', 'N/A','stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse','sheep', 'cow', 'elephant', 'bear', 'zebra', 'giraffe', 'N/A', 'backpack','umbrella', 'N/A', 'N/A', 'handbag', 'tie', 'suitcase', 'frisbee', 'skis','snowboard', 'sports ball', 'kite', 'baseball bat', 'baseball glove','skateboard', 'surfboard', 'tennis racket', 'bottle', 'N/A', 'wine glass','cup', 'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple', 'sandwich','orange', 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake','chair', 'couch', 'potted plant', 'bed', 'N/A', 'dining table', 'N/A','N/A', 'toilet', 'N/A', 'tv', 'laptop', 'mouse', 'remote', 'keyboard','cell phone', 'microwave', 'oven', 'toaster', 'sink', 'refrigerator', 'N/A','book', 'clock', 'vase', 'scissors', 'teddy bear', 'hair drier','toothbrush'
]# colors for visualization
COLORS = [[0.000, 0.447, 0.741], [0.850, 0.325, 0.098], [0.929, 0.694, 0.125],[0.494, 0.184, 0.556], [0.466, 0.674, 0.188], [0.301, 0.745, 0.933]]transform = T.Compose([T.Resize(800),T.ToTensor(),T.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])# for output bounding box post-processing
def box_cxcywh_to_xyxy(x):x_c, y_c, w, h = x.unbind(1)b = [(x_c - 0.5 * w), (y_c - 0.5 * h),(x_c + 0.5 * w), (y_c + 0.5 * h)]return torch.stack(b, dim=1)def rescale_bboxes(out_bbox, size):img_w, img_h = sizeb = box_cxcywh_to_xyxy(out_bbox)b = b * torch.tensor([img_w, img_h, img_w, img_h], dtype=torch.float32)return bdef plot_results(pil_img, prob, boxes):plt.figure(figsize=(16,10))plt.imshow(pil_img)ax = plt.gca()colors = COLORS * 100for p, (xmin, ymin, xmax, ymax), c in zip(prob, boxes.tolist(), colors):ax.add_patch(plt.Rectangle((xmin, ymin), xmax - xmin, ymax - ymin,fill=False, color=c, linewidth=3))# cl = p.argmax()# text = f'{CLASSES[cl]}: {p[cl]:0.2f}'# ax.text(xmin, ymin, text, fontsize=15,#         bbox=dict(facecolor='yellow', alpha=0.5))plt.axis('off')plt.show()model = torch.hub.load('facebookresearch/detr', 'detr_resnet50_panoptic', pretrained=True)
model.eval()url = 'http://images.cocodataset.org/val2017/000000039769.jpg'
im = Image.open(requests.get(url, stream=True).raw)# mean-std normalize the input image (batch-size: 1)
img = transform(im).unsqueeze(0)# propagate through the model
outputs = model(img)# show result of detection
# keep only predictions with 0.7+ confidence
probas = outputs['pred_logits'].softmax(-1)[0, :, :-1]
keep = probas.max(-1).values > 0.9
# convert boxes from [0; 1] to image scales
bboxes_scaled = rescale_bboxes(outputs['pred_boxes'][0, keep], im.size)
plot_results(im, probas[keep], bboxes_scaled)# show segmentation
# compute the scores, excluding the "no-object" class (the last one)
scores = outputs['pred_logits']
scores = scores.softmax(dim=-1)
scores = scores[..., :-1]
scores_onehot = scores.max(-1)
scores_onehot = scores_onehot[0]
# threshold the confidence
keep = scores_onehot > 0.85scores_selected = scores[keep]
labels = torch.argmax(scores_selected, dim=-1)masks = outputs['pred_masks'][keep].detach().cpu().numpy()
mask_i = masks[0, :, :]# plt.imshow(mask_i, cmap="viridis")
# plt.show()# ## Plot all the remaining masks
ncols = 5
fig, axs = plt.subplots(ncols=ncols, nrows=math.ceil(keep.sum().item() / ncols), figsize=(18, 10))
for line in axs:for a in line:a.axis('off')
for i, mask in enumerate(outputs['pred_masks'][keep].detach().cpu().numpy()):ax = axs[i // ncols, i % ncols]ax.imshow(mask, cmap="cividis")ax.text(0, 0, labels[i].cpu().numpy().item(0))ax.axis('off')
fig.tight_layout()plt.show()mask_pred = outputs['pred_masks'].sigmoid()
mask_pred = F.interpolate(mask_pred, size=(480, 640), mode='bilinear', align_corners=False)semseg = torch.einsum("bqc,bqhw->bchw", scores, mask_pred)
result = torch.argmax(semseg, dim=1)plt.figure(figsize=(12, 8))
# 第一个子图
plt.subplot(1, 1, 1)
# image1_rgb = cv2.cvtColor(img_color, cv2.COLOR_BGR2RGB)
plt.imshow(result[0, :, :].to("cpu").numpy())
plt.title('Image 1')
plt.axis('off')plt.show()

解释一下上面的代码,

plot_results(im, probas[keep], bboxes_scaled)

是把detection的结果可视化出来,本文略过的detection部分的内容。  plot_results()  下面的代码就是segmentation内容结果的可视化。模型的输出包括三个内容

pred_logits(1, 100, 251)中三个数字的含义是:1是batchsize,100是query的个数,251是分类数,去掉最后一个no-object不要,实际是250个类。可以这样理解,每一个query都会进入网络,但不是每一个query都能从图片找到东西,而找到东西的query,找到的instance所属于的类就是这个query所在的那一行中,最大的数所在列的index。如果一个图片里有两只猫,那结果就是有两个query,分别各自找到一只猫(也就是一个instance),这样也就实现了全景分割的功能。所以说DETR的结构是Mask2Former实现全景分割功能的基础。

pred_boxes是query所找到的instance对应的box。

pred_masks是query所找到的instance所在的像素。

这段代码:

# ## Plot all the remaining masks
ncols = 5
fig, axs = plt.subplots(ncols=ncols, nrows=math.ceil(keep.sum().item() / ncols), figsize=(18, 10))
for line in axs:for a in line:a.axis('off')
for i, mask in enumerate(outputs['pred_masks'][keep].detach().cpu().numpy()):ax = axs[i // ncols, i % ncols]ax.imshow(mask, cmap="cividis")ax.text(0, 0, labels[i].cpu().numpy().item(0))ax.axis('off')
fig.tight_layout()plt.show()

会画出这样的图像:

有94个query没有找到instance,有6个query找到了instance,2个query找到了猫,也就是250列的17列,两个query找到了遥控器(250列的74列)。

好了,现在DETR-seg的输出我们弄清楚了,接下来进到DETR内部去看看,这个模型封装在了torch.hub中,进入内部的正确方法,说实话笔者也不知道,这里笔者耍了一个不正经的小trick:

import torchmodel = torch.hub.load('facebookresearch/detr', 'detr_resnet50_panoptic', pretrained=True)from models.segmentation import DETRsegmmodel = DETRsegm(model)

上面的代码中,models.segmentation文件是DETR的github repository下载下来,里面有的内容,运行上面的代码,会报错

但是通过这个报错,我们可以找到torch.hub中DETR的源代码。这样就可以在源代码里打断点,看DETR 内部了。进入源代码,我们能看到这样的内容,注释的代码是笔者加进去的,不是torch自带的。

self.detr.backbone是resnet18。 这里,我们打开DETR的github代码——detr/models/backbone.py 找到  class BackboneBase 这个类。

这里的 def forward 中的out,就是 DETRsegm 中的 features

features, pos = self.detr.backbone(samples)

pos是正弦函数的 position embeding,就是输入transformer的encoder的位置编码。

在获取features时,DETR detection和Segmentation的区别在与,detection只拿resnet第四层的输出,而segmentation将每层的输出都拿出来。通过这段代码实现(缩进乱了,忽略)。

if return_interm_layers:return_layers = {"layer1": "0", "layer2": "1", "layer3": "2", "layer4": "3"}else:return_layers = {'layer4': "0"}self.body = IntermediateLayerGetter(backbone, return_layers=return_layers)

这里扯远一点,复习一下resnet18  resnet18在torch中也有集成,通过下面的代码得到,

import torch
import torchvision.models as models
from torchvision.models._utils import IntermediateLayerGetter
import cv2
import numpy as np# 加载一个预训练的 ResNet 模型
model = models.resnet18(pretrained=True)
model.to("cuda")# 定义要获取的中间层
layers = {'layer1': 'layer1', 'layer2': 'layer2', 'layer3': 'layer3', 'layer4': 'layer4'}# 创建 IntermediateLayerGetter
intermediate_layers = IntermediateLayerGetter(model, layers)image0 = cv2.imread("/home/robotics/dino/img/kitaku/002.jpg")
height0, width0, channels = image0.shape
image = cv2.resize(image0, (640, 480), interpolation=cv2.INTER_AREA)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)mean = np.array([0.485, 0.456, 0.406]) * 255
std = np.array([0.229, 0.224, 0.225]) * 255
image = image.astype(float)
for i in range(3):image[:, :, i] = (image[:, :, i] - mean[i]) / std[i]input_data = torch.from_numpy(image).permute(2, 0, 1).unsqueeze(dim=0).to(torch.float32).to("cuda")# 获取中间层的输出
outputs = intermediate_layers(input_data)# 打印输出
for key, value in outputs.items():print(f"{key}: {value.shape}")from torchview import draw_graph
model_graph = draw_graph(model, input_size=(1, 3, 480, 640))
model_graph.resize_graph(scale=5.0)
model_graph.visual_graph.render(format='svg')

通过draw_graph得到一个网络结构可视化图,格式是svg,如下图所示

也可以自己写代码,构建一个resnet18

import torch
import torch.nn as nn
from torch.nn import functional as Fclass RestNetBasicBlock(nn.Module):def __init__(self, in_channels, out_channels, stride):super(RestNetBasicBlock, self).__init__()self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1)self.bn1 = nn.BatchNorm2d(out_channels)self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=stride, padding=1)self.bn2 = nn.BatchNorm2d(out_channels)def forward(self, x):output = self.conv1(x)output = F.relu(self.bn1(output))output = self.conv2(output)output = self.bn2(output)return F.relu(x + output)class RestNetDownBlock(nn.Module):def __init__(self, in_channels, out_channels, stride):super(RestNetDownBlock, self).__init__()self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride[0], padding=1)self.bn1 = nn.BatchNorm2d(out_channels)self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=stride[1], padding=1)self.bn2 = nn.BatchNorm2d(out_channels)def forward(self, x):output = self.conv1(x)out = F.relu(self.bn1(output))out = self.conv2(out)out = self.bn2(out)return F.relu(x + out)class RestNet18(nn.Module):def __init__(self):super(RestNet18, self).__init__()self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3)self.bn1 = nn.BatchNorm2d(64)self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)self.layer1 = nn.Sequential(RestNetBasicBlock(64, 64, 1),RestNetBasicBlock(64, 64, 1))self.layer2 = nn.Sequential(RestNetDownBlock(64, 128, [2, 1]),RestNetBasicBlock(128, 128, 1))self.layer3 = nn.Sequential(RestNetDownBlock(128, 256, [2, 1]),RestNetBasicBlock(256, 256, 1))self.layer4 = nn.Sequential(RestNetDownBlock(256, 512, [2, 1]),RestNetBasicBlock(512, 512, 1))self.avgpool = nn.AdaptiveAvgPool2d(output_size=(1, 1))self.fc = nn.Linear(512, 10)def forward(self, x):out = self.conv1(x)out = self.layer1(out)out = self.layer2(out)out = self.layer3(out)out = self.layer4(out)out = self.avgpool(out)out = out.reshape(x.shape[0], -1)out = self.fc(out)return outif __name__ == "__main__":resnet18 = RestNet18()from torchview import draw_graphmodel_graph = draw_graph(resnet18, input_size=(1, 3, 480, 640))model_graph.resize_graph(scale=5.0)model_graph.visual_graph.render(format='svg')

好,回到DETR,还是看torch.hub中的这段detr代码:

 上面的代码中:

hs, memory = self.detr.transformer(src_proj, mask, self.detr.query_embed.weight, pos[-1])

self.detr.transformer就是下图经典的transformer结构,memory是encoder的输出  hs是decoder的输出。mask对于理解detr不重要,是masked attention中用到了。

pos和query_embed.weight分别对应上图的Positional Encoding和decoder中,output上面的OutputEmbeding。

DETR中transformer的理解,可以参看下面的代码,这段代码是笔者自己写的,对于DETR本身是没有用处的,只是为了方便理解,有错误的地方(feature那里只拿出resnet18最后一层的输出,是错的,不影响大局,懒得改了),自己理解时候做一个参考就好。

import torch
from torch import nn
from torchvision.models import resnet50
from models.transformer import TransformerEncoderLayer, TransformerEncoder, TransformerDecoderLayer, TransformerDecoder
from models.segmentation import MHAttentionMap, MaskHeadSmallConv
import timeclass DETR(nn.Module):def __init__(self, num_classes, d_model, nheads, dim_feedforward=2048,num_encoder_layers=6, num_decoder_layers=6, dropout=0.9, activation='relu', normalize_before=True):super().__init__()# We take only convolutional layers from ResNet-50 modelself.backbone = nn.Sequential(*list(resnet50(pretrained=True).children())[:-2])self.conv = nn.Conv2d(dim_feedforward, d_model, 1)encoder_layer = TransformerEncoderLayer(d_model, nheads, dim_feedforward,dropout, activation, normalize_before)encoder_norm = nn.LayerNorm(d_model) if normalize_before else Noneself.encoder = TransformerEncoder(encoder_layer, num_encoder_layers, encoder_norm)decoder_layer = TransformerDecoderLayer(d_model, nheads, dim_feedforward,dropout, activation, normalize_before)decoder_norm = nn.LayerNorm(d_model)self.decoder = TransformerDecoder(decoder_layer, num_decoder_layers, decoder_norm,return_intermediate=True)self.linear_class = nn.Linear(d_model, num_classes + 1)self.linear_bbox = nn.Linear(d_model, 4)num_queries = 100self.query_embed = nn.Embedding(num_queries, d_model)#position embeddingself.row_embed = nn.Parameter(torch.rand(50, d_model // 2))self.col_embed = nn.Parameter(torch.rand(50, d_model // 2))self.bbox_attention = MHAttentionMap(d_model, d_model, nheads, dropout=0.0)self.mask_head = MaskHeadSmallConv(d_model + nheads, [1024, 512, 256], d_model)def forward(self, inputs):#inputs是[1,3,800,1200]features = self.backbone(inputs)#x是[1,2048,25,38]hh = self.conv(features)#hh是[1,256,25,38]H, W = hh.shape[-2:]pos = torch.cat([self.col_embed[:W].unsqueeze(0).repeat(H, 1, 1),self.row_embed[:H].unsqueeze(1).repeat(1, W, 1),], dim=-1).flatten(0, 1).unsqueeze(1)bs, c, h, w = hh.shapequery_embed = self.query_embed.weight.unsqueeze(1).repeat(1, bs, 1)tgt = torch.zeros_like(query_embed)src = hh.flatten(2).permute(2, 0, 1)memory = self.encoder(src=src, pos=pos)hs = self.decoder(tgt, memory, pos=pos, query_pos=query_embed)hs = hs.transpose(1, 2)memory = memory.permute(1, 2, 0).view(bs, c, h, w)bbox_mask = self.bbox_attention(hs[-1], memory)seg_masks = self.mask_head(hh, bbox_mask, [features[0], features[0], features[0]])outputs_seg_masks = seg_masks.view(bs, self.detr.num_queries, seg_masks.shape[-2], seg_masks.shape[-1])#self.query_pos是[100,256]#src是encoder输入,tgt是decoder输入#h是[100,1,256]return outputs_seg_masks#coco是91个类, hidden dimension是256, 多头注意力是8, encoder,decoder layer都是6
device = torch.device("cuda")
detr = DETR(num_classes=91, d_model=256, nheads=8, num_encoder_layers=6, num_decoder_layers=6)
detr.eval().cuda()inputs = torch.randn(1, 3, 800, 1200).cuda()outputs_seg_masks = detr(inputs)
# print(logits, bboxes)
#logits是[100,1,92]
#bboxes是[100,1,4]

还是回到torch.hub那个detr代码,下面结合DETR论文中的那张图来理解一下这段代码

图中multi head attention部分,对应代码中

bbox_mask = self.bbox_attention(hs[-1], memory, mask=mask)

hs[-1]是左侧四个不同颜色的小框,memory是Encoded image,mask可以忽略,bbox_mask的尺寸是:(batch_size,query的个数,注意力头的个数,attention map的高和宽)

        temp = bbox_mask[0][20].squeeze().cpu().numpy()import matplotlib.pyplot as pltfor i in range(8):plt.imshow(temp[i, :, :], cmap="viridis")plt.show()

上面那段代码就是拿出第1个batchsize的第21个query的所有注意力头的attention map,也就是可以画出这几张图:

 

seg_masks = self.mask_head(src_proj, bbox_mask, [features[2].tensors, features[1].tensors, features[0].tensors])

mask_head对应图中这个结构:

输入是多头注意力map,以及resnet18这个backbone的4层的特征。

本文先到这,下一篇写DETR的损失函数。

未完待续

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

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

相关文章

【python入门】day26:统计字符串中出现指定字符的次数

案例 实际上if name“main”:就相当于是 Python 模拟的程序入口 。由于模块之间相互引用,不同模块可能都有这样的定义,而入口程序只能有一个,选中哪个入口程序取决于 ** ** name** **的值。 代码 #-*- coding:utf-8 -*- #开发时间&#xff…

SQL-分页查询and语句执行顺序

🎉欢迎您来到我的MySQL基础复习专栏 ☆* o(≧▽≦)o *☆哈喽~我是小小恶斯法克🍹 ✨博客主页:小小恶斯法克的博客 🎈该系列文章专栏:重拾MySQL 🍹文章作者技术和水平很有限,如果文中出现错误&am…

Pytest插件pytest-cov:优雅管理测试覆盖率

在软件开发中,测试覆盖率是评估测试质量的关键指标之一。为了更方便地统计和管理测试覆盖率,Pytest插件"pytest-cov"应运而生。本文将介绍"pytest-cov"的基本用法和优雅管理测试覆盖率的方法。 什么是pytest-cov? pytest-cov 是Pyt…

Docker数据卷与拦截与目录拦截

目录 高级容器挂载技术深度解析引言数据卷挂载原理解析应用场景使用介绍 目录挂载原理解析应用场景使用介绍 总结 高级容器挂载技术深度解析 引言 容器技术的快速发展使得容器挂载技术变得愈发重要。在容器化应用中,数据卷挂载和目录挂载是两种常见的挂载方式&…

【Python机器学习】SVM——调参

下面是支持向量机一个二维二分类数据集的训练结果: import mglearn import matplotlib.pyplot as plt from sklearn.svm import SVCplt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] False X,ymglearn.tools.make_handcrafted_dataset()…

ModuleNotFoundError: No module named ‘simple_knn‘

【报错】使用 AutoDL 复现 GaussianEditor 时引用 3D Gaussian Splatting 调用simple_knn 时遇到 ModuleNotFoundError: No module named ‘simple_knn‘ 报错: 【原因】 一开始以为是版本问题,于是将所有可能的版本都尝试了 (from versions: 0.1, 0.2…

笔试面试题——继承和组合

📘北尘_:个人主页 🌎个人专栏:《Linux操作系统》《经典算法试题 》《C》 《数据结构与算法》 ☀️走在路上,不忘来时的初心 文章目录 一、什么是菱形继承?菱形继承的问题是什么?二、什么是菱形虚拟继承&am…

SqlAlchemy使用教程(一) 原理与环境搭建

一、SqlAlchemy 原理及环境搭建 SqlAlchemy是1个支持连接各种不同数据库的Python库,提供DBAPI与ORM(object relation mapper)两种方式使用数据库。 DBAPI方式,即使用SQL方式访问数据库 ORM, 对象关系模型,是用 Python…

1.1 计算机网络在信息时代的作用

1.1 计算机网络在信息时代的作用 网络(Network)由若干结点(Node)和连接这些结点的链路(Link)所组成。网络中的结点可以是计算机、集线器、交换机或者路由器等。 图1-1 多个网络还可以通过路由器互连起来&a…

记录一下误删除libc.so.6的经历

起因: 在配置环境时,出现’GLIBCXX_3.4.29 not found’的错误,在解决这个问题的过程中,需要删除sudo rm /usr/lib/x86_64-linux-gnu/libstdc.so.6软连接,但是一不小心sudo rm /lib/x86_64-linux-gpu/libc.so.6&#xf…

使用主题模型和古老的人类推理进行无监督文本分类

一、说明 我在日常工作中不断遇到的一项挑战是在无法访问黄金标准标签的情况下标记文本数据。这绝不是一项微不足道的任务,在本文中,我将向您展示一种相对准确地完成此任务的方法,同时保持管道的可解释性和易于调整。 一些读者可能已经开始考…

docker-compose一键搭建zookeeper集群

1、setup.sh setup.sh脚本用来创建本地文件夹,这些文件夹会被挂载到docker容器。 #!/bin/bashmkdir -p "$PWD\zoo1\data" mkdir -p "$PWD\zoo1\datalog"mkdir -p "$PWD\zoo2\data" mkdir -p "$PWD\zoo2\datalog"mkdir -p…

计算机缺失msvcp120.dll的最新解决方法,实测可以完美修复

在计算机使用过程中,我们经常会遇到一些错误提示,其中之一就是“msvcp120.dll丢失”。msvcp120.dll是Microsoft Visual C Redistributable Package的一部分,它是运行许多基于Windows操作系统的应用程序所必需的动态链接库文件之一。如果计算机…

Jetson_yolov8_解决模型导出.engine遇到的问题、使用gpu版本的torch和torchvision、INT8 FP16量化加快推理

1、前情提要 英伟达Jetson搭建Yolov8环境过程中遇到的各种报错解决(涉及numpy、scipy、torchvision等)以及直观体验使用Yolov8目标检测的过程(CLI命令行操作、无需代码)-CSDN博客和YOLOv8_测试yolov8n.pt,yolov8m.pt训…

[C#]winform部署yolov5-onnx模型

【官方框架地址】 https://github.com/ultralytics/yolov5 【算法介绍】 Yolov5,全称为You Only Look Once version 5,是计算机视觉领域目标检测算法的一个里程碑式模型。该模型由ultralytics团队开发,并因其简洁高效的特点而备受关注。Yol…

nova组件讲解和glance对接swift

1、openstack架构 (1)openstack是一种SOA架构(微服务就是从这种架构中剥离出来的) (2)这种SOA架构,就是把每个服务独立成一个组件,每个组件通过定义好的api接口进行互通 &#xff…

使用递归将list转换成tree

在产品研发时遇到这样一个问题,对于省市区县这类三级联动的数据,前端插件需要一次把数据全部返回,单纯的使用接口查询字节的没办法满足要求。 如果一次把数据全部返回,前端使用起来很麻烦需要一条一条的进行查找。 常规的使用方…

软件测试|解读Python的requirements.txt文件:管理项目依赖的完整指南

简介 在Python项目中,管理依赖库是必不可少的。requirements.txt文件是一种常用的方式,用于列出项目所需的所有依赖库及其版本。本文将详细介绍requirements.txt的用法,帮助你更好地管理项目的依赖。 使用步骤 创建requirements.txt文件&am…

2024 爱分析 · AI 与大模型高峰论坛:和鲸喜获两项殊荣!

1 月 9 日下午,“2024 爱分析 AI 与大模型高峰论坛”在京举办。本次论坛以“智能涌现,价值焕新”为主题,汇聚众多专家学者、实践先驱,共同探讨 AI 与大模型在企业内的新场景、新价值、新路径。论坛中,和鲸科技成功入选…

全网最全postman接口测试教程和项目实战~从入门到精通!!!

Postman实现接口测试内容大纲一览: 一、什么是接口?为什么需要接口? 接口指的是实体或者软件提供给外界的一种服务。 因为接口能使我们的实体或者软件的内部数据能够被外部进行修改。从而使得内部和外部实现数据交互。所以需要接口。 比如&…