opensportslib 0.2.0.dev1__tar.gz → 0.2.0.dev3__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.dev3}/PKG-INFO +58 -2
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/README.md +57 -1
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/apis/__init__.py +2 -0
- opensportslib-0.2.0.dev3/opensportslib/apis/vqa.py +313 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/cli.py +4 -0
- opensportslib-0.2.0.dev3/opensportslib/configs/vqa/qwen.yaml +49 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/core/config/accessors.py +234 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/core/config/loader.py +1 -1
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/core/config/validate.py +15 -0
- opensportslib-0.2.0.dev3/opensportslib/core/trainer/vqa_trainer.py +797 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/core/utils/config.py +6 -2
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/core/utils/ddp.py +3 -2
- opensportslib-0.2.0.dev3/opensportslib/core/utils/hf_runtime.py +524 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/datasets/builder.py +4 -0
- opensportslib-0.2.0.dev3/opensportslib/datasets/vqa_dataset.py +165 -0
- opensportslib-0.2.0.dev3/opensportslib/metrics/vqa_metric.py +217 -0
- opensportslib-0.2.0.dev3/opensportslib/models/base/qwen_xvars.py +476 -0
- opensportslib-0.2.0.dev3/opensportslib/models/base/video_chatgpt_compat.py +298 -0
- opensportslib-0.2.0.dev3/opensportslib/models/base/xvars_videochatgpt.py +1115 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/models/builder.py +34 -0
- opensportslib-0.2.0.dev3/opensportslib/models/utils/vqa_prediction_priors.py +105 -0
- opensportslib-0.2.0.dev3/opensportslib/models/utils/vqa_prompting.py +56 -0
- opensportslib-0.2.0.dev3/opensportslib/models/utils/xvars_clip_index.py +99 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/setup/setup.py +36 -2
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3/opensportslib.egg-info}/PKG-INFO +58 -2
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib.egg-info/SOURCES.txt +18 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/pyproject.toml +1 -1
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/tests/conftest.py +140 -1
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/tests/test_config_architecture.py +46 -11
- opensportslib-0.2.0.dev3/tests/test_config_utils_smoke.py +84 -0
- opensportslib-0.2.0.dev3/tests/test_extract_xvars_features.py +134 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/tests/test_public_apis_smoke.py +15 -0
- opensportslib-0.2.0.dev3/tests/test_task_model_api_contract.py +919 -0
- opensportslib-0.2.0.dev3/tests/test_vqa_metrics_semantic.py +122 -0
- opensportslib-0.2.0.dev3/tests/test_vqa_training_lora.py +563 -0
- opensportslib-0.2.0.dev3/tests/test_vqa_xvars_videochatgpt.py +1387 -0
- opensportslib-0.2.0.dev3/tools/convert/build_xvars_indexes.py +200 -0
- opensportslib-0.2.0.dev3/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.dev3}/LICENSE +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/LICENSE-COMMERCIAL +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/MANIFEST.in +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/examples/quickstart/basic_classification.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/examples/quickstart/basic_localization.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/__init__.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/apis/base_task_model.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/apis/classification.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/apis/localization.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/configs/classification/default.yaml +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/configs/classification/sngar_frames.yaml +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/configs/classification/sngar_tracking.yaml +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/configs/classification/video.yaml +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/configs/default.yaml +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/configs/localization/calf_resnetpca512.yaml +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/configs/localization/default.yaml +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/configs/localization/netvladpp_resnetpca512.yaml +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/configs/localization/video_dali.yaml +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/configs/localization/video_ocv.yaml +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/core/__init__.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/core/config/__init__.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/core/config/conflicts.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/core/config/migrate.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/core/config/migrations/__init__.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/core/config/migrations/legacy_to_canonical.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/core/config/runtime_adapter.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/core/config/schema.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/core/config/schemas/__init__.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/core/config/schemas/schema_canonical.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/core/config/schemas/schema_legacy.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/core/loss/__init__.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/core/loss/builder.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/core/loss/calf.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/core/loss/ce.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/core/loss/combine.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/core/loss/nll.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/core/optimizer/__init__.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/core/optimizer/builder.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/core/sampler/weighted_sampler.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/core/scheduler/__init__.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/core/scheduler/builder.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/core/trainer/__init__.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/core/trainer/classification_trainer.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/core/trainer/localization_trainer.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/core/utils/checkpoint.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/core/utils/config_normalize.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/core/utils/data.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/core/utils/default_args.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/core/utils/lightning.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/core/utils/load_annotations.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/core/utils/seed.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/core/utils/video_processing.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/core/utils/wandb.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/datasets/__init__.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/datasets/classification_dataset.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/datasets/localization_dataset.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/datasets/utils/__init__.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/datasets/utils/tracking.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/legacy_config/classification.yaml +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/legacy_config/localization-e2e-ocv.yaml +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/legacy_config/localization-json_calf_resnetpca512.yaml +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/legacy_config/localization-json_netvlad++_resnetpca512.yaml +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/legacy_config/localization.yaml +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/legacy_config/sngar-frames.yaml +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/legacy_config/sngar-tracking.yaml +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/metrics/classification_metric.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/metrics/localization_metric.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/models/__init__.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/models/backbones/builder.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/models/base/contextaware.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/models/base/e2e.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/models/base/learnablepooling.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/models/base/tracking.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/models/base/vars.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/models/base/video.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/models/base/video_mae.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/models/heads/builder.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/models/neck/builder.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/models/utils/common.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/models/utils/impl/__init__.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/models/utils/impl/asformer.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/models/utils/impl/calf.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/models/utils/impl/gsm.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/models/utils/impl/gtad.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/models/utils/impl/tsm.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/models/utils/litebase.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/models/utils/modules.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/models/utils/shift.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/models/utils/utils.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/tools/__init__.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/tools/_common.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/tools/hf_transfer.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/tools/osl_json_to_parquet.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib/tools/parquet_to_osl_json.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib.egg-info/dependency_links.txt +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib.egg-info/entry_points.txt +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib.egg-info/requires.txt +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/opensportslib.egg-info/top_level.txt +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/setup.cfg +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/tests/test_classification_dataset_paths.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/tests/test_classification_trainer_dataloader.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/tests/test_config_split_override_sync.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/tests/test_conversion_tools.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/tests/test_hf_transfer_tools.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/tests/test_localization_dali_filenames.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/tests/test_package_smoke.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/tests/test_pretrained_config_merge_policy.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/tests/test_subset_train_infer_integration.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/tools/convert/build_soccernet_gar.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/tools/convert/build_soccernet_gar_action_spotting.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/tools/convert/osl_json_to_parquet_webdataset.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/tools/convert/parquet_webdataset_to_osl_json.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/tools/download/download_hf_repo.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/tools/download/download_osl_hf.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/tools/download/upload_osl_hf.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/tools/training/classification.py +0 -0
- {opensportslib-0.2.0.dev1 → opensportslib-0.2.0.dev3}/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.dev3
|
|
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
|
|
|
@@ -92,13 +93,26 @@ opensportslib setup --pyg
|
|
|
92
93
|
|
|
93
94
|
# Optional: install for DALI support
|
|
94
95
|
opensportslib setup --dali
|
|
95
|
-
|
|
96
|
+
|
|
97
|
+
# Optional: install the X-VARS-compatible VQA dependency profile
|
|
98
|
+
opensportslib setup --vqa_xvars
|
|
99
|
+
|
|
100
|
+
# Optional: install the Qwen-compatible VQA dependency profile
|
|
101
|
+
opensportslib setup --vqa_qwen
|
|
102
|
+
```
|
|
96
103
|
---
|
|
97
104
|
|
|
98
105
|
**Note:**
|
|
99
106
|
Run `opensportslib setup` to automatically configure dependencies.
|
|
100
107
|
If issues occur, manually install compatible versions of `torch`, `torchvision`, and related libraries according to your CUDA version or system compatibility.
|
|
101
108
|
|
|
109
|
+
For VQA, use exactly one backend-specific dependency profile:
|
|
110
|
+
|
|
111
|
+
- `--vqa_xvars` installs the X-VARS-compatible Hugging Face stack from `XVARS_DEPENDENCY_PINS`
|
|
112
|
+
- `--vqa_qwen` installs the Qwen-compatible Hugging Face stack from `QWEN_DEPENDENCY_PINS`
|
|
113
|
+
|
|
114
|
+
The `vqa_qwen` config supports `Qwen/Qwen2.5-7B-Instruct` and `Qwen/Qwen3.5-9B-Base`.
|
|
115
|
+
|
|
102
116
|
---
|
|
103
117
|
|
|
104
118
|
## Data and pretrained models
|
|
@@ -280,6 +294,39 @@ metrics_from_file = my_model.evaluate(
|
|
|
280
294
|
)
|
|
281
295
|
```
|
|
282
296
|
|
|
297
|
+
### VQA example
|
|
298
|
+
|
|
299
|
+
```python
|
|
300
|
+
from opensportslib.apis import VQAModel
|
|
301
|
+
|
|
302
|
+
my_model = VQAModel(
|
|
303
|
+
config="opensportslib/configs/vqa/qwen.yaml",
|
|
304
|
+
weights=None, # optional: path or Hugging Face model ID
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
predictions = my_model.infer(
|
|
308
|
+
test_set="/path/to/test_annotations.json",
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
# Headless single-video VQA uses the same prediction payload shape.
|
|
312
|
+
single_prediction = my_model.infer(
|
|
313
|
+
video_path="/path/to/video.mp4",
|
|
314
|
+
question="What card would you give? Why?",
|
|
315
|
+
)
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
Use `opensportslib/configs/vqa/xvars.yaml` with `opensportslib setup --vqa_xvars`
|
|
319
|
+
for the X-VARS-compatible backend, or `opensportslib/configs/vqa/qwen.yaml` with
|
|
320
|
+
`opensportslib setup --vqa_qwen` for the Qwen-compatible backend. The Qwen
|
|
321
|
+
backend currently supports `Qwen/Qwen2.5-7B-Instruct` and
|
|
322
|
+
`Qwen/Qwen3.5-9B-Base`.
|
|
323
|
+
|
|
324
|
+
For X-VARS, `feature_source: indexed_or_raw_clip` prefers indexed CLIP features
|
|
325
|
+
when available and falls back to extracting CLIP features from raw video during
|
|
326
|
+
`infer()`. Pre-extracted features remain the preferred path for parity, speed,
|
|
327
|
+
and reproducibility. See [docs/xvars_integration_phases.md](docs/xvars_integration_phases.md)
|
|
328
|
+
for the full X-VARS setup workflow.
|
|
329
|
+
|
|
283
330
|
|
|
284
331
|
---
|
|
285
332
|
|
|
@@ -316,6 +363,9 @@ Classify clips or event centered samples into predefined categories.
|
|
|
316
363
|
### Action Localization / Spotting
|
|
317
364
|
Predict when key events happen in long untrimmed sports videos.
|
|
318
365
|
|
|
366
|
+
### Visual Question Answering (VQA)
|
|
367
|
+
Answer natural-language questions about sports video clips.
|
|
368
|
+
|
|
319
369
|
### Action Retrieval
|
|
320
370
|
Search and retrieve relevant clips or moments from a collection of sports videos.
|
|
321
371
|
This is part of the roadmap and OSL data model, not a first-class OpenSportsLib
|
|
@@ -384,6 +434,12 @@ opensportslib setup --pyg
|
|
|
384
434
|
|
|
385
435
|
# Optional: install for DALI support
|
|
386
436
|
opensportslib setup --dali
|
|
437
|
+
|
|
438
|
+
# Optional: install the X-VARS-compatible VQA dependency profile
|
|
439
|
+
opensportslib setup --vqa_xvars
|
|
440
|
+
|
|
441
|
+
# Optional: install the Qwen-compatible VQA dependency profile
|
|
442
|
+
opensportslib setup --vqa_qwen
|
|
387
443
|
```
|
|
388
444
|
|
|
389
445
|
### Git workflow
|
|
@@ -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
|
|
|
@@ -57,13 +58,26 @@ opensportslib setup --pyg
|
|
|
57
58
|
|
|
58
59
|
# Optional: install for DALI support
|
|
59
60
|
opensportslib setup --dali
|
|
60
|
-
|
|
61
|
+
|
|
62
|
+
# Optional: install the X-VARS-compatible VQA dependency profile
|
|
63
|
+
opensportslib setup --vqa_xvars
|
|
64
|
+
|
|
65
|
+
# Optional: install the Qwen-compatible VQA dependency profile
|
|
66
|
+
opensportslib setup --vqa_qwen
|
|
67
|
+
```
|
|
61
68
|
---
|
|
62
69
|
|
|
63
70
|
**Note:**
|
|
64
71
|
Run `opensportslib setup` to automatically configure dependencies.
|
|
65
72
|
If issues occur, manually install compatible versions of `torch`, `torchvision`, and related libraries according to your CUDA version or system compatibility.
|
|
66
73
|
|
|
74
|
+
For VQA, use exactly one backend-specific dependency profile:
|
|
75
|
+
|
|
76
|
+
- `--vqa_xvars` installs the X-VARS-compatible Hugging Face stack from `XVARS_DEPENDENCY_PINS`
|
|
77
|
+
- `--vqa_qwen` installs the Qwen-compatible Hugging Face stack from `QWEN_DEPENDENCY_PINS`
|
|
78
|
+
|
|
79
|
+
The `vqa_qwen` config supports `Qwen/Qwen2.5-7B-Instruct` and `Qwen/Qwen3.5-9B-Base`.
|
|
80
|
+
|
|
67
81
|
---
|
|
68
82
|
|
|
69
83
|
## Data and pretrained models
|
|
@@ -245,6 +259,39 @@ metrics_from_file = my_model.evaluate(
|
|
|
245
259
|
)
|
|
246
260
|
```
|
|
247
261
|
|
|
262
|
+
### VQA example
|
|
263
|
+
|
|
264
|
+
```python
|
|
265
|
+
from opensportslib.apis import VQAModel
|
|
266
|
+
|
|
267
|
+
my_model = VQAModel(
|
|
268
|
+
config="opensportslib/configs/vqa/qwen.yaml",
|
|
269
|
+
weights=None, # optional: path or Hugging Face model ID
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
predictions = my_model.infer(
|
|
273
|
+
test_set="/path/to/test_annotations.json",
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
# Headless single-video VQA uses the same prediction payload shape.
|
|
277
|
+
single_prediction = my_model.infer(
|
|
278
|
+
video_path="/path/to/video.mp4",
|
|
279
|
+
question="What card would you give? Why?",
|
|
280
|
+
)
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
Use `opensportslib/configs/vqa/xvars.yaml` with `opensportslib setup --vqa_xvars`
|
|
284
|
+
for the X-VARS-compatible backend, or `opensportslib/configs/vqa/qwen.yaml` with
|
|
285
|
+
`opensportslib setup --vqa_qwen` for the Qwen-compatible backend. The Qwen
|
|
286
|
+
backend currently supports `Qwen/Qwen2.5-7B-Instruct` and
|
|
287
|
+
`Qwen/Qwen3.5-9B-Base`.
|
|
288
|
+
|
|
289
|
+
For X-VARS, `feature_source: indexed_or_raw_clip` prefers indexed CLIP features
|
|
290
|
+
when available and falls back to extracting CLIP features from raw video during
|
|
291
|
+
`infer()`. Pre-extracted features remain the preferred path for parity, speed,
|
|
292
|
+
and reproducibility. See [docs/xvars_integration_phases.md](docs/xvars_integration_phases.md)
|
|
293
|
+
for the full X-VARS setup workflow.
|
|
294
|
+
|
|
248
295
|
|
|
249
296
|
---
|
|
250
297
|
|
|
@@ -281,6 +328,9 @@ Classify clips or event centered samples into predefined categories.
|
|
|
281
328
|
### Action Localization / Spotting
|
|
282
329
|
Predict when key events happen in long untrimmed sports videos.
|
|
283
330
|
|
|
331
|
+
### Visual Question Answering (VQA)
|
|
332
|
+
Answer natural-language questions about sports video clips.
|
|
333
|
+
|
|
284
334
|
### Action Retrieval
|
|
285
335
|
Search and retrieve relevant clips or moments from a collection of sports videos.
|
|
286
336
|
This is part of the roadmap and OSL data model, not a first-class OpenSportsLib
|
|
@@ -349,6 +399,12 @@ opensportslib setup --pyg
|
|
|
349
399
|
|
|
350
400
|
# Optional: install for DALI support
|
|
351
401
|
opensportslib setup --dali
|
|
402
|
+
|
|
403
|
+
# Optional: install the X-VARS-compatible VQA dependency profile
|
|
404
|
+
opensportslib setup --vqa_xvars
|
|
405
|
+
|
|
406
|
+
# Optional: install the Qwen-compatible VQA dependency profile
|
|
407
|
+
opensportslib setup --vqa_qwen
|
|
352
408
|
```
|
|
353
409
|
|
|
354
410
|
### Git workflow
|
|
@@ -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("--vqa_xvars", action="store_true")
|
|
15
|
+
parser.add_argument("--vqa_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
|
+
vqa_xvars=args.vqa_xvars,
|
|
24
|
+
vqa_qwen=args.vqa_qwen
|
|
21
25
|
)
|
|
22
26
|
return 0
|
|
23
27
|
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
SYSTEM:
|
|
2
|
+
paths:
|
|
3
|
+
save_dir: ./checkpoints_vqa_qwen
|
|
4
|
+
|
|
5
|
+
MODEL:
|
|
6
|
+
components:
|
|
7
|
+
video_encoder:
|
|
8
|
+
kind: encoder
|
|
9
|
+
source:
|
|
10
|
+
provider: opensportslib
|
|
11
|
+
name: xvars_clip_features
|
|
12
|
+
load:
|
|
13
|
+
weights_path: /home/vorajv/X-VARS/weights/14_model.pth.tar
|
|
14
|
+
params:
|
|
15
|
+
feature_source: indexed_or_raw_clip
|
|
16
|
+
vision_tower: openai/clip-vit-large-patch14
|
|
17
|
+
feature_dim: 1024
|
|
18
|
+
overrides: {}
|
|
19
|
+
mm_projector:
|
|
20
|
+
kind: projector
|
|
21
|
+
source:
|
|
22
|
+
provider: opensportslib
|
|
23
|
+
params:
|
|
24
|
+
input_dim: 1024
|
|
25
|
+
overrides: {}
|
|
26
|
+
llm_decoder:
|
|
27
|
+
kind: decoder
|
|
28
|
+
source:
|
|
29
|
+
provider: huggingface
|
|
30
|
+
# Supported models: Qwen/Qwen2.5-7B-Instruct or Qwen/Qwen3.5-9B-Base
|
|
31
|
+
name: Qwen/Qwen3.5-9B-Base
|
|
32
|
+
params:
|
|
33
|
+
# Supported models: Qwen/Qwen2.5-7B-Instruct or Qwen/Qwen3.5-9B-Base
|
|
34
|
+
repo_id: Qwen/Qwen3.5-9B-Base
|
|
35
|
+
overrides: {}
|
|
36
|
+
metadata:
|
|
37
|
+
backend: qwen_xvars_infer
|
|
38
|
+
|
|
39
|
+
TRAIN:
|
|
40
|
+
execution:
|
|
41
|
+
prompt:
|
|
42
|
+
system_prompt: You are a football video assistant. Answer the VQA question using the provided video context and referee priors.
|
|
43
|
+
|
|
44
|
+
hf:
|
|
45
|
+
tokenizer_id: ${MODEL.components.llm_decoder.params.repo_id}
|
|
46
|
+
prefer_cuda: true
|
|
47
|
+
local_files_only: false
|
|
48
|
+
device_map: auto
|
|
49
|
+
offload_folder: ./hf_offload_qwen
|