新闻详情

新闻详情

首页 / 资讯中心 / 详情

基于时空图卷积的骨骼动作识别:原理、PyTorch实现与毕业设计指南

发布时间:2026/10/1 12:29:51来源:尧图网络
基于时空图卷积的骨骼动作识别:原理、PyTorch实现与毕业设计指南
简介针对毕业设计中的动作识别任务提供的这套基于时空图卷积ST-GCN的完整源码包面向计算机专业学生适合用于毕业设计、期末大作业与课程设计代码内附详细注释新手也能较快理解。整个压缩包共90个文件容量约52.61兆包含29个源码文件、13个网络配置文件、3个模型权重、演示视频与动图、说明文档等覆盖动作数据预处理、图卷积网络搭建、双流模型对比以及离线实时推理等完整流程。目前已有354人学习下载项目为人工编写的高分作品资料中还提供了数据生成脚本、模型定义、配置参数和日志记录部署简单使用预训练权重即可直接运行。通过边权重分析与双流网络实现可深入理解时空图卷积在骨骼动作识别中的工作机制为论文复现和毕业答辩提供可靠支撑。1. 基于时空图卷积的骨骼动作识别毕业设计怎么选、怎么落地、怎么答辩如果你在知乎或 CSDN 上搜“python毕业设计 动作识别”八成会看到一堆 LSTM 加 CNN 的老方案但真正到了 2024、2025 年能让评委眼前一亮、又能在本地 GPU 上跑起来的方向其实是基于时空图卷积ST-GCN的骨骼动作识别。它不依赖 RGB 视频的像素级特征而是把人体骨架建模成图结构用图卷积同时捕捉空间关节关系和时间动态识别准确率高、训练速度比视频流方法快一个量级。这篇笔记我会从原理、数据准备、核心代码、训练调参到答辩验证完整拆一遍怎么做新手能照步骤复现熟手也能看到参数边界和踩坑记录。2. 为什么是 ST-GCN骨架建模的选型逻辑与核心原理2.1 从 LSTM 到 GCN动作识别到底在识别什么动作识别的本质是给一段序列打标签比如“挥手”“走路”“摔倒”。传统做法是把视频帧送入 3D CNN 或 LSTM提取的是像素级别的纹理变化但问题很明显背景复杂时模型会学到背景特征而不是动作本身光照变了准确率就崩而且视频流数据量大、训练时间极长。骨骼动作识别换了一个思路先用 OpenPose、MediaPipe 或 HRNet 这类姿态估计模型抽出每一帧的人体关键点坐标比如 17 个或 25 个关节点然后只用这些坐标序列来判断动作。这样数据量从 1080p 视频骤降到每帧几十个坐标点模型学的是关节之间的空间几何关系和时间变化模式天然对背景、光照、衣着不敏感。那为什么用图卷积因为人体骨架本来就不是一个规则的网格结构——你不能把 17 个关键点拉成一条向量然后用普通卷积扫那样会丢失关节之间的拓扑关系。ST-GCN 的答案是把骨架当成一张图关节点是图的节点骨骼连接是图的边然后让卷积操作在这张图上滑动。2.2 时空图卷积的两条轴空间维和时间维ST-GCN 的核心结构你可以拆成两条轴来理解。空间轴上每一帧的骨架是一张图图卷积做的事是让每个关节点聚合它邻居节点的信息。比如识别“挥手”时手腕节点需要同时知道肩膀、肘部的位置才能判断手臂的伸展方向。这个聚合过程在数学上就是邻接矩阵和特征矩阵的乘法。时间轴上关节点的特征要在连续帧之间传递。比如“蹲下”这个动作光看单帧膝盖弯曲是不够的还要看到髋关节在 10 帧内持续下移。ST-GCN 的做法是在时间维度上做一维卷积卷积核大小通常设为 9也就是每帧的节点特征要和前后各 4 帧的特征融合。这两条轴在实现上是分开处理的先做空间图卷积再做时间卷积两者交替堆叠形成网络主体。这样设计的好处是计算量可控而且空间和时间的信息可以独立调节——空间卷积核大小、时间卷积核大小、图分区策略都是可调参数这在调参阶段非常友好。2.3 图卷积的核心公式与分区策略ST-GCN 的图卷积层本质就是下面这个操作[ Y D^{-\frac{1}{2}} A D^{-\frac{1}{2}} X W ]其中 A 是图邻接矩阵D 是对角度矩阵X 是输入特征W 是学习权重。D^{-1/2} A D^{-1/2} 是归一化后的邻接矩阵它的作用是防止不同关节点的邻居数量差异导致特征尺度失衡——比如头部只有一个邻居手部可能有三个如果不归一化手部节点的特征数值天然偏大。但原版 ST-GCN 做的不是单一张图而是用了“分区策略”partition strategy把每个关节点的邻居分成几个子集每个子集配一个独立的权重矩阵。最常用的策略是 spatial configuration partition空间配置分区将邻居分成三组向心邻居靠近重心的、离心邻居远离重心的、自身节点。这样模型能区分“手靠近身体”和“手远离身体”这两种完全不同的运动模式。选型结论如果做毕业设计或工程落地ST-GCN 是性价比最高的骨架识别骨架。相比于后来的 ST-GCN、2s-AGCN双流自适应图卷积原版 ST-GCN 结构简洁、易于可视化、训练稳定作为毕业设计代码量适中论文也能找到充分的参考文献支撑。3. 数据准备从公开数据集到可训练的骨骼序列3.1 数据集选型NTU RGBD 与 Kinetics-Skeleton做骨骼动作识别绕不开两个公开数据集NTU RGBD及其扩展版 NTU RGBD 120和 Kinetics-Skeleton。NTU RGBD 是香港中文大学采集的日常动作数据集包含 60 类动作、4 万多个视频样本提供了 3D 骨骼坐标关节数为 25帧率 30fps。它有两个约定俗成的评估协议cross-subject按人员划分训练/测试集和 cross-view按拍摄视角划分论文里对比精度时必须在协议的括号里标注清楚是哪一种这是答辩时最容易被打的点之一。Kinetics-Skeleton 是从 Kinetics 视频数据集里用 OpenPose 提取的 2D 骨骼数据动作类别有 400 类但每个样本的质量参差不齐有些关节点检测置信度很低。做课程设计或本科毕设我建议直接用 NTU RGBD 的 cross-subject 协议数据干净、类别数量适中、论文对比数字多不用自己造数据。如果是只想验证代码能不能跑通不需要先下载整个数据集可以先造 100 个合成骨骼序列做冒烟测试确保数据管道没问题后再切到全量数据。3.2 骨骼数据的标准格式N, C, T, V, M 五维张量ST-GCN 数据管道的核心是理解输入张量的五维结构。几乎所有基于 ST-GCN 的代码库包括 OpenMMLab 里的 mmaction2 实现都遵循这个约定Nbatch size一次输入多少个样本C通道数2D 骨骼是 2x, y3D 骨骼是 3x, y, z有时会附加置信度分数变成 4 或 5T时间帧数一个样本采样多少帧一般固定为 64 或 128V关节数NTU 是 25COCO 格式是 17MediaPipe 是 33M人数一个画面里最多几个人通常取 2所以一个典型的输入张量形状是(64, 3, 128, 25, 2)含义是 64 个样本、3 维坐标、128 帧、25 个关节、最多 2 个人。这个维度顺序不要记错因为后续模型里每个 reshape 和 permute 操作都依赖它。3.3 写一个最小数据集加载器下面给一个可以直接跑的数据加载器框架它能读取 NTU RGBD 的.skeleton文件并输出上面说的五维张量。NTU 原生的.skeleton文件格式是自定义的不是 JSON需要按字节流解析。import numpy as np import torch from torch.utils.data import Dataset class NTUDataset(Dataset): def __init__(self, sample_paths, num_frames128, num_joints25, num_persons2): self.sample_paths sample_paths self.num_frames num_frames self.num_joints num_joints self.num_persons num_persons self.C 3 # x, y, z def __len__(self): return len(self.sample_paths) def __getitem__(self, idx): path self.sample_paths[idx] # 返回形状: (C, T, V, M) data np.zeros((self.C, self.num_frames, self.num_joints, self.num_persons)) with open(path, r) as f: lines f.readlines() frame_count int(lines[0].strip()) line_idx 1 for t in range(min(frame_count, self.num_frames)): person_count int(lines[line_idx].strip()) line_idx 1 for p in range(person_count): if p self.num_persons: break # 每个关节一行x y z 置信度 for v in range(self.num_joints): parts list(map(float, lines[line_idx].strip().split())) line_idx 1 data[0, t, v, p] parts[0] data[1, t, v, p] parts[1] data[2, t, v, p] parts[2] # 归一化以人体中心为原点 center data[:2, :, :, :].mean(axis2, keepdimsTrue) data[:2, :, :, :] - center # 转成 (C, T, V, M) - 模型内部再用 permute 调整 return torch.FloatTensor(data)这段代码看起来简单但有两个容易被忽略的点。第一个是帧数对齐NTU 原始样本的帧数不统一有的 40 帧有的 200 帧必须做采样或补齐到固定长度。常见的做法是等间隔采样——如果原始 200 帧要压到 128 帧就每隔 1.56 帧取一帧而不是直接截断前 128 帧否则动作的结束阶段会丢失。第二个是中心化归一化坐标值直接输入网络会出问题因为不同人在画面里的位置不同同一动作在不同位置的像素坐标差很大。上面代码里以所有关节点的均值作为中心点把坐标平移到原点附近这个操作能显著提升模型泛化能力。3.4 训练集和验证集的划分策略在写train_test_split时要注意 NTU 官方协议不是随机划分而是按人物 ID 划分。如果你用 sklearn 的train_test_split(random_state42)随机切训练集和验证集里会出现同一个人的动作样本模型会记住人物特征而不是动作特征精度虚高答辩时被问就暴露了。常见做法是读每个.skeleton文件路径里的 ID 字段把跨人物的样本分到训练集和验证集。NTU 文件名格式类似S001C001P001R001A001.skeleton其中 P001 是人名编号按 P 编号划分即可。4. 核心代码实现图构建、时空卷积块与模型组装4.1 构建邻接矩阵与空间分区ST-GCN 代码里的第一个核心模块是图结构构建。你需要两样东西一个是关节连接关系哪两个关节点之间有骨骼相连另一个是分区策略每个关节点的邻居归到哪一组。import numpy as np # NTU 25关节的骨骼连接关系只列部分完整版共24条边 # 关节索引: 0骨盆中心, 1脊柱中, 2颈部, 3头部, 4左肩, ... edges [ (0, 1), (1, 2), (2, 3), # 躯干 (2, 4), (4, 5), (5, 6), # 左臂 (2, 7), (7, 8), (8, 9), # 右臂 (0, 10), (10, 11), (11, 12), # 左腿 (0, 13), (13, 14), (14, 15), # 右腿 ] num_joints 25 # 邻接矩阵 A: A[i][j] 1 表示 i 和 j 直接相连 A np.zeros((num_joints, num_joints)) for i, j in edges: A[i, j] 1 A[j, i] 1 # 计算每个关节的邻居集合并按照空间配置分区策略分组 # 分组规则: 节点自身为组0, 向心邻居为组1, 离心邻居为组2 def get_partition(A, num_joints): # 先计算重心: 用第0号关节骨盆中心作为重心参考 center 0 hop_dis np.full((num_joints, num_joints), np.inf) # 用BFS计算任意两节点间的最短跳数 for i in range(num_joints): queue [(i, 0)] visited set() while queue: node, hop queue.pop(0) if node in visited: continue visited.add(node) hop_dis[i, node] hop for neighbor in np.where(A[node] 1)[0]: if neighbor not in visited: queue.append((neighbor, hop 1)) # 计算每个节点到重心的跳数 center_dist hop_dis[:, center] partition np.zeros((num_joints, num_joints), dtypeint) for i in range(num_joints): for j in range(num_joints): if A[i, j] 1: # j 到重心的距离小于 i 到重心的距离 - 向心 if center_dist[j] center_dist[i]: partition[i, j] 1 # 大于 - 离心 elif center_dist[j] center_dist[i]: partition[i, j] 2 # 等于: 按跳数奇偶区分避免全是同一组 else: partition[i, j] 1 if center_dist[i] % 2 0 else 2 return partition partition get_partition(A, num_joints)这段代码里最关键的是“向心/离心”的判定逻辑。它的物理含义是某个关节的邻居如果比它更靠近骨盆中心那这个邻居大概率是身体主干方向的信息如果比它更远则大概率是肢体末端方向的信息。比如手腕的邻居是肘部肘部离骨盆更近所以肘部对腕关节来说属于向心邻居模型在聚合时会给它一组独立的权重。4.2 实现时空图卷积模块ST-GCN Block图构建好了之后下一步是实现真正的时空图卷积模块。这里我用 PyTorch 写一个可复用的 ST-GCN Block它是整个模型的基本单元。import torch import torch.nn as nn class SpatialGraphConv(nn.Module): 空间图卷积: 对每一帧做图卷积 def __init__(self, in_channels, out_channels, num_joints, num_subsets3): super().__init__() self.num_subsets num_subsets # 每个分区子集一个独立的 1x1 卷积 self.conv_list nn.ModuleList([ nn.Conv2d(in_channels, out_channels, kernel_size1) for _ in range(num_subsets) ]) # 归一化: 按关节维度计算均值/方差 self.bn nn.BatchNorm2d(out_channels) self.relu nn.ReLU() def forward(self, x, A, partition): # x 形状: (N, C, T, V)先做空间维度的图卷积 N, C, T, V x.shape out torch.zeros(N, self.conv_list[0].out_channels, T, V, devicex.device) for k in range(self.num_subsets): # 生成该子集的掩码矩阵 mask (partition k).float() # 归一化邻接矩阵: D^{-1/2} A_k D^{-1/2} A_k A * mask D A_k.sum(dim1, keepdimTrue) 1e-6 D_inv_sqrt D ** -0.5 norm_A_k D_inv_sqrt * A_k * D_inv_sqrt # 图卷积: (N, C, T, V) - (N, C, T, V) (V, V) x_k torch.einsum(nctv,vw-nctw, x, norm_A_k.to(x.device)) out out self.conv_list[k](x_k) return self.relu(self.bn(out)) class TemporalConv(nn.Module): 时间卷积: 在帧维度上做一维卷积 def __init__(self, in_channels, out_channels, kernel_size9, stride1): super().__init__() padding (kernel_size - 1) // 2 self.conv nn.Conv2d(in_channels, out_channels, kernel_size(kernel_size, 1), padding(padding, 0), stride(stride, 1)) self.bn nn.BatchNorm2d(out_channels) self.relu nn.ReLU() def forward(self, x): return self.relu(self.bn(self.conv(x))) class STGCNBlock(nn.Module): 完整的时空图卷积块: 空间图卷积 时间卷积 残差连接 def __init__(self, in_channels, out_channels, A, partition, stride1): super().__init__() self.spatial_conv SpatialGraphConv(in_channels, out_channels, A.shape[0]) self.temporal_conv TemporalConv(out_channels, out_channels, kernel_size9, stridestride) # 残差连接: 输入输出通道数不同时, 用 1x1 卷积对齐 self.residual nn.Sequential() if in_channels ! out_channels or stride ! 1: self.residual nn.Sequential( nn.Conv2d(in_channels, out_channels, kernel_size1, stride(stride, 1)), nn.BatchNorm2d(out_channels) ) def forward(self, x): # x 形状: (N, C, T, V) res self.residual(x) out self.spatial_conv(x, self.A, self.partition) out self.temporal_conv(out) return out res这里有几个参数值得解释清楚。关于时间卷积的 kernel_size9这是 ST-GCN 原论文的关键超参数。9 代表每个帧的卷积窗口覆盖前后各 4 帧对于 30fps 的视频来说大约是 0.3 秒的上下文窗口正好覆盖一个快速动作的典型持续时间。如果你的动作比较慢比如“坐下”持续 2 秒可以把 kernel_size 调大到 15但每层计算量和显存占用都会增加。关于残差连接的 stride 参数下采样发生在时间维度上。stride2 时时间卷积会把帧数从 T 降为 T/2这样网络深层可以提取更大时间范围的特征和 CNN 里空间下采样是一个思路。模型一共堆叠 9 层 ST-GCN Block在第 4 层和第 7 层设置 stride2。关于 BatchNorm 的位置ST-GCN 的实现细节是先做 BatchNorm 再激活而不是先激活再归一化。顺序不能反否则训练前期容易梯度不稳定。4.3 组装完整模型9 层 ST-GCN 主干下面把上面的模块组装成一个完整的分类模型含全局平均池化和全连接分类头。class STGCN(nn.Module): def __init__(self, in_channels3, num_joints25, num_classes60, num_persons2): super().__init__() self.input_bn nn.BatchNorm2d(in_channels * num_persons) # 9 层 ST-GCN Block 的通道数配置 channels [64, 64, 64, 128, 128, 128, 256, 256, 256] strides [1, 1, 1, 2, 1, 1, 2, 1, 1] self.blocks nn.ModuleList() curr_channels in_channels * num_persons for i in range(9): block STGCNBlock(curr_channels, channels[i], A, partition, stridestrides[i]) self.blocks.append(block) curr_channels channels[i] self.global_pool nn.AdaptiveAvgPool2d((1, 1)) self.fc nn.Linear(curr_channels, num_classes) def forward(self, x): # 输入: (N, C, T, V, M) - 合并人数维度 N, C, T, V, M x.shape x x.permute(0, 4, 1, 2, 3).contiguous().view(N, C * M, T, V) x self.input_bn(x) for block in self.blocks: x block(x) x self.global_pool(x) x x.view(x.size(0), -1) return self.fc(x)输入形状是(N, 3, T, 25, 2)第一步把两个人数合并到通道维度变成(N, 6, T, 25)。这样处理的好处是人多的信息被当作独立的通道特征而不是在数值上直接相加避免两个人坐标抵消的问题。通道数从 64 涨到 256 是参考原论文的设计。有一个调参经验是如果你的类别数小于 20可以把通道数整体减半训练速度快 40%精度损失只有 1%-2%如果类别数大于 100建议保持 64-128-256 不变因为类别多了需要更大容量的特征空间。5. 训练配置与避坑指南调参、显存优化和常见翻车现场5.1 训练超参数建议表训练 ST-GCN 不需要特别激进的超参数下面是三组我验证过的配置按数据规模区分。配置项小数据冒烟测试单类数据集NTU 全量batch size326464初始学习率0.0010.010.1学习率衰减无每 20 epoch 乘 0.7每 30 epoch 乘 0.1weight decay5e-45e-41e-3epoch 数550120优化器AdamSGD(0.9 momentum)SGD(0.9 momentum)时间帧数 T3264128冒烟测试时用 Adam 是因为它收敛快能快速暴露数据管道或模型维度错误正式训练切换成带动量的 SGD 是因为 Adam 在后期容易在最优解附近震荡最终收敛精度比 SGD 低 1-2 个百分点这在 NTU 榜单上可能就是好几个名次的差距。学习率衰减的策略上小数据集用指数衰减即可全量数据建议在第 60、第 90 个 epoch 各乘一次 0.1。我见过不少人在第 100 epoch 时学习率还没降下来loss 卡在 1.5 上下不去训练曲线像一条平线这就是学习率衰减时机不对导致的。5.2 显存优化骨骼数据也不一定能塞进 8G 显存很多人以为骨骼数据量小任意显卡都能跑。第一次跑全量 NTUbatch size64、T128 的时候显存直接飙到 11GB8G 显存的卡直接 OOM。原因是通道数到 256 后中间特征张量算下来一点不小。有两种常见解法。第一种是减小 batch size 到 32配合梯度累积效果基本无损。第二种是降低时间帧数 T 到 64精度会有约 2-3% 的下降但显存占用会少一半。# 梯度累积: 用 2 个小 batch 模拟一个大 batch optimizer.zero_grad() accum_steps 2 for i, (data, label) in enumerate(train_loader): output model(data) loss criterion(output, label) loss loss / accum_steps # 先缩放 loss.backward() if (i 1) % accum_steps 0: optimizer.step() optimizer.zero_grad()梯度累积的时候注意统一批大小如果目标 batch size 是 64显存只能跑 32那就设 accum_steps2每个小 batch 的 loss 除以 2 再反传。直接累加梯度不清零的话等效学习率会翻倍loss 曲线会异常震荡。5.3 避坑记录5 个高频踩坑点坑位一关节顺序不对导致精度暴跌。现象训练 loss 下降很快但验证集精度只有 20% 左右怎么调参都上不去。 原因训练集用的 NTU 关节顺序25 关节验证数据用的 COCO 格式17 关节或 OpenPose 输出顺序和 NTU 不一致模型等于在错误的位置读取关节坐标。 解决统一所有数据的关节映射表写一个reorder_joints()函数把 COCO 17 关节映射到 NTU 25 关节的对应位置或者反过来全套往 COCO 上对齐。这是最隐蔽也最容易犯的错误建议在加载器里加一个断言检查data.shape[2] num_joints并且对第一个样本做一次可视化输出确认关节对应正确。坑位二帧数采样方式错误。现象训练正常验证正常但测试集换了一批数据就崩。 原因训练数据都是整段动作长度均匀的测试数据有的很短直接截断导致动作起始帧丢失。 解决训练阶段用“随机裁剪”策略——在时间轴上随机选一段固定长度的连续帧测试阶段用“全序列等间隔采样”——把整段动作压缩到固定长度保留完整动作语义。两种策略都在训练和测试时都用同一种会低估真实泛化精度。坑位三多人场景下的人数排序不稳定。现象同一个动作被识别成不同类别反复横跳。 原因画面里出现两个人时模型不区分“主语”和“背景人”每次输入张量的第一个通道可能是不同的人。 解决按关节点的置信度总分排序得分高的人放在第一个通道。NTU 原始数据自带每帧每个关节的置信度取一个人所有关节的平均置信度排序即可。如果不做这一步模型等于在和人数顺序斗争精度极不稳定。坑位四训练前期 loss 直接炸成 NaN。现象第一个 epoch loss 就是 NaN完全无法训练。 原因图卷积的邻接矩阵归一化没有处理孤立节点——有些关节比如 NTU 里手腕末端在部分帧里置信度为 0所有邻居也都不存在导致分母为 0。 解决在D A_k.sum(dim1, keepdimTrue) 1e-6时加一个极小值更稳妥的做法是把置信度为 0 的关节点坐标直接置为 0并在归一化阶段给 D 加eps1e-6。如果加了 eps 还 NaN检查输入坐标是否含有 inf 值。坑位五PyTorch 版本差异导致模型权重加载失败。现象在服务器上训练好的模型本地加载报 key mismatch。 原因新版 PyTorch 对state_dict的键名处理有时会有细微差异尤其是使用ModuleList时。 解决加载时加一个strictFalse然后手动检查哪些层缺失更规范的做法是把模型类定义写进一个单独的model.py确保训练和推理时用的是同一份代码不要在两处复制粘贴模型定义。6. 让答辩更有底气的三个技巧可视化、消融和特征图分析到了训练收敛之后光有一个准确率数字是不够的评委一定会问“你怎么证明模型学到了动作特征而不是数据偏差”。我建议花一个下午做下面三件事比堆叠十个准确率点更有说服力。第一是输出每一帧的分类置信度热图。选取一个测试视频把模型在每一帧的输出概率画成随时间变化的堆叠折线图你能直观看到某个动作类别在哪个时间点开始占主导。比如“跌倒”动作模型通常会在躯干下压开始的第 20 帧左右把概率从 0.3 拉到 0.8这个图放答辩 PPT 里非常直观。第二是做一个简单的消融实验表。把完整模型的精度分别和“去掉时间卷积”“去掉残差连接”“只保留单一分区策略”做对比每项跑 20 个 epoch记录验证精度。这个表格可以直接证明模型的每个模块都有贡献评委问“为什么要用 ST-GCN”时可以直接指着表格说话。第三是可视化特征图这部分我用的方法是平均特征激活图。取测试集所有样本的第 5 层 ST-GCN Block 输出在关节维度上求均值然后绘制一个 25 关节的激活强度轮廓图叠加到人体骨架图上。你会发现不同类别的动作激活的关节分布完全不同握手类动作的激活热点集中在手指和腕关节走路类集中在髋关节和膝关节。这个可视化结果也常被用作论文插图。# 提取中间层特征并计算关节维度的平均激活强度 model.eval() activation_map {} def hook_fn(name): def hook(module, input, output): # output 形状: (N, C, T, V) - 在 N, C, T 维度上求均值, 留下 V 维激活 activation_map[name] output.detach().mean(dim(0, 2, 3)).cpu().numpy() return hook model.blocks[4].register_forward_hook(hook_fn(block4)) with torch.no_grad(): for data, _ in val_loader: model(data) break # 用 matplotlib 把 25 维的激活强度叠加到骨架图上 import matplotlib.pyplot as plt plt.bar(range(25), activation_map[block4]) plt.xlabel(Joint Index) plt.ylabel(Average Activation) plt.title(Spatial Activation Distribution) plt.savefig(activation_map.png)激活值在关节维度上的分布还有一个实际用途如果你发现某个关节始终是低激活比如第 8 号关节右肘在所有类别里几乎为 0那大概率是数据预处理时该关节坐标出了问题而不是模型没学到可以倒查数据管道。做完这三步你的毕业设计就已经不是“跑通了”的层面而是“有分析、有验证、有可视化”的完整技术闭环。我自己做这个方向时踩过关节顺序不统一的坑也废过一个星期去调一个因学习率衰减过晚导致的 loss 平台期如果这篇笔记能让你少走其中任何一步那目的就达到了。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

一键开关机芯片选型全攻略:PMOS、负载开关与专用控制器对比 2026/10/1 15:02:19

一键开关机芯片选型全攻略:PMOS、负载开关与专用控制器对比

1. 先搞清楚:一键开关机芯片到底解决什么问题1.1 它是“电源通路上的开关”,不是电源芯片很多刚开始做硬件的朋友,一听到“一键开关机芯片”这个名字,会下意识把它归类到电源芯片那一堆,以为它跟LDO、DC-DC一样是管电压…

阅读更多 →
ARMxy模块化工业控制器:一台设备取代PLC、网关与工控机 2026/10/1 15:02:19

ARMxy模块化工业控制器:一台设备取代PLC、网关与工控机

前几天在储能项目现场调设备,机柜里整整齐齐摆了三件东西:一台中端 PLC 负责和 BMS 联动、做联锁逻辑,一台工业网关把 Modbus、CAN 报文翻译成 OPC UA 上送给 EMS,还有一台无风扇工控机跑组态软件和本地数据库。三台设备三个品牌三…

阅读更多 →
Claude Code 查 AI 资讯:几类方案盘点 2026/10/1 15:02:19

Claude Code 查 AI 资讯:几类方案盘点

程序员每天要跟的 AI 动态散落在不少地方:X 上的讨论、Hacker News 的帖子、Reddit 的帖子、YouTube 视频、GitHub Trending、论文榜。逐个翻一遍成本不低。围绕“在哪里查、由谁维护、覆盖哪些源”这几个维度,下面盘点几类可用方案。 一、在编程 agent …

阅读更多 →
TensorFlow.js生产级实战:浏览器算力调度与内存治理 2026/10/1 15:02:19

TensorFlow.js生产级实战:浏览器算力调度与内存治理

1. 这不是“把模型搬进浏览器”那么简单:TensorFlow.js 的真实战场在哪?你肯定见过这类标题:“5分钟用 TensorFlow.js 在网页里跑 ResNet!”——然后点进去,发现只是加载一个预训练模型、传张本地图片、输出个分类标签…

阅读更多 →
STM32开发必懂:CubeMX、Keil、烧录与串口工具链分工详解 2026/10/1 15:02:19

STM32开发必懂:CubeMX、Keil、烧录与串口工具链分工详解

开篇先说实话:我写这篇的契机,是群里有个刚入坑 STM32 的朋友发了一段话,大意是“照着教程装完了 Keil、CubeMX、烧录软件和串口助手,四个软件整整齐齐躺在桌面上,但你要问我它们各自是干嘛的,我只会打开和…

阅读更多 →
2026年房屋建筑统一代码制度全面实施:资产管理系统如何实现“一码贯通”全生命周期管理? 2026/10/1 15:02:12

2026年房屋建筑统一代码制度全面实施:资产管理系统如何实现“一码贯通”全生命周期管理?

一、房屋建筑统一代码制度:从政策要求到落地目标2026年6月,住房城乡建设部、国家数据局联合印发《关于全面建立房屋建筑统一代码制度的通知》(建办〔2026〕32号),要求建立实施房屋建筑统一代码制度,赋予房屋…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉