Swin Transformer 架构解析及在UCI-HAR行为识别中的实现

Swin Transformer(Shifted Window Transformer)是计算机视觉领域中一种创新的Transformer变体,它结合了卷积神经网络(CNN)的局部特征提取能力和Transformer的全局建模能力,在图像分类、目标检测等任务中表现出色。

一. Swin Transformer的基础原理

1. Transformer在视觉任务中的挑战

Transformer最初是为自然语言处理设计的,通过自注意力机制捕捉序列中的长程依赖关系。然而,将其直接应用于视觉任务时,会遇到以下问题:

  • 计算复杂度过高:自注意力机制的计算复杂度为 ( O(N^2) ),其中 ( N ) 是序列长度。对于高分辨率图像,像素数量巨大,导致计算量不可接受。
  • 局部特征提取不足:图像具有天然的局部相关性,而Transformer的全局自注意力机制无法像CNN那样高效捕捉局部模式。

为了解决这些问题,Swin Transformer引入了窗口化自注意力移位窗口机制 ,在保持Transformer优势的同时,显著降低了计算复杂度并增强了局部建模能力

2. Swin Transformer的核心思想

Swin Transformer的设计围绕以下几个关键点展开:

  • 窗口化自注意力(Window-based Self-Attention, W-MSA):将图像划分为不重叠的窗口,仅在每个窗口内进行自注意力计算。
  • 移位窗口(Shifted Windows, SW-MSA):通过在相邻层之间移动窗口位置,实现窗口间的交互,弥补W-MSA缺乏跨窗口连接的不足。
  • 层次化结构:借鉴CNN的多尺度设计,Swin Transformer通过逐步合并patch降低分辨率,扩大感受野,形成类似金字塔的特征提取过程。

这些创新使得Swin Transformer在效率和性能之间取得了平衡,成为视觉任务中的一个重要突破。

二、 Swin Transformer的架构

Swin Transformer的整体架构包括Patch Embedding、多个Swin Transformer Block以及分类头。下面逐步介绍其主要组成部分。

1. Patch Embedding

Swin Transformer首先将输入图像划分为不重叠的patch,并将每个patch映射到一个固定维度的嵌入向量。对于输入图像 ( x \in \mathbb{R}^{H \times W \times C} ),通过一个卷积层(卷积核大小为patch size ( P \times P ))将图像划分为 ( \frac{H}{P} \times \frac{W}{P} ) 个patch,每个patch被投影到维度 ( D ) 的嵌入空间。

在代码中,SwinTransformer类的初始化部分定义了Patch Embedding:

python 复制代码
self.patch_conv = nn.Conv2d(
    in_channels=1,
    out_channels=embedding_dim,
    kernel_size=(patch_size, 1),
    stride=(patch_size, 1),
    padding=0
)

这里假设输入是时间序列数据(如传感器数据),形状为 [batch, channels, series, modal],通过卷积操作将时间轴(series)划分为patch,输出形状为 [batch, embedding_dim, patch_num, modal_leng]

2. 位置编码

与原始Vision Transformer(ViT)不同,Swin Transformer通常不依赖绝对位置编码,而是通过窗口化自注意力隐式学习位置信息。不过,在某些实现中仍会加入可学习的位置编码。在代码中,位置编码被定义为一个可学习参数:

python 复制代码
self.position_embedding = nn.Parameter(torch.zeros(1, self.modal_leng, self.patch_num, embedding_dim))

它与Patch Embedding的输出相加,提供初始的空间信息。

3. Swin Transformer Block

Swin Transformer Block是模型的核心单元,每个block包含以下步骤:

  1. Layer Normalization:对输入进行归一化。
  2. W-MSA或SW-MSA:奇数层使用窗口化自注意力(W-MSA),偶数层使用移位窗口自注意力(SW-MSA)。
  3. 残差连接:将注意力输出与输入相加。
  4. Layer Normalization:再次归一化。
  5. 前馈网络(FFN):通过两层MLP处理特征。
  6. 残差连接:将FFN输出与前一步输出相加。

在代码中,这部分逻辑被封装在ShiftWindowAttentionBlock类中。

4. 窗口化自注意力(W-MSA)

W-MSA将特征图划分为不重叠的窗口,每个窗口内的patch独立进行多头自注意力计算。假设输入特征图形状为 [batch, modal_leng, patch_num, input_dim],窗口大小为 window_size,则窗口数量为 patch_num // window_size。在每个窗口内,计算过程与标准Transformer的自注意力一致。

5. 移位窗口自注意力(SW-MSA)

SW-MSA通过移位窗口位置实现跨窗口的信息交换。具体来说,相邻层的窗口会向右下移动半个窗口大小(即 window_size // 2)。但移位会导致窗口划分不均匀,为此,Swin Transformer采用循环移位(cyclic shift)策略:

  • 将特征图整体向左上移动半个窗口大小。
  • 在移位后的特征图上划分窗口并计算自注意力。
  • 计算完成后通过反向移位恢复原始位置。

代码中通过torch.roll实现移位:

python 复制代码
if self.shift_size:
    x = torch.roll(x, shifts=-self.shift_size, dims=2)

此外,移位会引入不相关的patch之间的注意力计算,因此需要注意力掩码(attn_mask)来屏蔽这些无效连接。

6. Patch Merging

为了实现层次化特征提取,Swin Transformer在每个stage结束后通过Patch Merging合并相邻patch。代码中的patch_merging方法将每两个patch沿时间轴合并,通道数翻倍后通过线性层降回原维度:

python 复制代码
def patch_merging(self, x):
    batch, modal_leng, patch_num, input_dim = x.shape
    if patch_num % 2:
        x = nn.ZeroPad2d((0, 0, 0, 1))(x)
    x0 = x[:, :, 0::2, :]
    x1 = x[:, :, 1::2, :]
    x = torch.cat([x0, x1], dim=-1)
    x = nn.ReLU()(self.downsample_mlp(x))
    return x

7. 整体架构

Swin Transformer由多个stage组成,每个stage包含若干Swin Transformer Block和一个Patch Merging层。特征图分辨率逐步降低,通道数增加,最终通过全局平均池化和全连接层输出分类结果。在代码中,模型包含三个swin_transformer_block,每个block由一个W-MSA和一个SW-MSA组成。

三、 代码实现详解

以下是对Swin Transformer代码实现的详细补充说明:

1. ShiftWindowAttentionBlock

ShiftWindowAttentionBlock是Swin Transformer的核心模块,负责实现窗口多头自注意力(W-MSA)和移位窗口多头自注意力(SW-MSA)。以下是其初始化和前向传播的详细实现。

1.1 初始化

在初始化时,定义了以下关键参数:

  • patch_num:输入的patch数量,表示时间序列或空间维度上的分割块数。
  • input_dim:每个patch的嵌入维度。
  • head_num:多头自注意力机制中的注意力头数。
  • att_size :查询(Q)、键(K)、值(V)的维度,通常为input_dim // head_num
  • window_size:每个窗口的大小,用于划分patch。
  • shift:布尔值,表示是否执行窗口移位操作(SW-MSA需要移位,W-MSA不需要)。

如果shift=True,会生成注意力掩码,用于屏蔽移位后跨窗口的无效注意力连接。代码如下:

python 复制代码
if self.shift_size:
    img_mask = torch.zeros((1, 1, self.patch_num, 1))
    w_slices = (slice(0, -self.window_size),
                slice(-self.window_size, -self.shift_size),
                slice(-self.shift_size, None))
    cnt = 0
    for w in w_slices:
        img_mask[:, :, w, :] = cnt
        cnt += 1
    mask_windows = img_mask.reshape(1, 1, self.window_num, self.window_size, 1)
    mask_windows = mask_windows.view(-1, self.window_size)
    self.attn_mask = mask_windows.unsqueeze(1) - mask_windows.unsqueeze(2)
    self.attn_mask = self.attn_mask.masked_fill(self.attn_mask != 0, float(-100.0)).masked_fill(self.attn_mask == 0, float(0.0))
详细解释:
  1. img_mask
    • 初始化一个形状为[1, 1, patch_num, 1]的零张量,用于标记每个patch所属的窗口区域。
  2. w_slices
    • 定义三个切片,表示移位后patch的不同区域:
      • slice(0, -self.window_size):移位前的前部窗口。
      • slice(-self.window_size, -self.shift_size):移位后的中间过渡区域。
      • slice(-self.shift_size, None):移位后的后部窗口。
  3. 标记窗口
    • 通过循环为每个切片区域分配一个唯一的cnt值(0、1、2),标记不同的窗口。
  4. mask_windows
    • img_mask重塑为[1, 1, window_num, window_size, 1],再展平为[-1, window_size],表示每个窗口内的patch。
  5. self.attn_mask
    • 通过广播计算窗口内patch之间的差值,形状为[window_num, window_size, window_size]
    • 使用masked_fill操作:
      • 若差值不为0(跨窗口),设为-100.0,屏蔽注意力权重。
      • 若差值为0(同一窗口内),设为0.0,保留注意力权重。

1.2 前向传播

前向传播的实现分为以下步骤:

  1. 补齐patch数量

    • 使用nn.ZeroPad2dpatch_num维度上补零,确保patch_num能被window_size整除。
    • 计算窗口数量:window_num = patch_num // window_size
  2. 窗口移位

    • shift=True,使用torch.roll沿patch_num维度循环移位-self.shift_size个位置,实现SW-MSA的窗口移位。
  3. 窗口化

    • 将输入张量(假设形状为[batch, modal_leng, patch_num, input_dim])重塑为[batch, modal_leng, window_num, window_size, input_dim],以便在每个窗口内独立计算注意力。
  4. QKV计算

    python 复制代码
    q = self.query(window_x).reshape(batch, modal_leng, window_num, self.window_size, self.head_num, self.att_size).permute(0, 1, 2, 4, 3, 5)
    k = self.key(window_x).reshape(batch, modal_leng, window_num, self.window_size, self.head_num, self.att_size).permute(0, 1, 2, 4, 5, 3)
    v = self.value(window_x).reshape(batch, modal_leng, window_num, self.window_size, self.head_num, self.att_size).permute(0, 1, 2, 4, 3, 5)
    • 通过线性层self.queryself.keyself.value生成Q、K、V。
    • 重塑为多头形式([batch, modal_leng, window_num, window_size, head_num, att_size])。
    • 使用permute调整维度顺序,便于后续矩阵乘法。
  5. 注意力计算

    python 复制代码
    attn = torch.matmul(q, k) / self.att_size ** 0.5
    if self.shift_size:
        attn = attn + self.attn_mask.unsqueeze(1).unsqueeze(0).unsqueeze(0).to(attn.device)
    z = torch.matmul(nn.Softmax(dim=-1)(attn), v)
    • 计算Q和K的点积,缩放因子为1 / sqrt(att_size)
    • shift=True,添加注意力掩码,屏蔽跨窗口的连接。
    • 对注意力权重应用Softmax归一化,沿最后一个维度计算。
    • 将归一化后的注意力权重与V相乘,得到注意力输出z
  6. 反窗口化

    • z重塑回[batch, modal_leng, patch_num, head_num * att_size]
    • 通过线性层att_mlp恢复到[batch, modal_leng, patch_num, input_dim]
  7. 窗口回移

    • shift=True,使用torch.roll沿patch_num维度循环移位self.shift_size个位置,恢复原始patch顺序。
  8. 残差连接与FFN

    python 复制代码
    z = nn.ReLU()(short_cut + z)
    out = nn.ReLU()(z + self.forward_mlp(z))
    • 将注意力输出与输入的残差连接(short_cut)相加,应用ReLU激活。
    • 通过前馈网络self.forward_mlp(通常为两层MLP)处理,再次残差连接并激活。
  9. Patch Merging

    • shift=True,调用patch_merging方法,将patch_num降采样为原来的一半(例如通过线性层或卷积实现),实现时间轴分辨率的逐步降低。

2. SwinTransformer

SwinTransformer类封装了整个模型,包括输入嵌入、多阶段Swin Transformer块和最终分类层。

2.1 初始化

初始化时计算patch数量并定义各层:

python 复制代码
self.patch_num = self.series_leng // patch_size
self.swa = nn.Sequential(
    nn.Sequential(
        ShiftWindowAttentionBlock(patch_num=swin_transformer_block1_input_patch_num, ...),
        ShiftWindowAttentionBlock(patch_num=swin_transformer_block1_input_patch_num, ..., shift=True)
    ),
    nn.Sequential(
        ShiftWindowAttentionBlock(patch_num=swin_transformer_block2_input_patch_num, ...),
        ShiftWindowAttentionBlock(patch_num=swin_transformer_block2_input_patch_num, ..., shift=True)
    ),
    nn.Sequential(
        ShiftWindowAttentionBlock(patch_num=swin_transformer_block3_input_patch_num, ...),
        ShiftWindowAttentionBlock(patch_num=swin_transformer_block3_input_patch_num, ..., shift=True)
    )
)
详细解释:
  • self.patch_num
    • 输入序列长度series_leng除以补丁大小patch_size,得到初始patch数量。
  • self.swa
    • 一个nn.Sequential模块,包含三个阶段的Swin Transformer块。
    • 每个阶段包含两个ShiftWindowAttentionBlock
      • 第一个块shift=False,执行W-MSA。
      • 第二个块shift=True,执行SW-MSA并降采样。
    • 三个阶段逐步将时间轴分辨率降至1/8(例如从patch_numpatch_num//2,再到patch_num//4,最后到patch_num//8)。

2.2 前向传播

python 复制代码
def forward(self, x):
    x = self.patch_conv(x)
    x = self.position_embedding + x.permute(0, 3, 2, 1)
    x = self.swa(x)
    x = nn.Flatten()(x)
    x = self.dense_tower(x)
    return x
详细解释:
  1. 补丁嵌入
    • self.patch_conv(x):通过卷积层将输入[batch, 1, series, modal]转换为[batch, embedding_dim, patch_num, modal_leng],实现补丁嵌入。
  2. 位置编码
    • x.permute(0, 3, 2, 1):调整形状为[batch, modal_leng, patch_num, embedding_dim]
    • self.position_embedding相加,添加位置信息。
  3. Swin Transformer 块
    • self.swa(x):顺序执行三个阶段的Swin Transformer块,逐步提取多尺度特征并降采样。
  4. 展平与分类
    • nn.Flatten()(x):将输出展平为[batch, modal_leng * final_patch_num * embedding_dim]
    • self.dense_tower(x):通过全连接层(例如MLP)进行分类或回归,输出最终结果。

3. 完整代码

python 复制代码
import torch
import torch.nn as nn
import math

'''Swin Transformer'''
class ShiftWindowAttentionBlock(nn.Module):
    def __init__(self, patch_num, input_dim=512, head_num=4, att_size=64, window_size=4, shift=False):
        super().__init__()
        self.patch_num = patch_num
        self.head_num = head_num
        self.att_size = att_size
        self.window_size = window_size
        self.window_num = self.patch_num // self.window_size # window_num
        self.shift_size = window_size//2 if shift else 0
        self.query = nn.Linear(input_dim, head_num * att_size)
        self.key = nn.Linear(input_dim, head_num * att_size)
        self.value = nn.Linear(input_dim, head_num * att_size)

        # 判断是否使用窗口移位,如果使用窗口移位,会使用mask windows
        if self.shift_size: 
            img_mask = torch.zeros((1, 1, self.patch_num, 1)) # [1, 1, patch_num, 1]
            w_slices = (slice(0, -self.window_size),
                        slice(-self.window_size, -self.shift_size),
                        slice(-self.shift_size, None))
            cnt = 0
            for w in w_slices:
                img_mask[:, :, w, :] = cnt
                cnt += 1
            mask_windows = img_mask.reshape(1, 1, self.window_num, self.window_size, 1)   # [1, 1, window_num, window_size, 1]
            mask_windows = mask_windows.view(-1, self.window_size)  # [window_num, window_size]
            self.attn_mask = mask_windows.unsqueeze(1) - mask_windows.unsqueeze(2)  # [window_num, window_size, window_size]
            self.attn_mask = self.attn_mask.masked_fill(self.attn_mask != 0, float(-100.0)).masked_fill(self.attn_mask == 0, float(0.0)) # [window_num, window_size, window_size]

        # 多头自注意力后从att_size恢复成input_dim
        self.att_mlp = nn.Sequential( 
            nn.Linear(head_num*att_size, input_dim),
            nn.LayerNorm(input_dim)
        )

        # 多头自注意力后后进行前向全连接
        self.forward_mlp = nn.Sequential( 
            nn.Linear(input_dim, input_dim),
            nn.LayerNorm(input_dim)
        )

        # 每两个ShiftWindowAttentionBlock(即每个swin_transformer_block)进行一次1/2降采样并恢复维度
        if self.shift_size:
            self.downsample_mlp = nn.Sequential( 
                nn.Linear(input_dim*2, input_dim),
                nn.LayerNorm(input_dim)
            )
    
    def forward(self, x):
        '''
            x: [batch, modal_leng, patch_num, input_dim]
        '''
        # patch_num补成能够被window_size整除
        if x.size(-2) % self.window_size:
            x = nn.ZeroPad2d((0, 0, 0, self.window_size - x.size(-2) % self.window_size))(x)

        batch, modal_leng, patch_num, input_dim = x.size()
        short_cut = x # resdual
        
        # 窗口偏移
        if self.shift_size:
            x = torch.roll(x, shifts=-self.shift_size, dims=2) # 只在 patch_num 上 roll   [batch, modal_leng, patch_num, input_dim]

        # 窗口化 
        window_num = patch_num // self.window_size
        window_x = x.reshape(batch, modal_leng, window_num, self.window_size, input_dim) # [batch, modal_leng, window_num, window_size, input_dim]

        # 基于窗口的多头自注意力
        q = self.query(window_x).reshape(batch, modal_leng, window_num, self.window_size, self.head_num, self.att_size).permute(0, 1, 2, 4, 3, 5) # [batch, modal_leng, window_num, head_num, window_size, att_size]
        k = self.key(window_x).reshape(batch, modal_leng, window_num, self.window_size, self.head_num, self.att_size).permute(0, 1, 2, 4, 5, 3) # [batch, modal_leng, window_num, head_num, att_size, window_size]
        v = self.value(window_x).reshape(batch, modal_leng, window_num, self.window_size, self.head_num, self.att_size).permute(0, 1, 2, 4, 3, 5) # [batch, modal_leng, window_num, head_num, window_size, att_size]
        attn = torch.matmul(q, k) / self.att_size ** 0.5       # [batch, modal_leng, window_num, head_num, window_size, window_size]
        if self.shift_size: # 判断是否使用窗口移位,如果使用窗口移位,会使用mask windows
            attn = attn + self.attn_mask.unsqueeze(1).unsqueeze(0).unsqueeze(0).to(attn.device) # [batch, modal_leng, window_num, head_num, window_size, window_size] + [1, 1, window_num, 1, window_size, window_size]
        z = torch.matmul(nn.Softmax(dim=-1)(attn), v) # [batch, modal_leng, window_num, head_num, window_size, att_size]
        
        # 反窗口化 
        z = z.permute(0, 1, 2, 4, 3, 5).reshape(batch, modal_leng, window_num*self.window_size, self.head_num * self.att_size) # [batch, modal_leng, window_num*window_size, head_num * att_size]
        z = self.att_mlp(z) # [batch, modal_leng, patch_num, input_dim]
       
        # 窗口回移
        if self.shift_size:
            z = torch.roll(z, shifts=self.shift_size, dims=2) # 只在 patch_num 上 roll   [batch, modal_leng, patch_num, input_dim]
       
        # resdual
        z = nn.ReLU()(short_cut + z) # [batch, modal_leng, patch_num, input_dim]
        
        # mlp + resdual
        out = nn.ReLU()(z + self.forward_mlp(z)) # [batch, modal_leng, patch_num, input_dim]
        
        # 遇到一次窗口偏移就进行 1/2 降采样
        if self.shift_size:
            out = self.patch_merging(out) # [batch, modal_leng, patch_num/2, input_dim]
        return out

    def patch_merging(self, x):
        batch, modal_leng, patch_num, input_dim = x.shape
        if patch_num % 2: # patch_num补成偶数方便1/2降采样
            x = nn.ZeroPad2d((0, 0, 0, 1))(x)
        x0 = x[:, :, 0::2, :] # [batch, modal_leng, patch_num / 2, input_dim]
        x1 = x[:, :, 1::2, :] # # [batch, modal_leng, patch_num / 2, input_dim]
        x = torch.cat([x0, x1], dim=-1) # [batch, modal_leng, patch_num / 2, input_dim * 2]
        x = nn.ReLU()(self.downsample_mlp(x)) # [batch, modal_leng, patch_num / 2, input_dim]
        return x


class SwinTransformer(nn.Module):
    def __init__(self, train_shape, category, embedding_dim=256, patch_size=4, head_num=4, att_size=64, window_size=8):
        super().__init__()
        self.series_leng = train_shape[-2]
        self.modal_leng = train_shape[-1]
        self.patch_num = self.series_leng // patch_size
        
        self.patch_conv = nn.Conv2d(
            in_channels=1,
            out_channels=embedding_dim,
            kernel_size=(patch_size, 1),
            stride=(patch_size, 1),
            padding=0
        )

        # 位置信息
        self.position_embedding = nn.Parameter(torch.zeros(1, self.modal_leng, self.patch_num, embedding_dim))

        # patch_num维度降采样一次后的计算方式
        swin_transformer_block1_input_patch_num = math.ceil(self.patch_num / window_size) * window_size
        swin_transformer_block2_input_patch_num = math.ceil(math.ceil(swin_transformer_block1_input_patch_num / 2) / window_size) * window_size
        swin_transformer_block3_input_patch_num = math.ceil(math.ceil(swin_transformer_block2_input_patch_num / 2) / window_size) * window_size

        # Shift_Window_Attention_Layer
        # 共3个swin_transformer_block,每个swin_transformer_block对时序维降采样1/2,共降采样1/8
        self.swa = nn.Sequential(
            # swin_transformer_block 1
            nn.Sequential( 
                ShiftWindowAttentionBlock(patch_num=swin_transformer_block1_input_patch_num, input_dim=embedding_dim, head_num=head_num, att_size=att_size, window_size=window_size, shift=False),
                ShiftWindowAttentionBlock(patch_num=swin_transformer_block1_input_patch_num, input_dim=embedding_dim, head_num=head_num, att_size=att_size, window_size=window_size, shift=True)
            ),
            # swin_transformer_block 2
            nn.Sequential(
                ShiftWindowAttentionBlock(patch_num=swin_transformer_block2_input_patch_num, input_dim=embedding_dim, head_num=head_num, att_size=att_size, window_size=window_size, shift=False),
                ShiftWindowAttentionBlock(patch_num=swin_transformer_block2_input_patch_num, input_dim=embedding_dim, head_num=head_num, att_size=att_size, window_size=window_size, shift=True)
            ),
            # swin_transformer_block 3
            nn.Sequential(
                ShiftWindowAttentionBlock(patch_num=swin_transformer_block3_input_patch_num, input_dim=embedding_dim, head_num=head_num, att_size=att_size, window_size=window_size, shift=False),
                ShiftWindowAttentionBlock(patch_num=swin_transformer_block3_input_patch_num, input_dim=embedding_dim, head_num=head_num, att_size=att_size, window_size=window_size, shift=True)
            )
        )

        # classification tower
        self.dense_tower = nn.Sequential(
            nn.Linear(self.modal_leng * math.ceil(swin_transformer_block3_input_patch_num / 2) * embedding_dim, 1024),
            nn.LayerNorm(1024),
            nn.ReLU(),
            nn.Linear(1024, category)
        )
    
    def forward(self, x):
        '''
            x.shape: [b, c, series, modal]
        '''
        x = self.patch_conv(x) # [batch, embedding_dim, patch_num, modal_leng]
        x = self.position_embedding + x.permute(0, 3, 2, 1) 
        x = self.swa(x)
        x = nn.Flatten()(x)
        x = self.dense_tower(x)
        return x

Swin Transformer通过窗口化自注意力(W-MSA)和移位窗口自注意力(SW-MSA)机制,将Transformer的计算复杂度从全局平方级降低到线性级别,同时保留了全局建模能力。其层次化设计使其在多尺度特征提取上表现出色。其在时间序列数据上的应用具有很高的灵活性和实用性,特别适合多模态传感器数据的分析,下面我们将以UCI-HAR数据集为例,实现Swin Transformer在UCI-HAR上的实际应用结果。

四、UCI-HAR数据集实战结果

UCI-HAR 数据集由加州大学尔湾分校实验团队构建,是行为识别领域的经典基准数据集。其数据采集依托三星 Galaxy S2 智能手机内置的加速度计与陀螺仪传感器,以 50Hz 的采样频率记录人体运动特征。该数据集包含 30 名年龄在 19-48 岁的受试者,通过腰部佩戴设备完成六种日常活动:步行、上楼、下楼、静坐、站立和静躺。本研究采用滑动窗口技术对原始数据进行预处理,设置窗口长度为 128 个采样点,滑动步长 64 个采样点(即 50% 的窗口重叠率),最终形成 748,406 个样本单元。

1.训练结果

| Parameters | 11,896,838 | | FLOPs | 375.40 M | | Inference Time | 4.50 ms | | Val Accuracy | 0.9627 | | Test Accuracy | 0.9680 | | Accuracy | 0.9680 | | Precision | 0.9681 | | Recall | 0.9680 | | F1-score | 0.9679 |

模型在测试集上达到了 0.9680 的高准确率,同时精确率、召回率和 F1 分数均接近 0.9680,显示出模型在分类性能上的均衡性和稳定性。此外,推理时间仅为 4.50 毫秒,FLOPs 为 375.40 百万次,表明模型具有较高的计算效率。

2.每个类别的准确率

每个类别的准确率: Walking: 0.9767 Jogging: 0.9351 Sitting: 0.9433 Standing: 0.9775 Upstairs: 0.9634 Downstairs: 1.0000 从结果来看,模型在所有类别上均表现出色,尤其是 "Downstairs" 类别达到了 1.0000 的完美准确率,而 "Jogging" 类别准确率最低为 0.9351,可能是由于该活动与其他活动的动作特征较为接近,导致少量误分类。

3.混淆矩阵图及准确率和损失曲线图

归一化混淆矩阵展示了模型在 UCI-HAR 数据集上的分类性能,具体针对六个活动类别:Walking、Jogging、Sitting、Standing、Upstairs 和 Downstairs。矩阵为 6×6 网格,真值标签位于纵轴(行),预测标签位于横轴(列),每个单元格表示预测的归一化比例(取值范围为 0 至 1):

训练和验证的准确率及损失曲线图展示了模型在 100 个 epoch 内的性能变化:

模型在前 10 个 epoch 内快速学习数据的基本模式,随后进入细化阶段,准确率持续提升,损失稳步下降。训练与验证指标间的较小差距表明模型泛化能力强,过拟合程度较低。 本研究基于 SWIN 模型对 UCI-HAR 数据集进行了人体活动识别实验,取得了显著成果。模型在测试集上实现了 0.9680 的准确率,精确率为 0.9681,召回率为 0.9680,F1 分数为 0.9679,展现出优异的分类性能。每个类别的准确率分析显示,模型在六种活动中均表现良好,未来研究可进一步优化模型架构或引入高级数据增强技术,以提升对 "Jogging" 等较难类别的分类性能。同时也可以继续跟进实验,展示模型在更大规模数据集上的表现。

相关推荐
幻风_huanfeng5 分钟前
人工智能之数学基础:矩阵的相似变换的本质是什么?
人工智能·深度学习·线性代数·机器学习·矩阵·相似变换
把你拉进白名单17 分钟前
DIT详解
深度学习·aigc
把你拉进白名单21 分钟前
AE, VAE和VQ-VAE有什么区别?
深度学习·aigc
霖大侠2 小时前
REVISITING MAE PRE-TRAINING FOR 3D MEDICALIMAGE SEGMENTATION
人工智能·深度学习·机器学习·3d
WHATEVER_LEO2 小时前
【每日论文】MetaSpatial: Reinforcing 3D Spatial Reasoning in VLMs for the Metaverse
人工智能·深度学习·神经网络·计算机视觉·3d·自然语言处理
橙色小博2 小时前
最最最基本神经网络及其原理、程序
人工智能·深度学习·神经网络
import_random2 小时前
[机器学习]选型
机器学习
大模型真好玩2 小时前
新王登基!DeepSeek-V3-0324横空出世,国产大模型还得看DeepSeek(详细DeepSeek-V3-0324模型评测)
人工智能·深度学习·deepseek
HGCN20243 小时前
365打卡第J7周:对于ResNeXt-50算法的思考
python·深度学习·算法
wgc2k3 小时前
吴恩达深度学习复盘(1)神经网络与深度学习的发展
人工智能·深度学习