opensportslib 0.2.0.dev1__tar.gz → 0.2.0.dev2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {opensportslib-0.2.0.dev1/opensportslib.egg-info → opensportslib-0.2.0.dev2}/PKG-INFO +31 -1
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/README.md +30 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/apis/__init__.py +2 -0
- opensportslib-0.2.0.dev2/opensportslib/apis/vqa.py +313 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/cli.py +4 -0
- opensportslib-0.2.0.dev2/opensportslib/configs/vqa/qwen.yaml +228 -0
- opensportslib-0.2.0.dev2/opensportslib/configs/vqa/xvars_lora.yaml +243 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/core/config/accessors.py +234 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/core/config/validate.py +15 -0
- opensportslib-0.2.0.dev2/opensportslib/core/trainer/vqa_trainer.py +797 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/core/utils/config.py +6 -2
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/core/utils/ddp.py +3 -2
- opensportslib-0.2.0.dev2/opensportslib/core/utils/hf_runtime.py +524 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/datasets/builder.py +4 -0
- opensportslib-0.2.0.dev2/opensportslib/datasets/vqa_dataset.py +165 -0
- opensportslib-0.2.0.dev2/opensportslib/metrics/vqa_metric.py +217 -0
- opensportslib-0.2.0.dev2/opensportslib/models/base/qwen_xvars.py +476 -0
- opensportslib-0.2.0.dev2/opensportslib/models/base/video_chatgpt_compat.py +298 -0
- opensportslib-0.2.0.dev2/opensportslib/models/base/xvars_videochatgpt.py +1115 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/models/builder.py +34 -0
- opensportslib-0.2.0.dev2/opensportslib/models/utils/vqa_prediction_priors.py +105 -0
- opensportslib-0.2.0.dev2/opensportslib/models/utils/vqa_prompting.py +56 -0
- opensportslib-0.2.0.dev2/opensportslib/models/utils/xvars_clip_index.py +99 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/setup/setup.py +36 -2
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2/opensportslib.egg-info}/PKG-INFO +31 -1
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib.egg-info/SOURCES.txt +19 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/pyproject.toml +1 -1
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/tests/conftest.py +140 -1
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/tests/test_config_architecture.py +6 -0
- opensportslib-0.2.0.dev2/tests/test_config_utils_smoke.py +84 -0
- opensportslib-0.2.0.dev2/tests/test_extract_xvars_features.py +134 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/tests/test_public_apis_smoke.py +15 -0
- opensportslib-0.2.0.dev2/tests/test_task_model_api_contract.py +919 -0
- opensportslib-0.2.0.dev2/tests/test_vqa_metrics_semantic.py +122 -0
- opensportslib-0.2.0.dev2/tests/test_vqa_training_lora.py +563 -0
- opensportslib-0.2.0.dev2/tests/test_vqa_xvars_videochatgpt.py +1387 -0
- opensportslib-0.2.0.dev2/tools/convert/build_xvars_indexes.py +200 -0
- opensportslib-0.2.0.dev2/tools/convert/extract_xvars_clip_features.py +374 -0
- opensportslib-0.2.0.dev1/tests/test_config_utils_smoke.py +0 -46
- opensportslib-0.2.0.dev1/tests/test_task_model_api_contract.py +0 -402
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/LICENSE +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/LICENSE-COMMERCIAL +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/MANIFEST.in +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/examples/quickstart/basic_classification.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/examples/quickstart/basic_localization.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/__init__.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/apis/base_task_model.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/apis/classification.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/apis/localization.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/configs/classification/default.yaml +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/configs/classification/sngar_frames.yaml +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/configs/classification/sngar_tracking.yaml +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/configs/classification/video.yaml +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/configs/default.yaml +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/configs/localization/calf_resnetpca512.yaml +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/configs/localization/default.yaml +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/configs/localization/netvladpp_resnetpca512.yaml +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/configs/localization/video_dali.yaml +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/configs/localization/video_ocv.yaml +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/core/__init__.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/core/config/__init__.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/core/config/conflicts.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/core/config/loader.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/core/config/migrate.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/core/config/migrations/__init__.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/core/config/migrations/legacy_to_canonical.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/core/config/runtime_adapter.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/core/config/schema.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/core/config/schemas/__init__.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/core/config/schemas/schema_canonical.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/core/config/schemas/schema_legacy.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/core/loss/__init__.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/core/loss/builder.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/core/loss/calf.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/core/loss/ce.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/core/loss/combine.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/core/loss/nll.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/core/optimizer/__init__.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/core/optimizer/builder.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/core/sampler/weighted_sampler.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/core/scheduler/__init__.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/core/scheduler/builder.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/core/trainer/__init__.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/core/trainer/classification_trainer.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/core/trainer/localization_trainer.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/core/utils/checkpoint.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/core/utils/config_normalize.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/core/utils/data.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/core/utils/default_args.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/core/utils/lightning.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/core/utils/load_annotations.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/core/utils/seed.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/core/utils/video_processing.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/core/utils/wandb.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/datasets/__init__.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/datasets/classification_dataset.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/datasets/localization_dataset.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/datasets/utils/__init__.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/datasets/utils/tracking.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/legacy_config/classification.yaml +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/legacy_config/localization-e2e-ocv.yaml +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/legacy_config/localization-json_calf_resnetpca512.yaml +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/legacy_config/localization-json_netvlad++_resnetpca512.yaml +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/legacy_config/localization.yaml +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/legacy_config/sngar-frames.yaml +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/legacy_config/sngar-tracking.yaml +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/metrics/classification_metric.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/metrics/localization_metric.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/models/__init__.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/models/backbones/builder.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/models/base/contextaware.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/models/base/e2e.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/models/base/learnablepooling.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/models/base/tracking.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/models/base/vars.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/models/base/video.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/models/base/video_mae.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/models/heads/builder.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/models/neck/builder.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/models/utils/common.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/models/utils/impl/__init__.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/models/utils/impl/asformer.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/models/utils/impl/calf.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/models/utils/impl/gsm.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/models/utils/impl/gtad.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/models/utils/impl/tsm.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/models/utils/litebase.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/models/utils/modules.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/models/utils/shift.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/models/utils/utils.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/tools/__init__.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/tools/_common.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/tools/hf_transfer.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/tools/osl_json_to_parquet.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib/tools/parquet_to_osl_json.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib.egg-info/dependency_links.txt +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib.egg-info/entry_points.txt +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib.egg-info/requires.txt +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/opensportslib.egg-info/top_level.txt +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/setup.cfg +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/tests/test_classification_dataset_paths.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/tests/test_classification_trainer_dataloader.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/tests/test_config_split_override_sync.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/tests/test_conversion_tools.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/tests/test_hf_transfer_tools.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/tests/test_localization_dali_filenames.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/tests/test_package_smoke.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/tests/test_pretrained_config_merge_policy.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/tests/test_subset_train_infer_integration.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/tools/convert/build_soccernet_gar.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/tools/convert/build_soccernet_gar_action_spotting.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/tools/convert/osl_json_to_parquet_webdataset.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/tools/convert/parquet_webdataset_to_osl_json.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/tools/download/download_hf_repo.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/tools/download/download_osl_hf.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/tools/download/upload_osl_hf.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/tools/training/classification.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev2}/tools/training/localization.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: opensportslib
|
|
3
|
-
Version: 0.2.0.
|
|
3
|
+
Version: 0.2.0.dev2
|
|
4
4
|
Summary: OpenSportsLib is the professional library, designed for advanced video understanding in sports. It provides state-of-the-art tools for action recognition, spotting, retrieval, and captioning, making it ideal for researchers, analysts, and developers working with sports video data.
|
|
5
5
|
Author: Jeet Vora
|
|
6
6
|
Requires-Python: >=3.12
|
|
@@ -42,6 +42,7 @@ It provides a unified framework to **train, evaluate, and run inference** for ke
|
|
|
42
42
|
|
|
43
43
|
- **Action classification**
|
|
44
44
|
- **Action localization / spotting**
|
|
45
|
+
- **Visual Question Answering (VQA)**
|
|
45
46
|
- **Action retrieval**
|
|
46
47
|
- **Action description / captioning**
|
|
47
48
|
|
|
@@ -280,6 +281,32 @@ metrics_from_file = my_model.evaluate(
|
|
|
280
281
|
)
|
|
281
282
|
```
|
|
282
283
|
|
|
284
|
+
### VQA example
|
|
285
|
+
|
|
286
|
+
```python
|
|
287
|
+
from opensportslib.apis import VQAModel
|
|
288
|
+
|
|
289
|
+
my_model = VQAModel(
|
|
290
|
+
config="/path/to/vqa.yaml",
|
|
291
|
+
weights=None, # optional: path or Hugging Face model ID
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
predictions = my_model.infer(
|
|
295
|
+
test_set="/path/to/test_annotations.json",
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
# Headless single-video VQA uses the same prediction payload shape.
|
|
299
|
+
single_prediction = my_model.infer(
|
|
300
|
+
video_path="/path/to/video.mp4",
|
|
301
|
+
question="What card would you give? Why?",
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
metrics = my_model.evaluate(
|
|
305
|
+
test_set="/path/to/test_annotations.json",
|
|
306
|
+
predictions=predictions,
|
|
307
|
+
)
|
|
308
|
+
```
|
|
309
|
+
|
|
283
310
|
|
|
284
311
|
---
|
|
285
312
|
|
|
@@ -316,6 +343,9 @@ Classify clips or event centered samples into predefined categories.
|
|
|
316
343
|
### Action Localization / Spotting
|
|
317
344
|
Predict when key events happen in long untrimmed sports videos.
|
|
318
345
|
|
|
346
|
+
### Visual Question Answering (VQA)
|
|
347
|
+
Answer natural-language questions about sports video clips.
|
|
348
|
+
|
|
319
349
|
### Action Retrieval
|
|
320
350
|
Search and retrieve relevant clips or moments from a collection of sports videos.
|
|
321
351
|
This is part of the roadmap and OSL data model, not a first-class OpenSportsLib
|
|
@@ -7,6 +7,7 @@ It provides a unified framework to **train, evaluate, and run inference** for ke
|
|
|
7
7
|
|
|
8
8
|
- **Action classification**
|
|
9
9
|
- **Action localization / spotting**
|
|
10
|
+
- **Visual Question Answering (VQA)**
|
|
10
11
|
- **Action retrieval**
|
|
11
12
|
- **Action description / captioning**
|
|
12
13
|
|
|
@@ -245,6 +246,32 @@ metrics_from_file = my_model.evaluate(
|
|
|
245
246
|
)
|
|
246
247
|
```
|
|
247
248
|
|
|
249
|
+
### VQA example
|
|
250
|
+
|
|
251
|
+
```python
|
|
252
|
+
from opensportslib.apis import VQAModel
|
|
253
|
+
|
|
254
|
+
my_model = VQAModel(
|
|
255
|
+
config="/path/to/vqa.yaml",
|
|
256
|
+
weights=None, # optional: path or Hugging Face model ID
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
predictions = my_model.infer(
|
|
260
|
+
test_set="/path/to/test_annotations.json",
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
# Headless single-video VQA uses the same prediction payload shape.
|
|
264
|
+
single_prediction = my_model.infer(
|
|
265
|
+
video_path="/path/to/video.mp4",
|
|
266
|
+
question="What card would you give? Why?",
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
metrics = my_model.evaluate(
|
|
270
|
+
test_set="/path/to/test_annotations.json",
|
|
271
|
+
predictions=predictions,
|
|
272
|
+
)
|
|
273
|
+
```
|
|
274
|
+
|
|
248
275
|
|
|
249
276
|
---
|
|
250
277
|
|
|
@@ -281,6 +308,9 @@ Classify clips or event centered samples into predefined categories.
|
|
|
281
308
|
### Action Localization / Spotting
|
|
282
309
|
Predict when key events happen in long untrimmed sports videos.
|
|
283
310
|
|
|
311
|
+
### Visual Question Answering (VQA)
|
|
312
|
+
Answer natural-language questions about sports video clips.
|
|
313
|
+
|
|
284
314
|
### Action Retrieval
|
|
285
315
|
Search and retrieve relevant clips or moments from a collection of sports videos.
|
|
286
316
|
This is part of the roadmap and OSL data model, not a first-class OpenSportsLib
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
from opensportslib.apis.base_task_model import BaseTaskModel
|
|
5
5
|
from opensportslib.apis.classification import ClassificationModel
|
|
6
6
|
from opensportslib.apis.localization import LocalizationModel
|
|
7
|
+
from opensportslib.apis.vqa import VQAModel
|
|
7
8
|
import warnings
|
|
8
9
|
warnings.filterwarnings("ignore")
|
|
9
10
|
|
|
@@ -12,4 +13,5 @@ __all__ = [
|
|
|
12
13
|
"BaseTaskModel",
|
|
13
14
|
"ClassificationModel",
|
|
14
15
|
"LocalizationModel",
|
|
16
|
+
"VQAModel",
|
|
15
17
|
]
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
"""Public API for VQA tasks."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import os
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from opensportslib.apis.base_task_model import BaseTaskModel
|
|
10
|
+
from opensportslib.core.config.accessors import get_split_annotation_path, get_system_gpu_count, get_train_execution, get_vqa_backend
|
|
11
|
+
from opensportslib.core.utils.config import expand, resolve_config_omega
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _set_model_checkpoint_path(config, weights: str | None) -> None:
|
|
15
|
+
if weights is None:
|
|
16
|
+
return
|
|
17
|
+
model = getattr(config, "MODEL", None)
|
|
18
|
+
if model is None:
|
|
19
|
+
return
|
|
20
|
+
load = getattr(model, "load", None)
|
|
21
|
+
if load is None:
|
|
22
|
+
from types import SimpleNamespace
|
|
23
|
+
|
|
24
|
+
load = SimpleNamespace()
|
|
25
|
+
setattr(model, "load", load)
|
|
26
|
+
if isinstance(load, dict):
|
|
27
|
+
load["checkpoint_path"] = weights
|
|
28
|
+
else:
|
|
29
|
+
setattr(load, "checkpoint_path", weights)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class VQAModel(BaseTaskModel):
|
|
33
|
+
"""Top-level task wrapper for VQA."""
|
|
34
|
+
|
|
35
|
+
def _init_wandb(self, use_wandb: bool) -> None:
|
|
36
|
+
from opensportslib.core.utils.wandb import init_wandb
|
|
37
|
+
|
|
38
|
+
init_wandb(
|
|
39
|
+
self.config_path,
|
|
40
|
+
self.config,
|
|
41
|
+
run_id=os.environ["RUN_ID"],
|
|
42
|
+
use_wandb=use_wandb,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
@staticmethod
|
|
46
|
+
def _worker_ddp(
|
|
47
|
+
rank,
|
|
48
|
+
world_size,
|
|
49
|
+
config_path,
|
|
50
|
+
config,
|
|
51
|
+
return_queue=None,
|
|
52
|
+
train_set=None,
|
|
53
|
+
valid_set=None,
|
|
54
|
+
use_wandb=False,
|
|
55
|
+
):
|
|
56
|
+
import torch
|
|
57
|
+
from opensportslib.core.trainer.vqa_trainer import Trainer_VQA
|
|
58
|
+
from opensportslib.core.utils.ddp import ddp_cleanup, ddp_setup
|
|
59
|
+
from opensportslib.core.utils.wandb import init_wandb
|
|
60
|
+
from opensportslib.datasets.builder import build_dataset
|
|
61
|
+
|
|
62
|
+
logging.basicConfig(
|
|
63
|
+
level=logging.INFO,
|
|
64
|
+
format=f"[RANK {rank}] %(asctime)s | %(levelname)s | %(message)s",
|
|
65
|
+
force=True,
|
|
66
|
+
)
|
|
67
|
+
if rank != 0:
|
|
68
|
+
logging.getLogger().setLevel(logging.ERROR)
|
|
69
|
+
# Keep INFO on all ranks while debugging multi-GPU startup/hangs.
|
|
70
|
+
# This makes DDP failures visible instead of appearing as a silent stall.
|
|
71
|
+
#logging.getLogger().setLevel(logging.INFO)
|
|
72
|
+
|
|
73
|
+
is_ddp = world_size > 1
|
|
74
|
+
if is_ddp:
|
|
75
|
+
os.environ["RANK"] = str(rank)
|
|
76
|
+
os.environ["WORLD_SIZE"] = str(world_size)
|
|
77
|
+
os.environ["LOCAL_RANK"] = str(rank)
|
|
78
|
+
if "TORCH_DISTRIBUTED_DEBUG" not in os.environ:
|
|
79
|
+
os.environ["TORCH_DISTRIBUTED_DEBUG"] = "INFO"
|
|
80
|
+
torch.cuda.set_device(rank)
|
|
81
|
+
ddp_setup(rank, world_size)
|
|
82
|
+
logging.info(
|
|
83
|
+
"Initialized VQA DDP worker | rank=%s | world_size=%s | torch_distributed_debug=%s",
|
|
84
|
+
rank,
|
|
85
|
+
world_size,
|
|
86
|
+
os.environ.get("TORCH_DISTRIBUTED_DEBUG"),
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
try:
|
|
90
|
+
if rank == 0:
|
|
91
|
+
init_wandb(
|
|
92
|
+
config_path,
|
|
93
|
+
config,
|
|
94
|
+
run_id=os.environ["RUN_ID"],
|
|
95
|
+
use_wandb=use_wandb,
|
|
96
|
+
)
|
|
97
|
+
train_data = build_dataset(config, train_set, None, split="train")
|
|
98
|
+
valid_data = build_dataset(config, valid_set, None, split="valid")
|
|
99
|
+
trainer = Trainer_VQA(config)
|
|
100
|
+
ckpt = trainer.train(
|
|
101
|
+
None,
|
|
102
|
+
train_data,
|
|
103
|
+
valid_data,
|
|
104
|
+
rank=rank,
|
|
105
|
+
world_size=world_size,
|
|
106
|
+
use_wandb=use_wandb,
|
|
107
|
+
)
|
|
108
|
+
if rank == 0 and return_queue is not None:
|
|
109
|
+
return_queue.put(ckpt)
|
|
110
|
+
finally:
|
|
111
|
+
if is_ddp:
|
|
112
|
+
ddp_cleanup()
|
|
113
|
+
|
|
114
|
+
def _resolve_split_path(self, split: str, override: str | None = None) -> str:
|
|
115
|
+
if override is not None:
|
|
116
|
+
return expand(override)
|
|
117
|
+
path = get_split_annotation_path(self.config, split)
|
|
118
|
+
if not path:
|
|
119
|
+
raise ValueError(
|
|
120
|
+
f"Could not resolve path for split '{split}'. "
|
|
121
|
+
f"Expected DATA.common.splits.{split}.annotation_path."
|
|
122
|
+
)
|
|
123
|
+
return expand(path)
|
|
124
|
+
|
|
125
|
+
def load_weights(self, weights: str | None = None, **kwargs) -> None:
|
|
126
|
+
del kwargs
|
|
127
|
+
if weights is None:
|
|
128
|
+
raise ValueError("`weights` must be provided to load_weights().")
|
|
129
|
+
from opensportslib.core.trainer.vqa_trainer import Trainer_VQA
|
|
130
|
+
|
|
131
|
+
_set_model_checkpoint_path(self.config, weights)
|
|
132
|
+
self.trainer = Trainer_VQA(self.config)
|
|
133
|
+
self.trainer.load(weights)
|
|
134
|
+
self.last_loaded_weights = weights
|
|
135
|
+
self.best_checkpoint = weights
|
|
136
|
+
|
|
137
|
+
def train(
|
|
138
|
+
self,
|
|
139
|
+
train_set: str | None = None,
|
|
140
|
+
valid_set: str | None = None,
|
|
141
|
+
weights: str | None = None,
|
|
142
|
+
use_wandb: bool = True,
|
|
143
|
+
**kwargs,
|
|
144
|
+
) -> str | None:
|
|
145
|
+
del kwargs
|
|
146
|
+
import torch
|
|
147
|
+
import torch.multiprocessing as mp
|
|
148
|
+
|
|
149
|
+
self.config = resolve_config_omega(self.config, weights=weights)
|
|
150
|
+
if get_vqa_backend(self.config) == "qwen_xvars_infer":
|
|
151
|
+
raise ValueError("The 'qwen_xvars_infer' backend is inference-only and does not support train().")
|
|
152
|
+
train_set = self._resolve_split_path("train", train_set)
|
|
153
|
+
valid_set = self._resolve_split_path("valid", valid_set)
|
|
154
|
+
execution = get_train_execution(self.config)
|
|
155
|
+
backend = str(execution.get("training_backend", "placeholder")).lower()
|
|
156
|
+
if backend == "xvars_videochatgpt_lora":
|
|
157
|
+
world_size = torch.cuda.device_count() or get_system_gpu_count(self.config)
|
|
158
|
+
requested_gpus = get_system_gpu_count(self.config)
|
|
159
|
+
use_ddp = world_size > 1 and int(requested_gpus) > 1
|
|
160
|
+
logging.info(
|
|
161
|
+
"VQA train launch | mode=%s | world_size=%s",
|
|
162
|
+
"ddp" if use_ddp else "single",
|
|
163
|
+
world_size if use_ddp else 1,
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
ctx = mp.get_context("spawn")
|
|
167
|
+
queue = ctx.SimpleQueue()
|
|
168
|
+
if use_ddp:
|
|
169
|
+
mp.spawn(
|
|
170
|
+
VQAModel._worker_ddp,
|
|
171
|
+
args=(world_size, self.config_path, self.config, queue, train_set, valid_set, use_wandb),
|
|
172
|
+
nprocs=world_size,
|
|
173
|
+
)
|
|
174
|
+
else:
|
|
175
|
+
VQAModel._worker_ddp(
|
|
176
|
+
rank=0,
|
|
177
|
+
world_size=1,
|
|
178
|
+
config_path=self.config_path,
|
|
179
|
+
config=self.config,
|
|
180
|
+
return_queue=queue,
|
|
181
|
+
train_set=train_set,
|
|
182
|
+
valid_set=valid_set,
|
|
183
|
+
use_wandb=use_wandb,
|
|
184
|
+
)
|
|
185
|
+
ckpt = queue.get()
|
|
186
|
+
self.best_checkpoint = ckpt
|
|
187
|
+
self.last_loaded_weights = ckpt
|
|
188
|
+
return ckpt
|
|
189
|
+
|
|
190
|
+
raise ValueError(
|
|
191
|
+
f"Unsupported VQA training backend '{backend}'. "
|
|
192
|
+
"Only 'xvars_videochatgpt_lora' is supported."
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
def infer(
|
|
196
|
+
self,
|
|
197
|
+
test_set: str | None = None,
|
|
198
|
+
weights: str | None = None,
|
|
199
|
+
use_wandb: bool = True,
|
|
200
|
+
video_path: str | None = None,
|
|
201
|
+
question: str | None = None,
|
|
202
|
+
**kwargs,
|
|
203
|
+
) -> dict:
|
|
204
|
+
del kwargs
|
|
205
|
+
from opensportslib.core.trainer.vqa_trainer import Trainer_VQA
|
|
206
|
+
from opensportslib.datasets.builder import build_dataset
|
|
207
|
+
from opensportslib.models.builder import build_model
|
|
208
|
+
from opensportslib.core.utils.config import select_device
|
|
209
|
+
|
|
210
|
+
direct_requested = video_path is not None or question is not None
|
|
211
|
+
if direct_requested and test_set is not None:
|
|
212
|
+
raise ValueError("Provide either `test_set` or `video_path`/`question`, not both.")
|
|
213
|
+
if direct_requested and (not video_path or not str(question or "").strip()):
|
|
214
|
+
raise ValueError("Direct VQA inference requires both `video_path` and a non-empty `question`.")
|
|
215
|
+
|
|
216
|
+
self.config = resolve_config_omega(self.config, weights=weights)
|
|
217
|
+
backend = get_vqa_backend(self.config)
|
|
218
|
+
effective_weights = weights if weights is not None else self.last_loaded_weights
|
|
219
|
+
if backend == "qwen_xvars_infer" and effective_weights is not None:
|
|
220
|
+
raise ValueError("The 'qwen_xvars_infer' backend does not support adapter weights for infer().")
|
|
221
|
+
_set_model_checkpoint_path(self.config, effective_weights)
|
|
222
|
+
self.trainer = Trainer_VQA(self.config)
|
|
223
|
+
if effective_weights is not None:
|
|
224
|
+
# Validate OpenSportsLib adapter metadata before allocating the base model.
|
|
225
|
+
self.trainer.load(effective_weights)
|
|
226
|
+
resolved_video_path = None
|
|
227
|
+
if direct_requested:
|
|
228
|
+
resolved_video_path = expand(str(video_path))
|
|
229
|
+
if not os.path.isfile(resolved_video_path):
|
|
230
|
+
raise FileNotFoundError(f"Video file not found: {resolved_video_path}")
|
|
231
|
+
device = select_device(self.config.SYSTEM)
|
|
232
|
+
model, _ = build_model(self.config, device)
|
|
233
|
+
if direct_requested:
|
|
234
|
+
test_data = [
|
|
235
|
+
{
|
|
236
|
+
"id": os.path.splitext(os.path.basename(resolved_video_path))[0],
|
|
237
|
+
"question": str(question).strip(),
|
|
238
|
+
"references": [],
|
|
239
|
+
"video_path": resolved_video_path,
|
|
240
|
+
"video_spatio_temporal_features": None,
|
|
241
|
+
"prior_prediction_text": "",
|
|
242
|
+
"labels": {},
|
|
243
|
+
"metadata": {},
|
|
244
|
+
"_xvars_demo_parity_direct_infer": True,
|
|
245
|
+
}
|
|
246
|
+
]
|
|
247
|
+
else:
|
|
248
|
+
test_set = self._resolve_split_path("test", test_set)
|
|
249
|
+
test_data = build_dataset(self.config, test_set, None, split="test")
|
|
250
|
+
self._init_wandb(use_wandb=use_wandb)
|
|
251
|
+
return self.trainer.infer(model, test_data, use_wandb=use_wandb)
|
|
252
|
+
|
|
253
|
+
def evaluate(
|
|
254
|
+
self,
|
|
255
|
+
test_set: str | None = None,
|
|
256
|
+
weights: str | None = None,
|
|
257
|
+
predictions: str | dict | None = None,
|
|
258
|
+
use_wandb: bool = True,
|
|
259
|
+
**kwargs,
|
|
260
|
+
) -> dict | str | None:
|
|
261
|
+
del kwargs
|
|
262
|
+
from opensportslib.core.trainer.vqa_trainer import Trainer_VQA
|
|
263
|
+
from opensportslib.datasets.builder import build_dataset
|
|
264
|
+
|
|
265
|
+
self.config = resolve_config_omega(self.config, weights=weights)
|
|
266
|
+
test_set = self._resolve_split_path("test", test_set)
|
|
267
|
+
test_data = build_dataset(self.config, test_set, None, split="test")
|
|
268
|
+
self._init_wandb(use_wandb=use_wandb)
|
|
269
|
+
if predictions is None:
|
|
270
|
+
predictions = self.infer(test_set=test_set, weights=weights, use_wandb=use_wandb)
|
|
271
|
+
elif isinstance(predictions, str):
|
|
272
|
+
import json
|
|
273
|
+
|
|
274
|
+
with open(expand(predictions), encoding="utf-8") as f:
|
|
275
|
+
predictions = json.load(f)
|
|
276
|
+
self.trainer = self.trainer or Trainer_VQA(self.config)
|
|
277
|
+
return self.trainer.evaluate(predictions, test_data, use_wandb=use_wandb)
|
|
278
|
+
|
|
279
|
+
def save_predictions(
|
|
280
|
+
self,
|
|
281
|
+
output_path: str,
|
|
282
|
+
predictions: dict,
|
|
283
|
+
output_format: str = "osl",
|
|
284
|
+
) -> str:
|
|
285
|
+
"""Persist VQA predictions, optionally as X-VARS-compatible rows."""
|
|
286
|
+
|
|
287
|
+
if str(output_format).lower() != "xvars":
|
|
288
|
+
return super().save_predictions(output_path, predictions)
|
|
289
|
+
|
|
290
|
+
import json
|
|
291
|
+
|
|
292
|
+
payload = self._to_xvars_prediction_rows(predictions)
|
|
293
|
+
dst = expand(output_path)
|
|
294
|
+
os.makedirs(os.path.dirname(dst) or ".", exist_ok=True)
|
|
295
|
+
with open(dst, "w", encoding="utf-8") as f:
|
|
296
|
+
json.dump(payload, f)
|
|
297
|
+
return dst
|
|
298
|
+
|
|
299
|
+
@staticmethod
|
|
300
|
+
def _to_xvars_prediction_rows(predictions: dict[str, Any]) -> list[dict[str, Any]]:
|
|
301
|
+
rows = []
|
|
302
|
+
for item in predictions.get("data", []) if isinstance(predictions, dict) else []:
|
|
303
|
+
video_path = str(item.get("video_path") or "")
|
|
304
|
+
video_name = os.path.splitext(os.path.basename(video_path))[0] if video_path else str(item.get("id"))
|
|
305
|
+
rows.append(
|
|
306
|
+
{
|
|
307
|
+
"id": item.get("id"),
|
|
308
|
+
"video_name": video_name,
|
|
309
|
+
"Q": item.get("question"),
|
|
310
|
+
"pred": item.get("answer_text"),
|
|
311
|
+
}
|
|
312
|
+
)
|
|
313
|
+
return rows
|
|
@@ -11,6 +11,8 @@ def main(argv: Optional[list[str]] = None) -> int:
|
|
|
11
11
|
parser.add_argument("command", choices=["setup"])
|
|
12
12
|
parser.add_argument("--pyg", action="store_true")
|
|
13
13
|
parser.add_argument("--dali", action="store_true")
|
|
14
|
+
parser.add_argument("--xvars", action="store_true")
|
|
15
|
+
parser.add_argument("--qwen", action="store_true")
|
|
14
16
|
|
|
15
17
|
args = parser.parse_args(argv)
|
|
16
18
|
|
|
@@ -18,6 +20,8 @@ def main(argv: Optional[list[str]] = None) -> int:
|
|
|
18
20
|
setup(
|
|
19
21
|
pyg=args.pyg,
|
|
20
22
|
dali=args.dali,
|
|
23
|
+
xvars=args.xvars,
|
|
24
|
+
qwen=args.qwen
|
|
21
25
|
)
|
|
22
26
|
return 0
|
|
23
27
|
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
TASK: VQA
|
|
2
|
+
VERSION: 2
|
|
3
|
+
|
|
4
|
+
SYSTEM:
|
|
5
|
+
paths:
|
|
6
|
+
log_dir: ./logs
|
|
7
|
+
save_dir: ./checkpoints_vqa_qwen
|
|
8
|
+
work_dir: ./checkpoints_vqa_qwen
|
|
9
|
+
device: cuda
|
|
10
|
+
gpu:
|
|
11
|
+
count: 1
|
|
12
|
+
id: 0
|
|
13
|
+
reproducibility:
|
|
14
|
+
use_seed: true
|
|
15
|
+
seed: 42
|
|
16
|
+
|
|
17
|
+
DATA:
|
|
18
|
+
common:
|
|
19
|
+
dataset_name: OSL-XFoul
|
|
20
|
+
data_root: /home/vorajv/dataset/OSL-XFoul
|
|
21
|
+
feature_index: /home/vorajv/dataset/OSL-XFoul/feature_index.json
|
|
22
|
+
prediction_index: /home/vorajv/dataset/OSL-XFoul/prediction_index.json
|
|
23
|
+
runtime:
|
|
24
|
+
loader_backend: opencv
|
|
25
|
+
splits:
|
|
26
|
+
train:
|
|
27
|
+
annotation_path: /home/vorajv/dataset/OSL-XFoul/train.json
|
|
28
|
+
source_path: /home/vorajv/dataset/OSL-XFoul
|
|
29
|
+
dataloader:
|
|
30
|
+
batch_size: 1
|
|
31
|
+
shuffle: true
|
|
32
|
+
num_workers: 0
|
|
33
|
+
pin_memory: false
|
|
34
|
+
mp_context: spawn
|
|
35
|
+
persistent_workers: false
|
|
36
|
+
valid:
|
|
37
|
+
annotation_path: /home/vorajv/dataset/OSL-XFoul/valid.json
|
|
38
|
+
source_path: /home/vorajv/dataset/OSL-XFoul
|
|
39
|
+
dataloader:
|
|
40
|
+
batch_size: 1
|
|
41
|
+
shuffle: false
|
|
42
|
+
num_workers: 0
|
|
43
|
+
pin_memory: false
|
|
44
|
+
mp_context: spawn
|
|
45
|
+
persistent_workers: false
|
|
46
|
+
test:
|
|
47
|
+
annotation_path: /home/vorajv/dataset/OSL-XFoul/test.json
|
|
48
|
+
source_path: /home/vorajv/dataset/OSL-XFoul
|
|
49
|
+
dataloader:
|
|
50
|
+
batch_size: 1
|
|
51
|
+
shuffle: false
|
|
52
|
+
num_workers: 0
|
|
53
|
+
pin_memory: false
|
|
54
|
+
mp_context: spawn
|
|
55
|
+
persistent_workers: false
|
|
56
|
+
inputs:
|
|
57
|
+
video:
|
|
58
|
+
modality: video
|
|
59
|
+
representation: raw
|
|
60
|
+
source:
|
|
61
|
+
format: mp4
|
|
62
|
+
sampling:
|
|
63
|
+
num_frames: 100
|
|
64
|
+
input_fps: 25
|
|
65
|
+
target_fps: 17
|
|
66
|
+
start_frame: 63
|
|
67
|
+
end_frame: 87
|
|
68
|
+
transform: {}
|
|
69
|
+
augmentations: {}
|
|
70
|
+
params: {}
|
|
71
|
+
question:
|
|
72
|
+
modality: text
|
|
73
|
+
representation: raw
|
|
74
|
+
source:
|
|
75
|
+
format: json
|
|
76
|
+
sampling: {}
|
|
77
|
+
transform: {}
|
|
78
|
+
augmentations: {}
|
|
79
|
+
params: {}
|
|
80
|
+
|
|
81
|
+
MODEL:
|
|
82
|
+
runtime:
|
|
83
|
+
dtype: fp16
|
|
84
|
+
device: auto
|
|
85
|
+
compile: false
|
|
86
|
+
freeze: false
|
|
87
|
+
load:
|
|
88
|
+
checkpoint_path: null
|
|
89
|
+
pretrained: false
|
|
90
|
+
strict: true
|
|
91
|
+
map_location: null
|
|
92
|
+
format: auto
|
|
93
|
+
components:
|
|
94
|
+
video_encoder:
|
|
95
|
+
kind: encoder
|
|
96
|
+
source:
|
|
97
|
+
provider: opensportslib
|
|
98
|
+
name: xvars_clip_features
|
|
99
|
+
load:
|
|
100
|
+
weights_path: /home/vorajv/X-VARS/weights/14_model.pth.tar
|
|
101
|
+
params:
|
|
102
|
+
feature_source: indexed_or_raw_clip
|
|
103
|
+
vision_tower: openai/clip-vit-large-patch14
|
|
104
|
+
feature_dim: 1024
|
|
105
|
+
overrides: {}
|
|
106
|
+
mm_projector:
|
|
107
|
+
kind: projector
|
|
108
|
+
source:
|
|
109
|
+
provider: opensportslib
|
|
110
|
+
params:
|
|
111
|
+
input_dim: 1024
|
|
112
|
+
overrides: {}
|
|
113
|
+
llm_decoder:
|
|
114
|
+
kind: decoder
|
|
115
|
+
source:
|
|
116
|
+
provider: huggingface
|
|
117
|
+
# Qwen/Qwen2.5-7B-Instruct or Qwen/Qwen3.5-9B-Base
|
|
118
|
+
name: Qwen/Qwen3.5-9B-Base
|
|
119
|
+
params:
|
|
120
|
+
# Qwen/Qwen2.5-7B-Instruct or Qwen/Qwen3.5-9B-Base
|
|
121
|
+
repo_id: Qwen/Qwen3.5-9B-Base
|
|
122
|
+
overrides: {}
|
|
123
|
+
topology:
|
|
124
|
+
- from: video_encoder
|
|
125
|
+
to: mm_projector
|
|
126
|
+
- from: mm_projector
|
|
127
|
+
to: llm_decoder
|
|
128
|
+
metadata:
|
|
129
|
+
backend: qwen_xvars_infer
|
|
130
|
+
|
|
131
|
+
IO:
|
|
132
|
+
inputs:
|
|
133
|
+
video: video_encoder
|
|
134
|
+
question: llm_decoder
|
|
135
|
+
outputs:
|
|
136
|
+
answer_text: llm_decoder
|
|
137
|
+
explanation_text: llm_decoder
|
|
138
|
+
|
|
139
|
+
TRAIN:
|
|
140
|
+
trainer:
|
|
141
|
+
type: vqa
|
|
142
|
+
|
|
143
|
+
epochs: 3
|
|
144
|
+
|
|
145
|
+
criterion:
|
|
146
|
+
type: CrossEntropyLoss
|
|
147
|
+
|
|
148
|
+
optimizer:
|
|
149
|
+
type: AdamW
|
|
150
|
+
lr: 0.0002
|
|
151
|
+
weight_decay: 0.001
|
|
152
|
+
|
|
153
|
+
scheduler:
|
|
154
|
+
type: constant
|
|
155
|
+
|
|
156
|
+
execution:
|
|
157
|
+
enabled: true
|
|
158
|
+
training_backend: xvars_videochatgpt_lora
|
|
159
|
+
feature_backend: xvars_clip
|
|
160
|
+
view_sampling_policy: random_train_deterministic_eval
|
|
161
|
+
acc_grad_iter: 8
|
|
162
|
+
log_interval: 1
|
|
163
|
+
dry_run: false
|
|
164
|
+
|
|
165
|
+
xvars:
|
|
166
|
+
feature_mode: strict_xvars
|
|
167
|
+
projection_path: null
|
|
168
|
+
|
|
169
|
+
prompt:
|
|
170
|
+
style: detailed
|
|
171
|
+
system_prompt: You are a football video assistant. Answer the VQA question using the provided video context and referee priors.
|
|
172
|
+
include_priors: true
|
|
173
|
+
prediction_prior_adapter: xvars_referee
|
|
174
|
+
prior_fields: [action, offence, contact, bodypart]
|
|
175
|
+
video_token_len: 300
|
|
176
|
+
|
|
177
|
+
generation:
|
|
178
|
+
max_new_tokens: 128
|
|
179
|
+
temperature: 0.0
|
|
180
|
+
|
|
181
|
+
eval_profile:
|
|
182
|
+
metric_set: [exact_match, contains_match, token_f1, referee_semantic]
|
|
183
|
+
aggregation: mean
|
|
184
|
+
exclusions: []
|
|
185
|
+
|
|
186
|
+
sft:
|
|
187
|
+
max_seq_length: 480
|
|
188
|
+
include_video_tokens: true
|
|
189
|
+
disable_tqdm: false
|
|
190
|
+
reference_mode: all
|
|
191
|
+
append_eos_token: true
|
|
192
|
+
gradient_checkpointing: true
|
|
193
|
+
save_strategy: epoch
|
|
194
|
+
|
|
195
|
+
hf:
|
|
196
|
+
tokenizer_id: ${MODEL.components.llm_decoder.params.repo_id}
|
|
197
|
+
prefer_cuda: true
|
|
198
|
+
local_files_only: false
|
|
199
|
+
device_map: auto
|
|
200
|
+
offload_folder: ./hf_offload_qwen
|
|
201
|
+
|
|
202
|
+
lora:
|
|
203
|
+
r: 16
|
|
204
|
+
alpha: 32
|
|
205
|
+
dropout: 0.05
|
|
206
|
+
bias: none
|
|
207
|
+
prepare_kbit: true
|
|
208
|
+
target_modules: [mm_projector, upsample_features, up_proj, down_proj, gate_proj, k_proj, q_proj, v_proj, o_proj]
|
|
209
|
+
exclude_modules: '^base_lm\.model\.mm_projector$'
|
|
210
|
+
|
|
211
|
+
quantization:
|
|
212
|
+
enabled: false
|
|
213
|
+
load_in_4bit: true
|
|
214
|
+
bnb_4bit_quant_type: nf4
|
|
215
|
+
compute_dtype: float16
|
|
216
|
+
bnb_4bit_use_double_quant: true
|
|
217
|
+
|
|
218
|
+
checkpoint:
|
|
219
|
+
save_adapter: true
|
|
220
|
+
merge_and_save: false
|
|
221
|
+
|
|
222
|
+
selection:
|
|
223
|
+
monitor: loss
|
|
224
|
+
mode: min
|
|
225
|
+
|
|
226
|
+
checkpoint:
|
|
227
|
+
save_every: 1
|
|
228
|
+
save_best: true
|