ultralytics实现DeepSort目标追踪算法之特征提取网络

文章目录

    • DeepSort基本流程
    • DeepSort特征提取网络
      • Market-1501数据集
        • 目录结构
        • 命名规则
      • 数据集划分
    • 网络模型训练过程
      • 参数设置
      • 数据集加载
      • 特征提取网络定义
      • 预训练模型加载
      • 损失函数与优化器定义
      • mian函数调用
      • 训练过程
      • 验证过程
      • 平均指标与结果

DeepSort基本流程

DeepSort(Deep Learning-based SORT)是一种用于目标跟踪的算法,它结合了深度学习和SORT(Simple Online and Realtime Tracking)算法的优点。DeepSort主要用于视频监控、自动驾驶等领域的多目标跟踪。以下是DeepSort的基本流程:

1. 检测目标
首先,使用目标检测算法(如YOLO、Faster R-CNN等)在每一帧中检测出目标物体,并获取其边界框(bounding box)及相应的置信度分数。

2. 特征提取
对于每个检测到的目标,DeepSort使用一个深度学习模型(通常是一个卷积神经网络)来提取目标的特征向量。这些特征向量用于区分不同的目标。

3. 数据关联
DeepSort使用卡尔曼滤波器来预测目标的位置,并结合匈牙利算法(Hungarian Algorithm)进行数据关联。数据关联的步骤包括:

预测:使用卡尔曼滤波器对每个目标的状态进行预测。
计算距离:根据目标的边界框和特征向量计算检测框与跟踪框之间的距离,通常使用IoU(Intersection over Union)和特征距离(如余弦相似度)来进行综合评估。
匹配:通过匈牙利算法将检测到的目标与当前跟踪的目标进行匹配。

4. 更新跟踪器
对于匹配成功的目标,更新其状态(位置、速度等)和特征向量。对于未匹配的目标,可以选择将其标记为“丢失”或进行其他处理(如保留一段时间)。

5. 处理新目标
对于未被跟踪的检测到的目标,DeepSort会将其添加为新的跟踪目标,并初始化其状态。

6. 轨迹管理
DeepSort维护每个目标的轨迹,包括目标的生命周期和丢失状态,确保在目标消失后仍能保持一定的跟踪时间,以防止短暂的遮挡或消失导致的错误丢失。

7. 输出结果
最后,DeepSort将跟踪到的目标及其ID、边界框、特征向量等信息输出,供后续应用使用。

总结
DeepSort的优势在于它通过深度学习提取的特征向量提高了目标跟踪的准确性,尤其是在复杂场景和目标相似度较高的情况下。此外,结合卡尔曼滤波和匈牙利算法,使得它在实时性和准确性上都表现良好。

相较于Sort算法、ByteTrack追踪算法,DeepSort算法引入了深度学习的思想,即DeepSort中具有一个特征提取网络(ReID网络),这个网络可以提取目标的外观特征信息,这也是DeepSort最大的创新点,当然这也就意味着DeepSort追踪模型是需要训练的,其速度相较于先前的算法慢了许多。

事实上,在ultralytics框架中,其使用的目标追踪算法分别是ByteTrack以及BotSort算法,这里的DeepSort算法并非是该框架中集成的。但DeepSort目标追踪算法太具有代表性,因此我们很有必要去学习一下该算法。

DeepSort特征提取网络

其环境与ultralytics一致,当然还需要下载一些依赖,,这里博主就不做过多赘述了。

关于DeepSort,其组成便是Deep+Sort,其中Deep则是用于提取检测框中的目标特征的特征提取网络(这里可以是任意网络,如ResNet、DenseNet等),而Sort部分基本与Sort算法一致,其核心依旧是卡尔曼滤波与匈牙利匹配。

那么,既然DeepSort算法中具有特征提取网络,那么这个方法就不能直接拿来就用了,我们需要训练。

Market-1501数据集

要进行网络模型的训练,自然要使用数据集,这里使用的数据集为Market-1501:
Market-1501 数据集在清华大学校园中采集,夏天拍摄,在 2015 年构建并公开。它包括由6个摄像头(其中5个高清摄像头和1个低清摄像头)拍摄到的 1501 个行人、32668 个检测到的行人矩形框。每个行人至少由2个摄像头捕获到,并且在一个摄像头中可能具有多张图像。训练集有 751 人,包含 12,936 张图像,平均每个人有 17.2 张训练数据;测试集有 750 人,包含 19,732 张图像,平均每个人有 26.3 张测试数据。

目录结构

该数据集的目录结构如下:

Market-1501├── bounding_box_test├── 0000_c1s1_000151_01.jpg├── 0000_c1s1_000376_03.jpg├── 0000_c1s1_001051_02.jpg├── bounding_box_train├── 0002_c1s1_000451_03.jpg├── 0002_c1s1_000551_01.jpg├── 0002_c1s1_000801_01.jpg├── gt_bbox├── 0001_c1s1_001051_00.jpg├── 0001_c1s1_009376_00.jpg├── 0001_c2s1_001976_00.jpg├── gt_query├── 0001_c1s1_001051_00_good.mat├── 0001_c1s1_001051_00_junk.mat├── query├── 0001_c1s1_001051_00.jpg├── 0001_c2s1_000301_00.jpg├── 0001_c3s1_000551_00.jpg└── readme.txt
  1. bounding_box_test——用于测试集的 750 人,包含 19,732 张图像,前缀为 0000 表示在提取这 750人的过程中DPM(DPM(Deformable Part Model,一种传统的目标检测方法)检测错的图(可能与query是同一个人),-1 表示检测出来其他人的图(不在这 750 人中)
  2. bounding_box_train——用于训练集的 751 人,包含 12,936 张图像
  3. query——为 750 人在每个摄像头中随机选择一张图像作为query,因此一个人的query最多有 6 个,共有 3,368 张图像
  4. gt_query——matlab格式,用于判断一个query的哪些图片是好的匹配(同一个人不同摄像头的图像)和不好的匹配(同一个人同一个摄像头的图像或非同一个人的图像)
  5. gt_bbox——手工标注的bounding box,用于判断DPM检测的bounding box是不是一个好的box
命名规则

0001_c1s1_000151_01.jpg 为例

  1. 0001 表示每个人的标签编号,从0001到1501(训练集751人,测试集750人);
  2. c1 表示第一个摄像头(camera1),共有6个摄像头;
  3. s1 表示第一个录像片段(sequece1),每个摄像机都有数个录像段;
  4. 000151 表示 c1s1 的第000151帧图片,视频帧率25fps;
  5. 01 表示 c1s1_001051 这一帧上的第1个检测框,由于采用DPM检测器,对于每一帧上的行人可能会框出好几个bbox。00
    表示手工标注框

数据集划分

事实上,我们并不需要全部的文件,比如在这里我们只需要train文件即可,即我们将train文件拆分一下,从每个目标中拿出一张构成验证集,其余的作训练集,拆分代码如下:

import os
from shutil import copyfile# You only need to change this line to your dataset download path
download_path = './Market-1501-v15.09.15'if not os.path.isdir(download_path):print('please change the download_path')save_path = download_path + '/pytorch'
if not os.path.isdir(save_path):os.mkdir(save_path)save_path='./deep_sort_pytorch/deep_sort/deep/Market-1501'
train_path = download_path + '/bounding_box_train'
train_save_path =save_path+ '/train'
val_save_path = save_path+  '/test'if not os.path.isdir(save_path):os.mkdir(save_path)if not os.path.isdir(train_save_path):os.mkdir(train_save_path)os.mkdir(val_save_path)from tqdm import tqdm
for root, dirs, files in os.walk(train_path, topdown=True):for name in tqdm(files):if not name[-3:]=='jpg':continueID  = name.split('_')src_path = train_path + '/' + namedst_path = train_save_path + '/' + ID[0]if not os.path.isdir(dst_path):os.mkdir(dst_path)dst_path = val_save_path + '/' + ID[0]  #first image is used as val imageos.mkdir(dst_path)copyfile(src_path, dst_path + '/' + name)

划分的数据集如下,其已经都抠图:

在这里插入图片描述

网络模型训练过程

当我们将数据集拆分完成后,我们便可以进行模型的训练了,我们也来详细看看其是如何进行的:

参数设置

首先是参数设定,这里可以指定数据集、GPU、lr以及预训练模型

arser = argparse.ArgumentParser(description="Train on market1501")
parser.add_argument("--data-dir", default='Market-1501', type=str)
parser.add_argument("--no-cuda", action="store_true")#执行时输入参数--no-cuda时 action有效,是 --no-cuda=false
parser.add_argument("--gpu-id", default=0, type=int)
parser.add_argument("--lr", default=0.1, type=float)
parser.add_argument("--interval", '-i', default=20, type=int)
parser.add_argument('--resume', '-r', action='store_true') #执行时输入参数--resume时 action有效,是 resume=true
args = parser.parse_args()

测试能否使用 CUDA 以及 cuDNN

# device
device = "cuda:{}".format(args.gpu_id) if torch.cuda.is_available() and not args.no_cuda else "cpu"
# 基准测试模式是cuDNN的一个特性,它会自动选择对于给定任务的最优算法。
# 当cudnn.benchmark = True时,cuDNN会进行基准测试来找出最优的算法。 这通常会使训练或推理速度变慢,但可以提高准确性。
if torch.cuda.is_available() and not args.no_cuda:cudnn.benchmark = True

数据集加载

数据集加载,即加载训练集与验证集,其中首先是进行数据集变换,使用的是torchvision.transforms方法,并主要负责将图像转换为图像剪切、图像旋转(数据增强)以及归一化将数据格式转换为tensor格式。随后便是数据集加载了,调用的是torch.utils.data.DataLoader方法

root = args.data_dir
train_dir = os.path.join(root, "train")
test_dir = os.path.join(root, "test")
transform_train = torchvision.transforms.Compose([torchvision.transforms.RandomCrop((128, 64), padding=4),#随机位置裁剪指定尺寸 裁剪结果的尺寸是 (128, 64)torchvision.transforms.RandomHorizontalFlip(), #随机水平(左右)翻转# [0,255]的数据变张量, 例如原来是128*64*3 变为3*128*64 还要归一化到[0,1]torchvision.transforms.ToTensor(),# rgb三个通道的数据用公式(img-mean)/std将数据归一化到[-1,1]# 其中,均值和方差分别如下 mean=[0.485, 0.456, 0.406] and std=[0.229, 0.224, 0.225],三通道torchvision.transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])
transform_test = torchvision.transforms.Compose([torchvision.transforms.Resize((128, 64)),torchvision.transforms.ToTensor(),torchvision.transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])
trainloader = torch.utils.data.DataLoader(torchvision.datasets.ImageFolder(train_dir, transform=transform_train),batch_size=64, shuffle=True
)
testloader = torch.utils.data.DataLoader(torchvision.datasets.ImageFolder(test_dir, transform=transform_test),batch_size=64, shuffle=True
)

trainloaderdatasets如下:

在这里插入图片描述

testloaderdatasets如下,前面我们说过,test数据集中每个目标只选一个,所以751个目标,则选出了751张图,对应751个类别

在这里插入图片描述

特征提取网络定义

随后便是DeepSort中特征提取网络的调用:

net = Net(num_classes=num_classes)

我们看下这个网络具体是如何实现的,其实这里我们并不需要了解其具体结构,只需要知道其输入输出即可,根据 x = torch.randn(4,3,128,64)可知,其传入的数据即为图像,即(batch-size,通道数,宽,高)的格式,而根据最后连接的分类头nn.Linear(256, num_classes),可知,其最终输出的结果必与类别数有关。事实上最终的结果为(batch-size,751)即该图像中目标的类别。

import cv2
import torch
import torch.nn as nn
import torch.nn.functional as Fclass BasicBlock(nn.Module):def __init__(self, c_in, c_out,is_downsample=False):super(BasicBlock,self).__init__()self.is_downsample = is_downsampleif is_downsample:self.conv1 = nn.Conv2d(c_in, c_out, 3, stride=2, padding=1, bias=False)else:self.conv1 = nn.Conv2d(c_in, c_out, 3, stride=1, padding=1, bias=False)self.bn1 = nn.BatchNorm2d(c_out)self.relu = nn.ReLU(True)self.conv2 = nn.Conv2d(c_out,c_out,3,stride=1,padding=1, bias=False)self.bn2 = nn.BatchNorm2d(c_out)if is_downsample:self.downsample = nn.Sequential(nn.Conv2d(c_in, c_out, 1, stride=2, bias=False),nn.BatchNorm2d(c_out))elif c_in != c_out:self.downsample = nn.Sequential(nn.Conv2d(c_in, c_out, 1, stride=1, bias=False),nn.BatchNorm2d(c_out))self.is_downsample = Truedef forward(self,x):y = self.conv1(x)y = self.bn1(y)y = self.relu(y)y = self.conv2(y)y = self.bn2(y)if self.is_downsample:x = self.downsample(x)return F.relu(x.add(y),True)def make_layers(c_in,c_out,repeat_times, is_downsample=False):blocks = []for i in range(repeat_times):if i ==0:blocks += [BasicBlock(c_in,c_out, is_downsample=is_downsample),]else:blocks += [BasicBlock(c_out,c_out),]return nn.Sequential(*blocks)class Net(nn.Module):def __init__(self, num_classes=751, reid=False):super(Net,self).__init__()# 3 128 64self.conv = nn.Sequential(nn.Conv2d(3,64,3,stride=1,padding=1),nn.BatchNorm2d(64),nn.ReLU(inplace=True),# nn.Conv2d(32,32,3,stride=1,padding=1),# nn.BatchNorm2d(32),# nn.ReLU(inplace=True),nn.MaxPool2d(3,2,padding=1),)# 64 64 32self.layer1 = make_layers(64,64,2,False)# 64 64 32self.layer2 = make_layers(64,128,2,True)# 128 32 16self.layer3 = make_layers(128,256,2,True)# 256 16 8self.layer4 = make_layers(256,512,2,True)# 512 8 4self.avgpool = nn.AvgPool2d((8,4),1)# 512 1 1self.reid = reidself.classifier = nn.Sequential(nn.Linear(512, 256),nn.BatchNorm1d(256),nn.ReLU(inplace=True),nn.Dropout(),nn.Linear(256, num_classes),)def forward(self, x):x = self.conv(x)x = self.layer1(x)x = self.layer2(x)x = self.layer3(x)x = self.layer4(x)x = self.avgpool(x)x = x.view(x.size(0),-1)# B x 128if self.reid:x = x.div(x.norm(p=2,dim=1,keepdim=True)) # x.norm=(x1^p+...+xn^p)^(1/p)return x# classifierx = self.classifier(x)return xif __name__ == '__main__':net = Net()x = torch.randn(4,3,128,64)y = net(x)

事实上,这个特征提取网络为ResNet

在这里插入图片描述

预训练模型加载

加载预训练模型,当然在这里我们没有使用预训练模型

if args.resume:assert os.path.isfile("./checkpoint/ckpt.t7"), "Error: no checkpoint file found!"print('Loading from checkpoint/ckpt.t7')checkpoint = torch.load("./checkpoint/ckpt.t7")# import ipdb; ipdb.set_trace()net_dict = checkpoint['net_dict']net.load_state_dict(net_dict)best_acc = checkpoint['acc']start_epoch = checkpoint['epoch']

损失函数与优化器定义

定义损失函数与优化器,CrossEntropyLoss是分类损失函数,优化器选择SGD,即随机梯度下降法

criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(net.parameters(), args.lr, momentum=0.9, weight_decay=5e-4)
best_acc = 0.

mian函数调用

主函数即为main方法,可以看到其epoch为40,其主要过程为训练、验证、画图、更新lr

def main():for epoch in range(start_epoch, start_epoch+40):train_loss, train_err = train(epoch)test_loss, test_err = test(epoch)draw_curve(epoch, train_loss, train_err, test_loss, test_err)if (epoch+1) % 20 == 0:lr_decay()

训练过程

训练过程定义如下:

ef train(epoch):print("\nEpoch : %d" % (epoch+1))net.train() # 进入训练模式training_loss = 0. # 每20个批次的损失train_loss = 0. # 本epoch的损失之和correct = 0 # 本epoch识别正确的数量total = 0   # 本epoch要识别的图像总数interval = args.interval  #interval=20 每20个批次显示一下结果start = time.time()  #每20个批次计算一下耗时for idx, (inputs, labels) in enumerate(trainloader):# forwardinputs, labels = inputs.to(device), labels.to(device)outputs = net(inputs)loss = criterion(outputs, labels)# backwardoptimizer.zero_grad()#清空梯度,即每个批次清空一次loss.backward()#反向传播optimizer.step()#执行参数更新# accumuratingtraining_loss += loss.item() #每20个批次计算一次,这是根据后面的train_loss += loss.item()#这个epcho的损失correct += outputs.max(dim=1)[1].eq(labels).sum().item()#计算分类正确的数量total += labels.size(0)# printif (idx+1) % interval == 0: #每20个批次打印一下本epoch的结果,并设置损失为0,当然总损失是一直保留的end = time.time()print("[progress:{:.1f}%]time:{:.2f}s Loss:{:.5f} Correct:{}/{} Acc:{:.3f}%".format(100.*(idx+1)/len(trainloader), end-start, training_loss /interval, correct, total, 100.*correct/total))training_loss = 0.start = time.time()return train_loss/len(trainloader), 1. - correct/total#最终返回平均损失以及分类正确的比例

其核心部分便是通过遍历数据,获得预测结果,从而与真值计算损失,进而更新网络参数的部分

inputs, labels = inputs.to(device), labels.to(device)

在这里插入图片描述

得到的结果值如下,output结果维度为(64,751)

outputs = net(inputs)

在这里插入图片描述

将预测结果与真值计算损失:

loss = criterion(outputs, labels)

得到的loss值为:tensor(6.7166, device=‘cuda:0’, grad_fn=),这是整个批次(64张图像)的损失和

随后便是反向传播和更新参数了

loss.backward()
optimizer.step()

验证过程

这里虽说是验证过程,但其实其过程较训练过程极为相似,不同之处在于该过程并不需要进行反向梯度传播与模型参数更新,当然其需要计算验证损失,方便我们作图,事实上,这个过程是方便我们查看模型的训练是否符合预期,使我们能够在训练过程中及时发现过拟合、欠拟合等现象,从而对训练过程做出及时调整。

def test(epoch):global best_accnet.eval()test_loss = 0.correct = 0total = 0start = time.time()with torch.no_grad():for idx, (inputs, labels) in enumerate(testloader):inputs, labels = inputs.to(device), labels.to(device)outputs = net(inputs)loss = criterion(outputs, labels)test_loss += loss.item()correct += outputs.max(dim=1)[1].eq(labels).sum().item()total += labels.size(0)print("Testing ...")end = time.time()print("[progress:{:.1f}%]time:{:.2f}s Loss:{:.5f} Correct:{}/{} Acc:{:.3f}%".format(100.*(idx+1)/len(testloader), end-start, test_loss /len(testloader), correct, total, 100.*correct/total))# saving checkpointacc = 100.*correct/totalif acc > best_acc:best_acc = accprint("Saving parameters to checkpoint/ckpt.t7")checkpoint = {'net_dict': net.state_dict(),'acc': acc,'epoch': epoch,}if not os.path.isdir('checkpoint'):os.mkdir('checkpoint')torch.save(checkpoint, './checkpoint/ckpt.t7')return test_loss/len(testloader), 1. - correct/total

平均指标与结果

评价指标使用的是ACC,其计算公式如下:

在这里插入图片描述
训练40epoch后,结果如下:

由于评价指标是ACC,即将其看作分类任务来进行,这里num_class=751,即认为这751个目标是751个类别,这样方便计算。

在这里插入图片描述

在这里插入图片描述

至此,DeepSort算法 中的特征提取网络的训练、预测与验证过程的梳理边完成了。

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

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

相关文章

微博视频无水印下载的方法

在如今的数字时代,社交媒体平台如微博已经成为人们分享日常生活、获取新闻和娱乐内容的重要渠道。我们时常会在刷微博时看到一些有趣的视频图片,或是名人的访谈,或是搞笑的短片,有时甚至是一些珍贵的历史资料。这些视频不仅内容丰…

数据结构栈和队列

系统栈 程序运行中使用的栈,由操作系统维护 栈区:1,保存局部变量 2,函数的形参的返回值 3,函数的调用关系 函数中调用函数时会把调用函数的下一条指定的首地址保存在栈区。 (保护现…

Steam游戏截图方法

Steam游戏截图方法 截图快捷键 Steam游戏自带截图功能,在游戏中无需复杂的快捷键,仅需按下F12快捷键便可立即截图,官方说明如下。下文介绍使用方法。 查看截图 退出游戏后,在Steam界面点击查看 - 截图,即可查看截…

JAVA—反射

学习Java中关于反射的知识,以理解框架 目录 1.认识反射 2.获取类 3.获取构造器 4.获取成员变量​编辑 5.获取成员方法 6.作用 应用场景 1.认识反射 反射 加载类 并允许以编程的方式解刨类中的各种成分(成员变量 方法 构造器) 学习反射…

【Hadoop|HDFS篇】HDFS的读写流程

1. HDFS的写流程 1.1 剖析文件的写入 副本存储节点的选择问题: 第一个副本在Client所在的节点上,如果客户端在集群外,随机选一个。第二个副本在另一个机架的随机一个节点上。第三个副本在第二个副本所在的机架的随机节点上。 2. HDFS的写流…

机器学习和物联网驱动技术在加工过程中监测工具磨损:一项全面的综述

这篇论文的标题是《Machine-Learning and Internet-of-Things-Driven Techniques for Monitoring Tool Wear in Machining Process: A Comprehensive Review》,由 Sudhan Kasiviswanathan、Sakthivel Gnanasekaran、Mohanraj Thangamuthu 和 Jegadeeshwaran Rakkiya…

超越传统:Reflection 70B如何革新AI语言处理

Reflection 70B:AI语言模型的新里程碑🚀 AI领域迎来了革命性的变革,HyperWrite公司推出的开源AI大模型Reflection 70B,以其卓越的性能在多个基准测试中超越了GPT-4o和Llama 3.1。这款基于Meta的Llama 3.1 70B Instruct构建的模型…

经验笔记:DevOps

DevOps经验笔记 DevOps(Development and Operations)是一种旨在通过加强开发(Development)与运维(Operations)之间合作的文化理念、实践方法和工具集合。DevOps的目标是在软件开发生命周期中实现更高的效率…

为何iPhone 16系列的发布对苹果至关重要?

即将发布的iPhone 16系列对苹果来说将是至关重要的时刻,特别是在快速发展的AI智能手机市场背景下。随着Android制造商在集成先进AI功能方面领先一步,苹果正处于一个关键的转折点——赶上竞争对手不仅仅是选择,而是必须完成的任务。 AI竞赛&am…

ARM base instruction -- b.cs

B Branch causes an unconditional branch to a label at a PC-relative offset, with a hint that this is not a subroutine call or return. 在PC相对偏移处对标签进行无条件分支,并提示这不是子程序调用或返回。 操作码 助记符 标志 含义 0x2 …

vue-----window.open打开新窗口文件并且修改窗口标题下载文件

vue-----window.open打开新窗口文件并且修改窗口标题&&下载文件 // 下载word文件downloadFile(url, fileName) {const xhr new XMLHttpRequest();xhr.open("GET", url, true);xhr.responseType "blob";xhr.onload function () {if (xhr.status …

上交2024最新-《动手学大模型》实战教程及ppt分享!

本课介绍 今天分享一个上海交大的免费的大模型课程,有相关教程文档和Slides,目前是2.2K星标,还是挺火的! 《动手学大模型》系列编程实践教程,由上海交通大学2024年春季《人工智能安全技术》课程(NIS3353&…

传统CV算法——基于harris检测算法实现角点检测

角点 角点是图像中的一个特征点,指的是两条边缘交叉的点,这样的点在图像中通常表示一个显著的几角。在计算机视觉和图像处理中,角点是重要的特征,因为它们通常是图像中信息丰富的区域,可以用于图像分析、对象识别、3D…

如何在极狐GitLab中添加 SSH Key?

本文分享如何生成 SSH Key 并添加到极狐GitLab 中,然后用 SSH Key 进行代码拉取。 极狐GitLab 是 GitLab 在中国的发行版,可以私有化部署,对中文的支持非常友好,是专为中国程序员和企业推出的企业级一体化 DevOps 平台&#xff0…

43. 1 ~ n 整数中 1 出现的次数【难】

comments: true difficulty: 中等 edit_url: https://github.com/doocs/leetcode/edit/main/lcof/%E9%9D%A2%E8%AF%95%E9%A2%9843.%201%EF%BD%9En%E6%95%B4%E6%95%B0%E4%B8%AD1%E5%87%BA%E7%8E%B0%E7%9A%84%E6%AC%A1%E6%95%B0/README.md 面试题 43. 1 ~ n 整数中 1 …

《Transformer 模型》

一、引言 在自然语言处理领域,Transformer 模型的出现带来了革命性的变化。它以其强大的性能和并行计算能力,在机器翻译、文本生成、语言理解等众多任务中取得了显著的成果。本文将深入探讨 Transformer 模型的原理、结构、训练方法以及应用场景&#xf…

【Cesium实体创建】

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 Cesium目录 前言一、Cesium二、点 线 实体1.点实体2.线实体 总结 前言 提示:这里可以添加本文要记录的大概内容: 例如:随着人工智能的不…

C#游戏服务器开发框架设计与架构详解

我一直在思考一个问题,什么样的服务端框架最好用,最适合? 经过这些年的项目经验,其实最好用,最适合的游戏服务端框架就是自己结合公司项目需求,团队特点与技术能力,自己整合的游戏框架是最好用的。 很多新手会担心自己整合的框架…

JS生成二维码QRCode代码

JavaScript是一种广泛使用的前端编程语言,它不仅用于网页交互,还可以实现许多实用功能,如生成二维码。本篇文章将深入探讨如何使用JavaScript生成二维码,以及如何确保这种生成的二维码在各种浏览器和手机端都能正常工作&#xff0…

找不同-第15届蓝桥省赛Scratch初级组真题第4题

[导读]:超平老师的《Scratch蓝桥杯真题解析100讲》已经全部完成,后续会不定期解读蓝桥杯真题,这是Scratch蓝桥杯真题解析第183讲。 如果想持续关注Scratch蓝桥真题解读,可以点击《Scratch蓝桥杯历年真题》并订阅合集,…