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包含以下步骤:
- Layer Normalization:对输入进行归一化。
- W-MSA或SW-MSA:奇数层使用窗口化自注意力(W-MSA),偶数层使用移位窗口自注意力(SW-MSA)。
- 残差连接:将注意力输出与输入相加。
- Layer Normalization:再次归一化。
- 前馈网络(FFN):通过两层MLP处理特征。
- 残差连接:将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))
详细解释:
img_mask
:- 初始化一个形状为
[1, 1, patch_num, 1]
的零张量,用于标记每个patch所属的窗口区域。
- 初始化一个形状为
w_slices
:- 定义三个切片,表示移位后patch的不同区域:
slice(0, -self.window_size)
:移位前的前部窗口。slice(-self.window_size, -self.shift_size)
:移位后的中间过渡区域。slice(-self.shift_size, None)
:移位后的后部窗口。
- 定义三个切片,表示移位后patch的不同区域:
- 标记窗口 :
- 通过循环为每个切片区域分配一个唯一的
cnt
值(0、1、2),标记不同的窗口。
- 通过循环为每个切片区域分配一个唯一的
mask_windows
:- 将
img_mask
重塑为[1, 1, window_num, window_size, 1]
,再展平为[-1, window_size]
,表示每个窗口内的patch。
- 将
self.attn_mask
:- 通过广播计算窗口内patch之间的差值,形状为
[window_num, window_size, window_size]
。 - 使用
masked_fill
操作:- 若差值不为0(跨窗口),设为
-100.0
,屏蔽注意力权重。 - 若差值为0(同一窗口内),设为
0.0
,保留注意力权重。
- 若差值不为0(跨窗口),设为
- 通过广播计算窗口内patch之间的差值,形状为
1.2 前向传播
前向传播的实现分为以下步骤:
-
补齐patch数量:
- 使用
nn.ZeroPad2d
在patch_num
维度上补零,确保patch_num
能被window_size
整除。 - 计算窗口数量:
window_num = patch_num // window_size
。
- 使用
-
窗口移位:
- 若
shift=True
,使用torch.roll
沿patch_num
维度循环移位-self.shift_size
个位置,实现SW-MSA的窗口移位。
- 若
-
窗口化:
- 将输入张量(假设形状为
[batch, modal_leng, patch_num, input_dim]
)重塑为[batch, modal_leng, window_num, window_size, input_dim]
,以便在每个窗口内独立计算注意力。
- 将输入张量(假设形状为
-
QKV计算:
pythonq = 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.query
、self.key
、self.value
生成Q、K、V。 - 重塑为多头形式(
[batch, modal_leng, window_num, window_size, head_num, att_size]
)。 - 使用
permute
调整维度顺序,便于后续矩阵乘法。
- 通过线性层
-
注意力计算:
pythonattn = 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
。
- 计算Q和K的点积,缩放因子为
-
反窗口化:
- 将
z
重塑回[batch, modal_leng, patch_num, head_num * att_size]
。 - 通过线性层
att_mlp
恢复到[batch, modal_leng, patch_num, input_dim]
。
- 将
-
窗口回移:
- 若
shift=True
,使用torch.roll
沿patch_num
维度循环移位self.shift_size
个位置,恢复原始patch顺序。
- 若
-
残差连接与FFN:
pythonz = nn.ReLU()(short_cut + z) out = nn.ReLU()(z + self.forward_mlp(z))
- 将注意力输出与输入的残差连接(
short_cut
)相加,应用ReLU激活。 - 通过前馈网络
self.forward_mlp
(通常为两层MLP)处理,再次残差连接并激活。
- 将注意力输出与输入的残差连接(
-
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_num
到patch_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
详细解释:
- 补丁嵌入 :
self.patch_conv(x)
:通过卷积层将输入[batch, 1, series, modal]
转换为[batch, embedding_dim, patch_num, modal_leng]
,实现补丁嵌入。
- 位置编码 :
x.permute(0, 3, 2, 1)
:调整形状为[batch, modal_leng, patch_num, embedding_dim]
。- 与
self.position_embedding
相加,添加位置信息。
- Swin Transformer 块 :
self.swa(x)
:顺序执行三个阶段的Swin Transformer块,逐步提取多尺度特征并降采样。
- 展平与分类 :
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" 等较难类别的分类性能。同时也可以继续跟进实验,展示模型在更大规模数据集上的表现。