Swin transformer 论文阅读记录 代码分析


该篇文章,是我解析 Swin transformer 论文原理(结合pytorch版本代码)所记,图片来源于源paper或其他相应博客。

代码也非原始代码,而是从代码里摘出来的片段,配上简单数据,以便理解。

当然,也可能因为设置数据不当,造成误解,请多指教。

刚写了一部分。先发布。希望多多指正。


在这里插入图片描述
Figure 1.
(a) The proposed Swin Transformer builds hierarchical feature maps by merging image patches (shown in gray) in deeper layers ,
and has linear computation complexity to input image size due to computation of self-attention only within each local window (shown in red).
It can thus serve as a general-purpose backbone for both image classification and dense recognition tasks.
(b) In contrast, previous vision Transformers produce feature maps of a single low resolution and have quadratic computation complexity to input image size due to computation of self attention globally.

模型结构图

在这里插入图片描述
Figure 3.
(a) The architecture of a Swin Transformer (Swin-T);
(b) two successive Swin Transformer Blocks (notation presented with Eq. (3)).
W-MSA and SW-MSA are multi-head self attention modules with regular and shifted windowing configurations, respectively.

Stage 1 – Patch Embedding

It first splits an input RGB image into non-overlapping patches by a patch splitting module, like ViT.

Each patch is treated as a “token” and its feature is set as a concatenation of the raw pixel RGB values.

In our implementation, we use a patch size of 4×4 and thus the feature dimension of each patch is 4×4×3 = 48.(channel–3)

A linear embedding layer is applied on this raw-valued feature to project it to an arbitrary dimension (denoted as C).
这个表述,linear embedding layer,我感觉不太准确,但是,后半部分比较准确,哈哈,将channel–3变成了96.

Several Transformer blocks with modified self-attention computation (Swin Transformer blocks) are applied on these patch tokens.

The Transformer blocks maintain the number of tokens (H/4 × W/4), and together with the linear embedding are referred to as “Stage 1”.

代码

以下代码来自于model.py:

class PatchEmbed(nn.Module):
    """
    2D Image to Patch Embedding
    """
"""
@ time : 2024/12/17
"""
import torch
from PIL import Image
from torchvision import transforms
import matplotlib.pyplot as plt
import torch.nn as nn
import torch.nn.functional as F


class PatchEmbed(nn.Module):
    """
    2D Image to Patch Embedding
    """

    def __init__(self, patch_size=4, in_c=3, embed_dim=96, norm_layer=None):
        super().__init__()
        patch_size = (patch_size, patch_size)

        self.patch_size = patch_size
        self.in_chans = in_c
        self.embed_dim = embed_dim
        self.proj = nn.Conv2d(in_c, embed_dim, kernel_size=patch_size, stride=patch_size)
        self.norm = norm_layer(embed_dim) if norm_layer else nn.Identity()

    def forward(self, x):
        _, _, H, W = x.shape

        # padding
        # 如果输入图片的H,W不是patch_size的整数倍,需要进行padding
        pad_input = (H % self.patch_size[0] != 0) or (W % self.patch_size[1] != 0)

        if pad_input:
            # to pad the last 3 dimensions,
            # (W_left,W_right, H_top,H_bottom, C_front,C_back)
            x = F.pad(x,
                      (0, self.patch_size[1] - W % self.patch_size[1],
                       0, self.patch_size[0] - H % self.patch_size[0],
                       0, 0
                       )
                      )

        # 下采样patch_size倍
        x = self.proj(x)

        _, _, H, W = x.shape

        # flatten: [B, C, H, W] -> [B, C, HW]
        # transpose: [B, C, HW] -> [B, HW, C]
        x = x.flatten(2).transpose(1, 2)

        x = self.norm(x)
        print(x.shape)
        # torch.Size([1, 3136, 96])
		# 224/4 * 224/4 = 3136
		
        return x, H, W


if __name__ == '__main__':
    img_path = "tulips.jpg"

    img = Image.open(img_path)
    plt.imshow(img)
    # [N, C, H, W]
    print(img.size)
    # (500,375)

    #
    img_size = 224
    data_transform = transforms.Compose(
        [transforms.Resize(int(img_size * 1.14)),
         transforms.CenterCrop(img_size),
         transforms.ToTensor(),
         transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])]
    )
    img = data_transform(img)
    print(img.shape)
    # torch.Size([3, 224, 224])

    # expand batch dimension
    img = torch.unsqueeze(img, dim=0)
    print(img.shape)
    # torch.Size([1, 3, 224, 224])

    # split image into non-overlapping patches
    patch_embed = PatchEmbed(
        norm_layer=nn.LayerNorm
    )

    patch_embed(img)

Stage 2 – 3.2. Shifted Window based Self-Attention

Shifted window partitioning in successive blocks

The window-based self-attention module lacks connections across windows, which limits its modeling power.

To introduce cross-window connections while maintaining the efficient computation of non-overlapping windows,
we propose a shifted window partitioning approach which alternates between two partitioning configurations in consecutive Swin Transformer blocks.
为了在保持非重叠窗口高效计算的同时引入跨窗口连接,我们提出了一种移位窗口划分方法,该方法在连续的Swin Transformer块中交替使用两种不同的划分配置。

在这里插入图片描述
Figure 2.
In layer l (left), a regular window partitioning scheme is adopted, and self-attention is computed within each window.
In the next layer l + 1 (right), the window partitioning is shifted, resulting in new windows.
The self-attention computation in the new windows crosses the boundaries of the previous windows in layer l, providing connections among them.
在新窗口中进行的自注意力计算跨越了第l层中先前窗口的边界,从而在它们之间建立了连接。

Efficient batch computation for shifted configuration

An issue with shifted window partitioning is that it will result in more windows, and some of the windows will be smaller than M×M.

Here, we propose a more efficient batch computation approach by cyclic-shifting toward the top-left direction(向左上方向循环移动), as illustrated in Figure 4.

这里的 more efficient,是说相对于直观方法 padding—mask来说:

A naive solution is to pad the smaller windows to a size of M×M and mask out the padded values when computing attention.


在这里插入图片描述
Figure 4. Illustration of an efficient batch computation approach for self-attention in shifted window partitioning.


After this shift, a batched window may be composed of several sub-windows that are not adjacent in the feature map, so a masking mechanism is employed to limit self-attention computation to within each sub-window.
在此转换之后,批处理窗口可能由特征图中不相邻的几个子窗口组成,因此采用掩蔽机制将自注意力计算限制在每个子窗口内。

With the cyclic-shift, the number of batched windows remains the same as that of regular window partitioning, and thus is also efficient.
通过循环移位,批处理窗口的数量与常规窗口分区的数量保持不变,因此也是高效的。


上图和叙述,并不太直观,找了相关资料,一起分析:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
移动完成之后,4是一个单独区域,5、3为一组,7、1为一组,8、6、2、0为一组。

但,5、3本身是两个图像的边缘,混在一起计算不是乱了吗?一起计算也没问题,ViT也是全局计算的。

但,Swin-Transformer为了防止这个问题,在代码中使用了masked MSA,这样就能够通过设置蒙板来隔绝不同区域的信息了。

源码中具体的方法就是将不计算的位置元素减去100。

这里需要注意的是,在窗口数据进行滑动完之后,需要将数据还原回去,即挪回到原来的位置上。

代码

以下代码来自于model.py:

def window_partition(x, window_size: int):
    """
    将feature map按照window_size划分成一个个没有重叠的window
    主要思路是将feature转成 (num_windows*B, window_size*window_size, C)的shape,
    把需要self-attn计算的window排列到第0维,一次并行的qkv就可以了
    
    Args:
        x: (B, H, W, C)
        window_size (int): window size(M)

    Returns:
        windows: (num_windows*B, window_size, window_size, C)
    """
    
    B, H, W, C = x.shape
    # B,224,224,C
    # B,56,56,C
    
    x = x.view(B, H // window_size, window_size, W // window_size, window_size, C)
    # B,32,7,32,7,C
    # B,8,7,8,7,C
    
    # permute:
    # [B, H//Mh, Mh,    W//Mw, Mw, C] ->
    # [B, H//Mh, W//Mh, Mw,    Mw, C]
    # B,32,32,7,7,C
    # B,8,8,7,7,C
    
    # view:
    # [B, H//Mh, W//Mw, Mh, Mw, C] ->
    # [B*num_windows,   Mh, Mw, C]
    # B*1024,7,7,C
    # B*64,7,7,C
    
    # 32*32 = 1024
    # 224 / 7 = 32
    
    windows = x.permute(0, 1, 3, 2, 4, 5).contiguous()
    			.view(-1, window_size, window_size, C)

    return windows

分析:将 [B, C, 56, 56] 最后变成了[64B, C, 7, 7],原先的 B*C 张 56*56 的特征图,最后变成了 B*64*C张7*7的特征;

即,我们有64B个样本,每个样本包含C个7x7的通道。

注意,window_size–M–7,是每个window的大小,7*7,不是7*7个window,我刚开始混淆了这一点。


class BasicLayer(nn.Module):
    # A basic Swin Transformer layer for one stage.
	def __init__(self, dim, depth, num_heads, window_size,
                 mlp_ratio=4., qkv_bias=True, drop=0., attn_drop=0.,
                 drop_path=0., norm_layer=nn.LayerNorm, downsample=None, 
                 use_checkpoint=False):
        super().__init__()
        self.dim = dim
        self.depth = depth
        self.window_size = window_size
        self.use_checkpoint = use_checkpoint
        self.shift_size = window_size // 2
		# 7//2 = 3
		
        # build blocks
        self.blocks = nn.ModuleList([
            SwinTransformerBlock(
                dim=dim,
                num_heads=num_heads,
                window_size=window_size,
                shift_size=0 if (i % 2 == 0) else self.shift_size,
                mlp_ratio=mlp_ratio,
                qkv_bias=qkv_bias,
                drop=drop,
                attn_drop=attn_drop,
                drop_path=
                	drop_path[i] if isinstance(drop_path, list) else drop_path,
                norm_layer=norm_layer
            )
            for i in range(depth)
        ])
    	...
		# depth: 2, 2, 6, 2
        # 即,第一层,depth=2, 有两个SwinTransformerBlock,shift_size分别为:0,3
        # 即,第二层,depth=2, 有两个SwinTransformerBlock,shift_size分别为:0,3
        # 即,第三层,depth=6, 有两个SwinTransformerBlock,shift_size分别为:
        #	0,3,0,3,0,3
        # 即,第四层,depth=2, 有两个SwinTransformerBlock,shift_size分别为:0,3
        
    def create_mask(self, x, H, W):
    # calculate attention mask for SW-MSA
import numpy as np
import torch

H = 7
W = 7
window_size = 7
shift_size = 3

Hp = int(np.ceil(H / window_size)) * window_size
Wp = int(np.ceil(W / window_size)) * window_size

# 拥有和feature map一样的通道排列顺序,方便后续window_partition
img_mask = torch.zeros((1, Hp, Wp, 1))
# [1, Hp, Wp, 1]
print(img_mask, '\n')

h_slices = (
    slice(0, -window_size),
    slice(-window_size, -shift_size),
    slice(-shift_size, None)
)
print(h_slices, '\n')
# (slice(0, -7, None), slice(-7, -3, None), slice(-3, None, None))

w_slices = (
    slice(0, -window_size),
    slice(-window_size, -shift_size),
    slice(-shift_size, None)
)
print(w_slices, '\n')
# (slice(0, -7, None), slice(-7, -3, None), slice(-3, None, None))

cnt = 0
for h in h_slices:
    for w in w_slices:
        img_mask[:, h, w, :] = cnt
        cnt += 1

print(img_mask)

在这里插入图片描述

import torch

img_mask = torch.rand((2, 3))
print(img_mask)
'''
tensor([[0.7410, 0.6020, 0.5195],
        [0.9214, 0.2777, 0.8418]])
'''
attn_mask = img_mask.unsqueeze(1) - img_mask.unsqueeze(2)
print(attn_mask)
'''
tensor([[[ 0.0000, -0.1390, -0.2215],
         [ 0.1390,  0.0000, -0.0825],
         [ 0.2215,  0.0825,  0.0000]],

        [[ 0.0000, -0.6437, -0.0796],
         [ 0.6437,  0.0000,  0.5642],
         [ 0.0796, -0.5642,  0.0000]]])
'''

print(img_mask.unsqueeze(1))
'''
tensor([[[0.7410, 0.6020, 0.5195]],

        [[0.9214, 0.2777, 0.8418]]])
'''
print(img_mask.unsqueeze(2))
'''
tensor([[[0.7410],
         [0.6020],
         [0.5195]],

        [[0.9214],
         [0.2777],
         [0.8418]]])
'''

上面那个代码,需要根据下面这个代码对应着走,shift_size–torch.roll()

class SwinTransformerBlock(nn.Module):
    # Swin Transformer Block.
    ...
	def forward(self, x, attn_mask):
        H, W = self.H, self.W

        B, L, C = x.shape
        assert L == H * W, "input feature has wrong size"

        shortcut = x

        x = self.norm1(x)

        x = x.view(B, H, W, C)

        # pad feature maps to multiples of window size
        # 把feature map给pad到window size的整数倍

        pad_l = pad_t = 0
        pad_r = (self.window_size - W % self.window_size) % self.window_size
        pad_b = (self.window_size - H % self.window_size) % self.window_size

        # 注意F.pad的顺序,刚好是反着来的, 例如:
        # x.shape = (b, h, w, c)
        # x = F.pad(x, (1, 1, 2, 2, 3, 3))
        # x.shape = (b, h+6, w+4, c+2)

        # 源码可能有误,修改成下面的
        # x = F.pad(x, (0, 0, pad_l, pad_r, pad_t, pad_b))
        x = F.pad(x, (0, 0, pad_t, pad_b, pad_l, pad_r))

        _, Hp, Wp, _ = x.shape

        # cyclic shift
        if self.shift_size > 0:
            # paper中,滑动的size是窗口大小的/2(向下取整)
            # torch.roll以H,W的维度为例子,负值往左上移动,正值往右下移动。
            # 溢出的值在对角方向出现。即循环移动。
            shifted_x = torch.roll(x, 
            	shifts=(-self.shift_size, -self.shift_size), 
            	dims=(1, 2)
            	)
        else:
            shifted_x = x
            attn_mask = None

        # partition windows
        x_windows = window_partition(shifted_x, self.window_size)  
        # [nW*B, Mh, Mw, C]
        x_windows = x_windows.view(-1, self.window_size * self.window_size, C) 
        # [nW*B, Mh*Mw, C]
        
        # W-MSA/SW-MSA
        attn_windows = self.attn(x_windows, mask=attn_mask)  
        # [nW*B, Mh*Mw, C]
        ...

其中,torch.roll()方法简易示例如下:

import torch

x = torch.randn(1, 4, 4, 3)
print(x, '\n')

shifted_x = torch.roll(x, shifts=(-3, -3), dims=(1, 2))
print(shifted_x, '\n')

为了方便理解,我更换了维度:

import torch

x = torch.randn(1, 3, 7, 7)
print(x, '\n')

shifted_x = torch.roll(x, shifts=(-3, -3), dims=(2, 3))
print(shifted_x, '\n')

在这里插入图片描述

Relative position bias


Relative Position Bias通过给自注意力机制的输出加上一个与token相对位置相关的偏置项,从而增强了模型对局部和全局信息的捕捉能力。

实现方式:

1、构建相对位置索引:

  • 首先,需要确定一个 window size ,并在该窗口内计算token之间的相对位置。
  • 通过构建相对位置索引表(relative position index table),可以方便地查询任意两个token之间的相对位置。

2、可学习的偏置表:

  • 初始化一个与相对位置索引表大小相同的可学习参数表(relative position bias table),这些参数在训练过程中会被优化。
  • 根据相对位置索引,从偏置表中查询对应的偏置值,并将其加到自注意力机制的输出上。

3、计算过程:

  • 在自注意力机制的计算中,通常会将 Q、K 和 V 进行矩阵乘法运算,得到注意力得分。
  • 然后,将 Relative Position Bias 加到注意力得分上,再进行 softmax 运算,最后与 V 相乘得到最终的输出。

代码

import torch

coords_h = torch.arange(7)
coords_w = torch.arange(7)

a, b = torch.meshgrid([coords_h, coords_w], indexing="ij")

coords = torch.stack(torch.meshgrid([coords_h, coords_w], indexing="ij"))

coords_flatten = torch.flatten(coords, 1)
# [2, Mh*Mw]
# print(coords_flatten)
'''
tensor([
[0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3,
 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6],
[0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2,
 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6]
])
'''
# [2, Mh*Mw, 1] - [2, 1, Mh*Mw]
relative_coords = coords_flatten[:, :, None] - coords_flatten[:, None, :]
relative_coords = relative_coords.permute(1, 2, 0).contiguous()
# [Mh*Mw, Mh*Mw, 2]
'''
tensor([[[ 0,  0],
         [ 0, -1],
         [ 0, -2],
         ...,
         [-6, -4],
         [-6, -5],
         [-6, -6]],

        [[ 0,  1],
         [ 0,  0],
         [ 0, -1],
         ...,
         [-6, -3],
         [-6, -4],
         [-6, -5]],

        [[ 0,  2],
         [ 0,  1],
         [ 0,  0],
         ...,
         [-6, -2],
         [-6, -3],
         [-6, -4]],

        ...,

        [[ 6,  4],
         [ 6,  3],
         [ 6,  2],
         ...,
         [ 0,  0],
         [ 0, -1],
         [ 0, -2]],

        [[ 6,  5],
         [ 6,  4],
         [ 6,  3],
         ...,
         [ 0,  1],
         [ 0,  0],
         [ 0, -1]],

        [[ 6,  6],
         [ 6,  5],
         [ 6,  4],
         ...,
         [ 0,  2],
         [ 0,  1],
         [ 0,  0]]])
'''
relative_coords[:, :, 0] += 6
# shift to start from 0

relative_coords[:, :, 1] += 6
relative_coords[:, :, 0] *= 13

relative_position_index = relative_coords.sum(-1)
print(relative_position_index.shape)
# torch.Size([49, 49])

# print(relative_position_index)
'''
tensor([[ 84,  83,  82,  ...,   2,   1,   0],
        [ 85,  84,  83,  ...,   3,   2,   1],
        [ 86,  85,  84,  ...,   4,   3,   2],
        ...,
        [166, 165, 164,  ...,  84,  83,  82],
        [167, 166, 165,  ...,  85,  84,  83],
        [168, 167, 166,  ...,  86,  85,  84]])
'''

其中,

relative_coords = coords_flatten[:, :, None] - coords_flatten[:, None, :] 

这行代码是用来计算一个二维坐标点集 coords_flatten 中所有点对之间的相对坐标(或位移)。

未对行列加乘操作之前的矩阵,relative_coords 是一个形状为 (N, N, 2) 的数组,其中 relative_coords[i, j, :] 表示从点 i 到点 j 的相对坐标(或位移)。

在这里插入图片描述

结合其他博客的分析:

如图,假设我们现在有一个window-size=2的feature map;
这里面如果用绝对位置来表示位置索引;
然后如果用相对位置表示,就会有4个情况,但分别都是以自己为(0, 0)计算其他token的相对位置。
分别把4个相对位置展开,得到4x4的矩阵,如最下的矩阵所示。
在这里插入图片描述
请注意这里说的都是位置索引,并不是最后的位置编码。因为后面我们会根据相对位置索引去取对应位置的参数。取出来的值才是相对位置编码。
源码中,作者还将二维索引给转成了一维索引。如果直接将行列相加,就变成一维了。但这样(0, 1)和(1, 0)得到的结果都是1,这样肯定不行。来看看源码的做法怎么做的:
首先,所有行、列都加上M-1,其次将所有的行索引乘上2M-1
最后行索引和列索引相加,保证了相对位置关系,也不会出现0+1 = 1+0 的现象了。
在这里插入图片描述

刚刚也说了,之前计算的是相对位置索引,并不是实际位置偏执参数。

真正使用到的数值需要从relative position bias table,这个表的长度是等于(2M-1)X(2M-1)的。在代码中它是一个可学习参数。
在这里插入图片描述

import torch
from torch import nn

window_size = (7, 7)
num_heads = 3

relative_position_bias_table = nn.Parameter(
    torch.zeros((2 * window_size[0] - 1) * (2 * window_size[1] - 1), num_heads)
)
print(relative_position_bias_table)
......
nn.init.trunc_normal_(self.relative_position_bias_table, std=.02)

Stage 3 – patch merging layers

To produce a hierarchical representation, the number of tokens is reduced by patch merging layers as the network gets deeper.

The first patch merging layer concatenates the features of each group of 2×2 neighboring patches, and applies a linear layer on the 4C-dimensional concatenated features.
首个补丁合并层将每组2×2相邻补丁的特征进行拼接,并在拼接后的4C维特征上应用一个线性层。

This reduces the number of tokens by a multiple of 2×2=4(2 ×downsampling of resolution), and the output dimension is set to 2C.

Swin Transformer blocks are applied afterwards for feature transformation, with the resolution kept at H/8 × W/8.

同样,结合其他大神分析,图展示如下:

在这里插入图片描述

Related Work

Self-attention based backbone architectures

Instead of using sliding windows, we propose to shift windows between consecutive layers, which allows for a more efficient implementation in general hardware.

。。。。。

Cited link or paper name

  1. Swin Transformer: Hierarchical Vision Transformer using Shifted Windows.
  2. https://blog.csdn.net/weixin_42392454/article/details/141395092

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

LLMs之rStar:《Mutual Reasoning Makes Smaller LLMs Stronger Problem-Solvers》翻译与解读

LLMs之rStar:《Mutual Reasoning Makes Smaller LLMs Stronger Problem-Solvers》翻译与解读 导读:这篇论文提出了一种名为rStar的自我博弈互推理方法,用于增强小型语言模型 (SLMs) 的推理能力,无需微调或依赖更强大的模型。rStar…

CS 144 check5: down the stack (the network interface)

Lectures Note 略 Exercises TCP片段传输到对等方的过程: TCP-in-UDP-in-IP. Linux 提供了一种接口(即“数据报套接字”,UDPSocket),它允许应用程序仅提供用户数据报的有效载荷和目标地址,而内核则负责…

Llama 3 模型系列解析(一)

目录 1. 引言 1.1 Llama 3 的简介 1.2 性能评估 1.3 开源计划 1.4 多模态扩展 ps 1. 缩放法则 2. 超额训练(Over-training) 3. 计算训练预算 4. 如何逐步估算和确定最优模型? 2. 概述 2.1 Llama 3 语言模型开发两个主要阶段 2.2…

越疆科技营收增速放缓:毛利率未恢复,持续亏损下销售费用偏高

《港湾商业观察》施子夫 12月13日,深圳市越疆科技股份有限公司(以下简称,越疆科技,02432.HK)发布全球发售公告,公司计划全球发售4000万股股份,其中3800万股国际发售,200万股香港公开…

微信小程序实现画板画布自由绘制、选择画笔粗细及颜色、记录撤回、画板板擦、清空、写字板、导出绘图、canvas,开箱即用

目录 画板创建canvas绘制及渲染画笔粗细功能实现画笔颜色选择画笔痕迹撤回、板擦、画布清空canvas解析微信小程序中 canvas 的应用场景canvas 与 2D 上下文、webgl 上下文的关系图像的加载与绘制说明代码说明画板创建 canvas绘制及渲染 在wxml添加对应的canvas标签代码,并在j…

混合精度训练说明

什么是混合精度训练?混合精度训练有什么用? 这里总结一下。 本文总结自kapathy的build gpt2 通常在训练过程中,model里面的数据默认都是torch.float32类型, 也就是用32bit的float型数据表示变量。 比如特征提取中提取的特征&…

draw.io 导出svg图片插入word后模糊(不清晰 )的解决办法

通常我们将图片从draw.io导出为svg格式后插入word, 会发现字体不清晰,特别是使用宋体时,折腾了半天,得到如下办法: 方法1: 在draw.io中导出pdf文件,使用 PDF转SVG转换器 - SVGConverter 将其转换为svg, 完美呈现。 …

ARM学习(38)多进程多线程之间的通信方式

ARM学习(38)ARM学习(38)多进程多线程之间的通信方式 一、问题背景 笔者在调试模拟器的时候,碰到进程间通信的问题,一个进程在等另外一个进程ready的时候,迟迟等不到,然后通过调试发现,另外一个进程变量已经变化了,但是当前进程变量没变化,需要了解进程间通信的方式…

【动手学运动规划】 5.2 数值优化基础:梯度下降法,牛顿法

朕四季常服, 不过八套. — 大明王朝1566 道长 🏰代码及环境配置:请参考 环境配置和代码运行! 上一节我们介绍了数值优化的基本概念, 让大家对最优化问题有了基本的理解. 那么对于一个具体的问题, 我们应该如何求解呢? 这一节我们将介绍几个基本的求解…

24-12-22 pytorch学习 基础知识 帝乡明日到,犹自梦渔樵。

文章目录 pytorch学习 基础知识pytorch学习(1) Tensors1.1 初始化Tensor1.2 Tensor 的属性1.3 Tensors 的操作1.4 与 NumPy 的桥梁1.4.1 Tensor 到 NumPy 数组1.4.2 NumPy 数组 到 Tensor pytorch学习(2) 数据集和数据加载器2.1 加载一个数据集2.2 迭代和可视化数据集2.3 为你的…

Linux网络功能 - 服务和客户端程序CS架构和简单web服务示例

By: fulinux E-mail: fulinux@sina.com Blog: https://blog.csdn.net/fulinus 喜欢的盆友欢迎点赞和订阅! 你的喜欢就是我写作的动力! 目录 概述准备工作扫描服务端有那些开放端口创建客户端-服务器设置启动服务器和客户端进程双向发送数据保持服务器进程处于活动状态设置最小…

M3D: 基于多模态大模型的新型3D医学影像分析框架,将3D医学图像分析从“看图片“提升到“理解空间“的层次,支持检索、报告生成、问答、定位和分割等8类任务

M3D: 基于多模态大模型的新型3D医学影像分析框架,将3D医学图像分析从“看图片“提升到“理解空间“的层次,支持检索、报告生成、问答、定位和分割等8类任务 论文大纲理解1. 确认目标2. 分析过程(目标-手段分析)核心问题拆解 3. 实…

【102. 二叉树的层序遍历 中等】

题目: 给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。 示例 1: 输入:root [3,9,20,null,null,15,7] 输出:[[3],[9,20],[15,7]] 示例…

第四届电气工程与控制科学

重要信息 官网:www.ic2ecs.com 时间:2024年12月27-29日 简介 第四届电气工程与控制科学定于2024年12月27-29日在中国南京召开。主要围绕“电气工程“、”控制科学“、”机械工程“、”自动化”等主题展开,旨在为从电…

监控易在汽车制造行业信息化运维中的应用案例

引言 随着汽车制造行业的数字化转型不断深入,信息化类IT软硬件设备的运行状态监控、故障告警、报表报告以及网络运行状态监控等成为了企业运维管理的关键环节。监控易作为一款全面、高效的信息化运维管理工具,在汽车制造行业中发挥着重要作用。本文将结合…

大模型+安全实践之春天何时到来?

引子:距《在大模型实践旅途中摸了下上帝的脚指头》一文发布近一年,2024年笔者继续全情投入在大模型+安全上,深度参与了一些应用实践,包括安全大模型首次大规模应用在国家级攻防演习、部分项目的POC直到项目落地,也推动了一些场景安全大模型应用从0到3的孵化上市。这一年也…

大小端存储的问题

请你用C语言写一个简单的程序&#xff0c;判断你使用的主机是大端存储还是小端存储 #include <stdio.h> int main(){int x 0x11223344;char *p (char *)&x;if(0x44 *p){printf("小端\n");}else if(0x11 *p){printf("大端\n");}return 0; }

山景BP1048增加AT指令,实现单片机串口控制播放音乐(一)

1、设计目的 山景提供的SDK是蓝牙音箱demo&#xff0c;用户使用ADC按键或者IR遥控器&#xff0c;进行人机交互。然而现实很多场景&#xff0c;需要和单片机通信&#xff0c;不管是ADC按键或者IR接口都不适合和单片机通信。这里设计个AT指令用来和BP1048通信。AT指令如下图所示…

EMC VMAX/DMX 健康检查方法

近期连续遇到2个由于对VMAX存储系统没有做及时的健康检查&#xff0c;出现SPS电池故障没有及时处理&#xff0c;然后同一pair就是同一对的另外一个SPS电池再次出现故障&#xff0c;然后存储系统保护性宕机vault&#xff0c;然后业务系统挂掉的案例。 开始之前&#xff0c;先纠…

51c大模型~合集94

我自己的原文哦~ https://blog.51cto.com/whaosoft/12897659 #D(R,O) Grasp 重塑跨智能体灵巧手抓取&#xff0c;NUS邵林团队提出全新交互式表征&#xff0c;斩获CoRL Workshop最佳机器人论文奖 本文的作者均来自新加坡国立大学 LinS Lab。本文的共同第一作者为上海交通大…