Label Studio中用于时序视频多标签分类的TimelineLabels模型
本文档提供了关于如何在Label Studio中使用TimelineLabels模型对视频数据进行时序多标签分类的清晰全面指南。
通过在YOLO的分类能力之上集成LSTM神经网络——特别是利用YOLO最后一层的特征——该模型能够处理时序标注任务。 用户可以直接在标注配置中轻松自定义神经网络参数,使模型适应特定用例,或将该模型作为进一步开发的基础。
在可训练模式下,您需要先手动标注少量样本。每次点击提交按钮时,模型都会根据您提供的新标注重新训练。当模型开始在新任务上预测您训练过的标签时,它会自动将预测的标签填充到时间轴中。您可以验证或修改这些标签,更新它们将再次重新训练模型,从而帮助您逐步改进。
提示: 如果您正在寻找更高级的时间序列分类方法,请查看VideoMAE模型。虽然我们没有提供VideoMAE的示例后端,但您可以将其集成为您自己的机器学习后端。
安装与快速入门
在开始之前,您需要安装Label Studio ML后端。
本教程使用YOLO示例。有关在Label Studio中设置YOLO模型系列的详细说明,请参阅主README。
标注配置
<View>
<TimelineLabels name="label" toName="video"
model_trainable="true"
model_classifier_epochs="1000"
model_classifier_sequence_size="16"
model_classifier_hidden_size="32"
model_classifier_num_layers="1"
model_classifier_f1_threshold="0.95"
model_classifier_accuracy_threshold="0.99"
model_score_threshold="0.5"
>
<Label value="Ball touch" background="red"/>
<Label value="Ball in frame" background="blue"/>
</TimelineLabels>
<Video name="video" value="$video" height="700" frameRate="25.0" timelineHeight="200" />
</View>
重要提示: 您必须在Video标签中正确设置frameRate属性值。
所有视频应保持相同的帧率。否则,提交的标注将与视频不同步。
参数
| 参数 | 类型 | 默认值 | 描述 |
|---|---|---|---|
model_trainable |
bool | False | Enables the trainable mode, allowing the model to learn from your annotations incrementally. |
model_classifier_epochs |
int | 1000 | Number of training epochs for the LSTM neural network. |
model_classifier_sequence_size |
int | 16 | Size of the LSTM sequence in frames. Adjust to capture longer or shorter temporal dependencies, 16 frames are about ~0.6 sec with 25 frame rate. |
model_classifier_hidden_size |
int | 32 | Size of the LSTM hidden state. Modify to change the capacity of the LSTM. |
model_classifier_num_layers |
int | 1 | Number of LSTM layers. Increase for a deeper LSTM network. |
model_classifier_f1_threshold |
float | 0.95 | F1 score threshold for early stopping during training. Set to prevent overfitting. |
model_classifier_accuracy_threshold |
float | 1.00 | Accuracy threshold for early stopping during training. Set to prevent overfitting. |
model_score_threshold |
float | 0.5 | Minimum confidence threshold for predictions. Labels with confidence below this threshold will be disregarded. |
model_path |
string | None | Path to the custom YOLO model. See more in the section Your own custom models |
注意: 您可以直接在标注配置中通过调整标签内的属性来自定义神经网络参数。
使用模型
简单模式
在简单模式下,该模型使用预训练的YOLO类别生成预测,无需额外训练。
适用场景: 无需自定义训练的快速设置。可立即开始生成预测结果。
配置: 在标注配置中设置
model_trainable="false"(或省略该参数,因为默认值为false)。示例:
<View> <Video name="video" value="$video" height="700" frameRate="25.0" timelineHeight="200" /> <TimelineLabels name="label" toName="video" model_trainable="false"> <Label value="Ball" predicted_values="soccer_ball"/> <Label value="tiger_shark" /> TimelineLabels> View>
可训练模式
可训练模式使模型能够从您的标注中逐步学习。
它使用预训练的YOLO分类模型,并在顶层叠加自定义LSTM神经网络来捕捉视频数据中的时间依赖性。该LSTM模型从零开始训练,因此需要约10-20个标注良好的视频(每个视频500帧,约20秒)才能开始做出有意义的预测。
- 使用场景:当需要自定义标签或相比简单模式需要更高准确率时使用。
- 配置: 在标注配置中设置
model_trainable="true"。 - Training Process:
- 使用
TimelineLabels标签开始标注视频。 - 提交第一个标注后,模型开始训练。
partial_fit()方法允许模型随着每个新标注进行增量训练。
- 使用
- 需求: 大约需要10-20个标注任务才能达到合理的性能。
注意:Label标签中的predicted_values属性对于可训练模型没有意义。
示例:
<View>
<Video name="video" value="$video" height="700" frameRate="25.0" timelineHeight="200" />
<TimelineLabels name="label" toName="video"
model_trainable="true"
model_classifier_epochs="1000"
model_classifier_sequence_size="16"
model_classifier_hidden_size="32"
model_classifier_num_layers="1"
model_classifier_f1_threshold="0.95"
model_classifier_accuracy_threshold="0.99"
model_score_threshold="0.5">
<Label value="Ball in frame"/>
<Label value="Ball touch"/>
</TimelineLabels>
</View>
可训练模型的工作原理
可训练模式采用了一种自定义的时序LSTM分类模型实现。 该模型会随着每次新标注的提交或更新而进行增量训练, 并为视频中的每一帧生成预测结果。
1. 使用YOLO进行特征提取
- 预训练的YOLO模型: 使用YOLO分类模型(例如
yolov8n-cls.pt)从视频帧中提取特征。 - 层修改: 该模型移除了最后的分类层,以使用倒数第二层的特征表示(参见
utils/neural_nets.py::cached_feature_extraction())。 - 缓存预测: 使用缓存存储YOLO中间特征提取结果,以提高效率并支持实时增量训练。
用于特征提取的自定义YOLO模型
你可以按照主README中描述的步骤加载自己的YOLO模型。不过,该模型需要具备与yolov8-cls模型相似的架构。更多详情请参阅utils/neural_nets.py::cached_feature_extraction()。
缓存文件夹
它位于/app/cache_dir目录中,存储来自YOLO模型最后一层的缓存中间特征。
该缓存用于实时增量训练和预测加速。
2. LSTM神经网络
- 用途: 通过处理来自YOLO最后一层的特征向量序列,捕捉视频数据中的时间依赖性。
- Architecture:
- 输入层: 接收来自YOLO的特征向量。
- 全连接层:用于降维。
- 层归一化和Dropout: 提升训练稳定性并防止过拟合。
- LSTM层: 处理序列以建模时间关系。
- 输出层: 为每个时间步生成多标签预测。
- 损失函数: 使用二元交叉熵损失函数(带logits)进行多标签分类,并通过权重衰减实现L2正则化。
3. 使用 partial_fit() 进行增量训练
- 功能: 允许模型随着每个新标注更新其参数。
- Process:
- 使用
utils/converter.py::convert_timelinelabels_to_probs()从标注视频中提取特征和标签。 - 将数据预处理为适合LSTM输入的序列,按
model_classifier_sequence_size块大小进行分割。 - 以增量方式训练模型,基于F1分数和准确率阈值使用早停机制。
- 使用
- Advantages:
- 小样本学习: 能够从少量示例中进行学习。
- 避免过拟合: 使用F1分数和准确率进行早停可防止模型在有限数据上过拟合。
限制与注意事项
- 非最终生产模型: 虽然前景看好,但该模型目前主要作为演示用途,可能需要进一步验证才能投入生产环境使用。
- 性能取决于数据: 需要足够多样化的标注数据(至少10-20个已标注任务)才能开始运行。
- 参数敏感性: 调整神经网络参数可能会显著影响性能。
- 训练数据早停: 该模型基于训练数据的F1分数和准确率使用早停机制。这可能导致对训练数据的过拟合。之所以这样做是因为在单次标注更新时缺乏验证数据。
- YOLO模型的局限性: 该模型使用预训练的YOLO模型进行特征提取,该模型针对物体分类任务训练,可能不适用于所有用例(如事件检测)。此方法不会调整YOLO模型,仅基于YOLO最后一层训练LSTM部分。
- 标签平衡: 模型可能在处理不平衡标签时遇到困难。确保训练数据中的标签分布均匀。考虑修改损失函数(
BCEWithLogitsLoss)并使用类别正权重来解决此问题。 - 在所有数据上训练: 目前尚未实现在所有数据上进行训练的功能,因此模型仅基于最新标注进行训练。详情请参阅
timeline_labels.py::fit()。
示例用例:在足球视频中检测球体
设置
标注配置:
<View> <TimelineLabels name="videoLabels" toName="video"> <Label value="Ball touch" background="red"/> <Label value="Ball in frame" background="blue"/> TimelineLabels> <Video name="video" value="$video" height="700" timelineHeight="200" frameRate="25.0" /> View>连接模型后端:
创建一个新项目,进入设置 > 模型并添加YOLO后端。
- 在终端中导航至本仓库中的
yolo文件夹。 - 更新您的
docker-compose.yml文件。 - 执行
docker compose up以运行后端服务。 - 在项目设置中将此后端连接到您的Label Studio项目。请确保交互式预标注处于关闭状态(这是默认设置)。
- 在终端中导航至本仓库中的
标注与训练
标注视频:
- 将足球视频上传到项目中。
- 使用
控制标签来标注球在画面中可见的时间区间。
模型训练:
- 提交标注后,模型开始增量训练。
- 持续标注,直到模型开始做出准确的预测。
审核预测结果:
- 模型为未标注的视频提供标签建议。
- 验证并修正预测结果以进一步提升模型性能。
调整训练参数并重置模型
如果模型表现不佳,可以考虑修改标注配置中的LSTM和分类器训练参数。这些参数以model_classifier_为前缀。
更改这些参数后,模型将被重置:
model_classifier_sequence_sizemodel_classifier_hidden_sizemodel_classifier_num_layers- 标注配置中新增或移除的标签
因此您可能需要更新标注(点击更新)才能看到改进。
如需修改更多参数,可直接在utils/neural_nets.py::MultiLabelLSTM的代码中进行操作。
如果需要完全重置模型,你可以从/app/models目录中删除模型文件。
模型路径请参考timeline_labels.py::get_classifier_path(),通常以timelinelabels-前缀开头。
调试
要调试模型,您应该使用LOG_LEVEL=DEBUG环境变量运行它(参见docker-compose.yml),然后在(docker)控制台中检查日志。
将TimelineLabels区域转换为标签数组及反向转换
有两个主要功能用于将TimelineLabels区域转换为标签数组及反向转换:
utils/converter.py::convert_timelinelabels_to_probs()- 将TimelineLabels区域转换为标签数组utils/converter.py::convert_probs_to_timelinelabels()- 将标签数组转换为TimelineLabels区域
标签数组中的每一行对应视频中的一个帧。
该标签数组是一个二进制矩阵,其中每列对应一个标签。
如果该帧中存在该标签,则对应单元格设为1,否则设为0。
例如:
[
[0, 0, 1],
[0, 1, 0],
[1, 0, 0]
]
这对应第1、2、3帧中的标签[label3, label2, label1]。
更多示例请参见 tests/test_timeline_labels.py::test_convert_probs_to_timelinelabels()。
开发者指南
本指南深入探讨了Label Studio中TimelineLabels机器学习后端的架构与代码流程。内容包含类继承关系图和方法调用流程图,帮助开发者理解各组件间的交互方式。同时,指南还提供了关键方法和类的详细说明,重点标注了工作流中的起始点及其功能角色。
类继承关系图
下图展示了TimelineLabels ML后端中的类继承层次结构。
classDiagram
class ControlModel
class TimelineLabelsModel
class TorchNNModule
class BaseNN
class MultiLabelLSTM
ControlModel <|-- TimelineLabelsModel
TorchNNModule <|-- BaseNN
BaseNN <|-- MultiLabelLSTM
ControlModel: Label Studio中控制标签的基类。TimelineLabelsModel: 继承自ControlModel并为标签实现特定功能。torch.nn.Module: PyTorch中所有神经网络模块的基类。BaseNN: 神经网络的自定义基类,继承自torch.nn.Module。MultiLabelLSTM: 继承自BaseNN,实现了一个用于多标签分类的LSTM神经网络。
方法调用流程图
预测工作流
以下流程图描述了预测过程中的方法调用。
flowchart TD
A[TimelineLabelsModel.predict_regions]
A --> B{Is self.trainable?}
B -->|Yes| C[create_timelines_trainable]
B -->|No| D[create_timelines_simple]
C --> E[cached_feature_extraction]
C --> F[Load classifier using BaseNN.load_cached_model]
C --> G[classifier.predict]
C --> H[convert_probs_to_timelinelabels]
C --> I[Return predicted regions]
D --> J[cached_yolo_predict]
D --> K[Process frame results]
D --> L[convert_probs_to_timelinelabels]
D --> M[Return predicted regions]
训练工作流
以下流程图展示了训练过程中的方法调用。
flowchart TD
N[TimelineLabelsModel.fit]
N --> O{Event is 'ANNOTATION_CREATED' or 'ANNOTATION_UPDATED'?}
O -->|Yes| P[Extract task and regions]
P --> Q[Get model parameters]
Q --> R[Get video path]
R --> S[cached_feature_extraction]
S --> T[Prepare features and labels]
T --> U[Load or create classifier]
U --> V[classifier.partial_fit]
V --> W[classifier.save]
W --> X[Return True]
O -->|No| Y[Return False]
代码结构与说明
TimelineLabelsModel 类
文件: timeline_labels.py
TimelineLabelsModel 类继承自 ControlModel 基类,并为 控件标签实现了特定功能。
关键方法:
is_control_matched(cls, control): 类方法,用于检查提供的控制标签是否匹配标签。create(cls, *args, **kwargs): 类方法,用于创建模型实例,初始化如trainable和label_map等属性。predict_regions(self, video_path): 预测过程中调用的主要方法。根据trainable属性决定使用简单预测方法还是可训练预测方法。create_timelines_simple(self, video_path): 使用预训练的YOLO类进行预测,无需额外训练。- 调用
cached_yolo_predict从 YOLO 模型获取预测结果。 - 处理帧结果以提取概率。
- 将概率转换为时间轴标签。
- 调用
create_timelines_trainable(self, video_path): 使用自定义训练的LSTM神经网络进行预测。- 调用
cached_feature_extraction从视频中提取特征。 - 加载训练好的分类器模型。
- 使用分类器预测概率。
- 将概率转换为时间轴标签。
- 调用
fit(self, event, data, **kwargs): 当创建或更新新标注时调用。处理LSTM模型的增量训练。- 从标注的视频中提取特征和标签。
- 为LSTM输入预处理数据。
- 加载或初始化分类器。
- 在分类器上调用
partial_fit来更新模型参数。 - 保存更新后的分类器模型。
get_classifier_path(self, project_id): 根据项目ID和模型名称生成用于存储分类器模型的文件路径。
神经网络类
BaseNN 类
文件: neural_nets.py
BaseNN 类作为神经网络模型的基类,提供了保存、加载和管理标签映射的通用方法。
关键方法:
set_label_map(self, label_map): 存储标签映射字典。get_label_map(self): 获取标签映射字典。save(self, path): 使用torch.save将模型保存到指定路径。load(cls, path): 类方法,用于从指定路径加载已保存的模型。load_cached_model(cls, model_path): 如果存在则加载缓存的模型,否则返回None。
MultiLabelLSTM 类
文件: neural_nets.py
MultiLabelLSTM类继承自BaseNN,并实现了一个用于多标签分类的LSTM神经网络。
关键方法:
__init__(...): 初始化神经网络层和参数,包括输入大小、隐藏层、dropout和优化器设置。forward(self, x): 定义网络的前向传播过程。- 使用全连接层降低输入维度。
- 应用层归一化和dropout。
- 将数据通过LSTM层传递。
- 应用全连接层生成输出预测。
preprocess_sequence(self, sequence, labels=None, overlap=2): 通过分割和填充输入序列及标签,为训练做准备。partial_fit(self, sequence, labels, ...): 在新数据上增量训练模型。- 预处理输入序列。
- 创建一个用于批处理的DataLoader。
- 基于准确率和F1分数阈值运行带有早停机制的训练循环。
predict(self, sequence): 为给定的输入序列生成预测结果。- 将序列分割成块。
- 在评估模式下通过模型传递数据。
- 将输出连接起来以匹配原始序列长度。
起始点与执行流程
预测流程
预测请求: 当请求对视频进行预测时,会调用
TimelineLabelsModel.predict_regions(video_path)。确定模式:
可训练模式 (
self.trainable == True): 调用create_timelines_trainable(video_path).- 使用
cached_feature_extraction提取特征。 - 使用
BaseNN.load_cached_model加载训练好的分类器模型。 - 使用
classifier.predict(yolo_probs)预测概率。 - 使用
convert_probs_to_timelinelabels将概率转换为时间轴标签。
- 使用
简单模式 (
self.trainable == False): 调用create_timelines_simple(video_path).- 使用
cached_yolo_predict获取YOLO预测结果。 - 处理帧结果以提取概率。
- 使用
convert_probs_to_timelinelabels将概率转换为时间轴标签。
- 使用
返回预测结果: 该方法返回带有标签和时间戳的预测区域列表。
训练流程
事件触发器: 当标注事件发生时(例如'ANNOTATION_CREATED'或'ANNOTATION_UPDATED'),会调用
fit(event, data, **kwargs)方法。事件处理:
- 检查该事件是否与训练相关。
- 提取任务和标注数据。
参数提取: 从控制标签属性中获取模型参数,例如训练轮次(epochs)、序列长度(sequence size)、隐藏层大小(hidden size)和阈值(thresholds)。
数据准备:
- 获取与任务关联的视频路径。
- 使用
cached_feature_extraction从视频中提取特征。 - 将标注转换为适合训练的标签。
模型加载或初始化:
- 尝试使用
BaseNN.load_cached_model加载现有的分类器模型。 - 如果不存在模型或参数已更改,则初始化一个新的
MultiLabelLSTM模型。
- 尝试使用
训练:
- 调用
classifier.partial_fit(features, labels, ...)来增量训练模型。 - 训练包含基于准确率和F1分数阈值的早停机制,以防止过拟合。
- 调用
模型保存: 使用
classifier.save(path)将训练好的模型保存到磁盘。
实用工具与辅助函数
缓存预测与特征提取:
cached_yolo_predict(yolo_model, video_path, cache_params): 使用joblib的Memory缓存YOLO预测结果,避免重复计算。cached_feature_extraction(yolo_model, video_path, cache_params): 通过连接到YOLO模型的倒数第二层提取特征,并将结果缓存。
数据转换函数:
convert_probs_to_timelinelabels(probs, label_map, threshold): 将概率输出转换为适合Label Studio的时间轴标签。convert_timelinelabels_to_probs(regions, label_map, max_frame): 将标注区域转换回用于训练的概率序列。
结论
TimelineLabels ML后端与Label Studio无缝集成,为视频数据提供时序多标签分类功能。该架构利用预训练的YOLO模型进行特征提取,并通过LSTM神经网络增强以捕捉时序依赖关系。
理解类层次结构和方法流程对于希望扩展或修改后端的开发人员至关重要。通过遵循本指南中概述的起点和执行流程,开发人员可以更高效地浏览代码库,并实现自定义功能或优化。
注意:如需进一步开发或贡献,请参考README_DEVELOP.md文件,该文件提供了额外的指南和架构细节。