当前位置:   article > 正文

[Algorithm] BEVformer 源码细节学习&&ubuntu20.04下的环境配置&&目标跑起开源代码&&论文学习笔记_bevformer源码解析

bevformer源码解析

写在前面

计划从源码和先跑起来入手,随后分模块逐步学习。期间分享自己的困惑,有趣高效的语法现象。
之前学习了机器学习和神经网络(RNN) pytorch使用等相关知识,进行了两个demo的实战

如果对安装不感兴趣,欢迎大佬们阅读第二步交流指点。

学习资源 (不断更新)

第一步目标把开源代码跑起来

环境准备

https://zhuanlan.zhihu.com/p/424817205
可以按照以上内容先安装号显卡驱动、Cuda和配置路径。
显卡驱动安装后 我用smi显示不出来显卡信息。

在这里插入图片描述
我明明在自带的software中心中的driver选择了驱动,但是smi命令找不到显卡信息,最后通过gpt查询,原来显卡驱动一直没有加载。因为我开起来secure boot的签名验证。关闭secure boot 就好了。 在这里插入图片描述

BEV 相关环境准备

首先 conda 和pip换源操作可以参考:https://blog.csdn.net/h904798869/article/details/131719404
或者单用

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple +包名
  • 1

阿里云镜像:https://mirrors.aliyun.com/pypi/simple/
清华大学镜像:https://pypi.tuna.tsinghua.edu.cn/simple/
安装建议严格按照文档说明来,其中mmdection的内容建议按照maptr的来
BEVFormer
MapTR

其中:

  • Building wheel for mmcv-full (setup.py) … / 这一步很慢,安心等待
  • mmdection 用maptr的方法安装
  • https://github.com/zhiqi-li/storage/releases 这里我是用Windows的梯直接去仓库拿的,贼快
  • 包括预训练参数也可以直接挂梯子作者仓库去拿download
  • 安装cuda,cuda是系统级的东西不会在自己conda虚拟环境下,安装完成后别忘记添加路径,同时如果驱动安装好了就不必安装cuda自带的驱动,这里都有写明白https://blog.csdn.net/h904798869/article/details/131719404

记录我安装的几个问题 :

第一个大报错 是mmdetection编译报错,

查询了很久 发现根源是我的系统找不到有效的cuda
这里贴两个帖子,他们在讲解 pytorch cuda 和显卡驱动的 辩证关系
基本就是在排查 GPU驱动 Cuda版本 Pytorch版本之间的问题
https://zhuanlan.zhihu.com/p/658800083
https://zhuanlan.zhihu.com/p/91334380 我认为这个讲的最好
https://blog.csdn.net/qq_41094058/article/details/116207333
大家也可以使用我下面的代码验证以下
驱动安装说明
用nvcc -v可以看到cuda版本
在conda list中确认其他库的版本
lspci | grep -i nvidia 查看显卡型号

import torch

print('CUDA 可用:', torch.cuda.is_available())
if torch.cuda.is_available():
    print('可用的 CUDA 设备数:', torch.cuda.device_count())
    print('当前 CUDA 设备索引:', torch.cuda.current_device())
    print('当前 CUDA 设备名称:', torch.cuda.get_device_name(torch.cuda.current_device()))
else:
    print('CUDA 不可用')
import torch, torchvision
print(torch.__version__, torch.cuda.is_available())
# Pytorch 实际使用的运行时的 cuda 目录
import torch.utils.cpp_extension
print(torch.utils.cpp_extension.CUDA_HOME)
# 编译该 Pytorch release 版本时使用的 cuda 版本
import torch
print(torch.version.cuda )

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

如果pytorch成功导入了,但是出现false 则说明cuda设备不可用,可以去NVidia官网自动查找对应驱动https://www.nvidia.com/Download/index.aspx
以下是cuda的安装地址
https://developer.nvidia.com/cuda-downloads?target_os=Linux&target_arch=x86_64&Distribution=Ubuntu&target_version=22.04&target_type=runfile_local

此外也可以使用ubantu系统自己查到的驱动
在这里插入图片描述或者也可以使用

如此我可以使用上面的代码打印出内容了
在这里插入图片描述
我的nvidia-smi命令也可以正常输出了
在这里插入图片描述但依然有报错

我cuda toolkit和 nvidia driver是版本是可以匹配的,但cuda版本太高了我去
在这里插入图片描述然后重新严格安装配环境教程安装了cuda,不追求自己研究了

  • 第二个大报错 安装 Detectron2
python -m pip install 'git+https://github.com/facebookresearch/detectron2.git'
  • 1

长时间等待而后报错

git clone git@github.com:facebookresearch/detectron2.git

  • 1
  • 2

然后本地安装

pip install .
  • 1

第一次接触的库的笔记:
mmcv-full是一个针对计算机视觉任务的开源工具库
MMDetection(Masked Object Detection)是一个开源计算机视觉库,用于目标检测任务。它提供了丰富的目标检测算法和模型,包括 Faster R-CNN、Mask R-CNN 等
MMsegmentation(Image Segmentation)是一个开源计算机视觉库,用于图像分割任务。它包含了多种图像分割算法和模型,如 U-Net、DeepLabV3 等。
Detectron2 是由Facebook AI Research(FAIR)开发的开源目标检测库。它是原始 Detectron 库的继任者,为构建计算机视觉模型提供了灵活和模块化的框架,特别适用于目标检测和实例分割等任务

数据集下载

下载车身can数据、车辆位姿数据、地图场景数据、Camera Lidar传感器数据
在这里插入图片描述把map解压后的三个文件放入到mini的maps文件夹中

在这里插入图片描述展示结构如下
在这里插入图片描述在这里插入图片描述在这里插入图片描述

将data放入beformer下,然后

python tools/create_data.py nuscenes --root-path ./data/nuscenes --out-dir ./data/nuscenes --extra-tag nuscenes --version v1.0-mini --canbus ./data
  • 1

在其后按照提示和教程缺啥补啥

在这里插入图片描述

报错就把这里将所有的data.converter 前面的tools去掉

训练 测试 画图

BEV复现教程 结果如下,看起来还不太准确。

环境和数据整理完毕后,按照教程进行,我的显卡可以跑small 和Tiny,把base替换成一位上即可。
在这里插入图片描述

第二步开始论文详细阅读和代码解读

第二步论文阅读

论文初步阅读

在这里插入图片描述
两个注意力机制为代码和文章重点解读部分

在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

论文细节阅读

代码结构

在这里插入图片描述模型结构使用config管理参数的方式,在bevformer_XXX.py中是参数,具体的模型搭建其实在bevformer_head.py中 组件在modules中可以找到
在这里插入图片描述

再次整理细节重点学习 (全代码阅读 TSA SCA MSD,后面两节按照执行顺序总结的代码整理较乱,日后缓慢更新 )

重点是Encoder中的 BEVFormerLayer,有作者提出的Temporalsellfattention SpatialCrossAttention 和可变形注意力

由于代码使用了注册器,不太好直接寻找跳转链路,可以使用断点的方式按照顺序阅读。这里不做顺序阅读,而是将multi_scale_deformable_attn_function.py、spatial_cross_attention.py、temporal_self_attention.py的内容,有价值的每一句做注释解读,贴在下面。拓展语法和概念等则写于本章最后面。

temporal_self_attention.py 的解读
# ---------------------------------------------
# Copyright (c) OpenMMLab. All rights reserved.
# ---------------------------------------------
#  Modified by Zhiqi Li
# ---------------------------------------------

from projects.mmdet3d_plugin.models.utils.bricks import run_time
from .multi_scale_deformable_attn_function import MultiScaleDeformableAttnFunction_fp32
from mmcv.ops.multi_scale_deform_attn import multi_scale_deformable_attn_pytorch
import warnings
import torch
import torch.nn as nn
from mmcv.cnn import xavier_init, constant_init
from mmcv.cnn.bricks.registry import ATTENTION
import math
from mmcv.runner.base_module import BaseModule, ModuleList, Sequential
from mmcv.utils import (ConfigDict, build_from_cfg, deprecated_api_warning,
                        to_2tuple)

from mmcv.utils import ext_loader
ext_module = ext_loader.load_ext(
    '_ext', ['ms_deform_attn_backward', 'ms_deform_attn_forward'])


@ATTENTION.register_module()
class TemporalSelfAttention(BaseModule):
    """An attention module used in BEVFormer based on Deformable-Detr.

    `Deformable DETR: Deformable Transformers for End-to-End Object Detection.
    <https://arxiv.org/pdf/2010.04159.pdf>`_.

    Args:
        embed_dims (int): The embedding dimension of Attention.
            Default: 256.
        num_heads (int): Parallel attention heads. Default: 64.
        num_levels (int): The number of feature map used in
            Attention. Default: 4.
        num_points (int): The number of sampling points for
            each query in each head. Default: 4.
        im2col_step (int): The step used in image_to_column.
            Default: 64.
        dropout (float): A Dropout layer on `inp_identity`.
            Default: 0.1.
        batch_first (bool): Key, Query and Value are shape of
            (batch, n, embed_dim)
            or (n, batch, embed_dim). Default to True.
        norm_cfg (dict): Config dict for normalization layer.
            Default: None.
        init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization.
            Default: None.
        num_bev_queue (int): In this version, we only use one history BEV and one currenct BEV.
         the length of BEV queue is 2.
    """
# embed_dims (int): 注意力机制的嵌入维度。
# num_heads (int): 注意力机制中并行的注意头数。
# num_levels (int): 使用的特征图的数量。
# num_points (int): 每个注意头中每个查询点的采样点数。
# im2col_step (int): 在图像到列矩阵转换中使用的步长。
# dropout (float): 应用于 inp_identity 的 Dropout 层的丢弃率。
# batch_first (bool): Key、Query 和 Value 的形状是否为 (batch, n, embed_dim) 或 (n, batch, embed_dim)。
# norm_cfg (dict): 用于规范化层的配置字典。
# init_cfg (obj: mmcv.ConfigDict): 用于初始化的配置对象。
# num_bev_queue (int): 在这个版本中,我们只使用一个历史 Bird's Eye View(BEV)和一个当前 BEV。BEV 队列的长度为 2。

    def __init__(self,
                 embed_dims=256,
                 num_heads=8,
                 num_levels=4,
                 num_points=4,
                 num_bev_queue=2,
                 im2col_step=64,
                 dropout=0.1,
                 batch_first=True,
                 norm_cfg=None,
                 init_cfg=None):

        super().__init__(init_cfg)
        if embed_dims % num_heads != 0:#检查 embed_dims 特征维度是否可以被 num_heads 多头数量 整除,否则引发错误。
            raise ValueError(f'embed_dims must be divisible by num_heads, '
                             f'but got {embed_dims} and {num_heads}')
        dim_per_head = embed_dims // num_heads # 多头注意力量 划分特征
        self.norm_cfg = norm_cfg
        self.dropout = nn.Dropout(dropout)
        self.batch_first = batch_first
        self.fp16_enabled = False

        # you'd better set dim_per_head to a power of 2
        # which is more efficient in the CUDA implementation
        def _is_power_of_2(n):
            if (not isinstance(n, int)) or (n < 0):
                raise ValueError(
                    'invalid input for _is_power_of_2: {} (type: {})'.format(
                        n, type(n)))
            return (n & (n - 1) == 0) and n != 0

        if not _is_power_of_2(dim_per_head):
            warnings.warn(
                "You'd better set embed_dims in "
                'MultiScaleDeformAttention to make '
                'the dimension of each attention head a power of 2 '
                'which is more efficient in our CUDA implementation.')

        self.im2col_step = im2col_step
        self.embed_dims = embed_dims
        self.num_levels = num_levels
        self.num_heads = num_heads
        self.num_points = num_points
        self.num_bev_queue = num_bev_queue
        # 用于生成采样偏移的线性层。
        self.sampling_offsets = nn.Linear(
            embed_dims*self.num_bev_queue, num_bev_queue*num_heads * num_levels * num_points * 2)
        # 用于生成注意力权重的线性层
        self.attention_weights = nn.Linear(embed_dims*self.num_bev_queue,                                 
         num_bev_queue*num_heads * num_levels * num_points)
                 #: 用于投影值的线性层。 
        self.value_proj = nn.Linear(embed_dims, embed_dims)
        #用于输出投影的线性层。
        self.output_proj = nn.Linear(embed_dims, embed_dims)
        self.init_weights()

    def init_weights(self):
        """Default initialization for Parameters of Module."""
        constant_init(self.sampling_offsets, 0.)
        thetas = torch.arange(
            self.num_heads,
            dtype=torch.float32) * (2.0 * math.pi / self.num_heads)
        grid_init = torch.stack([thetas.cos(), thetas.sin()], -1)
        grid_init = (grid_init /
                     grid_init.abs().max(-1, keepdim=True)[0]).view(
            self.num_heads, 1, 1,
            2).repeat(1, self.num_levels*self.num_bev_queue, self.num_points, 1)

        for i in range(self.num_points):
            grid_init[:, :, i, :] *= i + 1

        self.sampling_offsets.bias.data = grid_init.view(-1)
        #用于将参数初始化为常量值。
        constant_init(self.attention_weights, val=0., bias=0.)
        #用于使用 Xavier 初始化参数
        xavier_init(self.value_proj, distribution='uniform', bias=0.)
        xavier_init(self.output_proj, distribution='uniform', bias=0.)
        self._is_init = True

    def forward(self,
                query,
                key=None,
                value=None,
                identity=None,
                query_pos=None,
                key_padding_mask=None,
                reference_points=None,
                spatial_shapes=None,
                level_start_index=None,
                flag='decoder',

                **kwargs):
        """Forward Function of MultiScaleDeformAttention.

        Args:
            query (Tensor): Query of Transformer with shape
                (num_query, bs, embed_dims).
            key (Tensor): The key tensor with shape
                `(num_key, bs, embed_dims)`.
            value (Tensor): The value tensor with shape
                `(num_key, bs, embed_dims)`.
            identity (Tensor): The tensor used for addition, with the
                same shape as `query`. Default None. If None,
                `query` will be used.
            query_pos (Tensor): The positional encoding for `query`.
                Default: None.
            key_pos (Tensor): The positional encoding for `key`. Default
                None.
            reference_points (Tensor):  The normalized reference
                points with shape (bs, num_query, num_levels, 2),
                all elements is range in [0, 1], top-left (0,0),
                bottom-right (1, 1), including padding area.
                or (N, Length_{query}, num_levels, 4), add
                additional two dimensions is (w, h) to
                form reference boxes.
            key_padding_mask (Tensor): ByteTensor for `query`, with
                shape [bs, num_key].
            spatial_shapes (Tensor): Spatial shape of features in
                different levels. With shape (num_levels, 2),
                last dimension represents (h, w).
            level_start_index (Tensor): The start index of each level.
                A tensor has shape ``(num_levels, )`` and can be represented
                as [0, h_0*w_0, h_0*w_0+h_1*w_1, ...].

        Returns:
             Tensor: forwarded results with shape [num_query, bs, embed_dims].
        """

# 输入参数:
# query (Tensor): Transformer 的查询张量,形状为 (num_query, bs, embed_dims)。
# key (Tensor): 键张量,形状为 (num_key, bs, embed_dims)。
# value (Tensor): 值张量,形状为 (num_key, bs, embed_dims)。
# identity (Tensor): 用于加法的张量,与 query 形状相同。如果为None,将使用 query。
# query_pos (Tensor): 用于 query 的位置编码。
# key_padding_mask (Tensor): 用于 query 的 ByteTensor,形状为 [bs, num_key]。
# reference_points (Tensor): 归一化的参考点,形状为 (bs, num_query, num_levels, 2),或 (N, Length_{query}, num_levels, 4)。这用于变形注意力。
# spatial_shapes (Tensor): 不同层级中特征的空间形状,形状为 (num_levels, 2),其中最后一个维度表示 (h, w)。
# level_start_index (Tensor): 每个层级的起始索引,形状为 (num_levels,)。
# 输出:
# 返回值: 形状为 [num_query, bs, embed_dims] 的张量,表示前向传播的结果。

# flag: 一个字符串参数,可能用于指定这个操作是在编码器(encoder)还是解码器(decoder)中。

        if value is None:
            assert self.batch_first
            bs, len_bev, c = query.shape # (num_query, bs, embed_dims)
            value = torch.stack([query, query], 1).reshape(bs*2, len_bev, c)
        #获取 query 张量的形状信息,并利用 torch.stack 和 reshape 函数将其复制为 value 张量
            # value = torch.cat([query, query], 0)

        if identity is None:
            identity = query
        if query_pos is not None:
            query = query + query_pos
            # 将位置编码加入到q中
        if not self.batch_first:
            # change to (bs, num_query ,embed_dims)
            query = query.permute(1, 0, 2)
            value = value.permute(1, 0, 2)
            #按照惯例整理顺序

        bs,  num_query, embed_dims = query.shape
        _, num_value, _ = value.shape# (num_key, bs, embed_dims)
        assert (spatial_shapes[:, 0] * spatial_shapes[:, 1]).sum() == num_value
        # (num_levels, 2),其中最后一个维度表示 (h, w) ???没看懂
        assert self.num_bev_queue == 2

        query = torch.cat([value[:bs], query], -1)
        value = self.value_proj(value)
#将 query 连接到 value 的前部分,并对 value 应用 self.value_proj
# 我的理解,由于value是上一时刻和上上bev的信息,如此增加模型在进行自注意力计算时对上下文的理解,而线性变换
#将输入数据映射到一个更高维度的空间,以便提高模型的表示能力
#gpt说如果一个查询需要依赖较远的位置的信息,通过将值信息添加到查询前面,
# 可以使得模型更容易捕捉到这些长距离的依赖关系,提高了模型对整个序列的建模能力。
        if key_padding_mask is not None:
            value = value.masked_fill(key_padding_mask[..., None], 0.0)
#如果存在 key_padding_mask,则使用 masked_fill 将 value 进行填充
        value = value.reshape(bs*self.num_bev_queue,
                              num_value, self.num_heads, -1)

        sampling_offsets = self.sampling_offsets(query)
        sampling_offsets = sampling_offsets.view(
            bs, num_query, self.num_heads,  self.num_bev_queue, self.num_levels, self.num_points, 2)
        attention_weights = self.attention_weights(query).view(
            bs, num_query,  self.num_heads, self.num_bev_queue, self.num_levels * self.num_points)
        attention_weights = attention_weights.softmax(-1)
# 利用线性层计算采样offset和权重,并且把权重归一化
        attention_weights = attention_weights.view(bs, num_query,
                                                   self.num_heads,
                                                   self.num_bev_queue,
                                                   self.num_levels,
                                                   self.num_points)

        attention_weights = attention_weights.permute(0, 3, 1, 2, 4, 5)\
            .reshape(bs*self.num_bev_queue, num_query, self.num_heads, self.num_levels, self.num_points).contiguous()
        sampling_offsets = sampling_offsets.permute(0, 3, 1, 2, 4, 5, 6)\
            .reshape(bs*self.num_bev_queue, num_query, self.num_heads, self.num_levels, self.num_points, 2)
#根据 reference_points 的形状不同(2 或 4),计算 sampling_locations
        if reference_points.shape[-1] == 2:
            offset_normalizer = torch.stack(
                [spatial_shapes[..., 1], spatial_shapes[..., 0]], -1)
            sampling_locations = reference_points[:, :, None, :, None, :] \
                + sampling_offsets \
                / offset_normalizer[None, None, None, :, None, :]
            #NONE插入维度
#将采样偏移 sampling_offsets 转化为相对于输入空间的实际位置。
        elif reference_points.shape[-1] == 4:
            sampling_locations = reference_points[:, :, None, :, None, :2] \
                + sampling_offsets / self.num_points \
                * reference_points[:, :, None, :, None, 2:] \
                * 0.5
        else:
            raise ValueError(
                f'Last dim of reference_points must be'
                f' 2 or 4, but get {reference_points.shape[-1]} instead.')
        if torch.cuda.is_available() and value.is_cuda:

            # using fp16 deformable attention is unstable because it performs many sum operations
            if value.dtype == torch.float16:
                MultiScaleDeformableAttnFunction = MultiScaleDeformableAttnFunction_fp32
            else:
                MultiScaleDeformableAttnFunction = MultiScaleDeformableAttnFunction_fp32
            output = MultiScaleDeformableAttnFunction.apply(
                value, spatial_shapes, level_start_index, sampling_locations,
                attention_weights, self.im2col_step)
        else:

            output = multi_scale_deformable_attn_pytorch(
                value, spatial_shapes, sampling_locations, attention_weights)

        # output shape (bs*num_bev_queue, num_query, embed_dims)
        # (bs*num_bev_queue, num_query, embed_dims)-> (num_query, embed_dims, bs*num_bev_queue)
        output = output.permute(1, 2, 0)

        # fuse history value and current value
        # (num_query, embed_dims, bs*num_bev_queue)-> (num_query, embed_dims, bs, num_bev_queue)
        output = output.view(num_query, embed_dims, bs, self.num_bev_queue)
        output = output.mean(-1)
#计算 output 张量中每个元素在最后一个维度上的均值
        # (num_query, embed_dims, bs)-> (bs, num_query, embed_dims)
        output = output.permute(2, 0, 1)

        output = self.output_proj(output)
        # out再整一次变换

        if not self.batch_first:
            output = output.permute(1, 0, 2)

        return self.dropout(output) + identity
    # 加一次dropout防止过拟合,同时引入残差连接或者跳跃连接,从而帮助梯度传播以及加速模型的训练
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
SCA

# ---------------------------------------------
# Copyright (c) OpenMMLab. All rights reserved.
# ---------------------------------------------
#  Modified by Zhiqi Li
# ---------------------------------------------

from mmcv.ops.multi_scale_deform_attn import multi_scale_deformable_attn_pytorch
import warnings
import torch
import torch.nn as nn
import torch.nn.functional as F
from mmcv.cnn import xavier_init, constant_init
from mmcv.cnn.bricks.registry import (ATTENTION,
                                      TRANSFORMER_LAYER,
                                      TRANSFORMER_LAYER_SEQUENCE)
from mmcv.cnn.bricks.transformer import build_attention
import math
from mmcv.runner import force_fp32, auto_fp16

from mmcv.runner.base_module import BaseModule, ModuleList, Sequential

from mmcv.utils import ext_loader
from .multi_scale_deformable_attn_function import MultiScaleDeformableAttnFunction_fp32, \
    MultiScaleDeformableAttnFunction_fp16
from projects.mmdet3d_plugin.models.utils.bricks import run_time
ext_module = ext_loader.load_ext(
    '_ext', ['ms_deform_attn_backward', 'ms_deform_attn_forward'])


@ATTENTION.register_module()
class SpatialCrossAttention(BaseModule):
    """An attention module used in BEVFormer.
    Args:
        embed_dims (int): The embedding dimension of Attention.
            Default: 256. 是bev线性变换后注意里特征数量
        num_cams (int): The number of cameras 摄像头的数量
        dropout (float): A Dropout layer on `inp_residual`.
            Default: 0.. 为了防止过拟合dropout层参数
        init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization.
            Default: None. 初始化参数
        deformable_attention: (dict): The config for the deformable attention used in SCA. SCA的可变性注意力参数
    """
    

    def __init__(self,
                 embed_dims=256,
                 num_cams=6,
                 pc_range=None,
                 dropout=0.1,
                 init_cfg=None,
                 batch_first=False,
                 deformable_attention=dict(
                     type='MSDeformableAttention3D',
                     embed_dims=256,
                     num_levels=4),
                 **kwargs
                 ):
        super(SpatialCrossAttention, self).__init__(init_cfg)

        self.init_cfg = init_cfg
        self.dropout = nn.Dropout(dropout)
        self.pc_range = pc_range
        self.fp16_enabled = False
        self.deformable_attention = build_attention(deformable_attention)
        self.embed_dims = embed_dims
        self.num_cams = num_cams
        self.output_proj = nn.Linear(embed_dims, embed_dims)
        self.batch_first = batch_first
        self.init_weight()

    def init_weight(self):
        """Default initialization for Parameters of Module."""
        xavier_init(self.output_proj, distribution='uniform', bias=0.)

        #以上初始化和TSA基本一致,没有笔记内容
    
    @force_fp32(apply_to=('query', 'key', 'value', 'query_pos', 'reference_points_cam'))
    def forward(self,
                query,
                key,
                value,
                residual=None,
                query_pos=None,
                key_padding_mask=None,
                reference_points=None,
                spatial_shapes=None,
                reference_points_cam=None,
                bev_mask=None,
                level_start_index=None,
                flag='encoder',
                **kwargs):
        """Forward Function of Detr3DCrossAtten.
        Args:
            query (Tensor): Query of Transformer with shape
                (num_query, bs, embed_dims).
                #Q
                #网上解释num_query类似于 DETR里面的 object_queries,也就是最多预测多少个目标
            key (Tensor): The key tensor with shape
                `(num_key, bs, embed_dims)`.
                # k
            value (Tensor): The value tensor with shape
                `(num_key, bs, embed_dims)`. (B, N, C, H, W)
            residual (Tensor): The tensor used for addition, with the
                same shape as `x`. Default None. If None, `x` will be used.
           #残差
             query_pos (Tensor): The positional encoding for `query`.
                Default: None.
            key_pos (Tensor): The positional encoding for  `key`. Default
                None.
                # q 和 k的位置编码
            reference_points (Tensor):  The normalized reference
                points with shape (bs, num_query, 4),
                all elements is range in [0, 1], top-left (0,0),
                bottom-right (1, 1), including padding area.
                or (N, Length_{query}, num_levels, 4), add
                additional two dimensions is (w, h) to
                form reference boxes.
                # 参考点归一化
                数据标准化( Standardization )是将数据转换为均值为0,方差为1的数据,也就是将数据按比例缩放,
                使得其分布具有标准正态分布。 数据归一化( Normalization )
                是将数据转换为满足0≤x≤1的数据,也就是将数据缩放到 [0,1]区间。
                #num_levels:The number of feature map used in
            Attention 被用于注意力的特征地图的数量
            key_padding_mask (Tensor): ByteTensor for `query`, with
                shape [bs, num_key].
                #k 注意力掩码
            spatial_shapes (Tensor): Spatial shape of features in
                different level. With shape  (num_levels, 2),
                last dimension represent (h, w).
                #空间形状,在不同level的特征的空间形状,最后一个维度2是(h,w)
            level_start_index (Tensor): The start index of each level.
                A tensor has shape (num_levels) and can be represented
                as [0, h_0*w_0, h_0*w_0+h_1*w_1, ...].
                # 开始遍历的index
        Returns:
             Tensor: forwarded results with shape [num_query, bs, embed_dims].
             # 返回查询数量 批次 特征数量
        """

        if key is None:
            key = query
        if value is None:
            value = key

        if residual is None:
            inp_residual = query
            # 残差链接网络传输值被初始化为query
            slots = torch.zeros_like(query)
            # 以 query的shape初始化 slot
        if query_pos is not None:
            query = query + query_pos
            # 同样地把线性层学习到的query位置编码和query叠加到一起

        bs, num_query, _ = query.size()#(num_query, bs, embed_dims)
        #这里是不是错了? 在input地方的备注维度顺序不同?


        D = reference_points_cam.size(3)
        indexes = []
        for i, mask_per_img in enumerate(bev_mask):
            index_query_per_img = mask_per_img[0].sum(-1).nonzero().squeeze(-1)
            indexes.append(index_query_per_img)
        max_len = max([len(each) for each in indexes])
        # 每个特征点对应一个mask点,特征点的值为false,就可以将其在注意力中抛弃
        # 举例子说明:如果mask_per_img =m torch.tensor([[1, 0, 1, 0],[1, 1, 0, 1]])      
        # sum_per_img = mask_per_img.sum(-1) 得到tensor[2,3]
        # nonzero_indices = sum_per_img.nonzero() 得到tensor [[0],[1]]
        # index_query_per_img = nonzero_indices.squeeze(-1)去除上一步操作后多出来的维度
        # 得到[0,1]
        # 最后用indexes 储存计算好的indices
        # each camera only interacts with its corresponding BEV queries. This step can  greatly save GPU memory.
        queries_rebatch = query.new_zeros(
            [bs, self.num_cams, max_len, self.embed_dims])
        reference_points_rebatch = reference_points_cam.new_zeros(
            [bs, self.num_cams, max_len, D, 2])
        
        for j in range(bs):
            for i, reference_points_per_img in enumerate(reference_points_cam):   
                index_query_per_img = indexes[i]
                queries_rebatch[j, i, :len(index_query_per_img)] = query[j, index_query_per_img]
                reference_points_rebatch[j, i, :len(index_query_per_img)] = reference_points_per_img[j, index_query_per_img]
        #重新计算q和reference point 根据上一步计算的index
        num_cams, l, bs, embed_dims = key.shape

        key = key.permute(2, 0, 1, 3).reshape(
            bs * self.num_cams, l, self.embed_dims)
        value = value.permute(2, 0, 1, 3).reshape(
            bs * self.num_cams, l, self.embed_dims)

        queries = self.deformable_attention(query=queries_rebatch.view(bs*self.num_cams, max_len, self.embed_dims), key=key, value=value,
                                            reference_points=reference_points_rebatch.view(bs*self.num_cams, max_len, D, 2), spatial_shapes=spatial_shapes,
                                            level_start_index=level_start_index).view(bs, self.num_cams, max_len, self.embed_dims)
        # 使用可变形注意力
        for j in range(bs):
            for i, index_query_per_img in enumerate(indexes):
                slots[j, index_query_per_img] += queries[j, i, :len(index_query_per_img)]
# 用计算好的 queries和indexed更新slots
        count = bev_mask.sum(-1) > 0
# 将bev_mask 按照最后一个维度相加 判断是否大于0 结果储存在count中
        count = count.permute(1, 2, 0).sum(-1)
        count = torch.clamp(count, min=1.0) # 将count的元素的 最小值设为1
        slots = slots / count[..., None]
        slots = self.output_proj(slots)

        return self.dropout(slots) + inp_residual # [num_query, bs, embed_dims].


@ATTENTION.register_module()
class MSDeformableAttention3D(BaseModule):
    """An attention module used in BEVFormer based on Deformable-Detr.
    `Deformable DETR: Deformable Transformers for End-to-End Object Detection.
    <https://arxiv.org/pdf/2010.04159.pdf>`_.
    Args:
        embed_dims (int): The embedding dimension of Attention.
            Default: 256.
        num_heads (int): Parallel attention heads. Default: 64.
        num_levels (int): The number of feature map used in
            Attention. Default: 4.
        num_points (int): The number of sampling points for
            each query in each head. Default: 4.
        im2col_step (int): The step used in image_to_column.
            Default: 64.
        dropout (float): A Dropout layer on `inp_identity`.
            Default: 0.1.
        batch_first (bool): Key, Query and Value are shape of
            (batch, n, embed_dim)
            or (n, batch, embed_dim). Default to False.
        norm_cfg (dict): Config dict for normalization layer.
            Default: None.
        init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization.
            Default: None.
    """
# embed_dims(嵌入维度):注意力机制中的嵌入维度。默认为256,影响了注意力机制中的向量表示维度。

# num_heads(注意力头数):并行的注意力头数。默认为64,控制了注意力机制中多头注意力的并行数量。

# num_levels(特征图数量):注意力中使用的特征图数量。默认为4,影响了注意力机制中特征图的层级数。

# num_points(采样点数):每个注意力头中每个查询点的采样点数。默认为4,决定了每个头部的注意力机制对查询点进行采样的数量。

# im2col_step(image_to_column 步长):在 image_to_column 操作中使用的步长。

# dropout(丢弃率):应用于 inp_identity 的 Dropout 层的丢弃率。默认为0.1,用于在训练中随机丢弃输入张量中的一部分元素,以防止过拟合。

# batch_first(批次优先):用于指定输入张量的维度顺序。如果为 True,表示输入张量的形状是(batch, n, embed_dim),否则为 (n, batch, embed_dim)。默认为 False。

# norm_cfg(归一化层配置):用于归一化层的配置字典。默认为 None,

# init_cfg(初始化配置):初始化配置的配置对象。
    def __init__(self,
                 embed_dims=256,
                 num_heads=8,
                 num_levels=4,
                 num_points=8,
                 im2col_step=64,
                 dropout=0.1,
                 batch_first=True,
                 norm_cfg=None,
                 init_cfg=None):
        super().__init__(init_cfg)
        if embed_dims % num_heads != 0:
            raise ValueError(f'embed_dims must be divisible by num_heads, '
                             f'but got {embed_dims} and {num_heads}')
        dim_per_head = embed_dims // num_heads # 每一个头的特征数量
        self.norm_cfg = norm_cfg
        self.batch_first = batch_first
        self.output_proj = None
        self.fp16_enabled = False

        # you'd better set dim_per_head to a power of 2
        # which is more efficient in the CUDA implementation
        def _is_power_of_2(n):
            if (not isinstance(n, int)) or (n < 0):
                raise ValueError(
                    'invalid input for _is_power_of_2: {} (type: {})'.format(
                        n, type(n)))
            return (n & (n - 1) == 0) and n != 0

        if not _is_power_of_2(dim_per_head):
            warnings.warn(
                "You'd better set embed_dims in "
                'MultiScaleDeformAttention to make '
                'the dimension of each attention head a power of 2 '
                'which is more efficient in our CUDA implementation.')

        self.im2col_step = im2col_step
        self.embed_dims = embed_dims
        self.num_levels = num_levels
        self.num_heads = num_heads
        self.num_points = num_points
        self.sampling_offsets = nn.Linear(
            embed_dims, num_heads * num_levels * num_points * 2)
        self.attention_weights = nn.Linear(embed_dims,
                                           num_heads * num_levels * num_points)
        self.value_proj = nn.Linear(embed_dims, embed_dims)
# 同 TSA的注意力
        self.init_weights()

    def init_weights(self):
        """Default initialization for Parameters of Module."""
        constant_init(self.sampling_offsets, 0.)
        #极坐标网格构建
        # 创建一个0到2pi 等分为8分的tensor
        thetas = torch.arange(
            self.num_heads,
            dtype=torch.float32) * (2.0 * math.pi / self.num_heads)
        # 初始化grid
        #利用三角函数计算每个角度对应的余弦和正弦值,然后通过torch.stack在最后一个维度
        #将这两个值堆叠在一起形成一个形状为(num_heads, 2)的张量。
        # 这个张量的每一行表示一个角度对应的极坐标中的(x, y)坐标,
        # 使用grid_init.abs().max(-1, keepdim=True)[0]计算每个行向量的绝对值中的最大值,
        # 并在最后一个维度上保持维度。然后,将grid_init除以这个最大值,实现归一化。
        # 最后,通过view函数将结果变形成形状为(num_heads, 1, 1, 2)的张量

        #最终的输出是一个形状为(num_heads, 1, 1, 2)的张量,
        # 表示了num_heads个头部的极坐标网格。每个头部的网格用一个(x, y)坐标表示,
        # 这个坐标在单位圆上,且在整个num_heads中均匀分布

        grid_init = torch.stack([thetas.cos(), thetas.sin()], -1)
        grid_init = (grid_init /
                     grid_init.abs().max(-1, keepdim=True)[0]).view(
            self.num_heads, 1, 1,
            2).repeat(1, self.num_levels, self.num_points, 1)
## 遍历第二个维度上,通过这种方式记录是第几个采样点的极坐标        
        for i in range(self.num_points):
            grid_init[:, :, i, :] *= i + 1

#grid_init.view(-1) 将 grid_init 张量展平为一个一维张量
        self.sampling_offsets.bias.data = grid_init.view(-1)
        constant_init(self.attention_weights, val=0., bias=0.)
        xavier_init(self.value_proj, distribution='uniform', bias=0.)
        xavier_init(self.output_proj, distribution='uniform', bias=0.)
        self._is_init = True

    def forward(self,
                query,
                key=None,
                value=None,
                identity=None,
                query_pos=None,
                key_padding_mask=None,
                reference_points=None,
                spatial_shapes=None,
                level_start_index=None,
                **kwargs):
        """Forward Function of MultiScaleDeformAttention.
        Args:
            query (Tensor): Query of Transformer with shape
                ( bs, num_query, embed_dims).
            key (Tensor): The key tensor with shape
                `(bs, num_key,  embed_dims)`.
            value (Tensor): The value tensor with shape
                `(bs, num_key,  embed_dims)`.
            identity (Tensor): The tensor used for addition, with the
                same shape as `query`. Default None. If None,
                `query` will be used.
            query_pos (Tensor): The positional encoding for `query`.
                Default: None.
            key_pos (Tensor): The positional encoding for `key`. Default
                None.
            reference_points (Tensor):  The normalized reference
                points with shape (bs, num_query, num_levels, 2),
                all elements is range in [0, 1], top-left (0,0),
                bottom-right (1, 1), including padding area.
                or (N, Length_{query}, num_levels, 4), add
                additional two dimensions is (w, h) to
                form reference boxes.
            key_padding_mask (Tensor): ByteTensor for `query`, with
                shape [bs, num_key].
            spatial_shapes (Tensor): Spatial shape of features in
                different levels. With shape (num_levels, 2),
                last dimension represents (h, w).
            level_start_index (Tensor): The start index of each level.
                A tensor has shape ``(num_levels, )`` and can be represented
                as [0, h_0*w_0, h_0*w_0+h_1*w_1, ...].
        Returns:
             Tensor: forwarded results with shape [num_query, bs, embed_dims].
        """

        if value is None:
            value = query
        if identity is None:
            identity = query
        if query_pos is not None:
            query = query + query_pos

        if not self.batch_first:
            # change to (bs, num_query ,embed_dims)
            query = query.permute(1, 0, 2)
            value = value.permute(1, 0, 2)

        bs, num_query, _ = query.shape
        bs, num_value, _ = value.shape
        assert (spatial_shapes[:, 0] * spatial_shapes[:, 1]).sum() == num_value

        value = self.value_proj(value)
        if key_padding_mask is not None:
            value = value.masked_fill(key_padding_mask[..., None], 0.0)
        value = value.view(bs, num_value, self.num_heads, -1)
        sampling_offsets = self.sampling_offsets(query).view(
            bs, num_query, self.num_heads, self.num_levels, self.num_points, 2)
        attention_weights = self.attention_weights(query).view(
            bs, num_query, self.num_heads, self.num_levels * self.num_points)

        attention_weights = attention_weights.softmax(-1)

        attention_weights = attention_weights.view(bs, num_query,
                                                   self.num_heads,
                                                   self.num_levels,
                                                   self.num_points)

        if reference_points.shape[-1] == 2:
            """
            For each BEV query, it owns `num_Z_anchors` in 3D space that having different heights.
            After proejcting, each BEV query has `num_Z_anchors` reference points in each 2D image.
            For each referent point, we sample `num_points` sampling points.
            For `num_Z_anchors` reference points,  it has overall `num_points * num_Z_anchors` sampling points.
            """
            offset_normalizer = torch.stack(
                [spatial_shapes[..., 1], spatial_shapes[..., 0]], -1)

            bs, num_query, num_Z_anchors, xy = reference_points.shape
            reference_points = reference_points[:, :, None, None, None, :, :]
            sampling_offsets = sampling_offsets / \
                offset_normalizer[None, None, None, :, None, :]
            bs, num_query, num_heads, num_levels, num_all_points, xy = sampling_offsets.shape
            sampling_offsets = sampling_offsets.view(
                bs, num_query, num_heads, num_levels, num_all_points // num_Z_anchors, num_Z_anchors, xy)
            sampling_locations = reference_points + sampling_offsets
            bs, num_query, num_heads, num_levels, num_points, num_Z_anchors, xy = sampling_locations.shape
            assert num_all_points == num_points * num_Z_anchors

            sampling_locations = sampling_locations.view(
                bs, num_query, num_heads, num_levels, num_all_points, xy)

        elif reference_points.shape[-1] == 4:
            assert False
        else:
            raise ValueError(
                f'Last dim of reference_points must be'
                f' 2 or 4, but get {reference_points.shape[-1]} instead.')

        #  sampling_locations.shape: bs, num_query, num_heads, num_levels, num_all_points, 2
        #  attention_weights.shape: bs, num_query, num_heads, num_levels, num_all_points
        #准备步骤基本可TSA相同

        if torch.cuda.is_available() and value.is_cuda:
            if value.dtype == torch.float16:
                MultiScaleDeformableAttnFunction = MultiScaleDeformableAttnFunction_fp32
            else:
                MultiScaleDeformableAttnFunction = MultiScaleDeformableAttnFunction_fp32
            output = MultiScaleDeformableAttnFunction.apply(
                value, spatial_shapes, level_start_index, sampling_locations,
                attention_weights, self.im2col_step)
        else:
            output = multi_scale_deformable_attn_pytorch(
                value, spatial_shapes, sampling_locations, attention_weights)
        if not self.batch_first:
            output = output.permute(1, 0, 2)

        return output

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371
  • 372
  • 373
  • 374
  • 375
  • 376
  • 377
  • 378
  • 379
  • 380
  • 381
  • 382
  • 383
  • 384
  • 385
  • 386
  • 387
  • 388
  • 389
  • 390
  • 391
  • 392
  • 393
  • 394
  • 395
  • 396
  • 397
  • 398
  • 399
  • 400
  • 401
  • 402
  • 403
  • 404
  • 405
  • 406
  • 407
  • 408
  • 409
  • 410
  • 411
  • 412
  • 413
  • 414
  • 415
  • 416
  • 417
  • 418
  • 419
  • 420
  • 421
  • 422
  • 423
  • 424
  • 425
  • 426
  • 427
  • 428
  • 429
  • 430
  • 431
  • 432
  • 433
  • 434
  • 435
  • 436
  • 437
  • 438
  • 439
  • 440
  • 441
  • 442
  • 443
  • 444
  • 445
  • 446
  • 447
  • 448
  • 449
  • 450
  • 451
  • 452
  • 453
  • 454
  • 455
  • 456
  • 457
  • 458
  • 459
  • 460
  • 461
  • 462
  • 463
拓展
关于embedding

embedding层的作用

我的理解,在之前NLP的学习中,一个词语放入到语义中可以使用一个多维度的向量在超空间里分解他的含义,然后这个向量(坐标亦或位置),表现出来这个字和不同维度轴的相关程度,从而说明了这个词语的含义。
gpt解释:
Embedding 层在深度学习中主要用于将高维的离散数据映射到低维的连续空间
Embedding 层将输入的离散数据,比如单词、类别标签等,映射到一个固定维度的实数向量。这使得模型能够更好地理解和处理这些数据,因为连续向量包含了更多的信息
Embedding 层会根据模型的训练数据学习出适合任务的特征表示。这意味着相似的类别或单词在嵌入空间中会有相似的表示,这有助于提高模型的泛化能力
Embedding 层可以将高维的离散数据映射到低维的连续空间。这有助于减少模型的参数数量,提高训练和推理效率
与独热编码等稀疏表示相比,Embedding 层提供了密集的表示,其中每个维度都包含信息。这可以减少存储需求,并更有效地传达模型学到的知识
其中:
• 连续的向量: 意味着向量的每个元素都可以是任意实数,而不仅仅是整数。在嵌入层中,这通常是为了获得更灵活、更具表达力的表示。
• 低维向量: 意味着向量的维度相对较低。在嵌入的上下文中,这有助于减少模型参数的数量,同时保留重要的特征。低维度的表示通常更容易被模型学习和泛化。

gpt举例:
一个emmeding的简单使用

import torch
import torch.nn as nn

# 假设我们有一个词汇表的大小为10,每个词的嵌入维度为3
vocab_size = 10
embedding_dim = 3

# 创建一个 Embedding 层
embedding_layer = nn.Embedding(vocab_size, embedding_dim)

# 定义一个输入,包含三个词的索引
input_indices = torch.tensor([1, 5, 9], dtype=torch.long)

# 将输入传递给嵌入层,得到嵌入向量
embedded_vector = embedding_layer(input_indices)

# 输出嵌入向量
print(embedded_vector)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
tensor([[-2.8465,  0.1365, -0.4851],
        [ 0.4402, -0.3163, -0.8770],
        [-0.4027, -0.1626,  0.3808]], grad_fn=<EmbeddingBackward0>)
  • 1
  • 2
  • 3

“每个词的嵌入维度为3” 意味着在嵌入层中为每个词分配的嵌入向量的维度是3。嵌入向量是一个实数向量,用于表示模型学习到的词汇表中每个词的语义信息。

在这个特定的例子中,每个词都被映射到一个包含3个实数值的向量。这三个值构成了嵌入向量的三个维度。这种表示是通过训练神经网络从数据中学习得到的,网络通过反向传播算法来调整嵌入向量的权重,以最好地适应训练数据。

通常,嵌入维度的选择是一个超参数,可以根据具体任务和数据的性质进行调整。较大的嵌入维度可以提供更丰富的语义信息,但也需要更多的计算资源。一般而言,在实际应用中,嵌入维度的选择可能会在试验和验证中进行调整,以找到适合特定任务的最佳值。

关于xavier

对于网络的每一层初始化权重时,使得输入和输出的方差相等。这有助于避免在网络的前向传播和反向传播中引入梯度爆炸或梯度消失的问题。
权重初始化解释集合
摘读如下

Xavier初始化,也称为Glorot初始化,是一种常用的参数初始化方法,旨在有效地初始化神经网络中的权重。它的设计思想是根据前一层和后一层的神经元数量来设置权重的初始值,以保持信号在前向传播过程中的适当范围;其核心思想是,保持输入信号和梯度的方差在不同层之间大致相等,避免在深层网络中产生梯度消失或梯度爆炸的问题。这种初始化方法有助于提供合适的梯度范围,促进网络的稳定训练和收敛。。具体而言,对于具有线性激活函数(如sigmoid和tanh)的网络层,Xavier初始化将权重初始化为均匀分布或高斯分布,其方差取决于前一层神经元数量n和后一层神经元数量m。

对于均匀分布的Xavier初始化(均匀版):

从均匀分布中随机初始化权重矩阵W,范围为[-a, a],其中a = sqrt(6 / (n + m))。
对于高斯分布的Xavier初始化(高斯版):

从高斯分布中随机初始化权重矩阵W,均值为0,方差为variance,其中variance = 2 / (n + m)。 核心设计思想解释
当我们训练深度神经网络时,梯度的传播是非常关键的。如果梯度在每一层传播时逐渐消失,即梯度接近于零,那么底层的参数将很难更新,导致网络难以学习有效的表示。相反,如果梯度在每一层传播时逐渐增大,即梯度爆炸,那么参数更新的幅度会非常大,导致训练不稳定甚至无法收敛。因此,要保持输入信号和梯度的方差在不同层之间大致相等,以确保在前向传播和反向传播过程中,信号和梯度能够保持合适的范围,从而促进网络的稳定训练和收敛。

如何保证输入信号和梯度的方差在不同层之间大致相等呢?在Xavier初始化中,存在递归的思想,其计算方式是递归的,即权重的初始范围是根据前一层和后一层的神经元数量进行计算的。

好的,让我们来看一个具有5层的神经网络的例子,以解释Xavier初始化是如何工作的。假设我们有一个具有以下结构的神经网络:输入层(100个神经元)

  • 隐藏层1(80个神经元) - 隐藏层2(60个神经元) - 隐藏层3(40个神经元) - 输出层(10个神经元)。现在,我们将使用Xavier初始化来初始化每一层的权重。

对于隐藏层1,我们需要计算它的权重初始范围。根据Xavier初始化的公式,我们需要知道前一层和后一层的神经元数量。在这种情况下,前一层是输入层,有100个神经元,后一层是隐藏层1本身,有80个神经元。根据公式,我们可以计算权重初始范围a:a
= sqrt(6 / (100 + 80)) ≈ 0.136。现在,我们可以从均匀分布[-0.136, 0.136]中随机初始化隐藏层1的权重矩阵。

接下来,我们继续计算隐藏层2的权重初始范围。前一层是隐藏层1,有80个神经元,后一层是隐藏层2本身,有60个神经元。我们使用相同的公式来计算权重初始范围a:a
= sqrt(6 / (80 + 60)) ≈ 0.153。然后,我们从均匀分布[-0.153, 0.153]中随机初始化隐藏层2的权重矩阵。

类似地,我们可以计算隐藏层3和输出层的权重初始范围,并进行相应的初始化。

通过这样的递归计算和初始化过程,Xavier初始化确保了每一层的权重都与前一层和后一层的神经元数量相关联。这有助于平衡信号和梯度的传播,避免梯度消失或梯度爆炸问题,从而提高神经网络的训练稳定性和收敛性能。

关于stack 和 reshape和permute和cat和view和NONE的用法
tmp_prev_bev = prev_bev[:, i].reshape(
                        bev_h, bev_w, -1).permute(2, 0, 1)
  • 1
  • 2

有我不熟悉的语法 gpt查询如下:
A:
prev_bev[:, i]:表示选择 prev_bev 张量中的所有行 ( 本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/394818

推荐阅读
相关标签