【RT-DETR有效改进】轻量化CNN网络MobileNetV2改进特征提取网络

前言

大家好,这里是RT-DETR有效涨点专栏

本专栏的内容为根据ultralytics版本的RT-DETR进行改进,内容持续更新,每周更新文章数量3-10篇。

专栏以ResNet18、ResNet50为基础修改版本,同时修改内容也支持ResNet32、ResNet101和PPHGNet版本其中ResNet为RT-DETR官方版本1:1移植过来的参数量基本保持一致(误差很小很小),不同于ultralytics仓库版本的ResNet官方版本,同时ultralytics仓库的一些参数是和RT-DETR相冲的所以我也是会教大家调好一些参数和代码,真正意义上的跑ultralytics的和RT-DETR官方版本的无区别

👑欢迎大家订阅本专栏,一起学习RT-DETR👑   

一、本文介绍

本文给大家带来的改进机制是MobileNetV2,其是专为移动和嵌入式视觉应用设计的轻量化网络结构。其在MobilNetV1的基础上采用反转残差结构和线性瓶颈层。这种结构通过轻量级的深度卷积和线性卷积过滤特征,同时去除狭窄层中的非线性,以维持表征能力。MobileNetV2在性能上和精度上都要比V1版本强很多,其在多种应用(如对象检测、细粒度分类、面部属性识别和大规模地理定位)中都展现了一定的有效性,这个模型大家看图片可以看出非常明显的上升趋势,但是我只训练了100epochs,但是如果可以加大一些轮次让其完全收敛估计精度差不多,但是MobileNetV2参数相对于ResNet18下降了百分之五十!

专栏链接:RT-DETR剑指论文专栏,持续复现各种顶会内容——论文收割机RT-DETR

目录

一、本文介绍

二、MobileNetV2的框架原理

2.1 MobileNetV2的基本原理

2.1.1 反转残差结构 

2.1.2 线性瓶颈层

2.1.3 SSDLite框架 

三、MobileNetV2的核心代码

四、手把手教你添加MobileNetV2网络结构

4.1 修改一

4.2 修改二 

4.3 修改三 

4.4 修改四

4.5 修改五

4.6 修改六

4.7 修改七 

4.8 修改八

4.9 RT-DETR不能打印计算量问题的解决

4.10 可选修改

五、MobileNetV2的yaml文件

5.1 yaml文件

5.2 运行文件

5.3 成功训练截图

六、全文总结


二、MobileNetV2的框架原理

官方论文地址:官方论文地址

官方代码地址:官方代码地址


2.1 MobileNetV2的基本原理

MobileNetV2是在MobileNetV1基础上提出来的,其不光具有V1的全部改进,还提出了采用反转残差结构和线性瓶颈层。这种结构通过轻量级的深度卷积和线性卷积过滤特征,同时去除狭窄层中的非线性,以维持表征能力。MobileNetV2通过这种设计提高了性能,并在多种任务和基准测试上表现出色。此外,它提出了一种新的框架SSDLite,用于移动设备上的目标检测,并展示了如何构建移动语义分割模型Mobile DeepLabv3。这种方法允许输入/输出域与变换的表达力解耦,为进一步分析提供了方便的框架。 

MobileNetV2的主要创新点包括:

1. 反转残差结构:使用轻量级的深度卷积作为扩展层来提高特征过滤的效率。
2. 线性瓶颈层:在狭窄的层中去除非线性激活函数,以保持网络的表征能力。
3. SSDLite框架:用于移动设备上的高效目标检测,它是一种简化和优化的SSD框架。
 


2.1.1 反转残差结构 

反转残差结构是MobileNetV2的关键特性,它采用轻量级的深度可分离卷积作为扩展层。这种结构首先使用1x1的卷积将输入特征图的通道数扩大,然后应用深度可分离卷积对这些扩展的特征图进行空间特征提取,最后再次通过1x1的卷积将通道数减少,恢复到原来的尺寸。这样的设计有效地提高了网络处理特征的效率,同时减少了参数数量和计算成本。通过这种方式,MobileNetV2能够在保持模型轻量的同时,提供足够的模型表现力,适用于移动和嵌入式设备上的高效计算。 

上图展示了残差块和反转残差块之间的区别:

(a) 残差:传统的残差块通过直接连接输入和输出来促进特征的传递,通常包含具有高通道数的层和ReLU激活函数。
(b) 反转残差块:在反转残差块中,连接是在瓶颈层之间,即通道数较少的层,而且去除了非线性激活函数,以保持特征的表达力。这种设计通常首先用一个扩展层增加通道数,然后应用深度卷积处理特征,并且在最后一个线性层减少通道数。


2.1.2 线性瓶颈层

线性瓶颈层是MobileNetV2架构中的另一个关键特性。在这种结构中,传统的非线性激活函数被有意地从瓶颈层中去除。瓶颈层是指那些通道数较少的卷积层,它们位于扩展层和压缩层之间。这样做的目的是为了减少信息在通过狭窄层时的损失,因为非线性操作可能会破坏特征中的一些信息。通过保持这些层的线性,网络能够维持更丰富的特征表示,这对于提高模型的整体性能至关重要。

总结:就是在一些卷积层里面把激活函数删除掉了,类似于v8中的Bottleneck模块,将其中的激活函数删除掉。


2.1.3 SSDLite框架 

SSDLite是一个轻量级的目标检测框架,专为移动设备优化。它是SSD框架的简化版本,通过使用深度可分离卷积替换SSD中的标准卷积,显著减少了计算量和模型的大小。SSDLite继承了SSD的单次检测机制,使得模型在进行目标检测时既高效又准确。这种设计使SSDLite非常适合在资源受限的设备上进行实时目标检测任务。

上图展示了可分离卷积块的演变。其中:

(a) 展示了常规的卷积。
(b) 展示了可分离卷积块,这种块首先使用深度卷积分别处理每个输入通道,然后用一个1x1的卷积组合这些特征。
(c) 展示了带有线性瓶颈的可分离卷积,它在瓶颈层中移除了非线性激活函数,以保持特征的表达力。
(d) 展示了带有扩展层的瓶颈结构,它使用一个扩展层放大特征空间,然后再用深度卷积和1x1卷积进行处理。

对角线阴影的纹理表示不包含非线性的层,最后的浅色层表示下一个块的开始。请注意,当堆叠时,2d和2c是等效的块。


三、MobileNetV2的核心代码

下面的代码是整个MobileNetV2的核心代码,大家如果想学习可以和上面的框架原理对比着看一看估计会有一定的收获,使用方式看章节四。

"""A from-scratch implementation of MobileNetV2 paper ( for educational purposes ).PaperMobileNetV2: Inverted Residuals and Linear Bottlenecks - https://arxiv.org/abs/1801.04381author : shubham.aiengineer@gmail.com
"""import torch
from torch import nn
from torchsummary import summaryclass ConvNormReLUBlock(nn.Module):def __init__(self,in_channels: int,out_channels: int,kernel_size: list,stride: int = 1,padding: int = 0,groups: int = 1,bias: bool = False,activation: bool = nn.ReLU6,):"""Constructs a block containing a combination of convolution, batchnorm and reluArgs:in_channels (int): input channelsout_channels (int): output channelskernel_size (list): kernel size parameter for convolutionstride (int, optional): stride parameter for convolution. Defaults to 1.padding (int, optional): padding parameter for convolution. Defaults to 0.groups (int, optional): number of blocked connections from input channel to output channel for convolution. Defaults to 1.bias (bool, optional): whether to enable bias in convolution. Defaults to False.activation (bool, optional): activation function to use. Defaults to nn.ReLU6."""super().__init__()self.conv = nn.Conv2d(in_channels,out_channels,kernel_size,stride=stride,padding=padding,groups=groups,bias=bias,)self.bn = nn.BatchNorm2d(out_channels)self.activation = activation()def forward(self, x):"""Perform forward pass."""x = self.conv(x)x = self.bn(x)x = self.activation(x)return xclass InverseResidualBlock(nn.Module):def __init__(self,in_channels: int,out_channels: int,expansion_factor: int = 6,stride: int = 1,):"""Constructs a inverse residual block with depthwise seperable convolutionArgs:in_channels (int): input channelsout_channels (int): output channelsexpansion_factor (int, optional): Calculating the input & output channel for depthwise convolution by multiplying the expansion factor with input channels. Defaults to 6.stride (int, optional): stride paramemeter for depthwise convolution. Defaults to 1."""super().__init__()hidden_channels = in_channels * expansion_factorself.residual = in_channels == out_channels and stride == 1self.conv1 = (ConvNormReLUBlock(in_channels, hidden_channels, (1, 1))if in_channels != hidden_channelselse nn.Identity() #  If it's not the first layer, then we need to add a 1x1 convolutional layer to expand the number of channels)self.depthwise_conv = ConvNormReLUBlock(hidden_channels,hidden_channels,(3, 3),stride=stride,padding=1,groups=hidden_channels,)self.conv2 = ConvNormReLUBlock(hidden_channels, out_channels, (1, 1), activation=nn.Identity)def forward(self, x):"""Perform forward pass."""identity = xx = self.conv1(x)x = self.depthwise_conv(x)x = self.conv2(x)if self.residual:x = torch.add(x, identity)return xclass MobileNetV2(nn.Module):def __init__(self,n_classes: int = 1000,input_channel: int = 3,dropout: float = 0.2,):"""Constructs MobileNetV2 architectureArgs:n_classes (int, optional): output neuron in last layer. Defaults to 1000.input_channel (int, optional): input channels in first conv layer. Defaults to 3.dropout (float, optional): dropout in last layer. Defaults to 0.2."""super().__init__()# The configuration of MobileNetV2# input channels, expansion factor, output channels, repeat, stride,config = ((32, 1, 16, 1, 1),(16, 6, 24, 2, 2),(24, 6, 32, 3, 2),(32, 6, 64, 4, 2),(64, 6, 96, 3, 1),(96, 6, 160, 3, 2),(160, 6, 320, 1, 1),)self.model = nn.Sequential(ConvNormReLUBlock(input_channel, 32, (3, 3), stride=2, padding=1))for in_channels, expansion_factor, out_channels, repeat, stride in config:for _ in range(repeat):self.model.append(InverseResidualBlock(in_channels=in_channels,out_channels=out_channels,expansion_factor=expansion_factor,stride=stride,))in_channels = out_channelsstride = 1self.index = [24, 32, 96, 320]self.width_list = [i.size(1) for i in self.forward(torch.randn(1, 3, 640, 640))]def forward(self, x):"""Perform forward pass."""results = [None, None, None, None]for model in self.model:x = model(x)if x.size(1) in self.index:position = self.index.index(x.size(1))  # Find the position in the index listresults[position] = xresults.append(x)return resultsif __name__ == "__main__":# Generating Sample imageimage_size = (1, 3, 224, 224)image = torch.rand(*image_size)# Modelmobilenet_v2 = MobileNetV2()# summary(#     mobilenet_v2,#     input_data=image,#     col_names=["input_size", "output_size", "num_params"],#     device="cpu",#     depth=2,# )out = mobilenet_v2(image)print("Output shape : ", out.shape)

四、手把手教你添加MobileNetV2网络结构

下面教大家如何修改该网络结构,主干网络结构的修改步骤比较复杂,我也会将task.py文件上传到CSDN的文件中,大家如果自己修改不正确,可以尝试用我的task.py文件替换你的,然后只需要修改其中的第1、2、3、5步即可。

⭐修改过程中大家一定要仔细⭐


4.1 修改一

首先我门中到如下“ultralytics/nn”的目录,我们在这个目录下在创建一个新的目录,名字为'Addmodules'(此文件之后就用于存放我们的所有改进机制),之后我们在创建的目录内创建一个新的py文件复制粘贴进去 ,可以根据文章改进机制来起,这里大家根据自己的习惯命名即可。


4.2 修改二 

第二步我们在我们创建的目录内创建一个新的py文件名字为'__init__.py'(只需要创建一个即可),然后在其内部导入我们本文的改进机制即可,其余代码均为未发大家没有不用理会!


4.3 修改三 

第三步我门中到如下文件'ultralytics/nn/tasks.py'然后在开头导入我们的所有改进机制(如果你用了我多个改进机制,这一步只需要修改一次即可)


4.4 修改四

添加如下两行代码!!!


4.5 修改五

找到七百多行大概把具体看图片,按照图片来修改就行,添加红框内的部分,注意没有()只是函数名(此处我的文件里已经添加很多了后期都会发出来,大家没有的不用理会即可)。

        elif m in {自行添加对应的模型即可,下面都是一样的}:m = m(*args)c2 = m.width_list  # 返回通道列表backbone = True


4.6 修改六

用下面的代码替换红框内的内容。 

if isinstance(c2, list):m_ = mm_.backbone = True
else:m_ = nn.Sequential(*(m(*args) for _ in range(n))) if n > 1 else m(*args)  # modulet = str(m)[8:-2].replace('__main__.', '')  # module type
m.np = sum(x.numel() for x in m_.parameters())  # number params
m_.i, m_.f, m_.type = i + 4 if backbone else i, f, t  # attach index, 'from' index, type
if verbose:LOGGER.info(f'{i:>3}{str(f):>20}{n_:>3}{m.np:10.0f}  {t:<45}{str(args):<30}')  # print
save.extend(x % (i + 4 if backbone else i) for x in ([f] if isinstance(f, int) else f) if x != -1)  # append to savelist
layers.append(m_)
if i == 0:ch = []
if isinstance(c2, list):ch.extend(c2)if len(c2) != 5:ch.insert(0, 0)
else:ch.append(c2)


4.7 修改七 

修改七这里非常要注意,不是文件开头YOLOv8的那predict,是400+行的RTDETR的predict!!!初始模型如下,用我给的代码替换即可!!!

代码如下->

 def predict(self, x, profile=False, visualize=False, batch=None, augment=False, embed=None):"""Perform a forward pass through the model.Args:x (torch.Tensor): The input tensor.profile (bool, optional): If True, profile the computation time for each layer. Defaults to False.visualize (bool, optional): If True, save feature maps for visualization. Defaults to False.batch (dict, optional): Ground truth data for evaluation. Defaults to None.augment (bool, optional): If True, perform data augmentation during inference. Defaults to False.embed (list, optional): A list of feature vectors/embeddings to return.Returns:(torch.Tensor): Model's output tensor."""y, dt, embeddings = [], [], []  # outputsfor m in self.model[:-1]:  # except the head partif m.f != -1:  # if not from previous layerx = y[m.f] if isinstance(m.f, int) else [x if j == -1 else y[j] for j in m.f]  # from earlier layersif profile:self._profile_one_layer(m, x, dt)if hasattr(m, 'backbone'):x = m(x)if len(x) != 5:  # 0 - 5x.insert(0, None)for index, i in enumerate(x):if index in self.save:y.append(i)else:y.append(None)x = x[-1]  # 最后一个输出传给下一层else:x = m(x)  # runy.append(x if m.i in self.save else None)  # save outputif visualize:feature_visualization(x, m.type, m.i, save_dir=visualize)if embed and m.i in embed:embeddings.append(nn.functional.adaptive_avg_pool2d(x, (1, 1)).squeeze(-1).squeeze(-1))  # flattenif m.i == max(embed):return torch.unbind(torch.cat(embeddings, 1), dim=0)head = self.model[-1]x = head([y[j] for j in head.f], batch)  # head inferencereturn x

4.8 修改八

我们将下面的s用640替换即可,这一步也是部分的主干可以不修改,但有的不修改就会报错,所以我们还是修改一下。


4.9 RT-DETR不能打印计算量问题的解决

计算的GFLOPs计算异常不打印,所以需要额外修改一处, 我们找到如下文件'ultralytics/utils/torch_utils.py'文件内有如下的代码按照如下的图片进行修改,大家看好函数就行,其中红框的640可能和你的不一样, 然后用我给的代码替换掉整个代码即可。

def get_flops(model, imgsz=640):"""Return a YOLO model's FLOPs."""try:model = de_parallel(model)p = next(model.parameters())# stride = max(int(model.stride.max()), 32) if hasattr(model, 'stride') else 32  # max stridestride = 640im = torch.empty((1, 3, stride, stride), device=p.device)  # input image in BCHW formatflops = thop.profile(deepcopy(model), inputs=[im], verbose=False)[0] / 1E9 * 2 if thop else 0  # stride GFLOPsimgsz = imgsz if isinstance(imgsz, list) else [imgsz, imgsz]  # expand if int/floatreturn flops * imgsz[0] / stride * imgsz[1] / stride  # 640x640 GFLOPsexcept Exception:return 0


4.10 可选修改

有些读者的数据集部分图片比较特殊,在验证的时候会导致形状不匹配的报错,如果大家在验证的时候报错形状不匹配的错误可以固定验证集的图片尺寸,方法如下 ->

找到下面这个文件ultralytics/models/yolo/detect/train.py然后其中有一个类是DetectionTrainer class中的build_dataset函数中的一个参数rect=mode == 'val'改为rect=False


五、MobileNetV2的yaml文件

5.1 yaml文件

大家复制下面的yaml文件,然后通过我给大家的运行代码运行即可,RT-DETR的调参部分需要后面的文章给大家讲,现在目前免费给大家看这一部分不开放。

# Ultralytics YOLO 🚀, AGPL-3.0 license
# RT-DETR-l object detection model with P3-P5 outputs. For details see https://docs.ultralytics.com/models/rtdetr# Parameters
nc: 80  # number of classes
scales: # model compound scaling constants, i.e. 'model=yolov8n-cls.yaml' will call yolov8-cls.yaml with scale 'n'# [depth, width, max_channels]l: [1.00, 1.00, 1024]backbone:# [from, repeats, module, args]- [-1, 1, MobileNetV2, []]  # 4head:- [-1, 1, Conv, [256, 1, 1, None, 1, 1, False]]  # 5 input_proj.2- [-1, 1, AIFI, [1024, 8]] # 6- [-1, 1, Conv, [256, 1, 1]]  # 7, Y5, lateral_convs.0- [-1, 1, nn.Upsample, [None, 2, 'nearest']] # 8- [3, 1, Conv, [256, 1, 1, None, 1, 1, False]]  # 9 input_proj.1- [[-2, -1], 1, Concat, [1]] # 10- [-1, 3, RepC3, [256, 0.5]]  # 11, fpn_blocks.0- [-1, 1, Conv, [256, 1, 1]]   # 12, Y4, lateral_convs.1- [-1, 1, nn.Upsample, [None, 2, 'nearest']] # 13- [2, 1, Conv, [256, 1, 1, None, 1, 1, False]]  # 14 input_proj.0- [[-2, -1], 1, Concat, [1]]  # 15 cat backbone P4- [-1, 3, RepC3, [256, 0.5]]    # X3 (16), fpn_blocks.1- [-1, 1, Conv, [256, 3, 2]]   # 17, downsample_convs.0- [[-1, 12], 1, Concat, [1]]  # 18 cat Y4- [-1, 3, RepC3, [256, 0.5]]    # F4 (19), pan_blocks.0- [-1, 1, Conv, [256, 3, 2]]   # 20, downsample_convs.1- [[-1, 7], 1, Concat, [1]]  # 21 cat Y5- [-1, 3, RepC3, [256, 0.5]]    # F5 (22), pan_blocks.1- [[16, 19, 22], 1, RTDETRDecoder, [nc, 256, 300, 4, 8, 3]]  # Detect(P3, P4, P5)


5.2 运行文件

大家可以创建一个train.py文件将下面的代码粘贴进去然后替换你的文件运行即可开始训练。

import warnings
from ultralytics import RTDETR
warnings.filterwarnings('ignore')if __name__ == '__main__':model = RTDETR('替换你想要运行的yaml文件')# model.load('') # 可以加载你的版本预训练权重model.train(data=r'替换你的数据集地址即可',cache=False,imgsz=640,epochs=72,batch=4,workers=0,device='0',project='runs/RT-DETR-train',name='exp',# amp=True)


5.3 成功训练截图

下面是成功运行的截图(确保我的改进机制是可用的),已经完成了有1个epochs的训练,图片太大截不全第2个epochs了。 


六、全文总结

从今天开始正式开始更新RT-DETR剑指论文专栏,本专栏的内容会迅速铺开,在短期呢大量更新,价格也会乘阶梯性上涨,所以想要和我一起学习RT-DETR改进,可以在前期直接关注,本文专栏旨在打造全网最好的RT-DETR专栏为想要发论文的家进行服务。

 专栏链接:RT-DETR剑指论文专栏,持续复现各种顶会内容——论文收割机RT-DETR

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

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

相关文章

4. 示例:更改监听端口

默认Spring Boot启动是监听在8080上的。 如果8080被使用&#xff0c;就会报以下错误。 这个时候可以更换一个新的端口。 server: port: 8180 然后再启动&#xff0c;启动成功并且绑定到端口8180。

开源免费的可私有化部署的白板excalidraw 详细部署教程

简介 excalidraw 是一款开源免费的虚拟白板&#xff0c;提供一个在线的实时协作白板工具&#xff0c;使用户能够创建简单的图形和图示。 excalidraw 的设计目标是提供一个易于使用的绘图工具&#xff0c;支持团队协作&#xff0c;同时具有跨平台和实时协作的功能。 简单易用&…

第十五届蓝桥杯单片机组——定时器的基本原理与应用

文章目录 一、定时器介绍二、定时器的应用2.1 定时功能❗注意事项&#xff01;❗什么是1T和12T 2.2 计数功能❗注意点 三、利用定时器计数功能实现555频率测量 一、定时器介绍 在没有钟表的时候&#xff0c;定时的方式通过有一注香的时间&#xff0c;或者一桶水的时间。前者烧香…

智慧灌区解决方案:针对典型灌区水利管理需求

​随着国家对农业水利的重视,各地积极推进智慧灌区建设,以实现对水资源的精准调度和科学化管理。下面我们针对典型灌区水利管理需求,推荐智慧灌区解决方案。 一、方案构成智慧水利解决方案- 智慧水利信息化系统-智慧水利平台-智慧水利公司 - 星创智慧水利 一、方案构成 (一)水…

WAF攻防相关知识点总结1--信息收集中的WAF触发及解决方案

什么是WAF WAF可以通过对Web应用程序的流量进行过滤和监控&#xff0c;识别并阻止潜在的安全威胁。WAF可以检测Web应用程序中的各种攻击&#xff0c;例如SQL注入、跨站点脚本攻击&#xff08;XSS&#xff09;、跨站请求伪造&#xff08;CSRF&#xff09;等&#xff0c;并采取相…

【迅搜19】扩展(二)TNTSearch和JiebaPHP方案

扩展&#xff08;二&#xff09;TNTSearch和JiebaPHP方案 搜索引擎系列的最后一篇了。既然是最后一篇&#xff0c;那么我们也轻松一点&#xff0c;直接来看一套非常有意思的纯 PHP 实现的搜索引擎及分词方案吧。这一套方案由两个组件组成&#xff0c;一个叫 TNTSearch &#xf…

基于springboot+vue的免税商品优选购物商城系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目背景…

Ubuntu18.04在线镜像仓库配置

在线镜像仓库 1、查操作系统版本 rootubuntu:~# lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 18.04.5 LTS Release: 18.04 Codename: bionic 2、原文件备份 sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak 3、查…

浅析Redis①:命令处理核心源码分析(上)

写在前面 Redis作为我们日常工作中最常使用的缓存数据库&#xff0c;其重要性不言而喻&#xff0c;作为普调开发者&#xff0c;我们在日常开发中使用Redis&#xff0c;主要聚焦于Redis的基层数据结构的命令使用&#xff0c;很少会有人对Redis的内部实现机制进行了解&#xff0c…

mac PyCharm 使用conda环境

1 使用conda创建虚拟环境 conda create -n test6 python3.9 -y conda activate test62 选择conda环境 本地 选择已经存在的conda环境 右下角会显示现在的环境。

推荐算法常见的评估指标

推荐算法评估指标比较复杂&#xff0c;可以分为离线和在线两部分。召回、粗排、精排和重排由于定位区别&#xff0c;其评估指标也会有一定区别&#xff0c;下面详细讲解。 1 召回评价体系 召回结果并不是最终推荐结果&#xff0c;其本质是为后续排序层服务的&#xff0c;故核…

高效火情监测,科技助力森林防火【数字地球开放平台】

数字地球开放平台-以卫星遥感为核心的空天信息服务开放平台 (geovisearth.com) 2019年3月30日&#xff0c;四川省凉山州木里县爆发了一场森林火灾&#xff0c;火点位于海拔3800米左右&#xff0c;地形险峻、坡度陡峭、谷深难以抵挡火势。在扑救的过程中&#xff0c;27名森林消防…