行为识别TSM训练ucf101数据集:TaoToken统一Key接入与config.toml配置骨架

发布时间:2026/9/27 12:58:07
行为识别TSM训练ucf101数据集:TaoToken统一Key接入与config.toml配置骨架 1. 从 UCF101 抽帧到 TSM 训练我踩过的坑与最终跑通的链路行为识别Action Recognition里TSMTemporal Shift Module算是性价比很高的方案它不增加额外参数量靠特征通道在时间维度上“移位”来建模时序推理速度比 3D 卷积友好得多。UCF101 则是这个方向最经典的入门数据集101 个动作类别、13320 段视频拿来验证训练链路是否跑通非常合适。这篇内容适合两类人一是刚接触行为识别、手里有 UCF101 或自采视频想按 UCF101 格式组织数据的人二是已经把 TSM 代码拉下来但卡在抽帧脚本、标签文件生成、config.toml配置和训练启动这几步的人。我会把从环境准备到训练启动的完整链路拆开讲重点交付一份可复制的config.toml配置骨架以及用 TaoToken 统一 Key 接入的方式让模型下载、脚本调试、训练日志分析这些环节不用再到处找零散的 API Key。整条链路我自己跑过一遍下面按顺序来。2. 原问题与场景为什么 TSM 训练 UCF101 容易卡住TSM 官方仓库temporal-shift-module提供了训练代码但数据准备部分几乎是空白。UCF101 下载下来是 101 个文件夹每个文件夹里是.avi短视频官方还额外给了classInd.txt、trainlist01.txt、testlist01.txt这些划分文件。问题在于TSM 训练读取的不是视频而是抽帧后的图片列表格式是「图片目录 帧数 类别」三列。官方没有提供 UCF101 的抽帧和列表生成脚本很多人到这里就卡住了。另一个坑是配置分散。TSM 的数据路径写在ops/dataset_config.py里训练超参写在命令行参数里预训练权重路径又单独传。一旦要换数据集或者换机器这些路径要改好几处很容易漏。我试过把路径硬编码在脚本里结果换到另一台机器上直接报FileNotFoundError排查了半天才发现是ROOT_DATASET没改。所以这篇的核心思路是把数据准备脚本、标签生成脚本、配置骨架、训练命令这四块固定下来形成一套可复制的流程。同时用 TaoToken 的统一 Key 来管理模型下载和调试环节的接口调用避免 Key 散落在各个脚本里。3. TaoToken 前置统一 Key 接入准备TaoToken 在这里的角色是统一管理接口访问的 Key。TSM 训练本身是本地 GPU 任务不依赖外部接口但在两个环节会用到一是下载预训练权重时如果走脚本自动化二是训练过程中用模型对话能力辅助分析日志、排查报错。把 Key 统一放在一个地方比在每个脚本里写死要清爽。接入步骤不复杂。先到官网注册并进入控制台在 API Keys 页面创建一个 Key。官网地址是https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content控制台入口在https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewriteAPI Keys 管理页在https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite。创建好之后把 Key 写进环境变量不要提交到 git。export TAOTOKEN_API_KEY你的Key export TAOTOKEN_BASE_URLhttps://taotoken.net/api这里注意API 地址是https://taotoken.net/api不带 UTM 参数这是给程序调用的。官网和控制台那些带 UTM 的链接是给人点击的别混用。如果你后续要用 Coding Plan 做长期编码辅助入口在https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite要验证模型对话能力入口在https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite。接入文档在https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite遇到参数问题先查这里。注意Key 只放在环境变量或本地.env文件里.env要加进.gitignore。训练脚本里用os.environ.get(TAOTOKEN_API_KEY)读取不要硬编码。4. 可复制配置config.toml 骨架与数据准备脚本4.1 数据准备抽帧与标签生成先把 UCF101 的视频按类别抽帧。需要装 ffmpeg抽帧脚本放在tools/vid2img_ucf101.py核心逻辑是遍历每个类别文件夹对每个.avi调 ffmpeg 按固定高度缩放抽帧输出image_00001.jpg这种序列。import os import sys import subprocess def class_process(dir_path, dst_dir_path, class_name): class_path os.path.join(dir_path, class_name) if not os.path.isdir(class_path): return dst_class_path os.path.join(dst_dir_path, class_name) os.makedirs(dst_class_path, exist_okTrue) for file_name in os.listdir(class_path): if not file_name.endswith(.avi): continue name, _ os.path.splitext(file_name) dst_directory_path os.path.join(dst_class_path, name) video_file_path os.path.join(class_path, file_name) if os.path.exists(dst_directory_path): if os.path.exists(os.path.join(dst_directory_path, image_00001.jpg)): continue subprocess.call(rm -r {}.format(dst_directory_path), shellTrue) os.makedirs(dst_directory_path, exist_okTrue) cmd ffmpeg -i {} -vf scale-1:480 {}/image_%05d.jpg.format( video_file_path, dst_directory_path) subprocess.call(cmd, shellTrue) if __name__ __main__: dir_path sys.argv[1] dst_dir_path sys.argv[2] for class_name in os.listdir(dir_path): class_process(dir_path, dst_dir_path, class_name)运行命令python tools/vid2img_ucf101.py /data/UCF101/videos /data/UCF101/jpg抽帧完再生成训练/验证列表。脚本tools/gen_label_ucf101.py读取classInd.txt和trainlist01.txt输出ucf101_rgb_train_split_1.txt和ucf101_rgb_val_split_1.txt每行格式是「图片目录 帧数 类别」。import os import glob import fnmatch ROOT /data/UCF101/jpg/ LABEL_DIR /data/UCF101/label def parse_ucf_splits(): class_ind [x.strip().split() for x in open(os.path.join(LABEL_DIR, classInd.txt))] class_mapping {x[1]: int(x[0]) - 1 for x in class_ind} def line2rec(line): items line.strip().split(/) label class_mapping[items[0]] vid items[1].split(.)[0] return vid, label splits [] for i in range(1, 4): train_list [line2rec(x) for x in open(os.path.join(LABEL_DIR, trainlist{:02d}.txt.format(i)))] test_list [line2rec(x) for x in open(os.path.join(LABEL_DIR, testlist{:02d}.txt.format(i)))] splits.append((train_list, test_list)) return splits def parse_directory(path): frame_folders [] for frame_name in glob.glob(os.path.join(path, *)): frame_folders.extend(glob.glob(os.path.join(frame_name, *))) dir_dict, rgb_counts {}, {} for f in frame_folders: k os.path.basename(f) dir_dict[k] f rgb_counts[k] len(fnmatch.filter(os.listdir(f), image_*)) return dir_dict, rgb_counts def build_split_list(split_tuple, frame_info, split_idx): split split_tuple[split_idx] def build_set_list(set_list): rgb_list [] for item in set_list: frame_dir frame_info[0][item[0]] rgb_cnt frame_info[1][item[0]] rgb_list.append({} {} {}\n.format(frame_dir, rgb_cnt, item[1])) return rgb_list return build_set_list(split[0]), build_set_list(split[1]) if __name__ __main__: spl parse_ucf_splits() f_info parse_directory(ROOT) for i in range(len(spl)): train_rgb, test_rgb build_split_list(spl, f_info, i) open(os.path.join(LABEL_DIR, ucf101_rgb_train_split_{}.txt.format(i 1)), w).writelines(train_rgb) open(os.path.join(LABEL_DIR, ucf101_rgb_val_split_{}.txt.format(i 1)), w).writelines(test_rgb)4.2 config.toml 配置骨架TSM 原生用命令行参数但路径和超参多了之后不好维护。我习惯用一个config.toml把数据路径、训练超参、预训练权重、TaoToken 相关配置集中管理训练脚本读取后拼成命令行。[dataset] name ucf101 modality RGB root_dataset /data/UCF101/jpg/ train_list /data/UCF101/label/ucf101_rgb_train_split_1.txt val_list /data/UCF101/label/ucf101_rgb_val_split_1.txt num_classes 101 [model] arch resnet num_segments 8 shift true shift_div 8 shift_place blockres dropout 0.8 consensus_type avg tune_from pretrained/TSM_kinetics_RGB_resnet50_shift8_blockres_avg_segment8_e50.pth [train] epochs 25 batch_size 16 lr 0.001 lr_steps [10, 20] gd 20 workers 16 eval_freq 1 gpus 0 [taotoken] api_key_env TAOTOKEN_API_KEY base_url https://taotoken.net/api对应的ops/dataset_config.py里ROOT_DATASET要指向抽帧后的图片根目录并且和列表文件里的路径拼接后能对上。列表文件里写的是绝对路径的话ROOT_DATASET可以留空写相对路径的话这里要补全。ROOT_DATASET /data/UCF101/jpg/ def return_ucf101(modality): filename_categories 101 if modality RGB: root_data ROOT_DATASET filename_imglist_train /data/UCF101/label/ucf101_rgb_train_split_1.txt filename_imglist_val /data/UCF101/label/ucf101_rgb_val_split_1.txt prefix image_{:05d}.jpg return filename_categories, filename_imglist_train, filename_imglist_val, root_data, prefix raise NotImplementedError5. 验证请求与成功结果训练启动与日志确认配置改完先做一次小规模验证确认数据路径和列表文件能对上。把epochs临时改成 1batch_size改成 4跑一遍看是否报错。python main.py ucf101 RGB \ --arch resnet \ --num_segment 8 \ --gd 20 \ --lr 0.001 \ --lr_steps 10 20 \ --epochs 1 \ --batch-size 4 \ -j 8 \ --dropout 0.8 \ --consensus_typeavg \ --eval-freq1 \ --shift \ --shift_div8 \ --shift_placeblockres \ --tune_frompretrained/TSM_kinetics_RGB_resnet50_shift8_blockres_avg_segment8_e50.pth看到类似下面的输出说明数据加载和模型构建都正常 fine-tuning from pretrained/TSM_kinetics_RGB_resnet50_shift8_blockres_avg_segment8_e50.pth #### Notice: keys that failed to load: set() Epoch: [0][0/100] Time 1.234 (1.234) Data 0.456 (0.456) Loss 4.5678 (4.5678) Acc1 1.00 (1.00)keys that failed to load是空集合说明预训练权重全部加载成功。如果这里出现一堆 key通常是tune_from的权重和当前arch不匹配或者num_classes对不上导致 fc 层被过滤这是正常的只要不是全部 key 都失败就行。验证通过后把epochs改回 25、batch_size改回 16正式训练。训练过程中eval-freq1会每个 epoch 跑一次验证日志里能看到 top1 和 top5 精度。UCF101 上 TSM ResNet50 从 Kinetics 预训练微调25 个 epoch 后 top1 通常能到 90% 以上。如果要用 TaoToken 的模型对话能力辅助分析训练日志可以这样调curl -X POST https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d { model: claude-3-5-sonnet, messages: [{role: user, content: 帮我分析这段训练日志的 loss 曲线是否正常...}] }返回里如果choices[0].message.content有正常回复说明 Key 和 base_url 都配对了。6. 本篇常见错排查6.1 抽帧后列表文件路径对不上报错FileNotFoundError: image_00001.jpg多半是列表文件里的路径和ROOT_DATASET拼接后不对。检查列表文件第一列是不是绝对路径如果是相对路径ROOT_DATASET要补全到类别文件夹的上一级。用head -1 ucf101_rgb_train_split_1.txt看一眼实际内容再手动ls一下那个目录确认图片存在。6.2 mobilenetv2 预训练权重加载失败用 mobilenetv2 时如果报 key 不匹配需要在main.py的tune_from加载逻辑里做.net前缀的替换。核心是遍历sd和model_dict把带.net和不带.net的 key 对应起来再过滤掉 fc 层。改完之后keys that failed to load应该只剩 fc 相关的少量 key。6.3 显存不足或 DataLoader 卡住batch_size16在 8G 显存上可能吃紧先降到 8 或 4 试。-j 16在 CPU 核数少的机器上会导致 DataLoader 卡住改成-j 4或-j 8。另外抽帧后的图片数量很大UCF101 全量抽帧有 150 万张以上磁盘 IO 是瓶颈建议把图片放在 SSD 上。6.4 TaoToken 请求返回 401先确认TAOTOKEN_API_KEY环境变量在当前 shell 里能echo出来。如果是在脚本里读确认脚本继承了环境变量。base_url 必须是https://taotoken.net/api不要带 UTM 参数也不要漏掉/api。如果还是 401到 API Keys 页面确认 Key 没过期、没被删除。7. 语义一致 CTA整条链路跑下来最花时间的其实是数据准备和路径排查训练本身反而简单。如果你在接入环节遇到 Key 管理的问题或者想用模型对话能力辅助排查训练报错可以走 API Keys 和接入文档这两条线API Keys 在https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite接入文档在https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite。如果你打算长期做行为识别方向的编码和 Agent 辅助Coding Plan 入口在https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite。想先验证模型对话是否可用直接去https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite试一条请求就行。